Three coordinated changes after reviewing the wave 2 subagent reports:
1. Restore Presentations as the 9th Studio workshop.
Phase 10's subagent dropped Presentations from the workshop grid
because the spec's eight-workshop list didn't include it. But
.planning/PROJECT.md explicitly lists "Presentations & video
generation via Remotion" as an Active v1.7 requirement and the
existing PresentationPanel.tsx is already a real working Remotion
generator. Dropping it was silent feature regression.
- workshops.ts: add "presentations" slug + Presentation Lucide
icon, placed between social and convert in canonical order
- classifyIntent.ts: add pitch-deck / slide-deck / demo-video /
keynote intent routing (before social so "pitch deck" wins)
- StudioWorkshopDetail.tsx: import PresentationPanel and add a
"presentation" case in WorkshopBody
- workshops.test.ts: expected canonical order updated to 9 slugs
- classifyIntent.test.ts: 4 new parameterized rows for presentations
- WorkshopCard.test.tsx: index 7 is now PRESENTATIONS, 8 is CONVERT
- WorkshopGrid.test.tsx: expected card count 9, canonical order
2. ProjectCard hero-stat derivatives instead of em-dash city.
The shared Project record has none of the fields the spec §7.1 card
depends on: milestoneProgress, nextGate, costBurned, per-project
agent count, phase/milestone array. Wave 2 shipped every card with
"—%" and blank hero numbers — visually underwhelming for a layout
whose whole point is the 72px volt performance stat.
Compute best-effort proxies on the client from data that exists:
- progress: closed_issues / total_issues × 100, from a single
issuesApi.list(companyId) query grouped by projectId
- nextGateName: first pending approval whose payload.projectId
matches, from a single approvalsApi.list(companyId, "pending")
query
- lastActivity: max(project.updatedAt, newest issue.updatedAt in
the project), rendered as "8m ago"-style diff
Each proxy is annotated with // TODO(phase-11.5) for replacement
when real backend aggregates land. phase, costBurnedCents, and
per-project agent count remain hard gaps — rendered as null which
the card surfaces as em-dashes. These three are explicitly queued
for Phase 11.5.
No backend changes; everything derives from existing endpoints.
Two new useQuery calls in Projects.tsx (issues + pending approvals)
both fire per-company, not per-project, so they stay cheap for the
~dozens-of-projects scale the list targets.
3. Spec updated 8 → 9 workshops everywhere it referred to the count.
docs/specs/2026-04-11-nexus-layout-overhaul.md:
- §2 IA table: 8 → 9 workshops
- §6 ASCII header: Eight → Nine
- §6.3 section title: Eight workshops → Nine workshops
- §11 decisions log #16: amendment note explaining the 8→9 bump
- §13 phase 10 description: 8-card → 9-card, with Presentations
explicitly called out
- "Folds into Studio as the 8th workshop" → "Folds into Studio
as a workshop (the legacy /convert route is preserved for
backwards compat)"
Verification: 75/75 studio tests passing; 52/52 projects tests
passing; tsc clean on studio/ + projects/ + Projects.tsx +
ProjectDetail.tsx + StudioWorkshopDetail.tsx.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
55 KiB
Nexus Layout & Information Architecture Overhaul
Status: Approved 2026-04-11 — ready for implementation planning
Branch: nexus/design-system-migration
Companion docs: DESIGN.md (visual language), MIGRATION-PLAN.md (visual migration phases 1–7 — this spec defines phases 8–16)
1. Thesis
Nexus is not a SaaS dashboard with a chat panel bolted on. Nexus is a chat with a workshop attached.
The current frame inverts this: a Paperclip-derived corporate dashboard occupies the center, and chat is a 380px slide-in panel on the right that competes with a PropertiesPanel for shared real estate. This is exactly backwards for a single-user, self-hosted personal AI tool whose tagline is "Your personal AI. That also builds things."
In the new frame, the Assistant is the canvas. Everything else — Studio, Projects, Settings — is a destination you visit and return from, not a peer that competes with chat for screen real estate. The Project Builder backend (issues, agents, gates, costs, activity, org) is demoted from global navigation to per-project tabs. You only see it when you've drilled into a specific project. There is no global "Issues" route, no global "Agents" route, no global "Costs" route — those concepts only exist scoped to a project.
2. Information architecture
Primary destinations (in the global icon rail, always visible)
| Slot | Destination | Route | Mental model |
|---|---|---|---|
| 1 | Assistant | /assistant |
The home screen. Voice in, voice out. The chat IS the app. |
| 2 | Studio | /studio |
Workshop selector for content generation (9 workshops). |
| 3 | Projects | /projects |
List of all projects with health stats. |
| 4 | Settings | /settings |
Workspace, Local AI, Cloud, Voice, Skills, Routines, Telegram, About. |
Four destinations. Period. No "Dashboard", no "Inbox", no "Recipes" slot, no "add company" button.
Secondary destinations (only inside Project Detail)
When you drill into /projects/:id, you enter Builder mode and a project tab strip appears under the header:
OVERVIEW · ISSUES · AGENTS · GATES · COSTS · ACTIVITY · ORG
These are scoped to one project. They are not addressable globally. Routes: /projects/:id/overview, /projects/:id/issues, etc.
Tertiary surfaces (live inside Settings)
- Skills — Skill Aggregator browse/install/assign UI
- Routines — workspace-level cron jobs (currently a top-level route, demoted)
- Telegram bridge — config, not nav
- Cloud providers / API keys — config
- Voice — STT/TTS config
Killed (removed from the app entirely)
| What | Why |
|---|---|
| Company switcher / "add company" / "manage companies" / multi-tenant UI | Nexus is single-workspace by design |
| Global Dashboard route as landing page | Assistant is the landing page; the dashboard concept becomes the Assistant's conversational greeting |
| Global Issues / Agents / Approvals / Costs / Activity / Inbox / Org / Goals routes | Demoted to per-project tabs (or fold into Overview) |
| Global Routines route | Moved to Settings (cron jobs are workspace config, not a workflow) |
ChatPanel.tsx as a slide-in right rail |
Killed entirely; chat becomes the Assistant route |
PropertiesPanel as a standing right-rail fixture |
Pages that need it render it inline themselves |
| 280px left sidebar with company switcher / sidebar projects / sidebar agents | Replaced by 56px icon rail |
MobileBottomNav (company-aware) |
Replaced by 4-icon bottom bar matching the desktop rail |
| Theme cycle button (Catppuccin/Tokyo/etc.) | Binary light/dark only, configured in Settings, no toggle in chrome |
ConvertPage as a top-level route |
Folds into Studio as a workshop (the legacy /convert route is preserved for backwards compat) |
InboxRootRedirect, LegacySettingsRedirect, OnboardingRoutePage URL machinery |
Simpler routing |
PluginPage as a top-level route slot |
Plugin pages render inside Settings or inside a project |
| Any UI string containing "company", "companies", "tenant", "workspace member" | Vocabulary cleanup; replace with "workspace" or remove |
3. Visual language (DESIGN.md inheritance)
The overhaul does not introduce a new visual language. It uses DESIGN.md verbatim. Recap of the binding rules for this spec:
- Canvas: pure black
#000000. No surface differentiation between rail/header/canvas — they all bleed into the same black. - Sole chromatic accent: Neon Volt
#faff69. Used for active states, borders on selected items, hover targets, hero performance numbers, link hover. Never as a fill background outside CTA buttons. - Secondary CTA: Forest Green
#166534for "Create project", "New", "Run" buttons. - Pressed/active text: Pale Yellow
#f4f692. - Workhorse border: Charcoal
rgba(65,65,65,0.8). The single border color for cards, dividers, inputs. - Typography: Inter (variable, weight 400–900). Inconsolata for code/numbers. Inter Black 900 for hero performance numbers (project %, milestone count). Uppercase + 1.4px tracking for section labels and breadcrumbs.
- Radius scale: 4px (sharp), 8px (comfortable), 9999px (pill). Nothing else.
- Depth: border-based, not shadow-based. Inset shadow Level 4 for "pressed into the surface" active states. Volt border highlight (Level 5) for featured/selected.
- Mode: dark default. Light mode is an accessibility alternative per MIGRATION-PLAN.md §5; this spec assumes dark mode for all wireframes.
Anything in this spec that conflicts with DESIGN.md, DESIGN.md wins. Update this spec, not DESIGN.md.
4. Global frame
Constant chrome that appears on every page. Two pieces: left icon rail and top strip.
4.1 Left icon rail
┌──┐
│ │ ← 8px top padding
│ ⬢│ ← Nexus mark (volt), 24×24, click → Assistant
│ │
│ ◆│ ← MessageCircle (Lucide) — Assistant
│ │
│ ▲│ ← Sparkles (Lucide) — Studio
│ │
│ ◇│ ← FolderKanban (Lucide) — Projects
│ │
│ │ ← (gap, flex-1)
│ │
│ ⚙│ ← Settings (Lucide) — Settings
│ │ ← 8px bottom padding
└──┘
56px wide, 100vh tall, pure black background, no border
Specs:
- Width: 56px, locked. No collapse, no expand, no hover-grow.
- Background:
#000000, no border, no shadow. - Position:
position: fixed; left: 0; top: 0; bottom: 0;— independent of page scroll. - Icons: Lucide icons at 20×20. Default state: silver
#a0a0a0, stroke 1.5. Hover: volt#faff69. Active (current route): volt + 2px volt vertical bar on the right edge of the rail aligned with the icon. Never a fill background behind the icon — DESIGN.md forbids volt as background. - Tooltips: on hover, render a tooltip 12px to the right of the rail with the destination name in uppercase, 11px Inter 600, 1.4px tracking, silver text on near-black
#141414with a 1px charcoal border. Sharp 4px radius. Delay 300ms. - Click: navigates immediately. No transitions on the rail itself; the canvas content fades.
- Nexus mark at top: small geometric logo in volt. Click →
/assistant. This is the only volt-filled element in the rail. - Settings pinned at bottom: flex layout (
flex-direction: column,Settingsslot hasmargin-top: auto).
Accessibility:
<nav aria-label="Primary">with each icon as<a>element.- Each link has
aria-labelmatching the tooltip text. aria-current="page"on the active link.- Focus ring uses volt at 2px offset.
4.2 Top strip
┌──────────────────────────────────────────────────────────────────┐
│ ASSISTANT ⌘K ◉ MIC │
└──────────────────────────────────────────────────────────────────┘
▲ ▲ ▲
│ │ │
mode label search voice
Specs:
- Height: 48px. Background:
#000000, bottom border: 1px charcoal. - Position:
position: sticky; top: 0;within the canvas column (not over the rail). - Left: mode label / breadcrumb. Inter 600, 14px, uppercase, 1.4px tracking. Silver for non-active segments, volt for the leaf segment (current location). Slash-separated when nested:
PROJECTS / NEXUS-DESIGN-MIGRATION. Click any non-leaf segment to navigate up. - Right cluster (right-aligned, 16px gap):
- ⌘K — palette trigger button. Renders the kbd glyph in Inconsolata 12px, charcoal background, pale silver border, 4px radius. Click or
Cmd+Kopens the global command palette. - ◉ MIC — global voice mic button. 32×32 round button (8px radius — not pill, per DESIGN.md "no pill except for toggles"). States:
- Idle: forest green dot (8×8) centered. No animation.
- Listening: volt fill, 1.5s pulse loop, expanding volt ring at 60% opacity.
- Speaking (TTS playback): silver fill, no pulse.
- Tap-to-talk from any page. See §5.5 for what voice does in each mode.
- ⌘K — palette trigger button. Renders the kbd glyph in Inconsolata 12px, charcoal background, pale silver border, 4px radius. Click or
- No theme toggle, no notifications icon, no user avatar, no sleep button. Single-user self-hosted; these are all noise.
4.3 The "killed" right rail
The current ChatPanel.tsx (380px slide-in chat) and PropertiesPanel (slide-in property inspector) do not exist as global elements in the new frame. There is no right rail in the chrome. Pages that need contextual side content render it inline within their own layout (e.g., IssueDetail may render an issue-properties column at >1280px; that's a page-level decision, not chrome).
5. Mode 1 — Assistant (/assistant)
The default landing screen and the gravitational center of the app.
5.1 Layout
┌──┬───────────────────────────────────────────────────────────────┐
│ │ ASSISTANT ⌘K ◉ MIC │
│ │───────────────────────────────────────────────────────────────│
│ │ │
│ ⬢│ │
│ │ TODAY · 10:42 │
│ ◆│ ───────────────── │
│ │ │
│ ▲│ ┌──────────────────────────────────────────┐ │
│ │ │ user message bubble (right-aligned) │ │
│ ◇│ └──────────────────────────────────────────┘ │
│ │ │
│ │ ┌──────────────────────────────────────────────┐ │
│ │ │ assistant message bubble (left-aligned) │ │
│ │ └──────────────────────────────────────────────┘ │
│ │ │
│ ⚙│ ╭────────────────────────────────────────────────────────╮ │
│ │ │ ░░ ▆▆ ▂▂ ░░ voice waveform │ │
│ │ ├────────────────────────────────────────────────────────┤ │
│ │ │ Type a message or hold space to talk… ↗ ◉ │ │
│ │ ╰────────────────────────────────────────────────────────╯ │
│ │ │
│ │ ⊕ Promote to project 📎 Attach 🧠 Memory 📁 History │
└──┴───────────────────────────────────────────────────────────────┘
5.2 Components
| Component | Purpose | Implementation note |
|---|---|---|
| Conversation thread | Full-bleed, no inner scroll columns. Max-width 760px centered in canvas. Messages alternate left/right with 16px gap. | Reuses message-rendering primitives from ChatMessageList.tsx but in a new container layout. |
| Day pip | TODAY · 10:42 between message groups. Inter 500, 11px, 1.4px tracking, silver. |
Render between message clusters when day or hour changes. |
| Voice waveform | Always visible above text input. Idle = flat baseline. Listening = volt bars. | Reuses VoiceWaveform.tsx; lift it from inside ChatPanel into the Assistant page. |
| Text input | Below waveform, same surface, hairline divider between. Sharp 8px radius, charcoal border, near-black #141414 fill, generous 24px vertical padding. |
New component AssistantInputBar.tsx. |
| Send / mic inline button | Right edge of input row. Mirrors the global mic in state. | Shared mic state via existing voice context. |
| Action strip | Below input bar. 4 contextual actions in an 8px-gap row. | New component. See below. |
5.3 The action strip
| Action | Visibility | Behavior |
|---|---|---|
| ⊕ Promote to project | Only when the active conversation is brainstormable (existing brainstormer pattern decides eligibility) | Triggers the promote-to-project transition (§5.6). Volt outline button. |
| 📎 Attach | Always | File picker. Reuses existing upload backend. |
| 🧠 Memory | Always | Opens memory slide-over from the right (340px wide). Shows what the assistant remembers about you, editable. |
| 📁 History | Always | Opens conversation list slide-over from the left (320px wide), butted against the icon rail. Lists conversations grouped by today/yesterday/this-week/older. |
The 160px conversation-list column inside the current ChatPanel moves to the History slide-over. No persistent column eats horizontal space.
5.4 The conversational home state
When you open Assistant with no active conversation, you do not see an empty thread. You see a status greeting from the assistant itself:
┌──────────────────────────────────────────────────────────┐
│ Good morning, Mikkel. │
│ │
│ Since we last talked: │
│ • nexus-design-migration: 4 commits, Phase 4 ready │
│ • personal-finance-dashboard: idle 3 days │
│ • 1 gate awaiting approval (nexus, Phase 4 audit) │
│ │
│ What do you want to do? │
└──────────────────────────────────────────────────────────┘
[voice waveform + input bar]
This replaces the global Dashboard. The dashboard is a conversational greeting from your assistant, not a grid of widgets. The data behind it (active agents, pending gates, recent completions, stale projects) comes from the same backend that the old Dashboard route used; the rendering is a single assistant message instead of a layout of cards.
Implementation: the home greeting is a synthesized assistant turn that the server generates on Assistant page load when no active conversation is selected. It's not a real chat message stored in the DB — it's a transient render.
5.5 Voice from anywhere
The mic in the top header is global. Tap it from any mode. Voice goes to the Assistant inbox, queued for next time you open Assistant.
- In Assistant mode: voice becomes a chat message in the active conversation (existing v1.6 behavior).
- In Studio / Projects / Settings: voice is captured and held as a pending Assistant message. A small volt indicator dot appears on the Assistant icon in the rail, indicating "you have queued voice waiting." Click Assistant → the queued voice is appended as the next user message and the assistant responds.
We deliberately do not parse voice as UI commands ("close issue 42") in this spec. Single inbox path; simpler; no failure modes around mode-specific intent parsing. If we ever want command-mode voice, that's a separate feature behind an explicit toggle.
5.6 The promote-to-project transition
The most important visual moment in the app. This is when a chat becomes a project.
Trigger: user clicks ⊕ Promote to project in the action strip.
Animation sequence (700ms total):
- 0–200ms: the conversation thread compresses from 100% canvas height to a 30% ribbon at the top. Easing: cubic-bezier(0.22, 1, 0.36, 1). Inset shadow Level 4 (DESIGN.md "pressed") fades in along the bottom edge of the compressed thread.
- 200–500ms: the brainstormer panel rises from the bottom of the canvas, occupying the bottom 70%. It contains the brainstormer flow: goal, acceptance criteria, gates, agent assignments (PM auto-assigned, Engineer template selected from skill aggregator).
- 500–700ms: a small
SOURCE CONVERSATIONlabel fades in above the compressed thread.
BEFORE AFTER (animation complete)
┌──────────────────────────┐ ┌──────────────────────────┐
│ ASSISTANT │ │ ASSISTANT │
│──────────────────────────│ │──────────────────────────│
│ │ │ SOURCE CONVERSATION │
│ full chat thread │ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
│ │ │ ▓ chat thread (30% ht) │
│ │ ───► │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │
│ │ │──────────────────────────│
│ │ │ BRAINSTORMER │
│ ┌─────────────────────┐ │ │ ────────── │
│ │ input + ⊕ promote │ │ │ Goal: ____________ │
│ └─────────────────────┘ │ │ Acceptance: _________ │
│ │ │ [ Create project ] │
└──────────────────────────┘ └──────────────────────────┘
On Create project:
- A new project row is created (existing project create endpoint).
- The brainstormer panel collapses.
- The compressed chat thread restores to full height.
- A small permanent banner at the top of the chat reads
→ Project: NEXUS-DESIGN-MIGRATION(link to project detail). - The user is not auto-navigated to the project. They stay in Assistant. The project appears in the Projects list and is also linked from the chat itself.
5.7 Origin chat preservation
The chat that births a project is preserved as a living conversation, not frozen as a spec snapshot. Decisions:
- The chat stays editable and continuable after promotion.
- The chat is permanently linked to the project (small banner at the top of the chat:
→ Project: PROJECT-NAME). - The project's
OVERVIEWtab has anORIGIN CHATcard linking back to the conversation. - New messages added to the chat after promotion do not automatically modify the project. The chat is the assistant's relationship; the project is the implementation contract.
- If the user wants to update the project from a later chat message, they explicitly use a
→ Update projectaction (out of scope for this spec; a future affordance).
6. Mode 2 — Studio (/studio)
Workshop selector for content generation. Replaces the current 7-tab ContentStudio.tsx and folds in ConvertPage.
6.1 Layout
┌──┬───────────────────────────────────────────────────────────────┐
│ │ STUDIO ⌘K ◉ MIC │
│ │───────────────────────────────────────────────────────────────│
│ │ │
│ ⬢│ STUDIO │
│ │ ─ ─ ─ ─ ─ │
│ ◆│ Nine workshops. Pick one or describe what you need. │
│ │ │
│ ▲│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ │ DIAGRAMS │ │ ICONS │ │ THEMES │ │
│ ◇│ │ Mermaid → │ │ SVG sets │ │ Color → full │ │
│ │ │ rendered │ │ │ │ palette │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ ⚙│ │ WALLPAPERS │ │ DOCUMENTS │ │ BRAND KITS │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │
│ │ │ SOCIAL │ │ CONVERT │ │
│ │ └──────────────┘ └──────────────┘ │
│ │ │
│ │ ───────── │
│ │ Or just describe it: "I need a 1920×1080 wallpaper of …" ↗ │
└──┴───────────────────────────────────────────────────────────────┘
6.2 Workshop card spec
- Layout: 3-column grid on
>= 1024px, 2-column on>= 640px, 1-column below. - Card: 1px charcoal border, 8px radius, transparent fill (canvas black shows through), 24px padding.
- Hover: background fades to near-black
#141414over 120ms, border stays charcoal, no transform. - Title: Inter 700, 24px, uppercase. Pure white.
- Subtitle: Inter 400, 14px, silver, single line.
- Icon glyph: Lucide icon at 32×32 in volt, top-right of card.
- Click: navigates to
/studio/{workshop-slug}.
6.3 Nine workshops
| Slug | Title | Source | Notes |
|---|---|---|---|
diagrams |
DIAGRAMS | Existing diagram-renderer | Mermaid → rendered SVG/PNG |
icons |
ICONS | Existing icon generator | SVG from description |
themes |
THEMES | Existing theme generator | Color → full theme with WCAG AA, exports |
wallpapers |
WALLPAPERS | Existing wallpaper-renderer | Desktop, mobile, social banners |
documents |
DOCUMENTS | Existing pdf-renderer | PDF reports, invoices, one-pagers |
brand-kits |
BRAND KITS | Existing brand-renderer | Full brand identity |
social |
SOCIAL | Existing social generator | Platform-ready posts, carousels |
convert |
CONVERT | ConvertPage.tsx |
File format conversion (folded in from /convert) |
Recipes (v1.8) is not a workshop. Recipes are intelligence embedded into Assistant suggestions, not a separate generation surface (see §10.2).
6.4 Workshop detail view
Each workshop, when opened, takes the user into a focused workspace:
- Mode label:
STUDIO / DIAGRAMS - Layout: two-column on desktop. Left column = parameters/prompt input. Right column = live preview.
- Action bar: bottom of right column.
[Save] [Export] [Send to Assistant]. - The "Send to Assistant" action queues the generated artifact as an attachment for the next Assistant message — closes the loop between Studio and Assistant.
6.5 Studio freeform prompt
Bottom of the workshop selector page: a freeform text input that routes to the right workshop based on intent ("I need a 1920×1080 wallpaper of …" → opens Wallpapers workshop with that prompt pre-filled). The routing is a small classifier that maps intent to workshop slug. If routing fails, the prompt becomes an Assistant message.
7. Mode 3 — Projects + Builder mode
7.1 Projects list (/projects)
┌──┬───────────────────────────────────────────────────────────────┐
│ │ PROJECTS ⌘K ◉ MIC │
│ │───────────────────────────────────────────────────────────────│
│ │ │
│ ⬢│ PROJECTS ⊕ NEW PROJECT (forest) │
│ │ ─ ─ ─ ─ ─ │
│ ◆│ │
│ │ ┌─────────────────────────────────────────────────────────┐ │
│ ▲│ │ NEXUS-DESIGN-MIGRATION ● 4 agents active │ │
│ │ │ ──────────────────────────── │ │
│ ◇│ │ 47% Phase 3 of 7 · Next gate: Phase 4 audit │ │
│ │ │ ▓▓▓▓▓░░░░░ │ │
│ │ │ $14.20 burned · last activity 8m ago │ │
│ ⚙│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ PERSONAL-FINANCE-DASHBOARD ◉ idle │ │
│ │ │ ... │ │
│ │ └─────────────────────────────────────────────────────────┘ │
└──┴───────────────────────────────────────────────────────────────┘
Project card:
- Layout: full-width on
< 1024px, 2-up on>= 1024px. Stack vertically with 16px gap. - Card: 1px charcoal border, 8px radius, transparent fill.
- Title row: project slug (uppercase) in Inter 700 16px white. Status dot on the right (forest = idle, volt pulse = working, pale yellow = waiting on gate).
- Hero stat: percentage in Inter Black 900, 72px, volt. This is the "performance stat" from DESIGN.md §4 — the visual proof that something is happening. Format:
47%with no extra label. - Sub-line:
Phase 3 of 7 · Next gate: Phase 4 audit— Inter 500, 14px, silver. - Progress bar: 8px tall, charcoal track, volt fill. Sharp 4px corners, no pill.
- Footer line:
$14.20 burned · last activity 8m ago— Inter 400, 12px, silver. - Click: navigates to
/projects/{slug}/overview.
No table view, no filters, no sort dropdowns. Search and filter live in ⌘K (active projects, projects with pending gates, etc.).
New project button: top-right corner, Forest Green per DESIGN.md secondary CTA. Click → opens an empty brainstormer panel inline (same component as the promote-to-project transition, but starting from a blank thread).
Empty state: if no projects exist, the canvas shows a single full-bleed message — NO PROJECTS YET in Inter Black 900 96px volt, with a ⊕ START YOUR FIRST PROJECT Forest Green CTA below.
7.2 Project Detail (/projects/:slug/...) — Builder mode
The header changes when you enter a project:
- Mode label becomes
PROJECTS / PROJECT-NAME(slash-separated breadcrumb, last segment in volt) - Project tab strip appears directly under the header, height 40px, charcoal bottom border:
OVERVIEW · ISSUES · AGENTS · GATES · COSTS · ACTIVITY · ORG
─────────
- Tabs: uppercase, 1.4px tracking, Inter 600 14px, silver default. Active tab: volt text + 2px volt underline. Hover: text shifts to volt.
- 24px gap between tabs, 24px left padding to align with mode label.
- The tab strip is sticky below the header strip (
position: sticky; top: 48px;).
7.2.1 OVERVIEW (default tab)
NEXUS-DESIGN-MIGRATION
─ ─ ─ ─ ─
47% 4 AGENTS ACTIVE
─── ────────────────
72px Inter Black volt Inter 700 14px silver
┌──────────────────────────────────────────────────────┐
│ Current milestone: Phase 4 — typography & radius │
│ ───────────────── │
│ • [✓] DESIGN.md drafted │
│ • [✓] MIGRATION-PLAN.md approved │
│ • [✓] Phase 1 — foundation │
│ • [✓] Phase 2 — status colors │
│ • [✓] Phase 3 — raw utility sweep │
│ • [○] Phase 4 — typography + radius ← NEXT GATE │
│ • [ ] Phase 5 — theme preview rewrite │
│ • [ ] Phase 6 — hljs │
│ • [ ] Phase 7 — visual QA │
└──────────────────────────────────────────────────────┘
┌──────────────────┐ ┌──────────────────┐
│ ORIGIN CHAT │ │ ACTIVITY (LAST 24H) │
│ "Don't just │ │ • 14 commits │
│ redesign the │ │ • 3 issues closed │
│ right rail …" │ │ • 1 gate awaiting │
│ → Open chat │ │ • $4.60 burned │
└──────────────────┘ └──────────────────┘
- Hero stat row: percentage at 72px Inter Black volt + agents-active counter at 14px silver. Two columns. The percent is the visual focal point of the page.
- Milestone checklist card: the project's milestones as a checklist. Charcoal border, 8px radius, 24px padding. Title in Inter 700 16px uppercase. Items in Inter 500 14px white (completed) or silver (pending). The next gate marker uses pale yellow
#f4f692for the bullet and a← NEXT GATElabel. - Origin chat card: small card that links back to the conversation that birthed this project. First line of the conversation is rendered as a quote.
- Activity card: rolling 24h activity feed. Reuses the global
Activitypage data, scoped to this project.
7.2.2 ISSUES tab
Project-scoped issue list. Reuses the existing IssuesList component but dropping the company filter (single workspace) and scoping by project ID. No global "Issues" route.
7.2.3 AGENTS tab
Project-scoped agent list. Shows agents assigned to this project, their roles (PM, Engineer, Generalist), status (idle/working/blocked), and current task. Reuses existing agent components but scoped.
7.2.4 GATES tab
Renamed from "Approvals" to "Gates" because it matches the brainstormer/pipeline mental model (each project has gates that the user approves to advance). Shows all pending and historical gate approvals for this project.
7.2.5 COSTS tab
Project-scoped cost burn. Agent-by-agent breakdown. Reuses cost-tracking backend.
7.2.6 ACTIVITY tab
Full activity feed for this project. The 24h preview on Overview is a slice of this.
7.2.7 ORG tab
Project-specific org chart (which agents report to which, who handles what). Shown as a tab only for projects with multi-agent orchestration; hidden for single-agent projects.
7.3 What's NOT in Builder mode
| Removed surface | Where it goes |
|---|---|
| Routines | Settings → Routines section |
| Goals | Folded into the milestone checklist on Overview |
| Inbox | ⌘K + the global Assistant icon dot indicator |
| Approvals (as global) | Becomes per-project Gates tab |
| Dashboard | Becomes Assistant conversational greeting |
8. Mode 4 — Settings (/settings)
8.1 Layout
Single-column scroll, no nested settings sub-routes. The current Paperclip nested settings tree (/instance/settings/general, /instance/settings/integrations, etc.) collapses into one scrollable page with cards.
SETTINGS
─ ─ ─ ─ ─
┌──────────────────────────────────────────────────────┐
│ WORKSPACE │
│ ───────── │
│ Root directory /home/mikkel/nexus [edit] │
│ Theme ● dark ○ light │
│ Re-run onboarding [open wizard] │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ LOCAL AI │
│ ───────── │
│ Hermes provider qwen-coder:32b (28GB VRAM) │
│ Whisper STT base.en (140MB) ▼ │
│ Piper TTS voice en_US-hfc_female-medium ▼ │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ CLOUD PROVIDERS │
│ ──────────────── │
│ Anthropic API key ●●●●●●●●●● (set) │
│ OpenAI API key (not set) │
│ Puter.js (zero-config, enabled) │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ SKILLS │
│ ────── │
│ [browse] [installed: 12] [groups: 4] │
│ ...skill aggregator UI... │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ ROUTINES │
│ ──────── │
│ Daily summary every 09:00 [edit] [pause] │
│ Cost report every Mon 08:00 │
│ + add routine │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ TELEGRAM BRIDGE │
│ ──────────────── │
│ Bot token ●●●●●●●●●● │
│ Allowed chat IDs [list] │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ ABOUT │
│ ────── │
│ Nexus 1.7-dev · Paperclip fork · MIT │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ DANGER ZONE │
│ ──────────── │
│ [Reset workspace] [Delete all conversations] │
└──────────────────────────────────────────────────────┘
8.2 Section card spec
- 1px charcoal border, 8px radius, transparent fill.
- 24px padding.
- Title: uppercase Inter 600 14px, 1.4px tracking, silver. Followed by a hairline rule.
- Content: rows of
label / value / actiontriples. - Section gap: 24px between cards.
8.3 Skills section
The Skill Aggregator (browse, install, assign per-agent) lives inside the SKILLS card. Same component, just rendered as a Settings section instead of a top-level page. Browse opens an inline drawer; assignments per-agent are configured here.
8.4 Routines section
Cron jobs that the workspace runs. Currently a top-level Routines route — demoted here. The current RoutineDetail page becomes a slide-over from the Routines section card.
9. Mobile
┌──────────────────────┐ ┌──────────────────────┐
│ ASSISTANT ◉ ⌘K │ │ STUDIO ◉ ⌘K │
│──────────────────────│ │──────────────────────│
│ │ │ ┌──────┐ ┌──────┐ │
│ chat thread │ │ │ DIAG │ │ ICON │ │
│ full bleed │ │ └──────┘ └──────┘ │
│ │ │ ┌──────┐ ┌──────┐ │
│ │ │ │ THEME│ │ WALL │ │
│ ┌──────────────────┐ │ │ └──────┘ └──────┘ │
│ │ ▒▒▒▒▒ waveform │ │ │ ┌──────┐ ┌──────┐ │
│ │ ──────────────── │ │ │ │ DOC │ │ BRAND│ │
│ │ Type or hold ◉ │ │ │ └──────┘ └──────┘ │
│ └──────────────────┘ │ │ ┌──────┐ ┌──────┐ │
│ │ │ │ SOC │ │ CONV │ │
│──────────────────────│ │──────────────────────│
│ ◆ ▲ ◇ ⚙ │ │ ◆ ▲ ◇ ⚙ │
└──────────────────────┘ └──────────────────────┘
9.1 Mobile changes from desktop
- Bottom tab bar replaces the left icon rail. Same 4 destinations (MessageCircle, Sparkles, FolderKanban, Settings). 56px tall, charcoal top border. Active tab: volt icon + volt bar above the icon.
- Top header strip stays. Loses ⌘K text (still tappable as an icon). Mic stays.
- Assistant on mobile is full-bleed. Reuses existing
MobileChatView.tsxpattern as the foundation. - Project sub-tabs become a horizontally-scrolling strip under the header. No shrinking, no wrapping.
- Studio grid drops to 2 columns on mobile.
- Promote-to-project transition on mobile uses a full-screen takeover instead of the split layout — the brainstormer slides up and covers the chat completely.
- History slide-over on mobile uses a full-screen sheet instead of a left drawer.
- Memory slide-over same.
9.2 Mobile breakpoint
Single breakpoint: >= 768px is desktop frame, < 768px is mobile frame. Matches the existing useMediaQuery("(min-width: 768px)") in the codebase.
10. Cross-cutting features
10.1 ⌘K command palette
Single global search and command surface. Opens with Cmd+K / Ctrl+K or by clicking the ⌘K button in the top strip.
Searches:
- Conversations (by title or recent message text)
- Projects (by name, status filter, gate filter)
- Issues (by title, by project)
- Agents (by name, by role)
- Recipes (by name, by tag)
- Settings (by section name)
- Studio workshops (by name)
Commands (no search):
New project→ opens brainstormerNew conversation→ starts a fresh Assistant threadOpen SettingsRe-run onboarding- Recent recipes (
Run "Generate weekly report")
Layout: centered modal, 600px wide, charcoal border, near-black fill, 8px radius. Search field at top in Inconsolata 16px. Results below in scrollable list with category headers in uppercase 11px silver. Selected result has a 2px volt left border and pale-yellow text. Keyboard nav: arrow keys + enter.
10.2 Recipes (v1.8) — embedded in Assistant
Recipes are not a destination. They are intelligence the Assistant uses to make suggestions in chat:
Assistant: "I notice you've done this kind of project before — want me to run the frontend-design + tdd + ship recipe? It took 3 hours and $4.20 last time."
[ Yes, run it ] [ Customize ] [ No, do it from scratch ]
When the user accepts, the recipe executes and the Assistant narrates progress in chat. Recipes are also browsable from ⌘K (recipes: prefix) but there is no recipe browser as a destination.
This means the icon rail has 4 slots, not 5. The reserved circle slot from the proposal is removed.
10.3 Voice as a global affordance
See §5.5. Voice is captured from any mode and queued in the Assistant inbox. Visual indicator: when there is queued voice waiting to be processed, a small volt dot appears next to the Assistant icon in the rail.
10.4 Notifications
There is no global notifications icon. The single notification surface is the volt dot on the Assistant icon when:
- Queued voice is waiting
- A pending gate needs approval
- A long-running agent task has completed
Click Assistant → the assistant tells you what's new in chat. No dropdown, no inbox panel. The Assistant's job is to tell you what changed.
11. Decisions log
Captured from the brainstorming conversation 2026-04-11. These are binding choices the spec is built on.
| # | Decision | Resolved |
|---|---|---|
| 1 | "Chat with a workshop attached" thesis | Approved as the product framing |
| 2 | Demote Paperclip pages (Issues/Agents/Routines/Goals/Approvals/Costs/Activity/Inbox/Org) from global routes to per-project tabs | Approved |
| 3 | Single workspace, no company switcher | Approved |
| 4 | Assistant is the default landing route | Approved |
| 5 | 56px left icon rail with 4 primary destinations | Approved |
| 6 | Voice mic in top header is global, always visible | Approved |
| 7 | ⌘K is the universal search; no separate search fields | Approved |
| 8 | Origin chat is editable and continuable after promotion (not frozen) | Approved |
| 9 | Recipes are embedded in Assistant suggestions, not a destination. Icon rail has 4 slots, not 5. | Approved |
| 10 | Assistant home state (no active conversation) is a conversational greeting summarizing project status — replaces the Dashboard | Approved |
| 11 | Voice in non-Assistant modes queues to Assistant inbox; no per-mode command parsing | Approved |
| 12 | Icon rail uses Lucide icons (MessageCircle, Sparkles, FolderKanban, Settings) in silver outline + volt active. No abstract geometry. | Approved |
| 13 | No sleep / lock button | Approved (kill it) |
| 14 | Skill Aggregator lives in Settings → Skills section | Approved |
| 15 | Promote-to-project transition: chat compresses to 30%, brainstormer rises into bottom 70%, inset shadow ripple | Approved — "ship that exact animation" |
| 16 | Studio is a workshop grid (9 cards), not tabs | Approved 2026-04-11; amended post-Wave-2 from 8 to 9 to restore Presentations (Active v1.7 req), see §6.3 |
| 17 | Convert folds into Studio as a workshop, no separate route | Approved |
| 18 | Project list cards use 72px Inter Black volt percentage as hero stat (DESIGN.md "performance stat" pattern) | Approved |
| 19 | ChatPanel as a global slide-in right rail is killed entirely | Approved |
| 20 | PropertiesPanel as a standing global fixture is killed | Approved |
| 21 | Single notification surface = volt dot on Assistant icon | Approved |
| 22 | Approvals → Gates rename (matches brainstormer/pipeline mental model) | Approved |
| 23 | Routines move to Settings → Routines section | Approved |
| 24 | Goals fold into milestone checklist on project Overview | Approved |
| 25 | Single mobile breakpoint at 768px; mobile uses bottom tab bar | Approved |
12. Out of scope (for this spec)
These are explicit non-decisions — we are not designing them in this overhaul, and any future work on them is a separate spec:
- Light mode visual treatment beyond what MIGRATION-PLAN.md §5 already specifies. Light mode is an accessibility alternative. The wireframes here assume dark mode.
- Per-project branding overrides. The existing
worktree-branding.tssystem is untouched. - Plugin marketplace UI. Plugins still install via CLI; in-app browse is future work.
- Voice command parsing in non-Assistant modes (Star Trek mode). Single inbox path only.
- Update-project-from-chat affordance. The chat is a relationship; the project is a contract; we don't sync the two except at promotion time.
- Multi-window / tear-off panels. Single window only.
- Multi-select / batch operations on projects or issues. Single-target only.
- Custom user themes. Binary light/dark only.
- High-contrast accessibility mode beyond WCAG AA. Not in scope.
13. Implementation phases (extends MIGRATION-PLAN.md to phases 8–16)
The visual migration plan ends at Phase 7 (visual QA of the ClickHouse repaint). This spec defines structural phases 8–16, executed in sequence after Phase 7 completes (or in parallel if a worktree is used).
Implementation directive (binding): Each phase below is implemented by dispatching subagents in parallel wherever phases or sub-tasks share no files. The user has explicitly requested subagent-driven development for this overhaul because of its size. Sequential implementation is wrong for this work. See MIGRATION-PLAN.md §11 (added in this revision) for the dispatch pattern.
| Phase | Title | Scope | Independence |
|---|---|---|---|
| 8 | Frame skeleton | New 56px icon rail, 48px top strip, killed sidebar/ChatPanel/PropertiesPanel as global elements. Routing simplified (no company prefix as a layout requirement). Old pages render in new frame but look weird — that's expected. | Foundational; blocks phases 9–15 |
| 9 | Assistant mode | Move PersonalAssistant.tsx to be the canonical Assistant route. Implement History (left) and Memory (right) slide-overs. Implement conversational home state for empty-conversation mode. Replace inline conversation column with slide-over. | Parallelizable with 10, 11 |
| 10 | Studio mode | Refactor ContentStudio.tsx from tabbed layout to a 9-card workshop grid. Fold ConvertPage in as a workshop. Keep the existing Presentations (Remotion) panel as a workshop. Add freeform Studio prompt with intent routing. Build workshop detail two-column layout. | Parallelizable with 9, 11 |
| 11 | Projects + Builder mode | Build new Projects list with hero-stat cards. Build Project Detail layout with 7-tab Builder strip. Demote global Issues/Agents/Approvals/Costs/Activity/Org/Goals/Inbox routes to per-project tabs. Rename Approvals → Gates. Reuse existing list components, scope by project ID. | Parallelizable with 9, 10 |
| 12 | Promote-to-project transition | The 700ms animation: chat compresses to 30%, brainstormer rises into 70%, inset shadow ripple, source-conversation label, post-creation banner linking chat to project. Origin chat preservation logic. | Depends on 9 (Assistant) and 11 (Projects) |
| 13 | Settings consolidation | Single-column Settings with all sections. Skills section (Skill Aggregator inline). Routines section (demoted from top-level). Re-run onboarding link. Drop nested settings routes. | Parallelizable with 12, 14, 15 |
| 14 | Voice + ⌘K globalization | Mic in top header with three states (idle/listening/speaking). Voice queues to Assistant inbox from any mode with volt-dot indicator on Assistant icon. ⌘K palette searches conversations, projects, issues, agents, recipes, settings, workshops. Replace any in-page search fields. | Parallelizable with 12, 13, 15 |
| 15 | Mobile parity | Bottom tab bar matching the desktop rail. Project sub-tabs as horizontal scroll strip. Mobile takeover for promote-to-project. Mobile sheets for History/Memory slide-overs. | Parallelizable with 12, 13, 14 |
| 16 | Cleanup pass | (a) Vocabulary sweep — remove "company" from all UI text, replace with "workspace" or remove. (b) Removal pass — delete dead code (old Layout sidebar, old ChatPanel global rail, old MobileBottomNav, theme cycle button code, dead route redirects, dead global pages). (c) Visual QA pass for the new frame. | Sequential at the end |
Estimated wave structure for parallel dispatch:
Wave 1: [ Phase 8 ] # foundational, alone
Wave 2: [ Phase 9 ] [ Phase 10 ] [ Phase 11 ] # parallel — disjoint files
Wave 3: [ Phase 12 ] [ Phase 13 ] [ Phase 14 ] [ Phase 15 ] # parallel
Wave 4: [ Phase 16 ] # sequential cleanup, alone
Each phase produces an independently committable, atomically reviewable change. The implementation plan (separate doc, written next via the writing-plans skill) will detail file-by-file changes per phase.
14. Open items deferred to implementation planning
These do not need to be answered to start writing the implementation plan, but they will need answers before the corresponding phase ships:
-
Animation primitives: the promote-to-project transition uses 700ms cubic-bezier easing. The codebase currently uses CSS transitions for everything. Decide whether to introduce Motion (
motion/react) for this single complex sequence or hand-roll with CSS keyframes. Recommendation: hand-roll. One-off animation does not justify a new dependency. -
Workshop intent classifier: the Studio freeform prompt routes to the right workshop. Decide whether classification runs server-side (LLM call) or client-side (regex/keyword matching). Recommendation: client-side regex first, LLM fallback only if regex fails.
-
Conversational home state freshness: the Assistant greeting on home-state load reads from project status. Decide cache TTL (refresh on every Assistant page open vs. cached for N minutes). Recommendation: refresh on every open, no cache. The data is small.
-
⌘K result ranking: when search returns a mix of conversations, projects, issues, etc., decide the ranking heuristic. Recommendation: recency-weighted, with current-project items boosted if user is currently in a project.
-
Project tab strip overflow on narrow desktop: if 7 tabs don't fit at narrow desktop widths, decide whether to wrap, scroll, or collapse to a
more ▼menu. Recommendation: horizontal scroll with edge fade, matching mobile.
15. Approval
Approved by Mikkel 2026-04-11 in conversation. Locked-in items: §1 (thesis), §2 (IA), §4 (frame), §5–§8 (modes), §9 (mobile), §10 (cross-cutting), §11 (decisions log), §13 (phase plan).
Next step: invoke the writing-plans skill to generate a per-phase implementation plan derived from this spec.