Estimate: 28m · Depends on: 1.5.3
Land the two cross-cutting shell features the top-nav reserves slots for: the theme toggle and the cmd-k command palette. Both fill the empty slots 1.5.3 left in TopNav.
Theme toggle ((authed)/_components/ThemeToggle.tsx): tri-state (light → dark → system), reads + writes the existing 1.0.5 ThemeProvider via its useTheme() hook. The icon cycles sun ↔ moon ↔ monitor (lucide-react icons). A Tooltip shows the current resolved theme on hover. Placed in TopNav in the slot between the workspace switcher and the cmd-k trigger.
Command palette:
components/ui/CommandPalette.tsx — generic primitive: a Radix Dialog.Root with an Input at top, a list of grouped actions below. Props: open / onOpenChange + groups: [{ heading, actions: [{ id, label, kbd?, onSelect }] }]. Keyboard: ↑↓ moves the highlighted row; ↵ invokes onSelect; esc closes. Filtering: simple substring match against label (no fuzz library — over-engineering for v1, document the deferral). Lives in /tokens with its variant matrix (empty query, filtered query, empty results).(authed)/_components/AppCommandPalette.tsx — the application composition: takes the same workspace/project list the layout already fetches, builds the action groups (Navigation: Go to Dashboard/Issues/Boards/Reports/Settings; Workspace: Switch to for each workspace; Project: Switch to for each project in active workspace; Account: Toggle theme, Sign out). Each action's onSelect dispatches the corresponding router push or Server Action.(authed)/_components/CommandPaletteProvider.tsx — a tiny client provider that owns the open state and registers the global ⌘K / Ctrl+K shortcut via the useShortcut hook from 1.5.2. Exposes a context method openCommandPalette() for the TopNav's "Search" button trigger.Button labeled "Search" with a trailing <kbd>⌘K</kbd> hint chip; clicking calls openCommandPalette().? globally to open a ShortcutsCheatsheet Dialog that lists every shortcut the shell registers (⌘K, ⌘, ?, esc). One-column table with a kbd-chip on the left and the action label on the right. Source the list from a single shared module lib/shortcuts.ts so the cheatsheet and the actual handlers can't diverge.What's deliberately NOT in here: fuzzy search across issues / projects / docs (Epic 6's Search Story owns that — when search lands, the palette grows a "Search" group fed by the search service; the primitive doesn't change shape). AI / chat affordances (Epic 7). Recent-items history (a finding to log: "palette has no recent-items list; add when usage data shows demand").
(authed)/_components/ThemeToggle.tsx ships and renders in TopNav; cycles light → dark → system; reads/writes via the existing useTheme() hook; an accessible label / tooltip announces the current resolved theme.components/ui/CommandPalette.tsx ships as a generic primitive (lives in /tokens with its variant matrix); takes typed group/action props.AppCommandPalette assembles navigation + workspace-switch + project-switch + account groups from the layout's already-fetched data; opens via ⌘K / Ctrl+K AND via the TopNav "Search" button.label; empty query shows all actions grouped by heading; no-match state shows "No actions match" hint.↑↓ cycles the highlighted row; ↵ invokes onSelect; esc closes; focus returns to the trigger on close.? opens the ShortcutsCheatsheet Dialog enumerating every shell shortcut; the list is sourced from lib/shortcuts.ts (single source of truth shared with the actual handlers).tests/components/command-palette.test.tsx cover: opens on ⌘K, filters on typed input, ↵ invokes the selected action, esc closes; tests/components/theme-toggle.test.tsx covers the tri-state cycle.lib/theme/ — existing ThemeProvider + useTheme() hookcomponents/ui/Dialog.tsx + Button.tsx + Tooltip.tsx — primitives to composeapp/(authed)/layout.tsx + TopNav.tsx — the slots to fill (1.5.3 left them empty)lib/hooks/useShortcut.ts from 1.5.2 — the keyboard-shortcut registration hook/design/shell/cmd-k.pen + shortcuts.pen from 1.5.1 — the visual contract