Adding a New Feature
This chapter walks through the typical path for adding a feature to CommandUI.
Step 1: Domain types
Section titled “Step 1: Domain types”Start in packages/domain/. Define any new types your feature needs. Domain types are pure TypeScript — no runtime dependencies, no React, no Zustand.
packages/domain/src/ index.ts — re-exports everything history.ts — HistoryItem, etc. workflow.ts — Workflow, WorkflowRun, etc. memory.ts — MemoryItem, MemorySuggestion, etc. settings.ts — SettingsSnapshot, etc.If your feature introduces a new entity (e.g., a Snippet type), create a new file and export from index.ts.
Step 2: API contract
Section titled “Step 2: API contract”If your feature needs backend communication, define the request/response types in packages/api-contract/:
// In packages/api-contract/src/export type MyFeatureRequest = { ... };export type MyFeatureResponse = { ... };Step 3: State store
Section titled “Step 3: State store”If your feature needs client-side state, add a store in packages/state/src/index.ts:
export const useMyFeatureStore = create<MyFeatureState>()((set) => ({ items: [], addItem: (item) => set((s) => ({ items: [...s.items, item] })), // ...}));Follow the existing pattern: state + setters, no side effects in stores.
Step 4: Backend command (Tauri)
Section titled “Step 4: Backend command (Tauri)”If your feature needs persistence or system access, add a Tauri command:
- Create a handler in
apps/desktop/src-tauri/src/commands/ - Register it in
main.rs - Add a client function in
apps/desktop/src/features/
Step 5: Mock bridge handler
Section titled “Step 5: Mock bridge handler”Add a mock handler in apps/desktop/src/lib/mockBridge.ts so browser preview works:
my_feature_action(args) { // Simulate the backend response return { ok: true, items: mockItems };},Step 6: UI component
Section titled “Step 6: UI component”Create your component in apps/desktop/src/components/. Follow existing patterns:
- Props type at the top
- Functional component with hooks
- Presentational — receives data and callbacks, doesn’t invoke backends directly
Step 7: Wire in AppShell
Section titled “Step 7: Wire in AppShell”AppShell is the coordinator. You’ll need to:
- Import your store and read state
- Add handler functions for user actions
- Pass data and callbacks as props to your component
- Add any needed event subscriptions
Step 8: CSS
Section titled “Step 8: CSS”Add styles to apps/desktop/src/styles/globals.css. Use existing CSS variables for colors, spacing, and radii.
Step 9: Keyboard shortcut (optional)
Section titled “Step 9: Keyboard shortcut (optional)”If your feature needs a shortcut, add it to the shortcut definitions in AppShell and the shortcuts.ts module.
Checklist
Section titled “Checklist”- Domain types defined
- API contract types defined (if needed)
- State store created (if needed)
- Backend command implemented (if needed)
- Mock bridge handler added
- Component created
- Wired into AppShell
- CSS added
-
pnpm typecheckpasses - Browser preview works
- Tests added for new logic