mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 08:03:14 +00:00
Polish contextual tour copy and targets
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -5363,6 +5363,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
data-contextual-tour-target="tasks-start-workspace"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
handleOpenOrUseGitHubPR(item)
|
||||
@@ -5411,6 +5412,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
data-contextual-tour-target="tasks-start-workspace"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
handleUseWorkItem(item)
|
||||
@@ -5645,6 +5647,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
data-contextual-tour-target="tasks-start-workspace"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
handleUseGitLabItem(item)
|
||||
@@ -5962,6 +5965,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
data-contextual-tour-target="tasks-start-workspace"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
handleUseLinearItem(issue)
|
||||
@@ -6185,6 +6189,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
data-contextual-tour-target="tasks-start-workspace"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
handleUseLinearItem(issue)
|
||||
|
||||
@@ -37,7 +37,7 @@ function AutoRenameBranchFromWorkControl(): JSX.Element {
|
||||
<div className="min-w-0">
|
||||
<div className="text-xs font-medium text-foreground">Auto-name from first message</div>
|
||||
<div className="mt-0.5 text-[11px] leading-4 text-muted-foreground">
|
||||
Works when you leave the generated name unchanged.
|
||||
Auto-generates a new name when you leave this text box empty.
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
|
||||
@@ -157,7 +157,7 @@ describe('ContextualTourOverlaySurface', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
renderSurface({
|
||||
progress: { current: 2, total: 2 },
|
||||
title: 'Start from tracked work',
|
||||
title: 'Start from work items',
|
||||
isLastStep: true,
|
||||
isFirstStep: false
|
||||
})
|
||||
@@ -165,7 +165,7 @@ describe('ContextualTourOverlaySurface', () => {
|
||||
|
||||
expect(markup).toContain('aria-valuenow="2"')
|
||||
expect(markup).toContain('aria-valuemax="2"')
|
||||
expect(markup).toContain('Start from tracked work')
|
||||
expect(markup).toContain('Start from work items')
|
||||
expect(markup).toContain('Done')
|
||||
})
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ describe('contextual tour gate', () => {
|
||||
})
|
||||
|
||||
it('skips missing later steps and keeps progress relative to visible steps', () => {
|
||||
const tour = getContextualTour('browser')
|
||||
const tour = getContextualTour('tasks')
|
||||
const visibleSelectors = new Set([tour.steps[0]!.targetSelector, tour.steps[2]!.targetSelector])
|
||||
const targetExists = (selector: string): boolean => visibleSelectors.has(selector)
|
||||
const visibleStepIndexes = getVisibleContextualTourStepIndexes(tour, targetExists)
|
||||
|
||||
@@ -536,6 +536,10 @@ export default function WorkspaceKanbanDrawer({
|
||||
onPointerDown={handleAreaSelectionPointerDown}
|
||||
>
|
||||
<WorkspaceKanbanAreaSelectionOverlay ref={areaSelectionOverlayRef} />
|
||||
<div
|
||||
className="pointer-events-none absolute left-1/2 top-1/2 size-6 -translate-x-1/2 -translate-y-1/2"
|
||||
data-contextual-tour-target="workspace-board-center"
|
||||
/>
|
||||
<WorkspaceKanbanPinDropTarget
|
||||
isDragOver={pinDragOver}
|
||||
onDragOver={handlePinDragOver}
|
||||
|
||||
@@ -47,6 +47,7 @@ export default function WorkspaceKanbanSettingsMenu({
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
aria-label="Workspace board settings"
|
||||
data-contextual-tour-target="workspace-board-settings"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
<Settings className="size-3.5" />
|
||||
|
||||
@@ -83,7 +83,9 @@ export default function WorkspaceKanbanStatusLane({
|
||||
<section
|
||||
data-workspace-status-drop-target=""
|
||||
data-workspace-status={status.id}
|
||||
data-contextual-tour-target="workspace-board-cards"
|
||||
data-contextual-tour-target={
|
||||
status.id === 'completed' ? 'workspace-board-done-lane' : undefined
|
||||
}
|
||||
className={cn(
|
||||
'group/lane',
|
||||
'relative flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-md border border-t-2 border-sidebar-border transition-colors',
|
||||
|
||||
@@ -1194,7 +1194,7 @@ describe('createUISlice contextual tours', () => {
|
||||
it('advances across visible steps and leaves completion to the overlay', () => {
|
||||
const store = createUIStore()
|
||||
const visibleSelectors = [
|
||||
'[data-contextual-tour-target="browser-address"], [data-orca-browser-address-bar="true"]',
|
||||
'[data-contextual-tour-target="browser-grab-control"]',
|
||||
'[data-contextual-tour-target="browser-annotation-control"]'
|
||||
]
|
||||
stubContextualTourTargets(visibleSelectors)
|
||||
@@ -1202,11 +1202,11 @@ describe('createUISlice contextual tours', () => {
|
||||
store.getState().requestContextualTour('browser', 'browser_visible')
|
||||
|
||||
store.getState().advanceContextualTour()
|
||||
expect(store.getState().activeContextualTourStepIndex).toBe(2)
|
||||
expect(store.getState().activeContextualTourStepIndex).toBe(1)
|
||||
|
||||
store.getState().advanceContextualTour()
|
||||
expect(store.getState().activeContextualTourId).toBe('browser')
|
||||
expect(store.getState().activeContextualTourStepIndex).toBe(2)
|
||||
expect(store.getState().activeContextualTourStepIndex).toBe(1)
|
||||
})
|
||||
|
||||
it('advances the active split step when the split command interaction is recorded', () => {
|
||||
|
||||
@@ -70,6 +70,69 @@ describe('contextual tour definitions', () => {
|
||||
expect(tour?.steps[1]?.secondaryAction).toBeUndefined()
|
||||
})
|
||||
|
||||
it('points the workspace board tour at the board center, done lane, and settings', () => {
|
||||
const tour = CONTEXTUAL_TOURS.find((entry) => entry.id === 'workspace-board') as
|
||||
| ContextualTour
|
||||
| undefined
|
||||
|
||||
expect(tour?.steps.map((step) => step.title)).toEqual([
|
||||
'Plan work on the board',
|
||||
'Move work through lanes',
|
||||
'Tune density'
|
||||
])
|
||||
expect(tour?.steps[0]).toMatchObject({
|
||||
targetSelector: '[data-contextual-tour-target="workspace-board-center"]',
|
||||
requiredForStart: true,
|
||||
preferredPlacement: 'bottom'
|
||||
})
|
||||
expect(tour?.steps[1]).toMatchObject({
|
||||
body: 'Drag workspaces between lanes as their status changes.',
|
||||
targetSelector:
|
||||
'[data-contextual-tour-target="workspace-board-done-lane"], [data-contextual-tour-target="workspace-board-lanes"]'
|
||||
})
|
||||
expect(tour?.steps[2]).toMatchObject({
|
||||
body: 'Use board settings to switch between detailed and compact cards.',
|
||||
targetSelector:
|
||||
'[data-contextual-tour-target="workspace-board-settings"], [data-contextual-tour-target="workspace-board-lanes"]'
|
||||
})
|
||||
})
|
||||
|
||||
it('points the tasks tour at the row workspace action before toolbar fallbacks', () => {
|
||||
const tour = CONTEXTUAL_TOURS.find((entry) => entry.id === 'tasks') as
|
||||
| ContextualTour
|
||||
| undefined
|
||||
const step = tour?.steps[2]
|
||||
|
||||
expect(step).toMatchObject({
|
||||
title: 'Start from work items',
|
||||
body: 'Use Start or Open on a task, issue, review, or merge request to bring its context into a workspace.'
|
||||
})
|
||||
expect(step?.targetSelector.split(', ')).toEqual([
|
||||
'[data-contextual-tour-target="tasks-start-workspace"]',
|
||||
'[data-contextual-tour-target="tasks-actions"]',
|
||||
'[data-contextual-tour-target="tasks-search-presets"]'
|
||||
])
|
||||
})
|
||||
|
||||
it('orders the automations tour as create, then results', () => {
|
||||
const tour = CONTEXTUAL_TOURS.find((entry) => entry.id === 'automations') as
|
||||
| ContextualTour
|
||||
| undefined
|
||||
|
||||
expect(tour?.steps.map((step) => step.title)).toEqual([
|
||||
'What is an automation?',
|
||||
'Find the results'
|
||||
])
|
||||
expect(tour?.steps[0]).toMatchObject({
|
||||
body: 'Automations run agent work on a schedule. Add an automation by clicking this button.',
|
||||
requiredForStart: true
|
||||
})
|
||||
expect(tour?.steps.map((step) => step.targetSelector)).toEqual([
|
||||
'[data-contextual-tour-target="automations-create"]',
|
||||
'[data-contextual-tour-target="automations-runs"]'
|
||||
])
|
||||
})
|
||||
|
||||
it('allows only workspace creation over its workspace composer modal', () => {
|
||||
const modalTours = (CONTEXTUAL_TOURS as readonly ContextualTour[]).filter(
|
||||
(tour) => tour.allowedActiveModals?.length
|
||||
|
||||
@@ -56,18 +56,21 @@ export const CONTEXTUAL_TOURS = [
|
||||
{
|
||||
title: 'Plan work on the board',
|
||||
body: 'Use the board when you want to see workspaces by status instead of by project.',
|
||||
targetSelector: '[data-contextual-tour-target="workspace-board-surface"]',
|
||||
requiredForStart: true
|
||||
targetSelector: '[data-contextual-tour-target="workspace-board-center"]',
|
||||
requiredForStart: true,
|
||||
preferredPlacement: 'bottom'
|
||||
},
|
||||
{
|
||||
title: 'Move work through lanes',
|
||||
body: 'Statuses make active, reviewing, and finished work easy to scan.',
|
||||
targetSelector: '[data-contextual-tour-target="workspace-board-lanes"]'
|
||||
body: 'Drag workspaces between lanes as their status changes.',
|
||||
targetSelector:
|
||||
'[data-contextual-tour-target="workspace-board-done-lane"], [data-contextual-tour-target="workspace-board-lanes"]'
|
||||
},
|
||||
{
|
||||
title: 'Drag cards and tune density',
|
||||
body: 'Drop cards into lanes, resize columns, or switch compact mode from the board controls.',
|
||||
targetSelector: '[data-contextual-tour-target="workspace-board-cards"]'
|
||||
title: 'Tune density',
|
||||
body: 'Use board settings to switch between detailed and compact cards.',
|
||||
targetSelector:
|
||||
'[data-contextual-tour-target="workspace-board-settings"], [data-contextual-tour-target="workspace-board-lanes"]'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -98,20 +101,14 @@ export const CONTEXTUAL_TOURS = [
|
||||
id: 'browser',
|
||||
steps: [
|
||||
{
|
||||
title: 'Preview the app here',
|
||||
body: 'Use the address bar for localhost, URLs, or search while you keep coding nearby.',
|
||||
targetSelector:
|
||||
'[data-contextual-tour-target="browser-address"], [data-orca-browser-address-bar="true"]',
|
||||
title: 'Grab page context for agents',
|
||||
body: 'Grab controls can copy elements or hand page context to an agent.',
|
||||
targetSelector: '[data-contextual-tour-target="browser-grab-control"]',
|
||||
requiredForStart: true
|
||||
},
|
||||
{
|
||||
title: 'Grab page context for agents',
|
||||
body: 'On supported local pages, grab controls can copy elements or hand page context to an agent.',
|
||||
targetSelector: '[data-contextual-tour-target="browser-grab-control"]'
|
||||
},
|
||||
{
|
||||
title: 'Mark design feedback in place',
|
||||
body: 'On supported local pages, annotate elements and send those notes to an agent.',
|
||||
body: 'Annotate elements and send those notes to an agent.',
|
||||
targetSelector: '[data-contextual-tour-target="browser-annotation-control"]'
|
||||
}
|
||||
]
|
||||
@@ -131,10 +128,10 @@ export const CONTEXTUAL_TOURS = [
|
||||
targetSelector: '[data-contextual-tour-target="tasks-search-presets"]'
|
||||
},
|
||||
{
|
||||
title: 'Start from tracked work',
|
||||
body: 'Open an item or create one, then use it to start a workspace with the right context.',
|
||||
title: 'Start from work items',
|
||||
body: 'Use Start or Open on a task, issue, review, or merge request to bring its context into a workspace.',
|
||||
targetSelector:
|
||||
'[data-contextual-tour-target="tasks-actions"], [data-contextual-tour-target="tasks-search-presets"]'
|
||||
'[data-contextual-tour-target="tasks-start-workspace"], [data-contextual-tour-target="tasks-actions"], [data-contextual-tour-target="tasks-search-presets"]'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -142,19 +139,14 @@ export const CONTEXTUAL_TOURS = [
|
||||
id: 'automations',
|
||||
steps: [
|
||||
{
|
||||
title: 'Review recurring work',
|
||||
body: 'The list shows scheduled agent work, next runs, and external automation sources.',
|
||||
targetSelector: '[data-contextual-tour-target="automations-list"]',
|
||||
title: 'What is an automation?',
|
||||
body: 'Automations run agent work on a schedule. Add an automation by clicking this button.',
|
||||
targetSelector: '[data-contextual-tour-target="automations-create"]',
|
||||
requiredForStart: true
|
||||
},
|
||||
{
|
||||
title: 'Create a schedule',
|
||||
body: 'Add an automation for recurring checks, maintenance, or follow-up agent work.',
|
||||
targetSelector: '[data-contextual-tour-target="automations-create"]'
|
||||
},
|
||||
{
|
||||
title: 'Run and inspect results',
|
||||
body: 'Use overview and runs to trigger work manually and review what happened.',
|
||||
title: 'Find the results',
|
||||
body: 'Runs show when automations executed, what happened, and where to inspect their output.',
|
||||
targetSelector: '[data-contextual-tour-target="automations-runs"]'
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user