diff --git a/apps/web/src/workbench/surfaces.test.ts b/apps/web/src/workbench/surfaces.test.ts index 25b8bcdbb..b352ee78d 100644 --- a/apps/web/src/workbench/surfaces.test.ts +++ b/apps/web/src/workbench/surfaces.test.ts @@ -59,7 +59,11 @@ describe('surfaceTabId', () => { expect(surfaceTabId(surface('finance'))).toBe('finance:finance') }) - it('is null for panels and non-tab routes', () => { + it('resolves the tab id for a panel surface with a companion route', () => { + expect(surfaceTabId(surface('tasks'))).toBe('tasks:tasks') + }) + + it('is null for routeless panels and non-tab routes', () => { expect(surfaceTabId(surface('explorer'))).toBeNull() // panel expect(surfaceTabId(surface('discover'))).toBeNull() // route, but untabbed expect(surfaceTabId(surface('analytics'))).toBeNull() @@ -91,16 +95,28 @@ describe('activateSurface (VS Code preview tabs, 0288)', () => { expect(consumePreviewIntent()).toBe(false) }) - it('drives the bottom island for panel surfaces without navigating or arming preview', () => { + it('drives the bottom island for routeless panel surfaces without navigating or arming preview', () => { const activated: string[] = [] activateSurface(surface('explorer'), { - navigate: () => expect.unreachable('panel surfaces do not navigate'), + navigate: () => expect.unreachable('routeless panel surfaces do not navigate'), setActiveSurface: (id) => activated.push(id) }) expect(activated).toEqual(['explorer']) expect(consumePreviewIntent()).toBe(false) }) + it('drives the bottom island AND opens the board for the Tasks panel surface', () => { + const activated: string[] = [] + const calls: Array<{ to: string }> = [] + activateSurface(surface('tasks'), { + navigate: (opts) => calls.push(opts), + setActiveSurface: (id) => activated.push(id) + }) + expect(activated).toEqual(['tasks']) + expect(calls).toEqual([{ to: '/tasks' }]) + expect(consumePreviewIntent()).toBe(true) // /tasks is a singleton tab route + }) + it('leaves the latch clean afterwards', () => { setPreviewIntent() expect(consumePreviewIntent()).toBe(true) diff --git a/apps/web/src/workbench/surfaces.ts b/apps/web/src/workbench/surfaces.ts index 6818568f5..a871da113 100644 --- a/apps/web/src/workbench/surfaces.ts +++ b/apps/web/src/workbench/surfaces.ts @@ -38,7 +38,11 @@ export interface SurfaceDef { kind: 'panel' | 'route' /** Slot view id (panel surfaces). */ viewId?: string - /** Route path (route surfaces). */ + /** + * Route path. Required for route surfaces; a panel surface may also carry + * one, in which case activating it drives the bottom island *and* opens the + * route in the editor (Tasks → the task board). + */ to?: string /** Live count source for the trailing badge. */ badge?: 'requests' @@ -58,7 +62,7 @@ export const SURFACES: SurfaceDef[] = [ badge: 'requests', emphasis: true }, - { id: 'tasks', label: 'Tasks', icon: CheckSquare2, kind: 'panel', viewId: 'tasks' }, + { id: 'tasks', label: 'Tasks', icon: CheckSquare2, kind: 'panel', viewId: 'tasks', to: '/tasks' }, { id: 'chats', label: 'Chats', icon: MessageSquare, kind: 'panel', viewId: 'chats' }, { id: 'today', label: 'Today', icon: Sunrise, kind: 'panel', viewId: 'today' }, { id: 'data', label: 'Data', icon: Database, kind: 'panel', viewId: 'data' }, @@ -85,33 +89,34 @@ export function pinnedSurfaces(navPinned: string[]): SurfaceDef[] { export const DEFAULT_SURFACE = 'explorer' /** - * The tab a route surface opens/promotes, or null for panel surfaces and + * The tab a surface's route opens/promotes, or null for routeless panels and * non-tab routes (Discover, Analytics). Lets a surface row promote its tab on * double-click without knowing the node model. */ export function surfaceTabId(surface: SurfaceDef): string | null { - if (surface.kind !== 'route' || !surface.to) return null + if (!surface.to) return null return tabIdForRoute(surface.to) } /** * Activating a surface: a **panel** drives the contextual bottom island - * (`activeSurface`); a **route** opens in the editor as a VS Code-style preview - * tab (0288) — a single click renders it italic and the next single-click open - * replaces it; double-clicking the row (or editing) promotes it. Pure so the - * decision is testable; the hook below wires the side-effecting deps. + * (`activeSurface`); a surface with a **route** opens it in the editor as a + * VS Code-style preview tab (0288) — a single click renders it italic and the + * next single-click open replaces it; double-clicking the row (or editing) + * promotes it. A panel surface that also carries a route (Tasks) does both. + * Pure so the decision is testable; the hook below wires the side-effecting + * deps. */ export function activateSurface( surface: SurfaceDef, deps: { navigate: (opts: { to: string }) => void; setActiveSurface: (id: string) => void } ): void { - if (surface.kind === 'route' && surface.to) { + if (surface.kind === 'panel') deps.setActiveSurface(surface.id) + if (surface.to) { // Only tab routes honour the preview latch; arming it for a non-tab route // (Discover, Analytics, Inbox) would leave it set for the next navigation. if (tabIdForRoute(surface.to)) setPreviewIntent() deps.navigate({ to: surface.to }) - } else { - deps.setActiveSurface(surface.id) } } diff --git a/apps/web/src/workbench/views/left.tsx b/apps/web/src/workbench/views/left.tsx index 563b36321..178c74c97 100644 --- a/apps/web/src/workbench/views/left.tsx +++ b/apps/web/src/workbench/views/left.tsx @@ -8,21 +8,11 @@ import { TasksDashboard } from './TasksPanel' import { useSavedViews } from './tray' export function TasksPanelView() { + // No board link here: activating the Tasks surface already opens the task + // board in the editor (see surfaces.ts). return ( -
-
- -
-
- - Open task board - - -
+
+
) } diff --git a/site/src/data/changelog/2026-07-11-clicking-tasks-opens-the-task-board.json b/site/src/data/changelog/2026-07-11-clicking-tasks-opens-the-task-board.json new file mode 100644 index 000000000..4917742ba --- /dev/null +++ b/site/src/data/changelog/2026-07-11-clicking-tasks-opens-the-task-board.json @@ -0,0 +1,8 @@ +{ + "id": "2026-07-11-clicking-tasks-opens-the-task-board", + "date": "July 11, 2026", + "title": "Clicking Tasks opens the task board", + "summary": "Selecting Tasks in the sidebar now opens the task board directly alongside the panel, so the extra \"Open task board\" link is gone.", + "highlights": [], + "tags": ["app", "tasks"] +}