Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
38d7621
docs(ui): add architecture README for @maka/ui
Astro-Han Jul 11, 2026
54e2f0d
docs(desktop): add architecture READMEs for the desktop app and renderer
Astro-Han Jul 11, 2026
019cb03
docs(ui): add architecture README for @maka/ui
Astro-Han Jul 11, 2026
7183d49
docs(desktop): add architecture READMEs for the desktop app and renderer
Astro-Han Jul 11, 2026
9384a7e
docs(frontend): fix round-3 review findings in READMEs
Astro-Han Jul 11, 2026
a1d7e42
docs(frontend): fix round-4 review findings in READMEs
Astro-Han Jul 11, 2026
c988401
docs(frontend): fix round-5 review findings in READMEs
Astro-Han Jul 11, 2026
3f51262
docs(frontend): fix round-6 review findings in READMEs
Astro-Han Jul 11, 2026
180a35d
docs(ui): fix the feature-component barrel rule (index.ts re-exports …
Astro-Han Jul 11, 2026
0c5cd10
docs(renderer): fix the @theme bridge and local-var rule
Astro-Han Jul 11, 2026
22bd75a
docs(renderer): describe the @theme bridge accurately (overlaps, not …
Astro-Han Jul 11, 2026
11a865a
docs(frontend): fix round-10 review findings
Astro-Han Jul 11, 2026
582f372
docs(frontend): fix round-11 review findings
Astro-Han Jul 11, 2026
8cfad5a
docs(ui): correct the LiveIndicator/streamVariants comments (#712 ret…
Astro-Han Jul 11, 2026
b7c6f85
docs(ui): revert the source-comment edits; README is the barrel-rule …
Astro-Han Jul 11, 2026
5b2bcf2
docs(ui): point inline barrel comments at README (true root correction)
Astro-Han Jul 11, 2026
dc75955
docs(ui): exhaustively point all chat.tsx barrel comments at README
Astro-Han Jul 11, 2026
66bcf23
docs(ui): revert all source-comment edits — PR is docs-only (3 READMEs)
Astro-Han Jul 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# @maka/desktop

The Electron desktop app: `main` (Node/Electron main process) + `preload` (context bridge) + `renderer` (React UI). This file covers the three-layer split and the IPC contract. For build/test commands and the test-layer selection guide, see the top-level `README.md`; for the renderer interior, see `src/renderer/README.md`.

## Three layers

| Layer | Path | Role |
|---|---|---|
| main | `src/main/` | Node/Electron main process. Owns window lifecycle, credentials, attachments, permissions, IPC handlers, and the bridge to `@maka/runtime` + `@maka/storage`. |
| preload | `src/preload/preload.ts` (single file) | `contextBridge.exposeInMainWorld('maka', …)` — the only surface the renderer may call to reach Node/Electron. No Node API is directly exposed. |
| renderer | `src/renderer/` | React UI body. See `src/renderer/README.md`. |

## main process layout

`src/main/` is flat with a naming convention:

| Suffix | Role | Examples |
|---|---|---|
| `*-ipc-main.ts` | Exports a `register*Ipc(...)` that wires `ipcMain.handle` / `ipcMain.on` for one IPC domain | `connections-ipc-main`, `daily-review-ipc-main`, `memory-ipc-main`, `web-search-ipc-main`, `workspace-resources-ipc-main` |
| `*-main.ts` / `*-service.ts` | A service owned by main (no `ipcMain` calls of its own) | `daily-review-main`, `system-prompt-main`, `oauth-model-connections-main`, `local-memory-service` |
| `*-guard.ts` | Validation / security boundary | `external-link-guard`, `open-path-guard`, `permission-response-guard` |
| (other) | Window, state, platform wiring | `main.ts` (entry), `main-window`, `window-state`, `theme-source`, `credential-store`, `skills`, `attachment-*` |

Sub-folders: `browser/` (embedded browser view), `oauth/`, `search/` (thread search), `web-search/`, `types/`. The browser IPC handler itself (`browser-ipc-main.ts`) is flat in `src/main/`, not under `browser/`.

`main.ts` startup order: stores and the runtime/controller are created synchronously at module load; `registerIpc()` runs at top level, **before** `app.whenReady()`; inside `whenReady`, the main window is created **hidden** early and background startup (credential migration, connection bootstrapping, telemetry, bots, gateway, schedulers) runs concurrently without blocking first paint. The window is created hidden and revealed after the renderer's first AppShell paint (the `window:notifyRendererReady` gate in `app.tsx`); a fallback timer reveals it if the renderer never signals, so a fail-soft loading state can show (e.g. if `main.tsx`'s onboarding prefetch times out). The real invariant for IPC: handlers must be registered before the renderer entry runs, because `main.tsx` prefetches the onboarding snapshot before mounting React. Background startup may mutate state after the renderer's first read, so don't assume it has already settled when wiring the UI.

## IPC contract

Three patterns, all rooted in preload's `maka` namespace. Channel names are `<domain>:<action>`.

- **Request/response** — `ipcRenderer.invoke('<domain>:<action>', …args)` in preload ↔ `ipcMain.handle('<domain>:<action>', …)`. The handler lives either inline in `main.ts` (e.g. `sessions:list`, `settings:get`) or in a `*-ipc-main.ts` extracted by domain (e.g. `connections-ipc-main`, `daily-review-ipc-main`). Both forms coexist; prefer extracting a new domain to its own `*-ipc-main.ts`.
- **Main→renderer push** — main sends through the safe-send guard (`safeSendToRenderer` via `mainWindowController.send`), not raw `webContents.send` (which throws when the window/`webContents` is destroyed); preload subscribes via `ipcRenderer.on` and returns an unsubscribe fn (e.g. `sessions:changed`, `plans:changed`, `artifacts:changed`, `gateway:statusChanged`). The safe-send contract test scans a fixed list of main-source files for direct `mainWindow.webContents.send(...)` forms — new `*-ipc-main.ts` files aren't auto-covered, so route sends through the guard in every new file (an alias for `mainWindow` can bypass the literal scan).
- **Renderer→main fire-and-forget** — `ipcRenderer.send('<domain>:<action>', …)` in preload ↔ `ipcMain.on('<domain>:<action>', …)`. Used when no response is needed (e.g. `browser:active-session`, `browser:setViewport`).

Adding a new IPC surface: if extracting, write the `*-ipc-main.ts` exporting a `register*Ipc(...)`, import it in `main.ts`, and call it inside `registerIpc()`; add the matching method to the `maka` namespace in `preload.ts`; add the method to the `window.maka` type in `src/global.d.ts` (the renderer's typed bridge — without it, renderer calls get a TS error); keep the `<domain>:<action>` channel naming. A handler file that isn't registered in `registerIpc()` compiles but never mounts.

## Data flow

```
renderer (React)
└─ window.maka.<ns>.<method>(…) // typed surface, see preload.ts
└─ ipcRenderer.invoke / send / on
└─ main: safeSendToRenderer / ipcMain.handle / ipcMain.on
└─ @maka/runtime (agent runtime) + @maka/storage (JSONL persistence)
```

The renderer never imports `@maka/runtime` or `@maka/storage` at runtime — all Node-side access goes through the preload `maka` bridge. The renderer only pulls `import type` from them for a few shared types. Types shared across the IPC boundary mostly come from `@maka/core`, with some from `@maka/runtime`, `@maka/storage`, and `@maka/ui` (see `preload.ts` imports).

## Convergence note

The renderer side carries the frontend convergence debt (hand-rolled CSS, primitive overrides); see `src/renderer/README.md`. The main process itself is not part of that convergence — its boundaries (IPC channel names, the preload bridge, the `*-guard.ts` files) are stable contract seams.
49 changes: 49 additions & 0 deletions apps/desktop/src/renderer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Renderer (`apps/desktop/src/renderer`)

The Electron renderer process: the React UI body of the Maka desktop app. React + Vite + Tailwind v4, consuming `@maka/ui` primitives. This is the **frontend governance hot zone** — most of the hand-rolled CSS and primitive-override transitional debt lives here.

For the main/preload/renderer split and the IPC contract, see `apps/desktop/README.md`. This file covers the renderer interior.

## Entry

`main.tsx` → `app.tsx` → `AppShell` (`app-shell.tsx`). `index.html` is the Vite HTML shell. `main.tsx` prefetches the onboarding snapshot before mounting React so the normal-path first commit paints the real surface (if the prefetch times out it mounts with `null` and a fail-soft loading state); `app.tsx` wraps `AppShell` in `ToastProvider` + `ErrorBoundary`.

`styles.css` is the **only** bundled style entry: it `@import`s Tailwind, fonts, `maka-tokens.css`, `reference-shell.css`, and every `styles/*.css`. Per CSS governance, `styles.css` may only contain `@import` / `@source` / `@theme` / top-level orchestration — real selector rules go in `styles/*.css`. One contract-pinned exception: `index.html` carries an inline `.maka-preload` skeleton with hardcoded colors (no CSS variables — `maka-tokens.css` hasn't loaded yet) so there's no blank window during the CSS + JS load gap; `createRoot` replaces it on mount.

## AppShell + the action modules

`app-shell.tsx` is the shell component: owns session state, wires the `@maka/ui` panels (SessionListPanel, ChatView, Composer — ChatView renders the tool stream via `ToolTrow`), and mounts lazy panels (ArtifactPane, BrowserPanel). It is supported by a set of `app-shell-*` modules, each a narrow slice of shell logic split by one concern (e.g. `app-shell-session-events.ts`, `app-shell-chat-actions.ts`, `app-shell-plan-actions.ts`, `app-shell-effects.ts`, `app-shell-stop-action.ts`, `app-shell-overlays.tsx`). Most follow `app-shell-<scope>-<action>.ts(x)`; a few single-word slices like `app-shell-effects.ts` or `app-shell-copy.ts` drop the action segment. Keep a slice to one concern; if it grows, split along the same seam.

`settings/` holds the settings pages and the `SettingsModal` shell — one page per `SettingsSection` (defined in `@maka/core`); the models/providers page is `ProvidersPanel`. Plus the `provider-*` files and the shared `settings-rows` / `settings-skeleton` / `settings-surface` helpers.

## Styles & tokens

| File | Role |
|---|---|
| `maka-tokens.css` | The main source of CSS tokens (color / shadow / typography / radius / spacing / motion / z / layout), plus a large recipe section at the tail (base styles, utilities, component recipes, animations). Transitional: tokens and recipes coexist in one file. Its `@theme inline` block holds color aliases. |
| `reference-shell.css` | A target-layout shell rebuild, hand-authored from a reference-implementation extract (its header comment documents the provenance). **Transitional** — meant to be folded back into the token/style system and removed. |
| `styles/*.css` | Per-surface hand-written recipes (e.g. `chat-*`, `sidebar`, `composer`, `palette`, `settings/*`, `module-pages/*`). |

Token authoring rule: custom CSS variables go in `maka-tokens.css`. The `@theme inline` Tailwind bridge is **not** cleanly split — both `maka-tokens.css` and `styles.css` carry one, and their color aliases overlap (e.g. `--color-background`, `--color-accent`, `--color-muted` appear in both, and `--color-muted` maps to `--foreground-5` in `styles.css` but to `--muted` in `maka-tokens.css`); `styles.css`'s block also carries the typography / line-height / font-weight / tracking / spacing / radius / shadow bridges. Some bridge values are contract-pinned to a file — spacing / letter-spacing / typography contracts pin theirs to `styles.css`, `foreground-tier` pins `--color-muted-foreground` to `maka-tokens.css`, `design-system-governance-406` pins `--color-control` to `styles.css` — but other overlapping color aliases (e.g. `--color-background`, `--color-accent`, `--color-muted`) aren't pinned to a file; check the owning contract before moving one. New component-local vars should carry `/* local: ... */` (existing ones don't all have it yet). No new hardcoded color / radius / z-index.

Note the `--foreground-N` split: the wash stops (`-2/-3/-5/-8/-10`) are surface fills for backgrounds and borders, **not** text. The 3-tier semantic aliases (`--foreground` / `--foreground-secondary` / `--muted-foreground`) are the text-color vocabulary. They are separate concerns — don't collapse the wash stops into the text aliases.

## New code: primitive first, CSS last

1. Reach for a `@maka/ui` primitive or a Tailwind utility class first.
2. Only if no primitive carries it, write CSS in the matching `styles/<surface>.css`, following `docs/frontend-css-governance.md` (layer rules, the unlayered override list, the `!important` audit, the dead-CSS allowlist).
3. Don't add a token without registering it in `maka-tokens.css`.

## Convergence direction (transitional surfaces)

Acknowledged transitional states — not TODOs; track work in issues/PRs.

- Hand-written `styles/*.css` recipes + overrides on `@maka/ui` primitives: end state is structure carried by primitives, renderer CSS left only with layout primitives can't cover. Per-recipe retirement is tracked in `notes/ui-convergence-map-2026-07-09.md`.
- `reference-shell.css`: end state is folded into the token/style system and the file removed.
- `maka-tokens.css` mixing tokens + recipes: end state is tokens-only here, recipes living on primitives / `styles/`.

## Contracts & guardrails

- CSS cascade / layer / `!important` / dead-CSS / token rules: `docs/frontend-css-governance.md`. The dead-CSS check runs from the repo root via `check:release` (`scripts/check-dead-css.mjs --check`); its baseline is `scripts/check-dead-css-baseline.json`.
- Component 5-state / ARIA / token / copy contracts: `docs/design-system.md`.
- Where either doc disagrees with the code or the contract tests, the code and the tests are the source of truth. Key guardrail tests live in `apps/desktop/src/main/__tests__/` (style-layer-cascade, important-audit, typography / spacing / radius / state-token / foreground-tier governance). Build/test entry points are the npm scripts in the root `package.json` (see the top-level `README.md`).
51 changes: 51 additions & 0 deletions packages/ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# @maka/ui

Shared UI layer for the Maka desktop app: React + Tailwind v4 + shadcn (base-nova) + `@base-ui/react`, bound to Maka's token system. Consumed at runtime by `apps/desktop`'s renderer; the preload bridge also imports types from it (`import type` only).

This package is the **target carrier of the frontend convergence**: hand-rolled renderer CSS recipes are being retired onto primitives exported here. When in doubt, extend a primitive rather than add CSS at the call site.

## Layer map

Four export surfaces, in the order to look:

| Surface | Role | Status |
|---|---|---|
| `src/primitives/` | One file per primitive (e.g. `accordion`, `badge`, `chip`, `dialog-header`, `input`, `page-header`, `tabs`, `textarea`, `toolbar`, `tooltip`, …). **New primitives go here.** | target layer |
| `src/ui.tsx` | Earlier Base UI wrappers + `buttonVariants` (cva) in one file (Button, Checkbox, Dialog/AlertDialog, Select, Switch, Toggle, Radio, Progress, Separator, Field/Label). | transitional — wrappers migrate into `primitives/` as touched (Badge moved to `primitives/badge.tsx` earlier; Button/Select/etc. still live here) |
| `src/*.tsx` / `src/*.ts` (top-level) | Feature components + pure logic (e.g. `chat-view.tsx`, `composer.tsx`, `permission-dialog.tsx`, `session-list-panel.tsx`, plus pure helpers like `materialize.ts`, `redact.ts`, `smooth-stream.ts`). | stable |
| `src/components.tsx` | Re-export barrel for the feature components above (ChatView, Composer, PermissionDialog, …). | stable |

`src/index.ts` is the package barrel. It follows an **off-barrel convention**: some styling tables and per-surface helpers are deliberately *not* re-exported, so they stay renamable/removable without a public-API break. A symbol earns barrel export when it has a **cross-package consumer or an explicit public-API need** — not merely a second in-package consumer (`attachment-file-card` has two in-package consumers, `chat-view` and `composer`, but stays off-barrel). Don't add to the barrel speculatively. This README is the source of truth for the barrel promotion rule.

## `data-slot` hooks

Most primitives expose a stable `data-slot="<name>"` attribute so renderer CSS can target a slot (e.g. `[data-slot="dialog-header"]`) rather than a drifting class. Exceptions without one: `choice-card`, `spinner`, `scroll-area`, `settings-segmented` — their styling lives on the consumer's class or an underlying Base UI component, so a `[data-slot="..."]` selector won't match. New primitives should still expose a `data-slot`.

## Consuming

```ts
import { Button, ChatView, Composer, Badge, Chip, PageHeader, useToast } from '@maka/ui';
```

Sub-path exports (declared in `package.json` `exports`): `@maka/ui/artifact-preview-registry`, `@maka/ui/assistant-stream`, `@maka/ui/icons`, `@maka/ui/maka-uri`, `@maka/ui/smooth-stream`. (`@maka/ui/icons` re-exports Lucide symbols; model-provider brand logos live in the renderer's `settings/provider-*`, not here — bot-provider logos are in `@maka/ui`'s `bot-brand-logo`.)

Renderer CSS may target a primitive via its `data-slot` attribute, never by overriding the primitive's own utility classes.

## Where new code goes

- **New primitive** (button-like, dialog-like, form control) → a new file in `src/primitives/`, exposing `data-slot`, re-exported from `index.ts` (primitives are the public surface).
- **New feature component** → top-level `src/<name>.tsx`, kept as a relative import until it has a cross-package consumer or an explicit public-API need; then re-export it from `src/components.tsx` (`index.ts` does `export * from './components.js'`, so it lands on the barrel automatically).
- **Don't** add a per-surface hand-rolled CSS recipe in the renderer if a primitive can carry it — extend the primitive's API/slots instead.
- **Don't** re-export a symbol onto the barrel without a cross-package consumer or explicit public-API need; keep it a relative import even with multiple in-package consumers (a cross-package consumer can't use a relative import — `previewVariants` is re-exported for exactly that reason).

## Convergence direction (transitional surfaces)

Acknowledged transitional states — not TODOs; track actual work in issues/PRs.

- `ui.tsx` ↔ `primitives/`: end state is one primitive layer in `primitives/`. Wrappers in `ui.tsx` move over when touched (Badge is the precedent). `buttonVariants` has external consumers, so its move is a coordinated rename, not a silent one.

## Contracts & guardrails

Component contracts (5-state, ARIA, keyboard, tone/token per component), the token registry, and anti-patterns live in `docs/design-system.md`. Where that doc disagrees with the code or the contract tests (`*-converge-contract.test.ts`, `state-token-governance-*`, …), the code and the tests are the source of truth.

Selected primitives and features have stories (`stories/`) and unit tests (`src/__tests__/`); coverage is partial, not exhaustive. Build/test entry points are the npm scripts in the root `package.json` (see the top-level `README.md`).
Loading