Create the app icon and finalize versioning

App identity for both editions: the three-lane glyph
(rectangle.split.3x1.fill) over a gentle gradient - teal for base
Lanework, dark graphite for Lanework Pro - rendered as 1024 masters and
packaged down the full macOS ladder into each target's own iconset,
replacing the borrowed pathfinder icon.

The About window arrives via IndieAbout (0.2.x): icon, copyright,
version/build/date read from the Info.plist that update_build_info.sh
already stamps on every build (CFBundleVersion 160 = git commit count,
BuildDate, BuildHash - verified in the built product), the version line
opening the bundled CHANGELOG.md, and the new ISC LICENSE.md one
document link away. Both files are authored to the Apple
inline-Markdown subset (no # headings, single-line paragraphs) and
bundle into both editions.

CHANGELOG.md replaces its placeholder with the real 2.0 first-release
notes - one sentence per user-facing feature.

The About box is also an edition seam: base's EditionAbout carries the
one-line Pro signpost 12-editions.md allots ("Lanework Pro adds
git-backed board history and sync."), and the Pro target compiles its
own signpost-less twin instead of that file - the no-#if file-level
doctrine applied to a string. README gains the identity bullet.

1649 green on both schemes; verify-editions.sh 26/26.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-29 12:43:26 -04:00
parent 1c263b9f2e
commit 28ef9eef7e
27 changed files with 119 additions and 1 deletions
+27 -1
View File
@@ -1,3 +1,29 @@
**July 2026**
Development begins — nothing user-facing yet.
Version 2.0: Lanework's first release — a kanban app whose boards are ordinary folders of Markdown files on your Mac.
Edit a board from any other app, script, or AI agent and the open window updates live; outside edits are first-class, never overwritten.
Cards open in their own window with a formatted preview, a Markdown editor with checkable task lists, and a raw-source view.
Attach files to a card by dropping them onto its face or into its card window.
Drag cards and lanes to rearrange them, move or copy them between boards, or drop files from Finder to create new cards.
Cut, copy, and paste cards and lanes — within a board, across boards, or as plain text into other apps.
Deleted cards land in a trash lane you can show beside your lanes; drag a card out (or cut and paste it) to restore it.
Undo and redo cover every board action, including bringing back a deleted lane with all its cards.
Start new boards from ten bundled templates, or save any board as a template of your own.
Search filters the board as you type, and new cards you create clear the filter so they never vanish under it.
Style cards and lanes from a twelve-color palette, tint lanes with a band, and give each board its own background.
The whole app works from the keyboard — arrow-key navigation, drag-free card moves, and Full Keyboard Access on every control.
VoiceOver reads boards as lanes of cards, announces outside changes in one polite digest, and names a focused card that was deleted externally.
Every board gets a guide file teaching AI agents the folder format, kept up to date automatically.
+31
View File
@@ -0,0 +1,31 @@
import IndieAbout
/// **Base Lanework's About-box configuration one of the two places base names Lanework Pro.**
///
/// 12-editions.md Quiet signposts: "Base presents as a complete app, not a demo: Pro is named
/// in exactly two places one line in the About box, and the contextual popover line on `.git`
/// boards. Nothing on the welcome screen, nothing in banners." The popover line is
/// `BoardGitNote`'s; this is the About line, riding as the second line of the copyright text
/// `IndieAbout` renders `copyrightText` as one multi-line `Text`, so the signpost is literally a
/// line in the box, no custom layout.
///
/// **The Pro target does not compile this file** (project.yml excludes it and compiles
/// `KanbanPro/Edition/EditionAbout.swift` instead): Pro's About has no signpost the app it
/// would point to is the one showing the box and falls back to the Info.plist copyright. The
/// difference is a file one target builds and the other does not, per the edition split's
/// no-`#if` doctrine (12-editions.md Targets).
enum EditionAbout {
/// The About window's content: version/build/date from the stamped Info.plist
/// (`update_build_info.sh` CFBundleVersion, BuildDate, BuildHash), the version line
/// opening the bundled CHANGELOG.md, and the bundled ISC LICENSE.md as the one document
/// link. Both documents are authored to the Apple inline-Markdown subset see the files'
/// own single-line paragraphs.
static var configuration: AppInfoConfiguration {
AppInfoConfiguration(
copyrightText: "© 2026 rzen\nLanework Pro adds git-backed board history and sync.",
documents: [.license(extension: "md")],
changelogDocument: .changelog()
)
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 KiB

After

Width:  |  Height:  |  Size: 496 KiB

+8
View File
@@ -1,4 +1,5 @@
import AppKit
import IndieAbout
import SwiftUI
/// The scene graph (02-architecture.md § Windows, § Launch and window lifecycle).
@@ -147,6 +148,13 @@ struct KanbanApp: App {
/// whole of the contract, and the system supplies the rest.
@CommandsBuilder
private var menuCommands: some Commands {
// The About window (11-command-nexus.md files About under the app menu's standard
// furniture): icon, version/build/date from the Info.plist that `update_build_info.sh`
// stamped at build time never a hardcoded string with the version line opening the
// bundled changelog. `EditionAbout` is the edition seam: base's carries the one-line Pro
// signpost (12-editions.md Quiet signposts), Pro's twin does not.
IndieAboutCommand(configuration: EditionAbout.configuration)
// The File group, in 11-command-nexus.md's own row order: New Card, New Lane, New Board,
// Open, Open Recent , Board Info, Duplicate, Save as Template, Reveal in Finder, Add
// Attachment, then the trash trio and Empty Trash. The board-scoped ones validate against
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 KiB

After

Width:  |  Height:  |  Size: 531 KiB

+18
View File
@@ -0,0 +1,18 @@
import IndieAbout
/// **Pro's About-box configuration** the twin of base's `Kanban/App/EditionAbout.swift`, which
/// the Pro target excludes (project.yml): same documents, no signpost line. Base's About carries
/// "Lanework Pro adds" because base is where a user discovers Pro exists (12-editions.md
/// Quiet signposts); Pro pointing at itself would be noise, so its copyright falls back to the
/// Info.plist string and the box is one line shorter.
enum EditionAbout {
/// See base's twin for the full story; the two must keep the same shape so `KanbanApp` can
/// read `EditionAbout.configuration` without knowing which edition it is in.
static var configuration: AppInfoConfiguration {
AppInfoConfiguration(
documents: [.license(extension: "md")],
changelogDocument: .changelog()
)
}
}
+7
View File
@@ -0,0 +1,7 @@
**ISC License**
Copyright (c) 2026 Lanework Contributors
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+2
View File
@@ -50,6 +50,8 @@ Lanework is in early development. This list tracks what has actually shipped and
- **Accessibility** — the board is a real VoiceOver surface, not a grid of unlabelled rectangles: lanes are containers read as "⟨title⟩, lane, N cards" (the count is the filter's, like the visible badge), each card is one flattened element carrying its title, its attachment count and its cut-pending state, and traversal follows card `order` rather than masonry column position. VO-Space toggles selection through the same funnel a ⌘-click uses, context-menu rows double as custom actions, lane titles are headings for the rotor, and the trash column pins last. A **live board announces itself**: a foreign edit lands as one polite, non-interrupting digest per reload — "Board changed: 2 cards edited, 1 card added" — while the app's own writes stay silent. Which is which is decided **per file by the write-provenance ledger**, never by which kind of reload delivered it: a reconciling sweep on wake or reactivation announces whatever changed in the blind window (the app never vouches for changes it didn't witness), and a foreign edit that lands on a file the app had just written is still announced. A card that disappears under the cursor is named rather than merely lost ("Card 'Fix login' was deleted externally"), with focus recovering to its lane; when the lane went too, the announcement names the *lane* and its count and focus walks up then sideways to whatever now holds its position. Bracketed operations say one thing at completion and never their internal churn, and the banner strip is an announced element in its own right — the read-only lock and reload breakage speak when they appear and when they clear. **Every size in the app is relative**: there is not one hard-coded point size left — the card face, the lane header, the masonry, the style editor's wells and every window's floor derive from the system body font, so the whole board grows with the system text size while the no-horizontal-scroll rule holds (the lanes compress, the strip never scrolls) and titles keep truncating gracefully. The system's visual accommodations are wired throughout: **Increase Contrast** thickens every border and selection ring and gives card and lane plates an outline they don't otherwise have, **Reduce Transparency** turns the transient search bar's glass and the trash column's washes solid, and **Reduce Motion** has a variant for every animated surface in the app — movement goes instant, appear/disappear goes crossfade, uniformly, the live-reload seam included. **A coloured board computes its own text colour.** The board background is the one surface the app lets a colour sit behind text, so the ink is chosen rather than assumed: WCAG relative luminance against the ≥ 4.5:1 threshold, with an `#RRGGBBAA` value composited over the window background of the appearance you are actually in — so lane and trash headers take light or dark glyphs on their own and re-decide the moment you switch to Dark Mode. One path serves both halves of the styling vocabulary: the twelve palette wells are pinned by a test that checks the ink the app *picks* for each of them in both appearances (a dark palette board is now readable in Light Mode, which it was not), and a hand-written hex — which stays fully honoured from disk — gets the identical computation as it renders. Nothing is ever said by colour alone (selection is a ring plus a trait, a cut card is dimmed plus "cut, pending paste", the trash header is hatched plus labelled, a mixed batch reads "mixed"), and under **Full Keyboard Access** the board is a single visible tab stop with the arrow grammar inside it while every control around it — lane buttons, popovers, the style grids, welcome rows, template tiles — is Tab-reachable, arrow-navigable and labelled.
- **App identity — icon, versioning, About** — each edition carries its own icon (the three-lane glyph: teal for Lanework, graphite for Lanework Pro) and a real About window: icon, copyright, version and build stamped at build time from git (`CFBundleVersion` = commit count, plus `BuildDate` and `BuildHash` in the Info.plist — never a hardcoded string), the version line opening the bundled end-user changelog, and the ISC license one link away. Base's About carries the one quiet line naming Lanework Pro; Pro's carries none, per the quiet-signposts rule (DESIGN/12).
## Development
The Xcode project is generated — `project.yml` is the source of truth, not the `.xcodeproj`:
+26
View File
@@ -17,6 +17,11 @@ packages:
swift-markdown:
url: https://github.com/apple/swift-markdown.git
minorVersion: 0.8.0
# The About window (both editions): app icon, version/build from the stamped Info.plist, and
# the bundled LICENSE/CHANGELOG documents. Pre-1.0 like swift-markdown, same pinning logic.
IndieAbout:
url: https://git.rzen.dev/rzen/indie-about.git
minorVersion: 0.2.2
settings:
base:
@@ -50,10 +55,20 @@ targets:
- path: Templates
type: folder
buildPhase: resources
# The About window's documents (LICENSE, CHANGELOG) — bundled so IndieAbout can render
# them in-app. Apple-inline-Markdown subset only: no `#` headings, no hard-wrapped
# paragraphs (every newline renders literally).
- path: LICENSE.md
type: file
buildPhase: resources
- path: CHANGELOG.md
type: file
buildPhase: resources
dependencies: &appDependencies
- package: Yams
- package: swift-markdown
product: Markdown
- package: IndieAbout
postBuildScripts: &updateBuildInfo
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
name: Update Build Info
@@ -90,6 +105,10 @@ targets:
- Kanban.entitlements
- Assets.xcassets
- Assets.xcassets/**
# Base's About-box configuration carries the one-line Pro signpost (12-editions.md ▸
# Quiet signposts); Pro compiles its own `KanbanPro/Edition/EditionAbout.swift` twin
# without it — the edition split's file-level doctrine, applied to a string.
- App/EditionAbout.swift
# Pro's own source root — reserved now, filled by pro-m1. See KanbanPro/Edition/ProEdition.swift.
- path: KanbanPro
excludes:
@@ -99,6 +118,13 @@ targets:
- path: Templates
type: folder
buildPhase: resources
# The About documents, same files as base — one license, one changelog, two bundles.
- path: LICENSE.md
type: file
buildPhase: resources
- path: CHANGELOG.md
type: file
buildPhase: resources
dependencies: *appDependencies
postBuildScripts: *updateBuildInfo
settings: