Skip to content
Development
Agent

renderer-patterns

All paths are relative to `apps/emdash-desktop/`.

From plugin
emdash
5.7k32 skills32 agents

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

All paths are relative to `apps/emdash-desktop/`.

Agent definition

renderer-patterns.md

Renderer Patterns

All paths are relative to `apps/emdash-desktop/`.

Feature `api/` Surfaces

A feature slice's `api/` directory is its contract with other slices, nothing more:

  • `api/` holds the Wire contract, shared types, the domain client, and store interfaces + selectors
  • Cross-slice UI flows through the contributions registries (`contributions/browser.ts`,

aggregated by `src/core/manifests/browser/browser-contributions.ts`), never through `api/`

  • React components never live under `api/`; they belong in `browser/`

(enforced by the `emdash/no-tsx-in-api` lint rule; the shrink-only allowlist at repo-root `tooling/oxlint/allowlists/api-surfaces.json` is empty and must stay that way)

Modal System

Modals are renderer-only feature contributions. They render as a stack, with only the top modal responding to outside presses and close commands.

  • `src/core/primitives/modals/react/` — modal definitions, catalog types, host context, the modal

store (`modal-store.ts` — active modal state and promise outcomes), and the close-guard hook (`use-close-guard.ts`)

  • `src/core/features/*/contributions/browser.ts` — feature-owned `modalDefs`
  • `src/core/manifests/browser/modal-catalog.ts` — application modal catalog
  • `src/core/manifests/browser/modal-api.ts` — catalog-bound `openModal`, `useOpenModal`, and

`useModalController`

  • `src/renderer/lib/modal/modal-renderer.tsx` — resolves and renders the active catalog definition

**Adding a modal:** 1. Create the component in its feature slice. Caller data is ordinary component props; completion uses `useModalController(id)`. 2. Define it with `defineModal<TResult>()({ id, component, ...chrome })`. 3. Add the definition to the owning slice's `modalDefs`. 4. Open it through the typed API and branch on the outcome:

const openMyModal = useOpenModal('myModal');
const outcome = await openMyModal({ projectId: '123' });
if (outcome.success) {
  useResult(outcome.data);
}

**Rules:**

  • The manifest catalog is the only runtime registry; do not add renderer-local registrations
  • Keep the catalog import type-only outside runtime resolution points
  • Use standalone `openModal` outside React and `useOpenModal` inside components
  • Use `useCloseGuard` during critical operations that must block passive dismissal
  • `useModalController` exposes `hasActiveCloseGuard` when modal UI must reflect guard state
  • Use `outcome.error.reason` when a chained flow must distinguish explicit back/cancel actions from

passive or navigation dismissal

View System

Views use a contributions + catalog + parameterized navigation pattern.

  • `src/core/primitives/views/` — `defineView` (schema-backed view definitions with `params`,

`layout`, and optional `historyKey`) and the React runtime bindings (`registerViewRuntime`, which binds slots such as `MainPanel`, `WrapView`, and `TitlebarSlot`)

  • `src/core/features/*/contributions/views.ts` — feature-owned view definitions
  • `src/core/manifests/browser/view-catalog.ts` — the aggregated application view catalog
  • `src/core/primitives/navigation/` — the navigation engine: `NavigationStore` and

`NavigationHistoryStore` (app-scoped), `getNavigation()` selectors, and React hooks (`useNavigate`, `useViewParams`, `useCurrentViewParams`); the renderer bootstrap seeds the catalog through `seedRendererNavigationHost()` before the app scope creates the stores

  • `src/core/primitives/layouts/react/layout-provider.tsx` — workspace chrome context: reads

the per-project workspace chrome command store and exposes the layout-storage facade

**Key behaviors:**

  • Calling a view definition is the only way to construct a `ViewRef`

(`taskViewDef({ projectId, taskId })`); the schema validates params at construction, and `safeRef()` is the boundary for untrusted values

  • `navigate(ref)` (from `useNavigate`) takes a `ViewRef`; definitions whose params are all

optional can be called without an argument

  • Params persist per-view (`useViewParams(def)` returns the current or last-recorded params);

`useCurrentViewParams(def)` also returns `setParams` for updating the active view's params

**Rules:**

  • Views without a `historyKey` are singleton history places; `historyKey` splits history per

entity (for example per task)

  • Add new views through the owning slice's `contributions/views.ts` and register them in

`src/core/manifests/browser/view-catalog.ts`

Workbench Layout State

Workbench layout follows a strict ownership model (see `.scratch/workbench-state-architecture/spec.md` history for rationale):

  • **Chrome state lives in command stores, one per subject.** Task chrome

(`sidebarCollapsed`, `sidebarTab`, `terminalDrawerOpen`) and workspace chrome (`leftSidebarOpen`, `zen`) are memento-backed state objects mutated only through named commands (`toggleSidebar`, `openSidebarTab`, `enterZenMode`, ...) — never through field setters. The shared mechanism is `defineChromeStore` in `src/core/primitives/chrome-stores/`.

  • **Panel visibility is store-driven conditional rendering, never programmatic panel

writes.** Closed = unmounted. Collapsible surfaces bind through `useCollapsiblePanelBinding` from `@emdash/ui` (next to `Resizable`), which turns drag-below-threshold into a semantic close command. No `panel.collapse()` / `expand()` / `resize()` / `setLayout()` calls exist in app code, and no `display:none` toggling of workbench surfaces.

  • **Pixel sizes belong to react-resizable-panels alone**, persisted via

`useResizableDefaultLayout` with a memento-backed `LayoutStorage` facade (`createLayoutStorage` in `src/core/primitives/mementos/browser/`). Sizes are never MobX observables and never persisted to localStorage. The workspace outer layout is app-scoped because it belongs to the workbench shell; task-internal panel layouts are task-scoped.

  • **Persisted view state renders below a hydration gate.** The task view gates on

`space.isHydrated`; the storage facade dev-asserts on reads b

Read more
Ships withemdash

Emdash is the Open-Source Agentic Development Environment (🧡 YC W26). Run multiple coding agents in parallel. Use any provider.

Get the whole plugin
Stats
5,748
Stars
589
Forks
Active
Maintenance
TypeScript
Language
Apache-2.0
License
8m ago
Last commit
1y ago
Created

Repo: generalaction/emdash

Other agents on emdash.