The run's paper trail — nine changelog entries and the README catches up
README: the lane header's chrome loses its + and gains the collapse story, card faces gain chips and the hero banner, keyboard navigation gains the sticky row, the clipboard bullet gains Copy Link, the symbol grid becomes three per-level shelves, and Printing gets the bullet it never had. CHANGELOG: everything user-facing since the last documented commit, the print feature and its entitlement fix folded into one honest entry. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -13,17 +13,17 @@ Lanework is in early development. This list tracks what has actually shipped and
|
||||
- **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 move a card's folder into the board's reserved `.trash/` at a caller-minted top rank, restore is the ordinary move back out, and purge is physical; 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. Strays are preserved verbatim everywhere with exactly one carve-out: a loose *file* dropped beside a card's `index.md` belongs in that card's `attachments/`, so the app moves it there — Finder-renamed on collision, byte-faithfully, without touching `index.md` — and says so in a dismissable warning row naming the card and the files. Detection stays read-only in the loader; the move is an ordinary bracketed write that waits out the read-only lock and never retries a failure in a loop. Stray *folders*, symlinks, and everything at board or lane level keep the verbatim posture untouched, and a paste normalizes at the import boundary so a pasted card lands already tidy. The one other scope on that promise is the handful of board-root names the app claims: a file or symlink squatting `.trash/`, a folder squatting `CLAUDE.md`, is moved aside by the same Finder-style ladder rather than deleted or worked around, since deletion is broken while it stands.
|
||||
- **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, and a per-board **write-provenance ledger** — in-memory, dying with the session — records a content hash, an absence marker or an old→new pair for every file the app writes, so a landing reload can tell its own echo from an outside edit file by file (final content decides: byte-identical is the app's, one byte different is somebody else's); 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 (below), 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, then per-card frame memory once you've placed one; follows its card across lanes; dismisses the moment its card leaves the board — into the trash, with its deleted lane, purged, or moved to another board). 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. Every open passes through a real loading window — it appears immediately at its saved frame under the registry's cached name, its content a centered spinner behind a ~200 ms grace, while the tree walk runs off the main actor (one walk per board however many windows ask at once), and ⌘W during the walk genuinely cancels it. When an **attended** open refuses, that same content area transforms in place into the **decision surface** — never a sheet, never a chained dialog: the walk's defects grouped by class, each class stating the problem once and listing the affected files with Reveal in Finder and Open in Editor, one class-level choice preselected to its default and a per-file override behind a disclosure. Only honest choices are offered — unparseable frontmatter gets Open in Editor and Re-check (or Skip below the root), a file from a newer Lanework gets Skip alone (and blocks the whole board at the root), a board root with no `index.md` gets a minted index, a root with no `schema` gets a `schema: 1` stamp. Repair and Open applies every chosen fix in one write bracket — ordinary app writes, and on git boards a separate heal-authored repair commit — then re-runs the whole walk, opening on a clean result and re-aggregating into the *same* surface otherwise; Re-check re-walks without writing; Cancel (and ⌘W) retires to welcome's row. Skips are per-open consent that rides the session and is never persisted, and the opened board carries a warning-tone notice naming what was left out, each with its own Reveal in Finder. Restoration failures keep the retire-to-welcome-row landing — repair is an attended act, and launch never chains dialogs.
|
||||
- **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 drag surface, a plain click selecting the lane and movement carrying it away.
|
||||
- **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, the colour written either way as a kebab-case palette name (12 icon tints, 12 backgrounds) or a `#RRGGBB[AA]` hex — `background` as a mapping, `{color: fern}`, which is the one shape that field takes. 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). A card has one presentation: selection changes only its styling, never its geometry, so the masonry never reflows on a click — the paperclip chip is the face's whole attachment story, and viewing the files themselves is the card window's job.
|
||||
- **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 collapse chevron — the whole title bar doubling as the drag surface, a plain click selecting the lane (a second click unselecting) and movement carrying it away. A collapsed lane draws as a slim vertical strip outside the width division — icon, count badge, and a book-spine title reading upward — expanding on a click, taking drops at its end, and carrying its fold when dragged.
|
||||
- **Card faces** — a card reads as a leading SF Symbol, its title (or a quiet untitled placeholder), quiet chips for attachments and comments when it has them, and — when a `hero:` key names one of its attachments — a full-width banner image above the title; otherwise 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, the colour written either way as a kebab-case palette name (12 icon tints, 12 backgrounds) or a `#RRGGBB[AA]` hex — `background` as a mapping, `{color: fern}`, which is the one shape that field takes. 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). A card has one presentation: selection changes only its styling, never its geometry, so the masonry never reflows on a click — the chips are the face's whole attachment-and-comment story, and viewing the files themselves is the card window's job.
|
||||
- **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 target that is trashed or deleted 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.
|
||||
|
||||
- **Drag & drop** — cards, in either container, and lanes all travel as real system drag sessions, so a drag crosses window boundaries, shows the system's own copy badge, and carries a full-size replica of what it picked up. A dashed shadow sits at the exact landing spot and the board reflows to make room; the proposal is pure geometry over an analytically reconstructed resting layout — never measured mid-animation frames — so the shadow is stable rather than jittery, and a lane only reflows once the cursor reaches where the dragged run would actually land, holding its last proposal across the ambiguous stretch in between. Dragging any member of a multi-selection drags the whole selection: N contiguous shadows, one insertion point, landing in flatten order. Locality picks the default the way Finder's volumes do — within a board a drag moves, between boards it copies, with ⌥ forcing copy and ⌘ forcing move and the badge tracking live as the cursor crosses a boundary; a lane reordering inside its own board ignores ⌥ entirely, and a lane carries exactly its cards either way, since the trash is board-level and there is nothing lane-nested to strip. Dragging a trash card onto a lane restores it at the drop position — an ordinary move — while dropping it on another board follows the same copy default every cross-board drag does, ⌘ forcing the true restore-move. The same gesture runs the other way: dropping a live card on the shown trash deletes it, exactly as ⌫ would, with the shadow always taking the topmost row — which the rank minting makes honest rather than arbitrary: every arrival really does land above the current top. A lane drag proposes the same delete over the column, and a trashed lane row drags back out to a strip slot the way a trash card drags back into a lane; a foreign board's item isn't deliverable there and neither is ⌥, since copying into the trash isn't a thing. Lanes taller than their viewport autoscroll from either edge, re-resolving the landing spot on every step so a stationary cursor still lands where the shadow shows. A foreign edit mid-drag re-grounds the drag rather than corrupting the drop: the zones re-derive against each new snapshot, a proposal whose lane was deleted withdraws and a release with none simply cancels, and a drag whose items all vanish dissolves itself. At release the board keeps drawing the dropped arrangement until the write round-trips through the watcher, so nothing snaps back for a frame; every drop is one write bracket — one reload, one commit — whatever the set's size. Files dragged in from Finder join the same dispatch: dropped on a card they copy into its `attachments/` (any type, multi-file, Finder-style renames on collision, the card highlighting while hovered), dropped on lane empty space they become one card per file — titled with the filename minus its extension, that file attached, landing at the drop position with a shadow per card. The trash column and its cards are inert to them, and a read-only board or an open inline editor refuses them outright.
|
||||
|
||||
- **The keyboard** — the board is fully operable without the mouse, and every board function has a menu item. Arrows walk to the nearest card in the direction — across a lane's interior masonry columns, across lanes, and into and out of the shown trash — with ⇧-arrows extending the range from the anchor and stopping dead at the board/trash and card/lane boundaries rather than silently reaching past them. ⌥-arrows jump: ⌥↑/⌥↓ to the lane's first/last card, ⌥←/⌥→ to the first/last lane (⌥→ reaching the trash when it's shown), and a second ⌥↑ on a lane's first card escalates into selecting the lane itself — the one keyboard entry to lane selection, from which ←/→ move between lanes, ↓ descends back into the cards, and an empty selection seeds at the first lane's first card so an arrow from nothing always means the same thing. The selection scrolls itself into view. The Board menu carries the rest: Open Card (⌘↩) — the one command that stays live mid-edit, committing the title and opening the window — Move Up/Move Down (⌥⌘↑/⌥⌘↓), which sort within a lane in logical order and gather a scattered selection into a block behind its first card on the first press, and Move Left/Move Right (⌘←/⌘→), which slide a selected lane one slot and never into the trash. Deleting picks the successor sibling Finder-style, so repeated ⌫ walks down a lane; an external deletion deliberately doesn't.
|
||||
- **The keyboard** — the board is fully operable without the mouse, and every board function has a menu item. Arrows walk to the nearest card in the direction — across a lane's interior masonry columns, across lanes, and into and out of the shown trash — remembering your row across lanes (a walk through a three-card lane and back returns to the eighth card, not the third) — with ⇧-arrows extending the range from the anchor and stopping dead at the board/trash and card/lane boundaries rather than silently reaching past them. ⌥-arrows jump: ⌥↑/⌥↓ to the lane's first/last card, ⌥←/⌥→ to the first/last lane (⌥→ reaching the trash when it's shown), and a second ⌥↑ on a lane's first card escalates into selecting the lane itself — the one keyboard entry to lane selection, from which ←/→ move between lanes, ↓ descends back into the cards, and an empty selection seeds at the first lane's first card so an arrow from nothing always means the same thing. The selection scrolls itself into view. The Board menu carries the rest: Open Card (⌘↩) — the one command that stays live mid-edit, committing the title and opening the window — Move Up/Move Down (⌥⌘↑/⌥⌘↓), which sort within a lane in logical order and gather a scattered selection into a block behind its first card on the first press, and Move Left/Move Right (⌘←/⌘→), which slide a selected lane one slot and never into the trash. Deleting picks the successor sibling Finder-style, so repeated ⌫ walks down a lane; an external deletion deliberately doesn't.
|
||||
|
||||
- **The clipboard** — ⌘X/⌘C/⌘V move cards *and* lanes, within a board and across boards, so structure transfers without a mouse. It's a hybrid: the pasteboard carries a small manifest plus the titles as plain text, while the real content — whole folders, attachments and strays and all — is snapshotted into Application Support the instant you press ⌘C, so a copy captures the item as it was at that moment and survives the original being deleted, its volume unmounting, or the app quitting and relaunching. The store keeps exactly one snapshot: every copy and every launch sweeps whatever the pasteboard no longer points at. If a snapshot has gone missing by the time you paste, the manifest still carries each item's full `index.md`, so the paste lands with its content intact — and says so out loud, naming exactly what was left behind ("Pasted 'Fix login' without its 2 attachments") rather than leaving you to find an empty `attachments/` later. Cut is Finder-style deferred: the items dim in place and stay put until a paste moves them, voiding if another app takes the pasteboard or the source board closes (the paste then quietly becomes a copy), and voiding *per item* if one is deleted in the meantime — so a paste moves whatever survived, and a cut emptied down to nothing simply does nothing. Paste lands after the anchor card, at a selected lane's bottom, or at the last member of a multi-selection in flatten order — the same anchor ⌘N uses — and a lane payload lands after the anchor lane or at the board's right end, which is one of the two ways out of a board with no lanes at all. Copies keep `created` and take fresh identities throughout; a lane carries exactly its cards, copied or moved, because the trash is board-level and there is nothing lane-nested to strip; pasting a lane back into its own board is the within-board duplicate the drag deliberately doesn't offer. The clipboard works on trash cards like on any card — ⌘C yields a live copy wherever you paste it, and ⌘X in the trash followed by ⌘V is the keyboard-native restore, a card into a lane and a trashed lane row after the anchor lane — while paste never targets the trash itself, and the read-only lock blocks cut without ever blocking copy, because copying out is a read.
|
||||
- **The clipboard** — ⌘X/⌘C/⌘V move cards *and* lanes, within a board and across boards, so structure transfers without a mouse. It's a hybrid: the pasteboard carries a small manifest plus the titles as plain text, while the real content — whole folders, attachments and strays and all — is snapshotted into Application Support the instant you press ⌘C, so a copy captures the item as it was at that moment and survives the original being deleted, its volume unmounting, or the app quitting and relaunching. The store keeps exactly one snapshot: every copy and every launch sweeps whatever the pasteboard no longer points at. If a snapshot has gone missing by the time you paste, the manifest still carries each item's full `index.md`, so the paste lands with its content intact — and says so out loud, naming exactly what was left behind ("Pasted 'Fix login' without its 2 attachments") rather than leaving you to find an empty `attachments/` later. Cut is Finder-style deferred: the items dim in place and stay put until a paste moves them, voiding if another app takes the pasteboard or the source board closes (the paste then quietly becomes a copy), and voiding *per item* if one is deleted in the meantime — so a paste moves whatever survived, and a cut emptied down to nothing simply does nothing. Paste lands after the anchor card, at a selected lane's bottom, or at the last member of a multi-selection in flatten order — the same anchor ⌘N uses — and a lane payload lands after the anchor lane or at the board's right end, which is one of the two ways out of a board with no lanes at all. Copies keep `created` and take fresh identities throughout; a lane carries exactly its cards, copied or moved, because the trash is board-level and there is nothing lane-nested to strip; pasting a lane back into its own board is the within-board duplicate the drag deliberately doesn't offer. The clipboard works on trash cards like on any card — ⌘C yields a live copy wherever you paste it, and ⌘X in the trash followed by ⌘V is the keyboard-native restore, a card into a lane and a trashed lane row after the anchor lane — while paste never targets the trash itself, and the read-only lock blocks cut without ever blocking copy, because copying out is a read. Copy Link on a card's context menu writes the card folder's file URL and plain path in one pasteboard write — a link to the files themselves, ready for notes, scripts, or Terminal.
|
||||
|
||||
- **Styling** — one style editor serves every anchor: a background grid of the twelve palette wells behind a leading None well that *removes* the key, and a curated grid of five dozen kanban-relevant SF Symbols behind a leading level-default well that does the same. It is selection-aware (the selected cards or lanes; the board with nothing selected) and states the current value per dimension across the whole target set — agreement selects a well, disagreement reads "—", and a hand-written hex or uncurated symbol states itself verbatim outside the grids, replaced by any well you choose. A batch applies as one bracketed commit that skips every target already carrying the value, and the open editor tracks its targets live: one deleted out from under it leaves the set, and the last one closes the editor rather than quietly retargeting the board. Reached from Board ▸ Style… (⌥⌘S) or a card's or lane's context menu, where a compact row of app-wide recent colours recolours in one click and a lane's menu also carries its width stepper. Colour renders at all three levels — a card's `background` as a left-edge stripe, a lane's as a full-width band along its top edge, the board's as the window's content background — each painting nothing at all when the value doesn't resolve, bytes on disk untouched. **A board can also wear a picture.** `background` is a mapping at every level — `{color: fern}` on a card or a lane — and a board's may name an image beside its colour: `background: {color: "#112233", image: art/sunset.jpg}` paints that image over that colour, scaled to fill, across the **whole window**: the content runs under a transparent title bar with a frosted strip keeping the toolbar and the board-name widget legible on top of it. The path is relative to the board folder, so the picture travels with the document when it is copied, zipped or synced (an absolute path, or one climbing out of the folder, simply paints nothing). Either half may stand alone, the colour shows through while a large photograph decodes off the main thread, and replacing the file in Finder swaps the backdrop live. The board popover's Theme tab dresses the board from presets: a Solid color / Pattern choice over one eight-hue carousel with chevron paging. Solid color filters by tone (defaulting to the current appearance) and saturation, and clicking a flat swatch writes that color and clears the image; Pattern adds color-count (mono, complementary duo, contrasting trio) and mesh-density filters over live previews of a faceted triangle-mesh recipe, with a Reroll button re-minting the geometry — clicking a swatch renders it at full resolution into the board folder as `facets.png` and points `background` at it, with the recipe's primary color written beside it as the fallback underlay. Preset or dragged in, a background is always static pixels — the raw file stays the escape hatch — and a board with no background of its own keeps the standard window chrome exactly as before.
|
||||
- **Styling** — one style editor serves every anchor: a background grid of the twelve palette wells behind a leading None well that *removes* the key, and a curated symbol grid — boards, lanes, and cards each drawing from their own kanban-relevant shelf — behind a leading level-default well that does the same. It is selection-aware (the selected cards or lanes; the board with nothing selected) and states the current value per dimension across the whole target set — agreement selects a well, disagreement reads "—", and a hand-written hex or uncurated symbol states itself verbatim outside the grids, replaced by any well you choose. A batch applies as one bracketed commit that skips every target already carrying the value, and the open editor tracks its targets live: one deleted out from under it leaves the set, and the last one closes the editor rather than quietly retargeting the board. Reached from Board ▸ Style… (⌥⌘S) or a card's or lane's context menu, where a compact row of app-wide recent colours recolours in one click and a lane's menu also carries its width stepper. Colour renders at all three levels — a card's `background` as a left-edge stripe, a lane's as a full-width band along its top edge, the board's as the window's content background — each painting nothing at all when the value doesn't resolve, bytes on disk untouched. **A board can also wear a picture.** `background` is a mapping at every level — `{color: fern}` on a card or a lane — and a board's may name an image beside its colour: `background: {color: "#112233", image: art/sunset.jpg}` paints that image over that colour, scaled to fill, across the **whole window**: the content runs under a transparent title bar with a frosted strip keeping the toolbar and the board-name widget legible on top of it. The path is relative to the board folder, so the picture travels with the document when it is copied, zipped or synced (an absolute path, or one climbing out of the folder, simply paints nothing). Either half may stand alone, the colour shows through while a large photograph decodes off the main thread, and replacing the file in Finder swaps the backdrop live. The board popover's Theme tab dresses the board from presets: a Solid color / Pattern choice over one eight-hue carousel with chevron paging. Solid color filters by tone (defaulting to the current appearance) and saturation, and clicking a flat swatch writes that color and clears the image; Pattern adds color-count (mono, complementary duo, contrasting trio) and mesh-density filters over live previews of a faceted triangle-mesh recipe, with a Reroll button re-minting the geometry — clicking a swatch renders it at full resolution into the board folder as `facets.png` and points `background` at it, with the recipe's primary color written beside it as the fallback underlay. Preset or dragged in, a background is always static pixels — the raw file stays the escape hatch — and a board with no background of its own keeps the standard window chrome exactly as before.
|
||||
|
||||
- **The trash** — deleting a card **moves** it: its folder travels into the board's reserved `.trash/`, always landing at the top, and View ▸ Show Trash reveals a trailing column where those cards live. A trashed card is an ordinary card in a special place — the same card face, the same colour stripe, the same attachments chip, the same search, the same selection, the same clipboard — so `.trash/` is self-describing in Finder and to agents, and there is no tombstone flag anywhere. **Lanes delete into the trash too**: the folder travels subtree-intact and shows as one distinct dimmed row carrying its title and held-card count — an opaque unit that never expands, whose cards aren't individually addressable, and which restores whole or purges whole (its confirmation counting the cards it would take with it). The column takes exactly one width unit while shown, so showing it re-divides the window rather than resizing it, and its newest-first order falls out of ordinary ranks with no timestamp sort. There is no Put Back: restore by dragging a card out into any lane at any position, or ⌘X in the trash and ⌘V into a lane — both are ordinary moves, so a restored card lands where you put it. Drop a live card on the column to delete it — the pointer's twin of ⌫, writing the identical move, and its shadow always takes the top row because that is genuinely where the card lands. Delete is one vocabulary staged by place: ⌫/⌘⌫ moves a board card to the trash and deletes a trash card permanently, and ⇧⌘⌫ Empty Trash… purges the whole container — each confirmed where the loss is real, named by count, and Empty Trash always covers the whole trash, never just what a filter is showing. Nothing edit-shaped — Open, Rename, Style…, Finder file drops — applies to a trash selection, and a selection never mixes trashed with live.
|
||||
|
||||
@@ -51,6 +51,7 @@ Lanework is in early development. This list tracks what has actually shipped and
|
||||
|
||||
- **Customizable toolbars** — both windows carry a real macOS toolbar: right-click ▸ Customize Toolbar…, drag to rearrange, the system overflow, and the Icon and Text / Icon Only / Text Only display options, with your arrangement remembered across launches. They are pure enhancement — every item is a menu command with a shortcut, so removing all of them costs you nothing but a click. The board ships with the search field alone, trailing, and offers New Card, New Lane, Zoom In, Zoom Out, Undo, Redo and Show Trash in the palette (Undo and Redo validate exactly as the Edit menu's rows do, and keep static labels because the menu's titles rewrite themselves); the board popover deliberately has no item, since the window-title chevron is its home. Take the search field out and ⌘F still summons search — the field appears in a strip just under the title bar and stays until the search clears, keeping the keyboard while you type. The card window ships Edit Body · Raw Source · Add Attachment, the first two as toggles showing their on-state, with Edit Body disabling while raw source is up and Add Attachment live in every mode.
|
||||
|
||||
- **Printing** — File ▸ Print (⌘P) prints the board in front as a flowing linear document — lanes left to right, each lane's cards top to bottom, never a screenshot of the strip — or, from a card window, that one card. A print-panel accessory with the system's live preview picks the components (title, icon and labels, rendered body, comments in either reading order), the page breaks (continuous, per lane, per card — real sheet boundaries), one base face and size every other size derives from, and a toggleable header and footer; option sets persist as named profiles, with Last Used auto-captured from the panel. Scope resolves at invocation — the board in front, the card window's card, or a polite refusal where neither exists — Finder-initiated prints of a board work too, and the trash is unreachable by construction.
|
||||
- **Undo** — ⌘Z and ⇧⌘Z are native macOS undo, per board: one stack owned by the board's session and shared by every window over it, so a card window's ⌘Z crosses the same step the board window's does, and another board's never does. Every app-mediated mutation registers an inverse at the write boundary — create, move, reorder, rename, restyle, resize, delete, and an Edit session's whole run of saves — with a restore registering as the ordinary move it is — one gesture to one step, named in the app's own vocabulary so the Edit menu reads "Undo Move 3 Cards" and the toolbar's twins light up and dim with it. Undoing is a real write, never an in-memory revert: it goes through the same atomic writer, echoes back through the watcher, and refreshes every window. Because the app is not the only writer, each step re-checks its target the moment you press ⌘Z — field by field, against what its own write left — and a step the disk has moved past is **skipped rather than applied**, with a quiet row saying which item changed outside Lanework, while ⌘Z falls through to the next step; a step that merely failed to write (a full disk, an unplugged volume) stays put to be retried. Permanent deletion and the duplicate-id repair are deliberately outside it — the confirmation is the safety — attachment add and remove register nothing in v1, and foreign edits never join the stack. The read-only lock disables Undo and Redo with every other mutating command and gives them back, stack intact, when it clears. The stack lives with the session and dies at close, standard macOS behaviour. This stack is the app's only history substrate, and the provider seam it binds through is kept deliberately open — a future provider (the deferred foreign-change journal) re-binds without re-plumbing.
|
||||
|
||||
- **The agent guide** — every board root carries a `CLAUDE.md` the app writes and keeps current: a condensed, agent-facing rendition of the schema — the folder layout, ordering arithmetic, creating and moving cards, the `.trash/` convention, `attachments/`, `modified-by` self-stamping, the colour and icon palettes, and the git etiquette — so any file-capable agent dropped into the folder already knows how to work the board. It is app-owned and version-gated by a marker in its first line: rewritten when missing or older, left byte-for-byte alone when current or newer, and re-checked on every reload, so a guide deleted or rolled back from outside heals by itself. A `CLAUDE.md` that isn't the app's is never clobbered — it moves to `CLAUDE.user.md` (the user's own extension point, which the app otherwise never touches), and if that name is taken the app simply doesn't write a guide. A symlink or folder wearing the name is moved aside — Finder-style, never destroyed, with a quiet row naming where it went — because the app owns that name; and a board on a read-only volume is skipped in silence: the guide is a courtesy and never an interruption.
|
||||
|
||||
Reference in New Issue
Block a user