Polish contextual tour copy and targets

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-06-01 04:10:19 -07:00
co-authored by Orca
parent 8ff8a086b0
commit 31d0c92a20
10 changed files with 105 additions and 38 deletions
+5
View File
@@ -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',
+3 -3
View File
@@ -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', () => {
+63
View File
@@ -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
+22 -30
View File
@@ -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"]'
}
]