Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.yourhq.ai/llms.txt

Use this file to discover all available pages before exploring further.

The interaction layer is what makes the app feel fast. This page documents the four systems that every module plugs into: the command palette, the keyboard-shortcut registry, the drag-and-drop convention, and realtime data subscriptions.

Command palette

Cmd/Ctrl+K from anywhere. The single search-and-jump surface in the app. apps/ui/src/components/shared/command-palette.tsx — built on cmdk.

Sections when no query

SectionContents
RecentLast 10 items the user opened (any type). Persisted in localStorage via addRecentItem() / getRecentItems(). Rendered with a Clock icon to distinguish from navigation entries.
NavigationAll app pages, gated by the workspace’s enabled modules. New modules register here.
CollectionsThe user’s collections — dynamic from the workspace.
Quick ActionsCreate Task, Add Contact, Create Organization, Create Knowledge, Register Agent.

Search results

Grouped by entity type when a query is present: Knowledge, Knowledge Chunks, Tasks, Contacts, Collections, Agents, Routines. Each result has a type-colored icon (blue for knowledge, amber for tasks, emerald for contacts, etc.), a title, and a subtitle showing matched snippet or metadata. Async search runs in a debounced effect; the palette shows “Searching…” while results are fetching.

Adding entries

A new module registers in two places:
  1. The Navigation array inside command-palette.tsx.
  2. The dashboard sidebar (see layout).
There is no third place — these are the canonical entry points to a module. Quick Actions are added to the same file. When a module owns a searchable entity, register a section in the search-results dispatcher. Search hits go through the same Supabase RPCs as the dedicated search APIs (search_knowledge_items, search_knowledge_chunks).

Keyboard shortcuts

apps/ui/src/components/shared/keyboard-shortcuts.tsx A single global registry, mounted once via KeyboardShortcutsProvider at the dashboard layout. Input-aware: skips when focus is in INPUT, TEXTAREA, or any element with contentEditable=true. Press ? to open the help sheet — every registered shortcut shows up there.

Global shortcuts

KeysAction
Cmd/Ctrl+KOpen command palette
Cmd/Ctrl+BToggle sidebar (or open mobile drawer)
?Open keyboard-shortcut help
EscapeClose current overlay (Sheet, Dialog, Popover, inline editor)

G-then-letter navigation

Press G, then the next key within 800ms, to jump to a module:
SequenceDestinationModule gate
G DDashboardAlways
G CContactsCRM enabled
G OOrganizationsCRM enabled
G TTasksAlways
G AAgentsAlways
G KKnowledgeAlways
G ECollectionsAlways
G RRoutinesAlways
G LActivityAlways
G NNotificationsAlways
G SSettingsAlways
Adding a new module: register the G-sequence in keyboard-shortcuts.tsx, gate it on the relevant ModulesContext flag, and the help sheet will pick it up.

Module-level shortcuts

A feature can register additional shortcuts via the useShortcuts hook. Examples worth using sparingly: C to open the create panel from a list view, / to focus the filter-bar search input. Every module-level shortcut shows in the help sheet — there is no hidden registry. Don’t register shortcuts that conflict with browser defaults (Cmd+W, Cmd+T, Cmd+R, Cmd+L) or with text editing (any single letter that types into an input — except in input-aware paths the registry already filters).

Drag and drop

The canonical library is @dnd-kit. Reach for it whenever a feature needs drag-and-drop. Reference implementation: apps/ui/src/components/shared/folder-tree.tsx — recursive tree with drag-to-reorder and drag-to-nest. The canonical setup:
  • DndContext at the boundary — sensors PointerSensor and KeyboardSensor both wired so drag works with mouse, touch, and keyboard.
  • SortableContext for ordered lists (kanban columns, file lists, sidebar groups).
  • useSortable on each draggable item; the hook returns setNodeRef, listeners, attributes, transform, and transition for the drag state.
  • DragOverlay for the floating drag preview — keeps the original item in place visually.
  • Modifiers (restrictToVerticalAxis, restrictToParentElement) when the gesture should be constrained.
The kanban board on tasks currently uses native HTML5 drag-drop; that is a known deviation from this canonical pattern and is part of the audit-and-fix work — new code should use @dnd-kit.

Visual conventions during drag

  • The dragged item’s source position renders at 50% opacity and removes its border-on-hover behavior.
  • The overlay clone gets a 1px border-strong and a slight shadow-lg.
  • Drop zones get a bg-surface-selected highlight on dragOver.
  • The drag is canceled on Escape, committed on drop. Persistence happens in the onDragEnd handler — the parent owns the data, the dragged item never persists itself.

Realtime

Lists subscribe to Supabase Realtime so other sessions and agent activity surface live, without a refresh. apps/ui/src/hooks/use-realtime-sync.ts — the multi-table version, used by orchestrator hooks (use-contacts, use-tasks, use-collection-records). apps/ui/src/hooks/use-realtime.ts — the single-table version, simpler API for one-off subscriptions. The pattern, in shape:
  • The hook subscribes on mount, unsubscribes on unmount.
  • Insert / update / delete events from any session (including agents) flow through.
  • Tenant-scoped RLS filters out events for other tenants — no client-side filtering needed.
  • Optimistic local mutations are reconciled with realtime echoes by ID — the hook does not double-apply.
Lists do not poll. There is no manual refresh button. If an event is missed (network blip), reconnection re-fetches the snapshot.

When to subscribe

  • Always — list views and detail views of records that other sessions or agents can change.
  • Sometimes — settings pages where stale data is just inconvenient (rather than wrong).
  • Never — pure UI state, transient form inputs, anything client-only.
If a hook subscribes, it owns the lifecycle — feature components don’t manage subscriptions directly.

Hover, focus, and selection feedback

Three subtle but consistent affordances:
  • Hoverbg-surface-hover (oklch(0 0 0 / 4%) light, oklch(1 0 0 / 5%) dark). 120ms transition.
  • Focus — visible ring, ring-ring token, 2px. Always visible via keyboard, never suppressed.
  • Selectedbg-surface-selected (slightly darker than hover). For multi-select, also a checkbox in the row’s leading slot.
Active row in lists or sidebar gets a 2px accent bar on the left (bg-foreground), not a heavy fill. Depth via accent, not background — see principles.

Pulling it together

Most modules touch all four systems. A correctly-built list view will:
  1. Render inside the shell with a PageHeader.
  2. Subscribe to its data via a realtime hook.
  3. Register its routes in the command palette and a G-shortcut.
  4. Use @dnd-kit if rows are reorderable.
  5. Compose its filters via the FilterBar and its rows via the DataTable.
If any of these is missing, the screen will feel inconsistent with the rest of the app — and the inconsistency is what users notice first.