A refused trash hover withdraws the trash's own promise — the fall-through holds only keepable ones
Gap #12 ruled into 04 ▸ the trash-drop bullet: the refusal fall-through's honest reading (the column declines to be a target, it does not cancel the held drag) covers only promises the refusal leaves keepable — lane shadows keep drawing and an ⌥-copy lands where they are. A standing proposal naming the trash itself is the promise the refusal just broke, so it withdraws: the drag goes proposal-less over the column, release with no proposal cancels in agreement with the empty picture, and lifting ⌥ re-asks at the same address. Implementation filed on the Implementation board. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -34,6 +34,7 @@ Toolbars are **pure enhancement**: every function they host already has a menu i
|
|||||||
|
|
||||||
- **`background`** on board / lane / card: palette name (kebab-case, hand-editable) or `#RRGGBB[AA]` hex. Board color paints the board window's content background (the surface behind and between lanes). Lane and card color are **edge accents, not fills** (settled in the pathfinder's treatment shootout — its settings matrix of C-series lane / K-series card variants landed on **C7 · full-column top edge** and **K1 · left edge stripe**): a lane's color paints a full-width band along its top edge, a card's a stripe along its left edge; the surfaces themselves keep the standard chrome, so colored title text never sits on a colored fill.
|
- **`background`** on board / lane / card: palette name (kebab-case, hand-editable) or `#RRGGBB[AA]` hex. Board color paints the board window's content background (the surface behind and between lanes). Lane and card color are **edge accents, not fills** (settled in the pathfinder's treatment shootout — its settings matrix of C-series lane / K-series card variants landed on **C7 · full-column top edge** and **K1 · left edge stripe**): a lane's color paints a full-width band along its top edge, a card's a stripe along its left edge; the surfaces themselves keep the standard chrome, so colored title text never sits on a colored fill.
|
||||||
- **`icon`**: SF Symbol per item with per-level defaults (board `rectangle.split.3x1`, lane `square.stack`, card `doc.text`).
|
- **`icon`**: SF Symbol per item with per-level defaults (board `rectangle.split.3x1`, lane `square.stack`, card `doc.text`).
|
||||||
|
- **The standard chrome is the pathfinder's surface stack** (settled 2026-08-06): the window keeps the neutral system background; every lane wears a quiet quaternary-wash plate (the trash plate's own figure — translucent, so a board-chosen color shows through and the board-level ink rule keeps its premise; opaque under Reduce Transparency, the trash precedent); every card sits on an opaque `controlBackgroundColor` plate — white over the washed lane in light appearance, a step *darker* than the window in dark. One plate value for every face a card draws (resting, replicas, placeholder, arriving), so a card is the same object wherever it renders.
|
||||||
- **`iconColor`**: resolved — **schema yes, control no**. The field renders when hand-written (tint palette name or hex); the app offers no control for it (Controls below).
|
- **`iconColor`**: resolved — **schema yes, control no**. The field renders when hand-written (tint palette name or hex); the app offers no control for it (Controls below).
|
||||||
- The pathfinder's palettes (12 icon tints, 12 backgrounds) carry over as the starting point. **The 12+12 split is a picker split, not a name split** (settled): color resolution searches foregrounds then backgrounds, so a hand-written `background: carnation` (an icon-tint name) resolves and paints — the split governs what the grids offer, never what a name means.
|
- The pathfinder's palettes (12 icon tints, 12 backgrounds) carry over as the starting point. **The 12+12 split is a picker split, not a name split** (settled): color resolution searches foregrounds then backgrounds, so a hand-written `background: carnation` (an icon-tint name) resolves and paints — the split governs what the grids offer, never what a name means.
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Every command is a menu item. The full inventory — every command and action, i
|
|||||||
The trash lane (03-board-ui.md ▸ Trash — cards and lanes moved into `<root>/.trash/`; lanes rejoined 2026-07-29 as opaque-unit rows) speaks the board's ordinary keyboard language when shown; hidden, it is invisible to every gesture — and **hiding it clears a trash selection** (nothing invisible stays selected, so the toggle-off drops the selection rather than leave commands enabled against rows nobody can see). Trash cards are ordinary cards; a trashed lane is one opaque row (title + card count) — the old liveness machinery stays retired: no ancestor walks, no entry-vs-universe split, one container boundary plus the board's own kind rule. Rules:
|
The trash lane (03-board-ui.md ▸ Trash — cards and lanes moved into `<root>/.trash/`; lanes rejoined 2026-07-29 as opaque-unit rows) speaks the board's ordinary keyboard language when shown; hidden, it is invisible to every gesture — and **hiding it clears a trash selection** (nothing invisible stays selected, so the toggle-off drops the selection rather than leave commands enabled against rows nobody can see). Trash cards are ordinary cards; a trashed lane is one opaque row (title + card count) — the old liveness machinery stays retired: no ancestor walks, no entry-vs-universe split, one container boundary plus the board's own kind rule. Rules:
|
||||||
|
|
||||||
- **Navigation**: the shown trash is the **last container for card navigation** — arrows walk into and out of it, and ⌥→ jumps to it; inside, plain arrows walk every row, card and lane row alike (navigation crosses kinds). The trash lane itself is never selectable *as a lane* (no lane op applies to it): with a lane selected, ←/→ and ⌥→ stop at the last real lane.
|
- **Navigation**: the shown trash is the **last container for card navigation** — arrows walk into and out of it, and ⌥→ jumps to it; inside, plain arrows walk every row, card and lane row alike (navigation crosses kinds). The trash lane itself is never selectable *as a lane* (no lane op applies to it): with a lane selected, ←/→ and ⌥→ stop at the last real lane.
|
||||||
- **Dropping a live card — or lane — on the shown trash deletes it** (lanes extended 2026-07-29): the drag is the pointer's delete gesture — release moves the dragged item(s) into `.trash/`; a lane drag over the shown trash proposes the delete alongside its strip slots. The drop diverges from positional drops in one way: **the shadow always takes the topmost position** — honest, not arbitrary: every trash arrival stamps `modified` and the trash sorts newest-first by that stamp (03 ▸ Trash), so a fresh delete genuinely lands on top. The trash takes no drops while hidden, like every gesture. Cross-board arrivals and ⌥-copies refuse too (a transfer-and-delete compound and a copy-into-the-trash are operations the design doesn't name), and a refusal falls through to the strip retarget rather than cancelling the held drag.
|
- **Dropping a live card — or lane — on the shown trash deletes it** (lanes extended 2026-07-29): the drag is the pointer's delete gesture — release moves the dragged item(s) into `.trash/`; a lane drag over the shown trash proposes the delete alongside its strip slots. The drop diverges from positional drops in one way: **the shadow always takes the topmost position** — honest, not arbitrary: every trash arrival stamps `modified` and the trash sorts newest-first by that stamp (03 ▸ Trash), so a fresh delete genuinely lands on top. The trash takes no drops while hidden, like every gesture. Cross-board arrivals and ⌥-copies refuse too (a transfer-and-delete compound and a copy-into-the-trash are operations the design doesn't name), and a refusal falls through to the strip retarget rather than cancelling the held drag. **A refusal arriving over a standing trash proposal withdraws it** (ruled 2026-08-06 — the stationary ⌥ flip made the state reachable without a mouse move): the fall-through's honest reading — the column declines to be a target, it does not cancel the drag the user is still holding — covers only promises the refusal leaves keepable: shadows standing in a lane keep drawing, and an ⌥-copy released over the column lands where they are. A standing proposal naming the trash itself is the one promise the refusal just broke — tombstone rows promising a delete the release will refuse — so it withdraws rather than holds: the drag goes proposal-less over the column (the foreign-reload rule's shape — the shadow withdraws, no proposal stands until the pointer reaches a live target, and a release with no valid proposal cancels, which now agrees with the empty picture). Lifting ⌥ re-asks at the same address (the stationary flip's re-read) and the trash's proposal returns.
|
||||||
- **Selection keeps one container boundary — and goes kind-blind inside the trash** (re-ruled 2026-07-31, superseding the lanes-rejoin pass's kind-homogeneous trash grammar): a selection never mixes trash items with board items, but *within* the trash cards and lane rows select together — clicks, ⇧-click ranges, ⇧-arrow extension, and the rubber band all sweep every row (the band's full-height backdrop covers both kinds), and Select All with a non-empty trash selection selects **all visible trash rows**. The live board keeps cards XOR lanes, and its Select All stays card-scoped, as everywhere. The guard moves to the exits (the mixed-payload drop refusal and ⌘C/⌘X validation below) — inside the trash the only verbs are Delete and the restore paths, so upstream homogeneity bought nothing the exits don't. ⇧-arrow extension still stops at the container boundary. Menu validation stays binary by container: Delete = move to trash on board selections, Delete = permanent on trash selections (03 ▸ Trash) — and Delete works on a mixed selection, the alert counting both kinds. An external move observed by reload re-resolves the selection by presence, as everywhere (02-architecture.md).
|
- **Selection keeps one container boundary — and goes kind-blind inside the trash** (re-ruled 2026-07-31, superseding the lanes-rejoin pass's kind-homogeneous trash grammar): a selection never mixes trash items with board items, but *within* the trash cards and lane rows select together — clicks, ⇧-click ranges, ⇧-arrow extension, and the rubber band all sweep every row (the band's full-height backdrop covers both kinds), and Select All with a non-empty trash selection selects **all visible trash rows**. The live board keeps cards XOR lanes, and its Select All stays card-scoped, as everywhere. The guard moves to the exits (the mixed-payload drop refusal and ⌘C/⌘X validation below) — inside the trash the only verbs are Delete and the restore paths, so upstream homogeneity bought nothing the exits don't. ⇧-arrow extension still stops at the container boundary. Menu validation stays binary by container: Delete = move to trash on board selections, Delete = permanent on trash selections (03 ▸ Trash) — and Delete works on a mixed selection, the alert counting both kinds. An external move observed by reload re-resolves the selection by presence, as everywhere (02-architecture.md).
|
||||||
- **Within-trash moves are inert**: no move or paste ever targets the trash (deleting is ⌫/⌘⌫ or the drag above), and ⌥⌘↑/⌥⌘↓ are inert on trash rows — the trash's order is its arrival order, not a workspace to arrange.
|
- **Within-trash moves are inert**: no move or paste ever targets the trash (deleting is ⌫/⌘⌫ or the drag above), and ⌥⌘↑/⌥⌘↓ are inert on trash rows — the trash's order is its arrival order, not a workspace to arrange.
|
||||||
- **Clipboard: the restore path.** ⌘C copies a trash card (a live copy lands wherever pasted — like copying out of Finder's Trash); **⌘X works** (resettled — it was disabled under the tombstone model): cut in the trash, paste is the keyboard-native restore, an ordinary folder move (10-accessibility.md's drag-free contract) — a card pastes into a lane, a trashed lane pastes after the anchor lane (the lane-paste rule above, verbatim). **⌘C and ⌘X validate against mixed selections** (ruled 2026-07-31, with kind-blind selection): the pasteboard's payload types are per-kind, so Cut and Copy grey out via ordinary menu validation while a trash selection mixes kinds — no failed gesture, no beep; the drag path's drop-time explanation (Drag-to-restore below) is where the rule teaches itself. **An item entering the trash voids its pending cut** (the deliberate-removal rule): a cut card — or lane — that gets deleted drops out of the pending cut, as under the old model.
|
- **Clipboard: the restore path.** ⌘C copies a trash card (a live copy lands wherever pasted — like copying out of Finder's Trash); **⌘X works** (resettled — it was disabled under the tombstone model): cut in the trash, paste is the keyboard-native restore, an ordinary folder move (10-accessibility.md's drag-free contract) — a card pastes into a lane, a trashed lane pastes after the anchor lane (the lane-paste rule above, verbatim). **⌘C and ⌘X validate against mixed selections** (ruled 2026-07-31, with kind-blind selection): the pasteboard's payload types are per-kind, so Cut and Copy grey out via ordinary menu validation while a trash selection mixes kinds — no failed gesture, no beep; the drag path's drop-time explanation (Drag-to-restore below) is where the rule teaches itself. **An item entering the trash voids its pending cut** (the deliberate-removal rule): a cut card — or lane — that gets deleted drops out of the pending cut, as under the old model.
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ enum Accommodations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A **translucent wash** — a tint laid over whatever happens to be behind it, and the shape
|
/// A **translucent wash** — a tint laid over whatever happens to be behind it, and the shape
|
||||||
/// every non-material translucency on the board takes: the trash column's plate and hatched
|
/// every non-material translucency on the board takes: every lane's plate, the trash column's
|
||||||
/// header, and the drag shadow's fill.
|
/// plate and hatched header, and the drag shadow's fill.
|
||||||
///
|
///
|
||||||
/// These are not glass, and the distinction matters enough to keep two types: a material samples
|
/// These are not glass, and the distinction matters enough to keep two types: a material samples
|
||||||
/// and blurs its backdrop, a wash simply composites at an alpha. But they fail the same way for
|
/// and blurs its backdrop, a wash simply composites at an alpha. But they fail the same way for
|
||||||
@@ -136,8 +136,17 @@ enum Accommodations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The trash column's plate — the quietest of the three, since the header above it carries the
|
/// Every lane's resting plate (03-board-ui.md § Styling ▸ Capabilities — the standard chrome's
|
||||||
/// column's identity.
|
/// middle step, the pathfinder's lane surface carried over). Translucent so a board-chosen
|
||||||
|
/// colour shows through it, which is what keeps `BoardTextInk`'s premise true for the header
|
||||||
|
/// text sitting on it.
|
||||||
|
static func lanePlateWash(reduceTransparency: Bool) -> Wash {
|
||||||
|
reduceTransparency ? .opaque : .translucent(opacity: 0.35)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The trash column's plate — the same figure as an ordinary lane's, kept as its own knob
|
||||||
|
/// because the trash answers to its own rendering spec (03-board-ui.md § Trash ▸ Rendering),
|
||||||
|
/// and the quietest of its treatments: the hatched header above carries the column's identity.
|
||||||
static func trashPlateWash(reduceTransparency: Bool) -> Wash {
|
static func trashPlateWash(reduceTransparency: Bool) -> Wash {
|
||||||
reduceTransparency ? .opaque : .translucent(opacity: 0.35)
|
reduceTransparency ? .opaque : .translucent(opacity: 0.35)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// The board's **standard chrome** — the neutral surface stack under every lane and card,
|
||||||
|
/// carried over from the pathfinder (03-board-ui.md § Styling: user colour is an edge accent;
|
||||||
|
/// these are the fills underneath, and they never carry the user's colour).
|
||||||
|
///
|
||||||
|
/// The stack is three steps deep, and each step has its own home: the window keeps the standard
|
||||||
|
/// background (`BoardView.boardBackground` paints over it only when the board *chooses* a colour),
|
||||||
|
/// every lane lays a quiet quaternary wash over that (`Accommodations.lanePlateWash`), and every
|
||||||
|
/// card sits on the opaque plate below — which is what makes a card pop against the washed lane in
|
||||||
|
/// both appearances: white on grey in light, a step *darker* than the window in dark.
|
||||||
|
enum BoardSurface {
|
||||||
|
|
||||||
|
/// A card's opaque plate — `controlBackgroundColor`, the pathfinder's card surface. Every face
|
||||||
|
/// on the board fills with this one value (the resting face, the drag replica, the lane
|
||||||
|
/// replica's miniatures, the placeholder editor and the arriving face), so a card is the same
|
||||||
|
/// object wherever it is drawn. Opaque deliberately: a card's title never sees the board
|
||||||
|
/// colour, which is why the ≥ 4.5:1 rule binds to the board level and not here
|
||||||
|
/// (10-accessibility.md; `BoardView.boardBackground`).
|
||||||
|
static var cardPlate: Color { Color(nsColor: .controlBackgroundColor) }
|
||||||
|
}
|
||||||
@@ -423,8 +423,9 @@ struct BoardView: View {
|
|||||||
/// hand-written hex, they reach the same place — has its text colour computed against the
|
/// hand-written hex, they reach the same place — has its text colour computed against the
|
||||||
/// threshold by `BoardTextInk`, composited over the window background in the active appearance
|
/// threshold by `BoardTextInk`, composited over the window background in the active appearance
|
||||||
/// and recomputed on an appearance flip; the two subtrees that sit on this fill
|
/// and recomputed on an appearance flip; the two subtrees that sit on this fill
|
||||||
/// (`LaneView.header` and `TrashLaneView.header` — every other surface on the board carries its
|
/// (`LaneView.header` and `TrashLaneView.header` — their plates are translucent washes the
|
||||||
/// own opaque plate) take the answer as a `\.colorScheme` override.
|
/// colour shows through, where every card carries its own opaque plate,
|
||||||
|
/// `BoardSurface.cardPlate`) take the answer as a `\.colorScheme` override.
|
||||||
///
|
///
|
||||||
/// **One path, two verification stories** (`ContrastMath`): the twelve palette pairs are checked
|
/// **One path, two verification stories** (`ContrastMath`): the twelve palette pairs are checked
|
||||||
/// statically, by a test over the ink this seam chooses for each of them (03-board-ui.md §
|
/// statically, by a test over the ink this seam chooses for each of them (03-board-ui.md §
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ struct CardFaceView: View, Equatable {
|
|||||||
// Constant, whether or not a stripe paints: every card's text sits on the same grid, so
|
// Constant, whether or not a stripe paints: every card's text sits on the same grid, so
|
||||||
// colouring a card never shifts its title relative to its uncoloured neighbours.
|
// colouring a card never shifts its title relative to its uncoloured neighbours.
|
||||||
.padding(.leading, stripeWidth)
|
.padding(.leading, stripeWidth)
|
||||||
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(.background.secondary))
|
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(BoardSurface.cardPlate))
|
||||||
.overlay(alignment: .leading) { accentStripe }
|
.overlay(alignment: .leading) { accentStripe }
|
||||||
// The selection treatment, which a hovering Finder file drag borrows outright: "the card
|
// The selection treatment, which a hovering Finder file drag borrows outright: "the card
|
||||||
// highlights while hovered" (04-interactions.md ▸ Drag and drop), and the accent stroke is
|
// highlights while hovered" (04-interactions.md ▸ Drag and drop), and the accent stroke is
|
||||||
@@ -510,7 +510,7 @@ struct CardFaceView: View, Equatable {
|
|||||||
width: BoardMetrics.cardReplicaWidth(measured: measuredWidth, bodyPointSize: pointSize),
|
width: BoardMetrics.cardReplicaWidth(measured: measuredWidth, bodyPointSize: pointSize),
|
||||||
alignment: .leading
|
alignment: .leading
|
||||||
)
|
)
|
||||||
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(.background.secondary))
|
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(BoardSurface.cardPlate))
|
||||||
.overlay(alignment: .leading) { accentStripe }
|
.overlay(alignment: .leading) { accentStripe }
|
||||||
.dragReplicaShadow()
|
.dragReplicaShadow()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ struct LaneView: View, Equatable {
|
|||||||
/// what "increased" does to a stroke.
|
/// what "increased" does to a stroke.
|
||||||
@Environment(\.colorSchemeContrast) private var contrast
|
@Environment(\.colorSchemeContrast) private var contrast
|
||||||
|
|
||||||
|
/// Reduce Transparency, for the lane plate's wash (`Accommodations.lanePlateWash` — translucent
|
||||||
|
/// normally, the standard secondary background under the setting; the trash plate's rule).
|
||||||
|
@Environment(\.accessibilityReduceTransparency) private var reduceTransparency
|
||||||
|
|
||||||
/// The window's appearance — the input to 10-accessibility.md's runtime-contrast rule, and the
|
/// The window's appearance — the input to 10-accessibility.md's runtime-contrast rule, and the
|
||||||
/// answer for every board whose background is not a hand-written hex (`headerInk`).
|
/// answer for every board whose background is not a hand-written hex (`headerInk`).
|
||||||
///
|
///
|
||||||
@@ -167,6 +171,10 @@ struct LaneView: View, Equatable {
|
|||||||
// lane is cut cards and all.
|
// lane is cut cards and all.
|
||||||
.cutTreatment(of: lane.id, in: store)
|
.cutTreatment(of: lane.id, in: store)
|
||||||
.onGeometryChange(for: CGFloat.self) { $0.size.height } action: { measuredHeight = $0 }
|
.onGeometryChange(for: CGFloat.self) { $0.size.height } action: { measuredHeight = $0 }
|
||||||
|
// Behind the selection wash, not composed into it: the plate is the lane's resting
|
||||||
|
// surface, the wash above it is the selection's own layer, and stacked `.background`s
|
||||||
|
// put the later one further back.
|
||||||
|
.background(lanePlate)
|
||||||
// **This lane's drop target**, on the whole body. It accepts *every* session type and routes
|
// **This lane's drop target**, on the whole body. It accepts *every* session type and routes
|
||||||
// internally — card sessions against this lane's masonry zones, lane sessions forwarded to
|
// internally — card sessions against this lane's masonry zones, lane sessions forwarded to
|
||||||
// the strip's logic, external Finder file sessions against those same zones — because
|
// the strip's logic, external Finder file sessions against those same zones — because
|
||||||
@@ -248,12 +256,13 @@ struct LaneView: View, Equatable {
|
|||||||
// since the ink they need is the same question and only the *verification* differs
|
// since the ink they need is the same question and only the *verification* differs
|
||||||
// (`ContrastMath`).
|
// (`ContrastMath`).
|
||||||
//
|
//
|
||||||
// A lane draws *no plate*: its background is the selection wash, which is `.clear` at
|
// A lane's plate is a *translucent wash* (`lanePlate`), so the title, the icon, the
|
||||||
// rest (`selectionBackground`), so the title, the icon, the count badge and the rename
|
// count badge and the rename field still land, effectively, on the board's `background`
|
||||||
// field all land directly on the board's `background` — the surface the design binds the
|
// — the surface the design binds the threshold to ("text does sit on it",
|
||||||
// threshold to ("text does sit on it", `BoardView.boardBackground`). The card faces below
|
// `BoardView.boardBackground`); the wash shifts it too little to change the answer. The
|
||||||
// are a different matter and deliberately untouched: they carry their own opaque plate
|
// card faces below are a different matter and deliberately untouched: they carry their
|
||||||
// (`CardFaceView`'s `.background.secondary`), so their titles never see the board colour.
|
// own opaque plate (`BoardSurface.cardPlate`), so their titles never see the board
|
||||||
|
// colour.
|
||||||
//
|
//
|
||||||
// **Placed here, not at the end of the chain**, which is the modifier order doing real
|
// **Placed here, not at the end of the chain**, which is the modifier order doing real
|
||||||
// work: everything above — including the new-card button in the overlay — is text on the
|
// work: everything above — including the new-card button in the overlay — is text on the
|
||||||
@@ -639,7 +648,7 @@ struct LaneView: View, Equatable {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
||||||
.fill(.background.secondary)
|
.fill(BoardSurface.cardPlate)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(minLength: 0)
|
Spacer(minLength: 0)
|
||||||
@@ -663,6 +672,10 @@ struct LaneView: View, Equatable {
|
|||||||
|
|
||||||
private var replicaHeight: CGFloat {
|
private var replicaHeight: CGFloat {
|
||||||
max(measuredHeight, BoardMetrics.laneReplicaMinimumHeight(bodyPointSize: pointSize))
|
max(measuredHeight, BoardMetrics.laneReplicaMinimumHeight(bodyPointSize: pointSize))
|
||||||
|
// The lane's own wash over an opaque base — the replica floats over whatever the cursor is
|
||||||
|
// above, so unlike the resting lane it cannot let the wash composite against the board; the
|
||||||
|
// opaque `.background` behind it stands in for the window's neutral surface.
|
||||||
|
.background(lanePlate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where the title bar sits inside the replica — its **centre**, measured down from the replica's
|
/// Where the title bar sits inside the replica — its **centre**, measured down from the replica's
|
||||||
@@ -977,13 +990,23 @@ struct LaneView: View, Equatable {
|
|||||||
/// (10-accessibility.md: "Increase Contrast strengthens borders and the selection indicator";
|
/// (10-accessibility.md: "Increase Contrast strengthens borders and the selection indicator";
|
||||||
/// `Accommodations`, and `CardFaceView.plateStroke` for the same three-way branch on a card).
|
/// `Accommodations`, and `CardFaceView.plateStroke` for the same three-way branch on a card).
|
||||||
///
|
///
|
||||||
/// A lane is otherwise bounded by nothing but the gap between it and its neighbour, which is the
|
/// A lane is otherwise bounded by its quiet wash and the gap between it and its neighbour,
|
||||||
/// distinction the setting most needs to restore here.
|
/// which is the distinction the setting most needs to restore here.
|
||||||
private var selectionStroke: some View {
|
private var selectionStroke: some View {
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
.strokeBorder(plateStroke, lineWidth: plateStrokeWidth)
|
.strokeBorder(plateStroke, lineWidth: plateStrokeWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The lane's resting plate — the standard chrome's middle step (03-board-ui.md § Styling ▸
|
||||||
|
/// Capabilities: window, washed lane, opaque card), the same quaternary wash the trash column
|
||||||
|
/// wears (`Accommodations.lanePlateWash`). Translucent deliberately: a board-chosen colour
|
||||||
|
/// shows through, so the header ink computed against the board's background (`headerInk`)
|
||||||
|
/// keeps its premise on a painted board.
|
||||||
|
private var lanePlate: some View {
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
|
.fill(Accommodations.lanePlateWash(reduceTransparency: reduceTransparency).style)
|
||||||
|
}
|
||||||
|
|
||||||
private var plateStroke: AnyShapeStyle {
|
private var plateStroke: AnyShapeStyle {
|
||||||
if isSelected {
|
if isSelected {
|
||||||
AnyShapeStyle(Color.accentColor)
|
AnyShapeStyle(Color.accentColor)
|
||||||
@@ -1171,7 +1194,7 @@ private struct NewCardStubView: View {
|
|||||||
.padding(BoardMetrics.cardContentPadding(bodyPointSize: pointSize))
|
.padding(BoardMetrics.cardContentPadding(bodyPointSize: pointSize))
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
||||||
.fill(.background.secondary)
|
.fill(BoardSurface.cardPlate)
|
||||||
)
|
)
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
||||||
@@ -1207,7 +1230,7 @@ private struct NewCardStubView: View {
|
|||||||
.padding(.leading, BoardMetrics.cardStripeWidth(bodyPointSize: pointSize))
|
.padding(.leading, BoardMetrics.cardStripeWidth(bodyPointSize: pointSize))
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
RoundedRectangle(cornerRadius: BoardMetrics.cardCornerRadius(bodyPointSize: pointSize))
|
||||||
.fill(.background.secondary)
|
.fill(BoardSurface.cardPlate)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ struct TrashLaneRowView: View {
|
|||||||
.padding(BoardMetrics.cardContentPadding(bodyPointSize: pointSize))
|
.padding(BoardMetrics.cardContentPadding(bodyPointSize: pointSize))
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
// Quieter than a card's plate, which is what "dimmed" is here: the cards beside it keep the
|
// Quieter than a card's plate, which is what "dimmed" is here: the cards beside it keep the
|
||||||
// ordinary secondary background, and this row sits a step further back.
|
// ordinary card plate (`BoardSurface.cardPlate`), and this row sits a step further back.
|
||||||
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(.background.tertiary))
|
.background(RoundedRectangle(cornerRadius: cornerRadius).fill(.background.tertiary))
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
|
|||||||
Reference in New Issue
Block a user