Files
lanework/README.md
T
rzen b4c90838b4 Build card faces with edge-accent styling
The card face becomes real: leading SF Symbol (card default doc.text,
tinted by a valid hand-written iconColor — schema yes, control no),
title or the quiet untitled placeholder, and a quiet paperclip when
the card has attachments — title-only by design, no body excerpt.
Color is the settled K1 edge accent, not a fill: background paints a
4pt stripe down the left edge, resolved through the ported pathfinder
palette (12 icon tints + 12 backgrounds carried over verbatim, plus
raw #RRGGBB[AA]); anything unresolvable paints nothing and stays on
disk exactly as written. The snapshot now carries each card's flat
attachment names — the loader's one read inside a card folder, shared
with the Writer's listing so the m5 carousel and m6 sidebar can never
disagree on order (Finder order, the Writer's existing comparator).
The face keeps its top-aligned structure so the sole-selection
carousel can expand inside the card without moving masonry neighbors.
18 new tests.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
2026-07-27 13:48:50 -04:00

30 lines
5.8 KiB
Markdown

# Lanework
A native macOS kanban app whose boards are plain folders and Markdown files. Every board, lane, and card is a folder with an `index.md` (YAML frontmatter + Markdown body); the filesystem is the source of truth and the app is a live view over it.
The defining consequence: anything that can read and write files is a first-class user of a board — text editors, Finder, shell scripts, git, and above all AI agents. Lanework's job is to be the best *human* surface over that substrate.
## Key features
Lanework is in early development. This list tracks what has actually shipped and grows milestone by milestone; the full design lives in [DESIGN/](DESIGN/).
- **Storage contract, read side** — frontmatter engine with a byte-perfect round-trip guarantee (unknown keys, comments, and formatting survive every rewrite; duplicate keys read last-wins; wrong-type scalars coerce read-side), gapped fractional ordering (Ranks), and a fail-fast board loader with UUID-gated level detection, warning-collecting skips, and tombstone-aware snapshots — pinned by a golden fixture suite of 18 on-disk boards.
- **Storage contract, write side** — BoardWriter turns every mutation into an atomic temp-file+rename over exactly the files it touches: creates mint lowercase-UUIDv4 identities and `.kanban` packages; moves keep the UUID (with per-folder collision repair at the cross-board import boundary); copies mint fresh identities at every level; deletes are in-place tombstones with position-perfect restore and physical purge; attachment imports never overwrite and never refuse (Finder-style renames). Every app write stamps `modified`, clears `modified-by`, and preserves everything it didn't change byte-for-byte; readable-but-uneditable frontmatter shapes refuse loudly instead of corrupting.
- **Live store** — every open board is one shared, watched, in-memory snapshot: an FSEvents folder watcher (debounced, `.git`-filtered, origin-reconciling) drives whole-tree reloads with a generation guard and single-flight coalescing; write brackets suppress self-echo; a file-identity-keyed store registry refcounts stores and watchers across windows and absorbs root renames via bookmark re-resolution (a vanished root locks the board and watches for its return); plus the board registry (recents, bookmarks, cached counts), the banner center's single precedence order, the dirty-buffer guard, and transient UI state.
- **Window architecture** — the three window types and their lifecycle: a welcome window (branding, failed-open reporting), one board window per root (per-board frame memory, repositioned onto a live screen), and at-most-one card window per card (last-used size, cascaded; follows its card across lanes; dismisses on delete, tombstone, or cross-board move). Closing a board window or quitting runs one strict close flush — card sessions end, pending work drains, the registry is stamped — before the store tears down; launch restores the boards whose open-now flags survived quit (or crash), a preference gating only whether the flags are consulted.
- **The board** — every lane always on screen, the window's width dividing across the lanes' width units with no horizontal scroll: cards flow into as many interior masonry columns as a lane is wide, a right-edge drag resizes between whole units by growing the *window* (snapping at the gap with release hysteresis, hard-stopping at the screen with rubber-band feedback), and ⌥⌘→/⌥⌘← re-divide the existing width instead. Lane chrome is a per-lane SF Symbol (unknown names fall back leniently), title or untitled placeholder, a card-count badge that counts exactly what's rendered, and a new-card button — the whole title bar doubling as the reorder drag surface, a plain click selecting the lane and movement carrying it above its siblings while they show the would-be order.
- **Card faces** — a card reads as a leading SF Symbol, its title (or a quiet untitled placeholder), and a quiet paperclip when it has attachments — title-only by design, no body excerpt. Colour is an edge accent rather than a fill: `background` paints a stripe down the card's left edge and `iconColor` tints the symbol, both written as a kebab-case palette name (12 icon tints, 12 backgrounds) or a `#RRGGBB[AA]` hex. Everything degrades rather than complains — an unreadable colour simply doesn't paint, and the value stays on disk exactly as written. Each card's snapshot carries its attachment names, listed flat and in Finder order (top-level files only; subfolders, hidden files, and symlinks are preserved but never surfaced).
- **Creating and renaming** — New Card (⌘N) files into the selected card's lane immediately after it, a selected lane's bottom, or the last-active lane, opening a focused pseudo-card that exists nowhere on disk until its title commits (Return commits and re-selects the lane, ⌘↩ also opens the card window, Escape or clicking away discards, and a failed create discards rather than waiting for a card that can't arrive). Inline rename — Return on a card, Board ▸ Rename for either kind — tracks its target by UUID, so a foreign move mid-edit is invisible and a tombstone or deletion discards the edit silently; committing empty removes the `title` key rather than writing a blank one. New Lane is ⇧⌘N. Every mutating command disables while an editor holds the keyboard and under the read-only lock.
## Development
The Xcode project is generated — `project.yml` is the source of truth, not the `.xcodeproj`:
```sh
xcodegen generate
xcodebuild build -project Kanban.xcodeproj -scheme Kanban -destination 'platform=macOS'
xcodebuild test -project Kanban.xcodeproj -scheme Kanban -destination 'platform=macOS,arch=arm64'
```
macOS 26+, Swift 6 (strict concurrency), SwiftUI, sandboxed. Internal codename `Kanban` (target, scheme, bundle id `dev.rzen.indie.Kanban`); the app ships under the display name **Lanework**.