Build lane chrome — title bar, badge, inline rename
The lane title bar becomes real: leading SF Symbol (hand-written names render leniently, unknown ones fall back to the level default), title or secondary untitled placeholder, a quiet count badge that counts exactly the cards the body renders (so the m5 search filter is followed by construction), and a new-card button. The whole bar is the reorder drag surface — no grip — with click-vs-movement splitting select from drag; a pure proposal function maps the drag to an insertion index and release commits through the Writer's same-parent degenerate reorder, compacting and retrying when midpoint precision runs out. Clicking never edits: inline rename is Return on the sole selected card or Board > Rename for either kind, a third transient editor beside the placeholder that tracks its target by UUID, commits on focus loss, discards silently when the target vanishes, and removes the title key on an empty commit. The new-card placeholder renders at last — the settled Cmd-N target rule (pure, tested) files it after the anchor card, at a selected lane's bottom, or into the last-active lane; Return commits and re-selects the lane, Cmd-Return also opens the card window, and a failed create discards the overlay. New Card / New Lane / Rename land in the menus with focused-editor and read-only validation; rename gets its own WriteOperation case in the banner vocabulary. 59 new tests. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -51,6 +51,7 @@ private let everyOperation: [WriteOperation] = [
|
||||
.purge(title: "Fix login"),
|
||||
.style(title: "Fix login"),
|
||||
.resize(title: "Fix login"),
|
||||
.rename(title: "Fix login"),
|
||||
.importAttachment(filename: "photo.png"),
|
||||
.listAttachments,
|
||||
.renumberChildren,
|
||||
@@ -66,6 +67,7 @@ private let titledOperations: [(with: WriteOperation, without: WriteOperation)]
|
||||
(.purge(title: "Fix login"), .purge(title: nil)),
|
||||
(.style(title: "Fix login"), .style(title: nil)),
|
||||
(.resize(title: "Fix login"), .resize(title: nil)),
|
||||
(.rename(title: "Fix login"), .rename(title: nil)),
|
||||
]
|
||||
|
||||
// MARK: - Ordering
|
||||
@@ -359,6 +361,20 @@ struct BannerCenterPhrasingTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Rename says the design's own sentence, and never borrows styling's")
|
||||
func renameHasItsOwnVerb() {
|
||||
// 02-architecture.md § Write-failure surfacing names this line verbatim when it settles
|
||||
// that "the vocabulary grows with the surfaces": inline rename gets its own case rather
|
||||
// than folding into the generic frontmatter bucket, so a failed rename must not tell the
|
||||
// user the app could not *restyle* anything.
|
||||
#expect(BannerCenter.headline(for: error(.rename(title: "Fix login"), .io(message: "the disk is full")))
|
||||
== "Couldn't rename 'Fix login' — the disk is full")
|
||||
#expect(BannerCenter.headline(for: error(.rename(title: nil), .io(message: "the disk is full")))
|
||||
== "Couldn't rename the item — the disk is full")
|
||||
#expect(BannerCenter.headline(for: error(.rename(title: "Fix login")))
|
||||
!= BannerCenter.headline(for: error(.style(title: "Fix login"))))
|
||||
}
|
||||
|
||||
@Test("The cause tail comes from the error's reason and nowhere else")
|
||||
func causeTailCarriesTheDiagnosis() {
|
||||
#expect(BannerCenter.headline(for: error(.move(title: "Fix login"), .io(message: "the disk is full")))
|
||||
|
||||
Reference in New Issue
Block a user