Build the full-visibility lane layout
The lane strip replaces the placeholder board: window width divides
across the lanes' width units (no horizontal scroll, no minimum width,
degenerate compression accepted), a lane of n units flowing its cards
into n round-robin masonry columns via a measurement-cached Layout.
Width has two deliberately opposite controls, both landing here: the
right-edge drag (ported verbatim from the pathfinder's ColumnResize)
freezes the 1x standard at drag start, snaps between integer widths
with the asymmetric shadow-leads tick and 10pt re-entry, grows the
window one standard width per snap so siblings keep their exact
pixels, and rubber-bands at the screen's visible frame — uncapped
otherwise; the Increase/Decrease Lane Width items (new Board menu,
Cmd-Opt-arrows) are the stepper's keyboard face and re-divide the
existing window width instead, never touching the window. Width
changes write through the new .resize WriteOperation ("Couldn't
resize…" in the banner vocabulary, which grows with the surfaces by
design); malformed width values render as one unit and stay untouched
on disk. 27 new tests port the pathfinder's resize-math suite onto
the uncapped range and pin the write path's fidelity.
Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -11,9 +11,9 @@ import os
|
||||
///
|
||||
/// Almost everything here is about beginning and ending: acquiring the shared store, stamping the
|
||||
/// registry, holding the board's security-scoped access, remembering the window's frame, and running
|
||||
/// the close flush before any of it is let go. The board *itself* — lanes, cards, drag, the whole of
|
||||
/// 03-board-ui.md — is a placeholder below, deliberately throwaway and confined to one small view so
|
||||
/// the milestone that builds the real thing replaces exactly that and nothing else.
|
||||
/// the close flush before any of it is let go. The board *itself* — the lane strip and everything in
|
||||
/// it — is `BoardView`'s (03-board-ui.md); this file hands it the store and the window and stays out
|
||||
/// of the way.
|
||||
///
|
||||
/// ### Failure opens welcome
|
||||
///
|
||||
@@ -63,8 +63,13 @@ struct BoardWindowHost: View {
|
||||
case let .open(store):
|
||||
VStack(spacing: 0) {
|
||||
BannerStripView(rows: store.bannerRows) { store.banners.dismiss($0) }
|
||||
PlaceholderBoardView(store: store)
|
||||
// The window is handed to the board as a closure, not a value: `WindowAccessor`
|
||||
// attaches after this body first runs, and the lane-resize drag needs the *live*
|
||||
// window to grow at its right edge (03-board-ui.md § Lane).
|
||||
BoardView(store: store, window: { windowController.window })
|
||||
}
|
||||
// "The board in front", for the menu items that act on it (`LaneWidthCommands`).
|
||||
.focusedSceneValue(\.boardStore, store)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,54 +167,3 @@ struct BoardWindowHost: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - The placeholder board
|
||||
|
||||
/// Stand-in for the board (03-board-ui.md): the title and a list of lane titles, and nothing else.
|
||||
///
|
||||
/// **Deliberately throwaway.** The next milestone builds the full-visibility lane layout — masonry
|
||||
/// cards, drag, the trash quasi-lane, the toolbar — and replaces this view wholesale. It is kept in
|
||||
/// one small view with no state of its own so that replacement is a deletion rather than an
|
||||
/// untangling. What it does prove today is that the window renders the *store's* snapshot: an
|
||||
/// external edit shows up here through the watcher like it will in the real thing.
|
||||
private struct PlaceholderBoardView: View {
|
||||
|
||||
let store: BoardStore
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text(AppModel.displayName(of: store))
|
||||
.font(.largeTitle)
|
||||
|
||||
if liveLanes.isEmpty {
|
||||
Text("No lanes yet")
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
ForEach(liveLanes) { lane in
|
||||
HStack(alignment: .firstTextBaseline, spacing: 8) {
|
||||
Text(lane.title.value ?? "Untitled")
|
||||
.font(.headline)
|
||||
.foregroundStyle(lane.title.value == nil ? .secondary : .primary)
|
||||
Text("\(liveCardCount(in: lane))")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(24)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tombstoned lanes render nowhere on the board (03-board-ui.md collapses them into the trash
|
||||
/// quasi-lane) — true of the placeholder as much as of the real layout.
|
||||
private var liveLanes: [Lane] {
|
||||
store.snapshot.lanes.filter { !$0.isDeleted }
|
||||
}
|
||||
|
||||
private func liveCardCount(in lane: Lane) -> Int {
|
||||
lane.cards.filter { !$0.isDeleted }.count
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user