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:
@@ -146,10 +146,16 @@ struct CardFaceView: View, Equatable {
|
||||
/// `Accommodations`, which owns what "increased" does to a stroke.
|
||||
@Environment(\.colorSchemeContrast) private var contrast
|
||||
|
||||
/// The board's ruler (03-board-ui.md ▸ Layout — zoom; `BoardZoom`). **The environment is what
|
||||
/// makes zoom reach a card face at all**: this view is `.equatable()`, and the gate above compares
|
||||
/// nothing that moves with the level — but it does not compare environment values either, because
|
||||
/// SwiftUI invalidates on those itself. A level threaded any other way would be swallowed here.
|
||||
@Environment(\.boardZoom) private var zoom
|
||||
|
||||
/// The live body metric — every figure this face lays out on is a multiple of it
|
||||
/// (`BoardMetrics`, 10-accessibility.md's full-relative-scaling rule). Read here rather than
|
||||
/// passed in, which is `CardAttachmentsSection`'s pattern on the card-window side.
|
||||
private var pointSize: CGFloat { BoardMetrics.bodyPointSize }
|
||||
private var pointSize: CGFloat { zoom.bodyPointSize }
|
||||
|
||||
/// The plate's corner radius — shared with the accent stripe, which rounds its left corners to
|
||||
/// exactly this so the stripe reads as part of the card's edge rather than a bar laid over it.
|
||||
@@ -400,7 +406,7 @@ struct CardFaceView: View, Equatable {
|
||||
// The dragged items' sizes, frozen at drag start — the pickup transition scales the
|
||||
// replica, and its lingering "last measured frame" would mis-size the shadow and the
|
||||
// span-cap (03-board-ui.md § Motion).
|
||||
heights: ordered.map { drops.registry.heights[$0] ?? LaneDropRegistry.nominalCardHeight },
|
||||
heights: ordered.map { drops.registry.heights[$0] ?? drops.registry.nominalCardHeight },
|
||||
container: .board,
|
||||
source: store
|
||||
)
|
||||
@@ -447,7 +453,7 @@ struct CardFaceView: View, Equatable {
|
||||
drops.session.beginCards(
|
||||
rows.map(\.id),
|
||||
folders: payload.folders,
|
||||
heights: rows.map { drops.registry.heights[$0.id] ?? LaneDropRegistry.nominalCardHeight },
|
||||
heights: rows.map { drops.registry.heights[$0.id] ?? drops.registry.nominalCardHeight },
|
||||
container: .trash,
|
||||
source: store,
|
||||
mixesKinds: mixesKinds
|
||||
@@ -492,7 +498,7 @@ struct CardFaceView: View, Equatable {
|
||||
.foregroundStyle(iconTint)
|
||||
.imageScale(.medium)
|
||||
Text(card.title.value ?? "Untitled")
|
||||
.font(.body)
|
||||
.boardFont(.body)
|
||||
.lineLimit(4)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
attachmentsIndicator
|
||||
@@ -512,7 +518,7 @@ struct CardFaceView: View, Equatable {
|
||||
)
|
||||
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(BoardSurface.cardPlate))
|
||||
.overlay(alignment: .leading) { accentStripe }
|
||||
.dragReplicaShadow()
|
||||
.dragReplicaShadow(zoom: zoom)
|
||||
}
|
||||
|
||||
// MARK: - Context menus
|
||||
@@ -682,10 +688,10 @@ struct CardFaceView: View, Equatable {
|
||||
if case let .board(openCard) = role { openCard(id) }
|
||||
}
|
||||
)
|
||||
.font(.body)
|
||||
.boardFont(.body)
|
||||
} else {
|
||||
Text(card.title.value ?? "Untitled")
|
||||
.font(.body)
|
||||
.boardFont(.body)
|
||||
.foregroundStyle(card.title.value == nil ? .secondary : .primary)
|
||||
.lineLimit(4)
|
||||
}
|
||||
@@ -715,7 +721,7 @@ struct CardFaceView: View, Equatable {
|
||||
private var attachmentsIndicator: some View {
|
||||
if !card.attachments.isEmpty {
|
||||
Image(systemName: "paperclip")
|
||||
.font(.caption)
|
||||
.boardFont(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user