Agentic AI Atlasby a5c.ai
OverviewWikiGraphFor AgentsEdgesSearchWorkspace
/
GitHubDocsDiscord
iiRecord
Agentic AI Atlas · WebUI Compendium Migration — Implementation Plan
page:docs-superpowers-plans-2026-04-29-webui-compendium-migrationa5c.ai
Search record views/
Record · tabs

Available views

II.Record viewspp. 1 - 1
overviewarticlejsongraph
II.
Page JSON

page:docs-superpowers-plans-2026-04-29-webui-compendium-migration

Structured · live

WebUI Compendium Migration — Implementation Plan json

Inspect the normalized record payload exactly as the atlas UI reads it.

File · wiki/docs/superpowers/plans/2026-04-29-webui-compendium-migration.mdCluster · wiki
Record JSON
{
  "id": "page:docs-superpowers-plans-2026-04-29-webui-compendium-migration",
  "_kind": "Page",
  "_file": "wiki/docs/superpowers/plans/2026-04-29-webui-compendium-migration.md",
  "_cluster": "wiki",
  "attributes": {
    "nodeKind": "Page",
    "sourcePath": "docs/superpowers/plans/2026-04-29-webui-compendium-migration.md",
    "sourceKind": "repo-docs",
    "title": "WebUI Compendium Migration — Implementation Plan",
    "displayName": "WebUI Compendium Migration — Implementation Plan",
    "slug": "docs/superpowers/plans/2026-04-29-webui-compendium-migration",
    "articlePath": "wiki/docs/superpowers/plans/2026-04-29-webui-compendium-migration.md",
    "article": "\n# WebUI Compendium Migration — Implementation Plan\n\n> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.\n\n**Goal:** Migrate the adapters webui from mixed Radix/Tailwind/hand-rolled CSS to compendium as sole design system. Remove deprecated kanban package dependency. Fix all broken layouts and overlays.\n\n**Architecture:** Two layers — compendium provides all UI primitives and design tokens; the webui owns pages, hooks, services, and types directly. No Radix UI, no Tailwind, no Next.js shims.\n\n**Tech Stack:** React 18, React Router v6, Vite 8, @a5c-ai/compendium, Zustand, TypeScript\n\n**Spec:** `docs/superpowers/specs/2026-04-29-webui-compendium-migration-design.md`\n\n**Working directory:** `packages/adapters/webui`\n\n---\n\n## Phase 1 — Shell Replacement\n\n### Task 1: Create app.css and update main.tsx entry point\n\nReplace the three CSS imports (compendium/css, kanban/globals.css, styles/global.css) with a single `styles/app.css` that imports compendium tokens and defines minimal app layout.\n\n**Files:**\n- Create: `src/styles/app.css`\n- Modify: `src/main.tsx`\n\n- [ ] **Step 1: Create `src/styles/app.css`**\n\n```css\n@import '@a5c-ai/compendium/css';\n\n/* ── App shell layout ─────────────────────────────────────────────── */\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\nhtml, body, #root {\n  min-height: 100vh;\n  margin: 0;\n}\n\nbody {\n  color: var(--tkc-ink);\n  background: var(--tkc-paper);\n}\n\na { color: inherit; }\nimg { display: block; max-width: 100%; }\n\n.app-shell {\n  display: flex;\n  height: 100vh;\n}\n\n.app-main {\n  flex: 1;\n  min-width: 0;\n  display: flex;\n  flex-direction: column;\n  overflow-y: auto;\n  background: var(--tkc-paper);\n}\n\n.app-content {\n  flex: 1;\n  padding: var(--tk-space-lg, 24px);\n  max-width: 1200px;\n  width: 100%;\n  margin: 0 auto;\n}\n\n.app-topbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: var(--tk-space-md, 16px) var(--tk-space-lg, 24px);\n  border-bottom: 1px solid var(--tkc-rule);\n}\n\n.app-topbar h2 {\n  margin: 0;\n  font-size: 1.25rem;\n}\n\n.app-topbar__actions {\n  display: flex;\n  align-items: center;\n  gap: var(--tk-space-sm, 8px);\n}\n\n.connection-pill {\n  display: inline-block;\n  padding: 2px 10px;\n  border-radius: 999px;\n  font-size: 0.75rem;\n  font-family: var(--font-mono, monospace);\n  border: 1px solid var(--tkc-rule);\n}\n\n.connection-connected { color: var(--tkc-emerald); border-color: var(--tkc-emerald); }\n.connection-disconnected { color: var(--tkc-cinnabar); border-color: var(--tkc-cinnabar); }\n.connection-connecting { color: var(--tkc-amber); border-color: var(--tkc-amber); }\n\n/* ── Login page ───────────────────────────────────────────────────── */\n\n.login-page {\n  min-height: 100vh;\n  display: grid;\n  place-items: center;\n  padding: clamp(20px, 4vw, 40px);\n  background: var(--tkc-paper);\n}\n\n.login-card {\n  width: min(42rem, 100%);\n  padding: clamp(24px, 4vw, 40px);\n}\n\n/* ── Page layout helpers ──────────────────────────────────────────── */\n\n.stack {\n  display: grid;\n  gap: 16px;\n}\n\n.dashboard-layout {\n  display: grid;\n  gap: 16px;\n  align-content: start;\n}\n\n.flow-grid {\n  display: grid;\n  gap: 16px;\n  align-content: start;\n}\n\n.pairing-layout {\n  display: grid;\n  gap: 16px;\n  align-content: start;\n}\n\n.settings-grid {\n  display: grid;\n  gap: 16px;\n}\n\n.list-grid {\n  display: grid;\n  gap: 16px;\n}\n\n.summary-grid {\n  display: grid;\n  gap: 16px;\n}\n\n/* ── Responsive ───────────────────────────────────────────────────── */\n\n@media (max-width: 768px) {\n  .app-shell {\n    flex-direction: column;\n  }\n\n  .app-content {\n    padding: var(--tk-space-md, 16px);\n  }\n}\n```\n\n- [ ] **Step 2: Update `src/main.tsx` to use only app.css**\n\nReplace the contents of `src/main.tsx` with:\n\n```tsx\nimport React from 'react';\nimport ReactDOM from 'react-dom/client';\n\nimport { App } from './App.js';\nimport './styles/app.css';\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>,\n);\n```\n\n- [ ] **Step 3: Verify build compiles**\n\nRun: `npm run build`\nExpected: Build succeeds (pages will look different but app renders)\n\n- [ ] **Step 4: Commit**\n\n```bash\ngit add src/styles/app.css src/main.tsx\ngit commit -m \"feat(webui): replace triple CSS imports with single app.css using compendium tokens\"\n```\n\n---\n\n### Task 2: Rewrite AppShell layout in router.tsx\n\nReplace `AppChrome` with `AppShell` using flexbox layout. Remove the `webui-rail` mobile nav. Keep all routes unchanged.\n\n**Files:**\n- Modify: `src/router.tsx`\n\n- [ ] **Step 1: Rewrite the AppChrome component as AppShell**\n\nReplace the `AppChrome` function (lines 59-148) in `src/router.tsx` with:\n\n```tsx\nfunction AppShell(): JSX.Element {\n  const navigate = useNavigate();\n  const location = useLocation();\n  const { store } = useGateway();\n  const { logout } = useGatewayAuth();\n  const { mode, toggle } = useThemeMode();\n  const [paletteOpen, setPaletteOpen] = useState(false);\n  const sessions = useStore(store, useShallow((state) => Object.values(state.sessions.byId)));\n\n  const recentSessionActions = useMemo(\n    () =>\n      buildRecentSessionActions(sessions).map((action) => ({\n        id: action.id,\n        label: action.label,\n        run: () => navigate(action.to),\n      })),\n    [navigate, sessions],\n  );\n\n  const actions = useMemo(\n    () => [\n      { id: 'projects', label: 'Open projects', run: () => navigate('/projects') },\n      { id: 'runs', label: 'Open runs', run: () => navigate('/runs') },\n      { id: 'new-session', label: 'Start session', run: () => navigate('/sessions/new') },\n      { id: 'sessions', label: 'Browse sessions', run: () => navigate('/sessions') },\n      { id: 'workspaces', label: 'Open workspaces', run: () => navigate('/workspaces') },\n      { id: 'inbox', label: 'Open hook inbox', run: () => navigate('/inbox') },\n      { id: 'pair', label: 'Pair device', run: () => navigate('/pair-device') },\n      { id: 'theme', label: `Switch to ${mode === 'light' ? 'dark' : 'light'} theme`, run: () => toggle() },\n      { id: 'logout', label: 'Forget token', run: () => logout() },\n      ...recentSessionActions,\n    ],\n    [logout, mode, navigate, recentSessionActions, toggle],\n  );\n\n  React.useEffect(() => bindGlobalHotkeys({ openPalette: () => setPaletteOpen(true) }), []);\n\n  return (\n    <div className=\"app-shell\">\n      <Sidebar />\n      <div className=\"app-main\">\n        <TopBar pathname={location.pathname} onOpenPalette={() => setPaletteOpen(true)} />\n        <main className=\"app-content\">\n          <Routes>\n            <Route path=\"/\" element={<Navigate to=\"/projects\" replace />} />\n            <Route path=\"/agents\" element={<AgentsPage />} />\n            <Route path=\"/sessions\" element={<SessionsPage />} />\n            <Route path=\"/sessions/new\" element={<NewRunPage />} />\n            <Route path=\"/sessions/pending/:runId\" element={<SessionPendingPage />} />\n            <Route path=\"/runs/:runId\" element={<SessionPendingPage />} />\n            <Route path=\"/sessions/:sessionId\" element={<SessionDetailPage />} />\n            <Route path=\"/sessions/:agent/:sessionId\" element={<LegacySessionRouteRedirect />} />\n            <Route path=\"/pair-device\" element={<PairDevicePage />} />\n            <Route element={<KanbanLayout />}>\n              <Route path=\"/projects\" element={<ProjectsPage />} />\n              <Route path=\"/projects/:projectId/board\" element={<ProjectBoardPage />} />\n              <Route path=\"/projects/:projectId/list\" element={<ProjectListPage />} />\n              <Route path=\"/projects/:projectId/issues/new\" element={<ProjectIssueCreatePage />} />\n              <Route path=\"/projects/:projectId/issues/:issueId\" element={<ProjectIssuePage />} />\n              <Route path=\"/projects/:projectId/workspaces/new\" element={<ProjectWorkspaceCreatePage />} />\n              <Route path=\"/projects/:projectId/issues/:issueId/workspace/new\" element={<IssueWorkspaceCreatePage />} />\n              <Route path=\"/issues/:issueId\" element={<IssueDetailPage />} />\n              <Route path=\"/runs\" element={<KanbanRunsPage />} />\n              <Route path=\"/workspaces\" element={<KanbanWorkspacesPage />} />\n              <Route path=\"/workspaces/new\" element={<HostWorkspaceCreatePage />} />\n              <Route path=\"/inbox\" element={<KanbanInboxPage />} />\n              <Route path=\"/automations\" element={<AutomationsPage />} />\n              <Route path=\"/settings\" element={<KanbanSettingsPage />} />\n            </Route>\n            <Route path=\"/legacy-home\" element={<HomePage />} />\n            <Route path=\"/legacy-workspaces\" element={<WorkspacesPage />} />\n            <Route path=\"/legacy-inbox\" element={<HookInboxPage />} />\n            <Route path=\"/legacy-settings\" element={<SettingsPage />} />\n          </Routes>\n        </main>\n      </div>\n      <CommandPalette actions={actions} open={paletteOpen} onClose={() => setPaletteOpen(false)} />\n    </div>\n  );\n}\n```\n\nAlso update `AppRouter` to reference `AppShell`:\n\n```tsx\nexport function AppRouter(): JSX.Element {\n  return (\n    <Routes>\n      <Route path=\"/login\" element={<LoginPage />} />\n      <Route\n        path=\"*\"\n        element={\n          <RequireAuth>\n            <AppShell />\n          </RequireAuth>\n        }\n      />\n    </Routes>\n  );\n}\n```\n\n- [ ] **Step 2: Update TopBar to use new class names**\n\nIn `src/shell/TopBar.tsx`, replace the class names:\n\n```tsx\nimport React from 'react';\nimport { useConnection } from '@a5c-ai/genty-ui';\nimport { Button } from '@a5c-ai/compendium';\nimport { titleForPath } from './navigation.js';\n\nexport function TopBar(props: { pathname: string; onOpenPalette(): void }): JSX.Element {\n  const connection = useConnection();\n  return (\n    <header className=\"app-topbar\">\n      <h2>{titleForPath(props.pathname)}</h2>\n      <div className=\"app-topbar__actions\">\n        <span className={`connection-pill connection-${connection.status}`}>{connection.status}</span>\n        <Button type=\"button\" size=\"sm\" onClick={props.onOpenPalette}>\n          Command palette\n        </Button>\n      </div>\n    </header>\n  );\n}\n```\n\n- [ ] **Step 3: Update Sidebar to use compendium classes**\n\nIn `src/shell/Sidebar.tsx`, replace with compendium-styled sidebar:\n\n```tsx\nimport React from 'react';\nimport { NavLink } from 'react-router-dom-v6';\nimport { cx } from '@a5c-ai/compendium';\n\nexport function Sidebar(): JSX.Element {\n  return (\n    <aside className=\"tkc-panel\" style={{ width: 240, padding: '24px 16px', display: 'flex', flexDirection: 'column', gap: 8, borderRight: '1px solid var(--tkc-rule)', flexShrink: 0 }}>\n      <p style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--tkc-ink-soft)', margin: '0 0 4px 8px' }}>adapters</p>\n      <nav style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n        {[\n          { to: '/projects', label: 'Projects' },\n          { to: '/runs', label: 'Runs' },\n          { to: '/agents', label: 'Agents' },\n          { to: '/sessions', label: 'Sessions' },\n          { to: '/sessions/new', label: 'New session' },\n          { to: '/workspaces', label: 'Workspaces' },\n          { to: '/inbox', label: 'Hook inbox' },\n          { to: '/automations', label: 'Automations' },\n          { to: '/pair-device', label: 'Pair device' },\n          { to: '/settings', label: 'Settings' },\n        ].map((item) => (\n          <NavLink\n            key={item.to}\n            to={item.to}\n            className={({ isActive }) => cx('tkc-tree__node', isActive && 'tkc-tree__node--selected')}\n            style={{ textDecoration: 'none', padding: '6px 12px', borderRadius: 6, fontSize: '0.875rem' }}\n          >\n            {item.label}\n          </NavLink>\n        ))}\n      </nav>\n    </aside>\n  );\n}\n```\n\n- [ ] **Step 4: Verify build compiles**\n\nRun: `npm run build`\nExpected: Build succeeds. Shell layout uses flexbox. CommandPalette renders as centered overlay via compendium Portal.\n\n- [ ] **Step 5: Commit**\n\n```bash\ngit add src/router.tsx src/shell/Sidebar.tsx src/shell/TopBar.tsx\ngit commit -m \"feat(webui): replace AppChrome with AppShell using compendium layout primitives\"\n```\n\n---\n\n### Task 3: Delete old CSS files\n\nNow that app.css is in place and the shell uses new class names, remove the old CSS files.\n\n**Files:**\n- Delete: `src/kanban/globals.css`\n- Delete: `src/styles/global.css`\n\n- [ ] **Step 1: Delete old CSS files**\n\n```bash\ngit rm src/kanban/globals.css src/styles/global.css\n```\n\n- [ ] **Step 2: Update any remaining imports of these files**\n\nSearch for imports of the deleted files. The only import was in `main.tsx` (already updated in Task 1). Check for any other references:\n\n```bash\ngrep -rn \"globals.css\\|styles/global.css\" src/ --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.css\"\n```\n\nExpected: No matches (main.tsx was already updated).\n\n- [ ] **Step 3: Verify build compiles**\n\nRun: `npm run build`\nExpected: Build succeeds. Some pages may have unstyled elements — that's expected, they'll be fixed in Phase 4.\n\n- [ ] **Step 4: Commit**\n\n```bash\ngit commit -m \"chore(webui): delete kanban/globals.css and styles/global.css — replaced by app.css\"\n```\n\n---\n\n## Phase 2 — UI Primitive Swap\n\n### Task 4: Replace cn() with cx() across codebase\n\nThe `cn()` utility (clsx + tailwind-merge) is replaced by compendium's `cx()` (simple string join).\n\n**Files:**\n- Delete: `src/kanban/lib/cn.ts`\n- Modify: All files that import `cn` from `@/lib/cn`\n\n- [ ] **Step 1: Find all files importing cn**\n\n```bash\ngrep -rn \"from.*['\\\"]@/lib/cn['\\\"]\" src/ --include=\"*.ts\" --include=\"*.tsx\" -l\n```\n\n- [ ] **Step 2: Replace all imports**\n\nFor every file found, replace:\n```typescript\nimport { cn } from '@/lib/cn';\n```\nwith:\n```typescript\nimport { cx } from '@a5c-ai/compendium';\n```\n\nThen rename all `cn(` calls to `cx(` in those files.\n\n- [ ] **Step 3: Delete the cn.ts file**\n\n```bash\ngit rm src/kanban/lib/cn.ts\n```\n\n- [ ] **Step 4: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 5: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): replace cn() with compendium cx()\"\n```\n\n---\n\n### Task 5: Replace Radix UI wrapper components with compendium\n\nReplace each wrapper in `src/kanban/components/ui/` with compendium equivalents.\n\n**Files:**\n- Delete: `src/kanban/components/ui/button.tsx`\n- Delete: `src/kanban/components/ui/card.tsx`\n- Delete: `src/kanban/components/ui/tabs.tsx`\n- Delete: `src/kanban/components/ui/accordion.tsx`\n- Delete: `src/kanban/components/ui/tooltip.tsx`\n- Delete: `src/kanban/components/ui/badge.tsx`\n- Delete: `src/kanban/components/ui/separator.tsx`\n- Delete: `src/kanban/components/ui/scroll-area.tsx`\n- Modify: All files that import from these wrappers\n\n- [ ] **Step 1: Map all imports of UI wrappers**\n\n```bash\ngrep -rn \"from.*components/ui/\" src/ --include=\"*.ts\" --include=\"*.tsx\" | grep -v \"node_modules\"\n```\n\nBuild a mapping of what each consumer imports and what the compendium replacement is.\n\n- [ ] **Step 2: Update Button imports**\n\nEvery file importing `Button` from `@/components/ui/button` changes to:\n```typescript\nimport { Button } from '@a5c-ai/compendium';\n```\n\nFor files using `buttonVariants` — remove the import, apply compendium's built-in button classes directly. The compendium Button accepts `variant` prop (\"primary\" | \"ghost\") and `size` prop (\"sm\").\n\n- [ ] **Step 3: Update Tabs imports**\n\nReplace:\n```typescript\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';\n```\nwith:\n```typescript\nimport { Tabs } from '@a5c-ai/compendium';\n```\n\nCompendium Tabs uses an `items` prop array instead of render children:\n```tsx\n<Tabs\n  value={activeTab}\n  onChange={setActiveTab}\n  items={[\n    { value: 'overview', label: 'Overview', body: <OverviewContent /> },\n    { value: 'logs', label: 'Logs', body: <LogsContent /> },\n  ]}\n/>\n```\n\nEach consumer using the old Radix pattern needs restructuring to the `items` array pattern.\n\n- [ ] **Step 4: Update Accordion imports**\n\nReplace:\n```typescript\nimport { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion';\n```\nwith:\n```typescript\nimport { Accordion } from '@a5c-ai/compendium';\n```\n\nCompendium Accordion uses `items` array:\n```tsx\n<Accordion items={[{ key: 'details', heading: 'Details', body: <DetailsContent /> }]} />\n```\n\n- [ ] **Step 5: Update Tooltip imports**\n\nReplace:\n```typescript\nimport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip';\n```\nwith:\n```typescript\nimport { Tooltip } from '@a5c-ai/compendium';\n```\n\nCompendium Tooltip wraps the trigger child and takes a `label` prop:\n```tsx\n<Tooltip label=\"Helpful text\">\n  <button>Hover me</button>\n</Tooltip>\n```\n\n- [ ] **Step 6: Replace Badge/StatusBadge with Tag**\n\nReplace:\n```typescript\nimport { Badge } from '@/components/ui/badge';\n```\nwith:\n```typescript\nimport { Tag } from '@a5c-ai/compendium';\n```\n\nMap badge variants to Tag: `<Tag>running</Tag>` (Tag accepts children as text content).\n\n- [ ] **Step 7: Replace Separator with rule**\n\nReplace:\n```typescript\nimport { Separator } from '@/components/ui/separator';\n```\nwith a plain `<hr className=\"tkc-rule\" />`. No component import needed.\n\n- [ ] **Step 8: Replace ScrollArea with native scroll**\n\nReplace:\n```typescript\nimport { ScrollArea } from '@/components/ui/scroll-area';\n```\nwith a plain `<div style={{ overflowY: 'auto' }}>`. No component import needed.\n\n- [ ] **Step 9: Replace Card with tkc-panel class**\n\nReplace:\n```typescript\nimport { Card, CardHeader, CardContent } from '@/components/ui/card';\n```\nwith plain divs using compendium panel class:\n```tsx\n<div className=\"tkc-panel\">\n  <div style={{ padding: 16 }}>{/* header */}</div>\n  <div style={{ padding: 16 }}>{/* content */}</div>\n</div>\n```\n\n- [ ] **Step 10: Delete all UI wrapper files**\n\n```bash\ngit rm src/kanban/components/ui/button.tsx\ngit rm src/kanban/components/ui/card.tsx\ngit rm src/kanban/components/ui/tabs.tsx\ngit rm src/kanban/components/ui/accordion.tsx\ngit rm src/kanban/components/ui/tooltip.tsx\ngit rm src/kanban/components/ui/badge.tsx\ngit rm src/kanban/components/ui/separator.tsx\ngit rm src/kanban/components/ui/scroll-area.tsx\n```\n\n- [ ] **Step 11: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 12: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): replace Radix UI wrappers with compendium components\"\n```\n\n---\n\n### Task 6: Replace dialog/modal usage with compendium Modal\n\nReplace Radix Dialog usage in settings-modal, shortcuts-help, and dialog-shell with compendium's Modal.\n\n**Files:**\n- Modify: All files using `@radix-ui/react-dialog` or `dialog-shell`\n- Delete: `src/kanban/components/shared/dialog-shell.tsx` (if it exists)\n\n- [ ] **Step 1: Find all dialog/modal usage**\n\n```bash\ngrep -rn \"react-dialog\\|DialogShell\\|dialog-shell\\|RadixDialog\" src/ --include=\"*.ts\" --include=\"*.tsx\" -l\n```\n\n- [ ] **Step 2: Replace with compendium Modal**\n\nFor each consumer, replace:\n```tsx\nimport * as Dialog from '@radix-ui/react-dialog';\n// ...\n<Dialog.Root open={open} onOpenChange={setOpen}>\n  <Dialog.Portal>\n    <Dialog.Overlay />\n    <Dialog.Content>\n      <Dialog.Title>Title</Dialog.Title>\n      {children}\n    </Dialog.Content>\n  </Dialog.Portal>\n</Dialog.Root>\n```\n\nwith:\n```tsx\nimport { Modal } from '@a5c-ai/compendium';\n// ...\n<Modal open={open} onClose={() => setOpen(false)} title=\"Title\">\n  {children}\n</Modal>\n```\n\n- [ ] **Step 3: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 4: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): replace Radix Dialog with compendium Modal\"\n```\n\n---\n\n### Task 7: Replace notification system with compendium ToastProvider\n\nReplace the custom NotificationProvider with compendium's ToastProvider.\n\n**Files:**\n- Modify: `src/App.tsx`\n- Modify: `src/providers/NotificationProvider.tsx`\n- Modify: Any files using `useNotifications()` or the notification context\n\n- [ ] **Step 1: Add ToastProvider to App.tsx**\n\nIn `src/App.tsx`, wrap the app with compendium ToastProvider:\n\n```tsx\nimport React from 'react';\nimport { BrowserRouter } from 'react-router-dom-v6';\nimport { ToastProvider } from '@a5c-ai/compendium';\n\nimport { GatewayProvider } from './providers/GatewayProvider.js';\nimport { NotificationProvider } from './providers/NotificationProvider.js';\nimport { ThemeProvider } from './providers/ThemeProvider.js';\nimport { AppRouter } from './router.js';\n\nexport function App(): JSX.Element {\n  return (\n    <BrowserRouter>\n      <ThemeProvider>\n        <ToastProvider>\n          <GatewayProvider>\n            <NotificationProvider>\n              <AppRouter />\n            </NotificationProvider>\n          </GatewayProvider>\n        </ToastProvider>\n      </ThemeProvider>\n    </BrowserRouter>\n  );\n}\n```\n\n- [ ] **Step 2: Update NotificationProvider to use compendium toasts**\n\nIn `src/providers/NotificationProvider.tsx`, replace the custom notification logic with compendium's `useToasts()`:\n\n```tsx\nimport React from 'react';\nimport { useGatewayAuth } from './GatewayProvider.js';\nimport { useHookRequests } from '@a5c-ai/genty-ui';\nimport { useToasts } from '@a5c-ai/compendium';\n\nfunction NotificationBridge(): null {\n  const hooks = useHookRequests();\n  const { push } = useToasts();\n\n  React.useEffect(() => {\n    if (document.hidden && hooks.unread.length > 0) {\n      for (const hook of hooks.unread) {\n        push({ title: 'Hook request', message: hook.title || 'Pending approval', kind: 'info' });\n      }\n    }\n  }, [hooks.unread, push]);\n\n  return null;\n}\n\nexport function NotificationProvider(props: { children: React.ReactNode }): JSX.Element {\n  const { isAuthenticated } = useGatewayAuth();\n  return (\n    <>\n      {isAuthenticated ? <NotificationBridge /> : null}\n      {props.children}\n    </>\n  );\n}\n```\n\n- [ ] **Step 3: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 4: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): replace custom notifications with compendium ToastProvider\"\n```\n\n---\n\n### Task 8: Remove Radix and Tailwind dependencies\n\nRemove all deprecated dependencies from package.json now that nothing imports them.\n\n**Files:**\n- Modify: `packages/adapters/webui/package.json`\n\n- [ ] **Step 1: Remove dependencies**\n\n```bash\nnpm uninstall @radix-ui/react-accordion @radix-ui/react-dialog @radix-ui/react-scroll-area @radix-ui/react-separator @radix-ui/react-slot @radix-ui/react-tabs @radix-ui/react-tooltip class-variance-authority clsx tailwind-merge\n```\n\n- [ ] **Step 2: Remove dev dependencies**\n\n```bash\nnpm uninstall --save-dev tailwindcss\n```\n\n- [ ] **Step 3: Verify no remaining Radix/Tailwind imports**\n\n```bash\ngrep -rn \"@radix-ui\\|tailwind-merge\\|class-variance-authority\\|clsx\" src/ --include=\"*.ts\" --include=\"*.tsx\"\n```\n\nExpected: No matches.\n\n- [ ] **Step 4: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 5: Commit**\n\n```bash\ngit add package.json package-lock.json\ngit commit -m \"chore(webui): remove Radix UI, Tailwind, CVA, clsx dependencies\"\n```\n\n---\n\n## Phase 3 — Move Kanban Internals Into Webui\n\n### Task 9: Move hooks from kanban/hooks/ to hooks/\n\nMove all React hooks from the kanban subdirectory to the webui's own hooks directory.\n\n**Files:**\n- Move: `src/kanban/hooks/*` → `src/hooks/`\n- Modify: All files importing from `@/hooks/`\n\n- [ ] **Step 1: Move hook files**\n\n```bash\nmkdir -p src/hooks\ncp src/kanban/hooks/*.ts src/hooks/\ncp src/kanban/hooks/*.tsx src/hooks/\n```\n\n- [ ] **Step 2: Update import paths in moved hooks**\n\nEach hook may import from `@/lib/` (which maps to `src/kanban/lib/`). These will be updated in Task 10 when we move lib files. For now, update the Vite alias or use relative paths.\n\n- [ ] **Step 3: Update all consumers to import from new path**\n\nReplace `from '@/hooks/...'` with `from '@webui/hooks/...'` (or relative paths) across all page components.\n\n- [ ] **Step 4: Delete old hook files**\n\n```bash\ngit rm -r src/kanban/hooks/\n```\n\n- [ ] **Step 5: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 6: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): move hooks from kanban/hooks/ to hooks/\"\n```\n\n---\n\n### Task 10: Move lib, services, and types from kanban/ to webui root\n\nMove the library code, services, and type definitions.\n\n**Files:**\n- Move: `src/kanban/lib/*` → `src/lib/`\n- Move: `src/kanban/types/*` → `src/types/`\n- Modify: All files importing from `@/lib/` or `@/types/`\n\n- [ ] **Step 1: Move lib and types**\n\n```bash\nmkdir -p src/lib src/lib/services src/types\ncp src/kanban/lib/*.ts src/lib/\ncp src/kanban/lib/services/*.ts src/lib/services/\ncp src/kanban/types/*.ts src/types/\n```\n\n- [ ] **Step 2: Update all import paths**\n\nReplace `@/lib/` imports with `@webui/lib/` (or relative) across the codebase. Same for `@/types/`.\n\n- [ ] **Step 3: Delete old directories**\n\n```bash\ngit rm -r src/kanban/lib/ src/kanban/types/\n```\n\n- [ ] **Step 4: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 5: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): move lib/ and types/ from kanban/ to webui root\"\n```\n\n---\n\n### Task 11: Move page components and delete kanban directory\n\nMove remaining page components and shared components out of the kanban directory. Delete Next.js shims. Delete the kanban directory.\n\n**Files:**\n- Move: `src/kanban/components/shared/*` → `src/components/shared/`\n- Move: `src/kanban/components/dashboard/*` → `src/components/dashboard/`\n- Move: `src/kanban/components/details/*` → `src/components/details/`\n- Move: `src/kanban/components/pipeline/*` → `src/components/pipeline/`\n- Move: `src/kanban/components/events/*` → `src/components/events/`\n- Move: `src/kanban/components/breakpoint/*` → `src/components/breakpoint/`\n- Move: `src/kanban/components/review/*` → `src/components/review/`\n- Move: `src/kanban/components/notifications/*` → `src/components/notifications/`\n- Move: remaining kanban components as needed\n- Delete: `src/kanban-shims/`\n- Delete: `src/kanban/`\n\n- [ ] **Step 1: Move component directories**\n\n```bash\nmkdir -p src/components/{shared,dashboard,details,pipeline,events,breakpoint,review,notifications,sessions,runs,workspaces,automations,task-tags,adapters}\n# Copy each component group\ncp -r src/kanban/components/shared/* src/components/shared/\ncp -r src/kanban/components/dashboard/* src/components/dashboard/\ncp -r src/kanban/components/details/* src/components/details/\ncp -r src/kanban/components/pipeline/* src/components/pipeline/\ncp -r src/kanban/components/events/* src/components/events/\ncp -r src/kanban/components/breakpoint/* src/components/breakpoint/\ncp -r src/kanban/components/review/* src/components/review/\ncp -r src/kanban/components/notifications/* src/components/notifications/\ncp -r src/kanban/components/sessions/* src/components/sessions/\ncp -r src/kanban/components/runs/* src/components/runs/\ncp -r src/kanban/components/workspaces/* src/components/workspaces/\ncp -r src/kanban/components/automations/* src/components/automations/\ncp -r src/kanban/components/task-tags/* src/components/task-tags/\ncp -r src/kanban/components/adapters/* src/components/adapters/\ncp -r src/kanban/components/providers/* src/components/providers/\n```\n\n- [ ] **Step 2: Update all import paths in moved files**\n\nAll `@/components/` imports now resolve to `src/components/` instead of `src/kanban/components/`. Update the Vite alias `@/` in `vite.config.ts` to point to `src/` instead of `src/kanban/`:\n\n```typescript\n// In vite.config.ts, change:\n'@/': path.resolve(__dirname, 'src/kanban/'),\n// To:\n'@/': path.resolve(__dirname, 'src/'),\n```\n\n- [ ] **Step 3: Remove Next.js shims and update imports**\n\nReplace all `next/link` imports with React Router `Link`:\n```typescript\n// Old:\nimport Link from 'next/link';\n// New:\nimport { Link } from 'react-router-dom-v6';\n```\n\nReplace all `next/navigation` imports:\n```typescript\n// Old:\nimport { useRouter, usePathname, useSearchParams } from 'next/navigation';\n// New:\nimport { useNavigate, useLocation, useSearchParams } from 'react-router-dom-v6';\n```\n\nDelete the shim files:\n```bash\ngit rm -r src/kanban-shims/\n```\n\n- [ ] **Step 4: Delete the kanban directory**\n\n```bash\ngit rm -r src/kanban/\n```\n\n- [ ] **Step 5: Update Vite alias config**\n\nIn `vite.config.ts`, remove the `@/` → `src/kanban/` alias and replace with `@/` → `src/`:\n\n```typescript\nresolve: {\n  alias: {\n    '@/': path.resolve(__dirname, 'src/'),\n    '@webui/': path.resolve(__dirname, 'src/'),\n    // Remove next/link, next/navigation aliases — shims are gone\n  }\n}\n```\n\n- [ ] **Step 6: Verify build compiles**\n\nRun: `npm run build`\nThis is the hardest step — expect import resolution errors. Fix each one by updating the import path.\n\n- [ ] **Step 7: Commit**\n\n```bash\ngit add -A && git commit -m \"refactor(webui): move all components out of kanban/, delete kanban/ and kanban-shims/\"\n```\n\n---\n\n## Phase 4 — Restyle Page Components\n\n### Task 12: Restyle page components with compendium tokens\n\nGo through each page component and replace Tailwind utility classes with compendium design tokens and component classes. This is the largest task by file count but each page is independent.\n\n**Approach:** For each file, replace:\n- `className=\"flex items-center gap-2\"` → `style={{ display: 'flex', alignItems: 'center', gap: 8 }}`\n- `className=\"text-sm text-muted-foreground\"` → `style={{ fontSize: '0.875rem', color: 'var(--tkc-ink-soft)' }}`\n- `className=\"bg-card border rounded-lg p-4\"` → `className=\"tkc-panel\"`\n- `className=\"font-mono text-xs\"` → `style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}`\n\n**Key compendium token mappings:**\n- Colors: `var(--tkc-ink)`, `var(--tkc-ink-soft)`, `var(--tkc-paper)`, `var(--tkc-rule)`\n- Status: `var(--tkc-emerald)`, `var(--tkc-cinnabar)`, `var(--tkc-amber)`, `var(--tkc-cyan)`\n- Spacing: `var(--tk-space-sm)` (8px), `var(--tk-space-md)` (16px), `var(--tk-space-lg)` (24px)\n- Typography: `var(--font-display)`, `var(--font-body)`, `var(--font-mono)`\n- Panels: `.tkc-panel` class for card-like containers\n- Rules: `<hr className=\"tkc-rule\" />` for separators\n\n- [ ] **Step 1: Restyle dashboard components**\n\nWork through `src/components/dashboard/` files, replacing Tailwind classes with compendium tokens.\n\n- [ ] **Step 2: Restyle session/run detail components**\n\nWork through `src/components/details/`, `src/components/pipeline/`, `src/components/events/`.\n\n- [ ] **Step 3: Restyle shared components**\n\nWork through `src/components/shared/` — app-header, error-boundary, page-shell (if kept), status-badge replacements.\n\n- [ ] **Step 4: Restyle remaining page-specific components**\n\nWork through automations, breakpoint, review, notifications, workspaces, runs, sessions components.\n\n- [ ] **Step 5: Restyle page files**\n\nUpdate `src/pages/` files: LoginPage, ProjectsPage, SessionsPage, AgentsPage, etc.\n\n- [ ] **Step 6: Verify build compiles**\n\nRun: `npm run build`\n\n- [ ] **Step 7: Commit**\n\n```bash\ngit add -A && git commit -m \"style(webui): restyle all page components with compendium design tokens\"\n```\n\n---\n\n## Phase 5 — Cleanup & Verify\n\n### Task 13: Remove Tailwind config and unused files\n\n**Files:**\n- Delete: `tailwind.config.*` (if exists)\n- Delete: `postcss.config.*` (if exists)\n- Modify: `vite.config.ts` (remove Tailwind plugin if present)\n\n- [ ] **Step 1: Find and delete Tailwind/PostCSS config files**\n\n```bash\nls tailwind.config.* postcss.config.* 2>/dev/null\ngit rm tailwind.config.* postcss.config.* 2>/dev/null || true\n```\n\n- [ ] **Step 2: Clean Vite config**\n\nRemove any Tailwind-related Vite plugins from `vite.config.ts`. Remove unused path aliases (next/link, next/navigation, react-native).\n\n- [ ] **Step 3: Verify no Tailwind utility classes remain**\n\n```bash\ngrep -rn \"className=\\\"[^\\\"]*\\b(flex|grid|items-|justify-|gap-|p-[0-9]|m-[0-9]|text-|bg-|border-|rounded-|w-|h-|min-|max-)\" src/ --include=\"*.tsx\" -l\n```\n\nFix any remaining Tailwind classes found.\n\n- [ ] **Step 4: Final build verification**\n\nRun: `npm run build`\nExpected: Clean build, no warnings about missing modules.\n\n- [ ] **Step 5: Run existing tests**\n\nRun: `npm test`\nExpected: Tests pass (some may need import path updates).\n\n- [ ] **Step 6: Commit**\n\n```bash\ngit add -A && git commit -m \"chore(webui): remove Tailwind config, clean up Vite aliases\"\n```\n\n---\n\n### Task 14: Final verification and phase commit\n\n- [ ] **Step 1: Verify all routes render**\n\nStart dev server and manually navigate: `/login` → `/projects` → `/runs` → `/sessions` → `/agents` → `/workspaces` → `/inbox` → `/automations` → `/settings` → `/pair-device`\n\n- [ ] **Step 2: Verify CommandPalette**\n\nPress Cmd+K (or Ctrl+K). Verify:\n- Palette appears as centered overlay with scrim backdrop\n- Search filtering works\n- Arrow key navigation works\n- Escape closes\n\n- [ ] **Step 3: Verify theme toggle**\n\nOpen CommandPalette → \"Switch to dark theme\". Verify:\n- All pages switch to void/dark theme\n- Colors, backgrounds, and text are readable\n- Toggle back to light theme works\n\n- [ ] **Step 4: Verify mobile viewport**\n\nResize browser to 375px width. Verify:\n- Sidebar collapses\n- Content is readable\n- No horizontal overflow\n\n- [ ] **Step 5: Final commit**\n\n```bash\ngit add -A && git commit -m \"feat(webui): complete compendium migration — all layouts, overlays, and pages use compendium design system\"\n```\n",
    "documents": []
  },
  "outgoingEdges": [],
  "incomingEdges": [
    {
      "from": "page:docs",
      "to": "page:docs-superpowers-plans-2026-04-29-webui-compendium-migration",
      "kind": "contains_page"
    }
  ]
}

Shortcuts

Back to overview
Open graph tab