Architecture
Hudson is a monorepo with a web workspace, a web SDK, Apple-native Swift packages, and small runtime services.
Monorepo layout
hudson/
apps/web/ # Next.js 16 app + marketing + deploy (App Router)
app/
app/page.tsx # `/app` route — mounts <WorkspaceShell>
layout.tsx # Root HTML + globals.css
globals.css # Tailwind v4 + scrollbar styles
apps/ # App implementations
registry.ts # Aggregates built-in + local apps
stage-design/ # Compact reference app
theme-designer/
hudson-docs/
hudson-ai/
intent-explorer/
hud-logger/
services/
terminal/
workspaces/
hudsonOS.ts # Default workspace composition
index.ts # Re-exports
local/
apps.local.ts # Gitignored — dev-local app registrations
workspaces.json
lib/ # Shared utilities
hooks/ # Shared hooks
services/ # Service registry (hx, etc.)
api/ # Next.js API routes (AI, saves, etc.)
packages/
web/
hudsonkit/ # Web SDK (workspace-internal for now)
src/
index.ts # Public main entry — types, hooks, AI, platform
app-shell.ts # `hudsonkit/app-shell` subpath
chrome.ts # `hudsonkit/chrome`
overlays.ts # `hudsonkit/overlays`
context-menu.ts # `hudsonkit/context-menu`
canvas.ts # `hudsonkit/canvas`
windows.ts # `hudsonkit/windows`
theme.ts # `hudsonkit/theme`
terminal.ts # `hudsonkit/terminal`
shell.ts # `hudsonkit/shell` (back-compat barrel)
workspace/shell/ # WorkspaceShell — multi-app orchestrator (`hudsonkit/workspace`)
styles/bundle.css # Source for the compiled CSS bundle
dist/styles.css # Compiled via `bun run build:css`
components/
AppShell.tsx # Single-app shell (default)
chrome/ # Frame, NavigationBar, SidePanel, StatusBar, CommandDock, Minimap, ZoomControls, AnimationTimeline
canvas/Canvas.tsx
windows/AppWindow.tsx
overlays/ # CommandPalette, TerminalDrawer, HudsonContextMenu
AI.tsx, TerminalRelay.tsx
types/
app.ts # HudsonApp interface
workspace.ts # HudsonWorkspace interface
intent.ts, port.ts, service.ts
hooks/ # usePersistentState, useAppSettings, useHudsonAI, useTerminalRelay
platform/ # Platform adapter (web, desktop)
lib/ # theme, sounds, logger, viewport, manifest
create-hudson-app/ # App scaffold CLI
native/
apple/
HudsonKit/ # Swift package for iOS/macOS shells
services/
hudson-relay/ # WebSocket PTY relay service
docs/ # Architecture, case study, builder notes
Data flow (WorkspaceShell)
apps/web/app/app/page.tsx
└── <WorkspaceShell workspaces={allWorkspaces} defaultWorkspaceId="hudsonOS" />
│
├── Nests every app's Provider recursively
│ AppA.Provider → AppB.Provider → ... → WorkspaceShellInner
│
└── WorkspaceShellInner
├── Calls each app's hooks (useCommands, useStatus, useSearch, ...)
├── Merges commands from all apps + shell commands → CommandPalette
├── Renders Frame with chrome (NavigationBar, SidePanel, StatusBar, CommandDock)
├── Canvas mode: wraps windowed apps in AppWindow, renders native apps directly on canvas
├── Panel mode: single focused app fills the viewport inset
└── Merges app intents → IntentCatalog (for LLM/voice/search)
Data flow (AppShell)
app/page.tsx (consumer app — e.g. premotion, external project)
└── <AppShell app={catalogApp} />
│
└── app.Provider wraps everything
└── AppShellInner reads app.hooks, fills slots
├── NavigationBar (title + search + nav center + nav actions)
├── SidePanel left (app.slots.LeftPanel + LeftFooter + CommandDock)
├── SidePanel right (app.slots.Inspector | RightPanel + tools accordion)
├── Content area (app.slots.Content)
├── StatusBar (app.hooks.useStatus + terminal toggle + clock)
├── TerminalDrawer (app.slots.Terminal or placeholder)
└── CommandPalette (fed by useCommands + shell shortcuts)
Key architectural decisions
Provider-first state
Each app owns its state via a React context Provider. The shell never touches app internals — it only reads through the app's declared hooks. Apps stay fully decoupled.
Hook Bridge pattern
The shell calls an app's hooks inside that app's Provider scope via a small internal Bridge component. This means hooks can call useMyAppContext() safely. The shell doesn't import app modules — it only calls the hook functions registered in the HudsonApp object.
Refs, not state, during drag/pan/resize
Window bounds, pan/zoom offsets, and resize deltas are tracked in useRef during interaction — React re-renders are suppressed. State gets flushed on a debounce (BOUNDS_FLUSH_MS = 500 in WorkspaceShell.tsx) so the minimap and persistence observe stable values without dragging the whole tree on every mouse move.
See perf-drag-resize-patterns.md for the full set of tricks.
Static intent declarations
Intents are declared as plain data (not runtime functions) so they can be indexed, serialized, and searched without executing app logic. An intent executor bridges discovered intents back to the app's live commands at runtime.
Recursive Provider nesting
All app Providers wrap the entire workspace content. This enables cross-app context sharing when needed; isolated state is the default.
Registry-driven app loading
Hudson's own demo registers its built-in apps in apps/web/app/apps/registry.ts and merges in optional developer-local apps from a gitignored apps/web/app/local/apps.local.ts (auto-created as an empty stub on first run). The same split — committed shared registry + gitignored local override — works for any consumer that wants a stable default workspace alongside per-developer private apps.
State persistence
All persistent UI state uses usePersistentState() backed by localStorage:
| Key pattern | Purpose |
|---|---|
hudson.ws.{workspaceId}.win.{appId} | Window bounds (canvas mode) |
hudson.ws.{workspaceId}.mode | Current workspace mode |
hudson.session | Active session / focused app |
appshell.{appId}.left / .right | Side panel collapsed state (AppShell) |
appshell.{appId}.leftW / .rightW | Side panel widths (AppShell) |
{appId}.{key} | App-specific state (owned by app Provider) |
App code should prefer the {appId}.{key} namespace and use usePersistentState('myapp.notes', []) — the hook handles serialization, SSR safety, and cross-tab sync.
Build & dev
| Command | Purpose |
|---|---|
bun install | Install all workspace deps |
bun dev | Start Hudson dev server on :3500 |
bun run build | Production build |
bun run lint | ESLint |
bun run relay | Start the terminal relay WS server |
cd packages/web/hudsonkit && bun run build:css | Rebuild the SDK's compiled CSS bundle |