The board learns to zoom — eight rungs on one ruler, and Actual Size is the untouched board
View ▸ Zoom In / Zoom Out / Actual Size (⌘+ / ⌘− / ⌘0): 75%–200% in eight rungs, app-wide and persisted (the Show Comments precedent) — a viewing comfort, not a property of any one board. The level travels as BoardZoomContext in the environment, injected on BoardView alone so the banner strip, search bar, sheets and popovers stay at the system size; the environment is also what carries it through CardFaceView's equality gate, which compares nothing that moves with the level. Every BoardMetrics figure follows zoom.bodyPointSize — card and lane chrome, drag replicas and the count badge, the resize handle, the trash column — and the drop registry carries the ruler for event-time reads, with the autoscroller's three reaches turning font-derived (reachSide named as the stripGap it always equalled). Lanes still divide the window; zoom never moves the window or its floor. The toolbar gains a catalog-only Zoom In/Out pair mirroring the menu rows' predicate; zoom holds shut mid-drag (frozen geometry), each rung announces itself to VoiceOver, and the render suite pins both invariants: a rung repaints every face, a no-op Actual Size repaints nothing. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -94,9 +94,14 @@ struct LaneView: View, Equatable {
|
||||
/// against the colours of the appearance the window is now in.
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
/// The board's ruler (03-board-ui.md ▸ Layout — zoom; `BoardZoom`), injected on the strip by
|
||||
/// `BoardWindowHost`. An `@Environment` read rather than a value passed down deliberately: this
|
||||
/// view is `.equatable()`, and environment values are the one input the gate cannot suppress.
|
||||
@Environment(\.boardZoom) private var zoom
|
||||
|
||||
/// The live body metric — every figure this lane lays out on is a multiple of it
|
||||
/// (`BoardMetrics`, 10-accessibility.md's full-relative-scaling rule).
|
||||
private var pointSize: CGFloat { BoardMetrics.bodyPointSize }
|
||||
/// (`BoardMetrics`, 10-accessibility.md's full-relative-scaling rule), at the board's zoom.
|
||||
private var pointSize: CGFloat { zoom.bodyPointSize }
|
||||
|
||||
/// Spacing between cards, and between the interior columns.
|
||||
private var cardSpacing: CGFloat { BoardMetrics.cardSpacing(bodyPointSize: pointSize) }
|
||||
@@ -476,10 +481,10 @@ struct LaneView: View, Equatable {
|
||||
// and open[s] the card window", and only a card has one to open).
|
||||
onCommitAndOpen: { store.commitRename() }
|
||||
)
|
||||
.font(.headline)
|
||||
.boardFont(.headline)
|
||||
} else {
|
||||
Text(lane.title.value ?? "Untitled")
|
||||
.font(.headline)
|
||||
.boardFont(.headline)
|
||||
.foregroundStyle(lane.title.value == nil ? .secondary : .primary)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
@@ -502,7 +507,7 @@ struct LaneView: View, Equatable {
|
||||
/// body renders (see `renderedCards`).
|
||||
private var countBadge: some View {
|
||||
Text("\(renderedCards.count)")
|
||||
.font(.caption)
|
||||
.boardFont(.caption)
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.horizontal, BoardMetrics.badgeHorizontalPadding(bodyPointSize: pointSize))
|
||||
@@ -640,7 +645,7 @@ struct LaneView: View, Equatable {
|
||||
.foregroundStyle(.secondary)
|
||||
.imageScale(.medium)
|
||||
Text(card.title.value ?? "Untitled")
|
||||
.font(.body)
|
||||
.boardFont(.body)
|
||||
.lineLimit(2)
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
@@ -663,7 +668,7 @@ struct LaneView: View, Equatable {
|
||||
.background(lanePlate)
|
||||
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(.background))
|
||||
.clipShape(RoundedRectangle(cornerRadius: cornerRadius))
|
||||
.dragReplicaShadow()
|
||||
.dragReplicaShadow(zoom: zoom)
|
||||
}
|
||||
|
||||
/// The replica's size — **the lane's own**, floored for a lane that has not measured itself yet
|
||||
@@ -841,6 +846,10 @@ struct LaneView: View, Equatable {
|
||||
// engagement rect simply scrolls nothing.
|
||||
.task(id: drops.session.isDraggingCards) {
|
||||
guard drops.session.isDraggingCards else { return }
|
||||
// The engagement rect's reaches are distances to this lane's own furniture, so they are
|
||||
// measured on the board's ruler (03-board-ui.md ▸ Layout — zoom). Read once at the head
|
||||
// of the drag, which is once per level: zoom is inert while a session is in flight.
|
||||
autoScroller.bodyPointSize = pointSize
|
||||
await autoScroller.run()
|
||||
}
|
||||
}
|
||||
@@ -867,7 +876,7 @@ struct LaneView: View, Equatable {
|
||||
if let proposal = drops.session.fileLaneProposal(onBoardRooted: store.rootKey, laneID: lane.id) {
|
||||
return ShadowRun(
|
||||
position: proposal.index,
|
||||
heights: Array(repeating: LaneDropRegistry.nominalCardHeight, count: proposal.count)
|
||||
heights: Array(repeating: drops.registry.nominalCardHeight, count: proposal.count)
|
||||
)
|
||||
}
|
||||
return nil
|
||||
@@ -1162,9 +1171,13 @@ private struct NewCardStubView: View {
|
||||
/// Increase Contrast, for the editor well's stroke below (10-accessibility.md; `Accommodations`).
|
||||
@Environment(\.colorSchemeContrast) private var contrast
|
||||
|
||||
/// The board's ruler (`BoardZoom`) — the same environment the real face reads, so the placeholder
|
||||
/// and the card that replaces it are drawn at one zoom.
|
||||
@Environment(\.boardZoom) private var zoom
|
||||
|
||||
/// The live body metric — the same one the real face reads, which is what makes "the numbers are
|
||||
/// the same numbers rather than equal ones" survive the move to font-derived metrics.
|
||||
private var pointSize: CGFloat { BoardMetrics.bodyPointSize }
|
||||
private var pointSize: CGFloat { zoom.bodyPointSize }
|
||||
|
||||
var body: some View {
|
||||
switch phase {
|
||||
@@ -1189,7 +1202,7 @@ private struct NewCardStubView: View {
|
||||
if let id = commit() { openCard(id) }
|
||||
}
|
||||
)
|
||||
.font(.body)
|
||||
.boardFont(.body)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(BoardMetrics.cardContentPadding(bodyPointSize: pointSize))
|
||||
.background(
|
||||
@@ -1221,7 +1234,7 @@ private struct NewCardStubView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
.imageScale(.medium)
|
||||
Text(store.transient.newCardPlaceholder?.draftTitle ?? "")
|
||||
.font(.body)
|
||||
.boardFont(.body)
|
||||
.lineLimit(4)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user