# Card Window

The standalone per-card window. Opened by fast double-click, ⌘↩, or the context menu; at most one window per card (reopening a live card focuses the existing window); board and card windows share one live store, so edits reflect everywhere instantly (02-architecture.md).

> **Status: designed** — composition, body column, and the attributes sidebar are all settled. The storage-facing rules the pathfinder settled painfully — Preview/Edit over WYSIWYG, the untouched-body byte-identical guarantee, validate-before-write on the raw outlet, dirty-buffer-wins — carry over unchanged.

## Composition

Two full-height columns: a wide **body column** (leading) and a narrow **attributes sidebar** (trailing), each scrolling independently. Visual reference: [assets/card-sidebar-reference.png](assets/card-sidebar-reference.png) — a GitLab-style issue pane; its *pattern* (stacked small-caps sections, quiet read-first rows, actions at the bottom) is what carries over, not its enhanced-schema content.

- **Body column, top to bottom**: the **title field** — large, borderless; edits write through to frontmatter on commit (Return or focus loss); clearing it removes the `title` key (titles are optional — the untitled placeholder shows here as on the face); Return commits and moves focus into the body. **Escape abandons** (settled — the board inline rename's abandon, applied here): the field reverts to the on-disk title and focus moves into the body, never a commit. The Edit-mode collision resolves by focus, 06-history-undo.md's first-responder rule: while the title field is focused, Escape is the title abandon even with the body in Edit; with the body editor focused, Escape is the Edit→Preview flip as specified. Beneath it, a **quiet created/modified line** ("Created ⟨date⟩ · Modified ⟨date⟩ · by ⟨modified-by⟩", secondary styling, omitting whichever keys are absent — the "by" segment renders only when the self-reported provenance stamp is present, 01-storage-format.md; this is provenance made visible where git history may not exist) — the read-only readout the pathfinder dropped with its inspector, back where the mockup puts it. Then the **body** (Preview/Edit, below).
- **Attributes sidebar**: everything about the card that isn't the body — sections below. Fixed narrow width derived from font metrics (full relative scaling, 10-accessibility.md); the window's resize flex goes to the body.

(The pathfinder's compositions are both gone: the metadata bar — labels, assignees, due — left with those fields' move to the enhanced schema, and the horizontal attachments strip dissolves into the sidebar.)

## Body: Preview / Edit — not WYSIWYG

Settled the hard way in the pathfinder (WYSIWYG built, then reversed): the body is **read/write raw Markdown with a fully rendered preview**. `TextEditor` can't lay out tables, so rendered-reading + raw-editing is the honest, lossless pairing for a file-backed app.

### Mode grammar

- **Preview is the resting state**: a card opens in Preview — **unless its body is empty, which opens straight into Edit** with the cursor ready (a new card has nothing to preview, so ⌘↩ during creation flows title → body without a mode stop).
- **⌘E toggles** (View ▸ Edit Body, checkmark state — 11-command-nexus.md). **Return in Preview also enters Edit** — the board's edit key applied to the body; fixed grammar like the board's Return, not a menu item. **Escape in Edit returns to Preview.**
- **Clicking never edits** (the same pivot as the board's click grammar, 04-interactions.md): clicking the rendered body selects text — Preview is selectable and copyable — and nothing else. The one interactive exception is task-list checkboxes, below.
- **Leaving Edit flushes the debounce** (mode flip, raw-source entry, window close) — the preview never lags the text that produced it, and neither does disk.

### Preview

- Renders headings, bold/italic/code, bullet/ordered/task lists, fenced + indented code, nested quotes, GFM tables (per-column alignment, columns sized to contents with the browser sizing rule), thematic breaks, HTML shown **verbatim as literal code-styled text** (never interpreted — no web view, per 00-vision.md's no-web-tech stance), and images resolved against the card's own folder (`![](attachments/shot.png)`).
- **Remote images are never fetched** — Preview does no networking (sandbox-quiet, files-first). An `![](https://…)` renders as a quiet placeholder chip carrying the alt text (or the URL); the file-relative form above is the supported image story.
- **Task-list checkboxes are live**: clicking a `- [ ]` / `- [x]` checkbox flips exactly that marker in the source — a single-character textual edit; every other byte of the body is untouched. This is the deliberate exception to "Preview only reads": checklists are kanban's working currency, and a mode flip to tick a box is ceremony. A toggle is an ordinary user edit — the standard atomic write, auto-committed and undoable on git boards. **The pointer-free path is the system focus model** (settled): checkboxes — like Preview's links — are real controls in the keyboard-focus and accessibility tree, so Full Keyboard Access Tab-reaches them and Space toggles, and VoiceOver toggles with VO-Space (10-accessibility.md's real-accessible-checkboxes promise, honored natively). Without FKA they are not in the key loop — standard macOS content behavior, so ordinary Tab users never wade through a long checklist. In-content controls are *content*, not commands: no menu item, no chord — 04's every-function-has-a-menu-item contract covers commands, and 11-command-nexus.md scopes them accordingly. Under the read-only lock (02-architecture.md) the controls disable in place — an in-content mutation menu validation can't reach (and not the only such path: the attachment row's ⌫/Remove shares the posture — 02's every-entry-point predicate).
- Links: external URLs open in the browser; relative links open the target file with its default app (resolved against the card folder, like images).
- **Edit ▸ Find (⌘F) is find-in-text here** — the standard find bar over the focused body surface (Preview's selectable text, the Edit editor, raw source); board search is a board-window concern (04-interactions.md ▸ Search).

### Edit

- A monospaced editor with **lightweight Markdown syntax highlighting** — headings emphasized, bold/italic rendered as such, code tinted, link targets and structural markers dimmed. Highlighting is presentation only: the text is the raw Markdown, character for character — no hidden transforms, no smart substitutions.
- Saved on a ~700 ms debounce; flushed on leaving Edit, entering source mode, and window close.
- **⌘Z here is the text view's own undo** — session-scoped, ending when the editor loses focus or the mode flips; it works on every board, git or not. Board-level undo routing: 06-history-undo.md ▸ Undo routing; commit granularity (one commit per Edit session — the Edit→Preview flip is the effective Save button; never per save tick): 06 ▸ Rules ▸ Auto-commit.

### Write rules (settled, storage-facing)

- **An untouched body is never rewritten** — plain string comparison, so it stays byte-identical on disk. Three-gate write: untouched → never re-serialized; reverted → not written; echo of an external edit → not written back.
- **Concurrent external edits — dirty buffer wins.** A dirty Edit buffer is never reloaded under the cursor: while the user has unsaved keystrokes, watcher reloads update everything else (board, Preview, other windows) but leave the buffer alone; the debounced save then writes it — deliberate last-writer-wins, the same no-merge-UI philosophy as sync (07-sync-collab.md). A clean buffer follows disk. On git boards, flush-before-overwrite (06-history-undo.md) guarantees the overwritten external version was committed first — one revert away; on no-git and repo-nested boards it is lost — the app manages no git there, so flush-before-overwrite never runs (07's accepted caveat).

## Raw source outlet

A toggle (View ▸ Raw Source, ⌥⌘E — 11-command-nexus.md) swaps the **entire content area — title, body, and sidebar —** for the literal on-disk `index.md` (frontmatter and all) in a monospaced editor with Cancel/Apply: the same frontmatter is being edited as raw text, so interactive controls over it would fight the raw edit. Entering source mode flushes any pending title/body edits first, then reads the file fresh from disk. Apply validates through the same fail-fast parse the loader uses (detailed alert on error, stays in source mode) before writing byte-for-byte (including a `modified-by` stamp the user typed or kept — Apply is the one app write that doesn't clear it, 01-storage-format.md); the watcher reload then refreshes every window. Cancel (and window close) discards without ceremony. This is the escape hatch that keeps *everything* — unknown keys, exotic formatting — reachable in-app. A pull landing mid-session neither blocks on the open buffer nor invalidates it (07-sync-collab.md — same-card signpost, Apply stays last-writer-wins); branch switch and undo restore instead settle it explicitly via save-or-discard (06-history-undo.md ▸ Branch switching).

Key grammar in source mode, completing the window's key story: **Escape is Cancel**, **⌘↩ is Apply**, and toggling off via ⌥⌘E (menu or toolbar) is **Apply too** — leaving-by-toggle commits, mirroring leaving-Edit-flushes; a failed validation keeps source mode open (toggle stays checked) with the alert. Return just types — it's an editor. View ▸ Edit Body (⌘E) disables while source mode is active, matching its toolbar item.

## The attributes sidebar

Stacked sections under small-caps headers, in this order; quiet rows, read-optimized, edit affordances only where a section edits. The sidebar is the card's non-body inventory — future attributes (enhanced-schema fields, if they ever ship) slot in as new sections rather than growing new bars.

### Attachments

- Shows **every top-level file of `attachments/`** — including files also embedded in the body (settled: the section is the card's complete file inventory, no reference-tracking magic; an image appearing in both places is honest, not a bug). Subfolders are tolerated but not surfaced (01-storage-format.md's attachments rules).
- **Compact rows**: small QuickLook thumbnail (Finder-icon fallback) + middle-truncated filename, one row per file. The section header carries a quiet add affordance; empty, the section stays with a one-line hint (drop files, or File ▸ Add Attachment…, ⇧⌘A) — the drop surface remains the **whole window** (name collisions auto-rename, Finder-style — 01-storage-format.md). **Drop precedence is split by payload** (settled): file drops import as attachments anywhere in the window — Edit mode included, the text editor never intercepts a file drop; dragged *text* lands in the Edit editor at the caret within its bounds as ordinary insertion, and is inert elsewhere in the window.
- Row interactions: double-click or Return opens; context menu Open / Reveal in Finder / Remove (moves to the **system** Trash, never hard-deletes — 03-board-ui.md's naming constraint keeps this distinct from board deletion); rows drag out their file URL.
- **Keyboard-native, new in the rewrite** (the pathfinder's strip was pointer-only): the section is focusable; arrows move between rows, **Space QuickLooks** the selected row, Return opens it, ⌫ removes it (same system-Trash semantics).

### Style

The card-level styling home: the **embedded style editor** — background palette grid (with the leading None well) and curated symbol grid, per 03-board-ui.md ▸ Styling ▸ Controls. Card styling is discoverable here without a context menu; the same component appears in the board popover and behind Style….

### Details — unknown frontmatter keys

- **Read-only key/value rows for every unknown frontmatter key**, shown only when any exist, in file order (01-storage-format.md preserves key order verbatim — the sidebar honors it). This is the files-first payoff surfaced: an agent overlay (`project:`, `sphere:` — 08-agent-integration.md) or any hand-added key is visible to the human without opening raw source.
- Values render as plain text, leniently — exotic YAML shapes display best-effort, never error. Reserved enhanced-schema keys (`labels`, `assignees`, `due`, `remote`, …) are ordinary unknown keys in this version and appear here like any other — no special rendering.
- Editing is deliberately not offered: the **raw source outlet** is the write path for frontmatter the app doesn't own.

### History (git boards only)

- **The card's commit trail, read-only** (settled): every commit that touched this card's folder, newest first — semantic subject, relative date, author. The trail is where the commit-message and attribution machinery (06-history-undo.md) pays off for humans: "Move card 'X' to Doing — 2 days ago — Claude" reads as a story, agent and hand edits included (foreign authorship and `modified-by` refinement render as the author).
- The listing **follows the card across lane moves** (path changes; the UUID folder is the identity to track). A cross-board arrival starts fresh — not because identity changes (a move keeps its UUID; the import boundary remints only on collision — 01-storage-format.md) but because the destination board's repo has no commits for the arriving folder; the old board keeps the old trail either way.
- The section is **absent** on boards without app-managed git (mode none, repo-nested) — same honesty rule as the popover's git section (06-history-undo.md). Rows are focusable (arrows), but carry **no actions in v1** — restoring an old version stays a git-client task for now; a per-row forward-restore and lane history are wishlist items, deliberately.
- Menu path (contract): **View ▸ History** in the card window focuses the section — no default chord.

### Actions (bottom)

- **Delete** — moves the card to the trash (destructive styling; the window then dismisses itself per Deletion & lifecycle below; recoverable from the board's trash lane — 03-board-ui.md).
- **Reveal in Finder** — the card's folder.

## Window

- **The subtitle shows the card's place**: "⟨board⟩ › ⟨lane⟩" under the window title, live-updating as the card moves (the window follows its card).
- New windows open at the last-used card-window size, cascaded; frames restore per card across relaunch where state restoration allows.
- **Toolbar (settled — 03-board-ui.md ▸ Toolbar)**: default set Edit Body (single toggle, on-state in Edit) · Raw Source (toggle; while active, Edit Body disables) · Add Attachment; user-customizable like the board window's.

## Deletion & lifecycle

- The window follows its card across lanes (keyed by board URL + GUID) — *within its board*. A **cross-board move dismisses the window like a delete**: the card left this board — its UUID travels with the move (reminted only on an import-boundary collision, 01-storage-format.md's identity lifecycle), but the window's key is board URL + GUID, and the board half no longer names it.
- Window dismisses itself if the card is deleted — and **entering the trash counts as deleted** (resettled 2026-07-28, the materialized trash): ⌫ on the board closes the card's open window (the card left the working set; restore and reopen if it was a slip), an external move into `.trash/` observed by reload does the same, and deleting the card's *lane* deletes the card with it — the window dismisses because the card is gone. Restoring reopens nothing — reopening is the user's act, like any open. **Dismissal never eats typed work silently where a save can land** (settled): a dirty Edit buffer flushes into the card's folder at its new `.trash/` location before the window dismisses — a surgical body write, so the keystrokes survive a later restore and enter history on git boards (the composer reads it as an edit to a trashed card — accurate). An open raw-source buffer discards instead: its Apply would write a whole stale `index.md` over the trashed card — a delete is never fought by a stale buffer. A card whose lane was deleted discards both — nowhere left to write. A card hard-deleted externally (folder gone) discards both — nowhere left to write, the inline-rename rule. Cards in the shown trash lane don't open at all — restore first (03-board-ui.md). Reopening a live card focuses the existing window.

## Changes from Kanban

- **Two-column composition** replaces the pathfinder's vertical title/strip/body stack: body column (title atop it, created/modified line beneath) plus a full-height attributes sidebar.
- **The sidebar revives the dropped readouts**: created/modified return under the title; unknown frontmatter keys get the read-only Details section (the pathfinder's inspector casualties, rehomed).
- **Empty body opens in Edit**; Return in Preview enters Edit; Escape returns to Preview (the pathfinder always opened in Preview, toggle-only).
- **Live task-list checkboxes in Preview** — the pathfinder's preview was fully inert.
- **Syntax-highlighted editor** — the pathfinder used a plain monospaced `TextEditor`.
- **Keyboard-navigable attachments** with Space-QuickLook — the strip was pointer-only.
- The window gains a live board › lane subtitle.

## Open questions

None currently — the embedded-attachment duplication question is closed (the sidebar shows every file), and the click-grammar pivot is recorded in 04-interactions.md.
