Remove the face carousel — one card presentation

03's resettlement reverses the pathfinder carry-over: the
selection-keyed dual presentation proved undesirable, so a card has one
presentation — selection changes styling, never geometry, and the
masonry never reflows on click. Deleted the carousel view (page dots,
glass underlay, scroll-tick monitor), the QuickLook thumbnail cache
(sole consumer), the pure paging/suppression rules, and the
sole-selected animation key — Motion now keys transactions on the
search query and the drop proposal only. The attachment chip stays as
the face's whole attachment story; viewing media is the card window's
job. No carousel state had leaked beyond the view layer.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-28 08:11:59 -04:00
parent 3f4125e324
commit 1020d9fca4
14 changed files with 47 additions and 1143 deletions
-21
View File
@@ -117,16 +117,6 @@ struct MotionCurveTests {
#expect(Motion.laneResizeWindowDuration == 0.2)
#expect(Motion.laneResize(reduced: false) == .snappy(duration: Motion.laneResizeWindowDuration))
}
/// **The carousel expansion is the structural voice** (03-board-ui.md § Motion's first named
/// narrow key). What the change *is* is positional the card grows and its column-mates slide
/// down so it takes the general snappy spring rather than the filtering one. Pinned as an
/// identity with `structural` rather than as a literal, because if the two ever diverge it
/// should be a deliberate edit to one line.
@Test func theCarouselExpansionIsTheStructuralVoice() {
#expect(Motion.carouselExpansion(reduced: false) == Motion.structural(reduced: false))
#expect(Motion.carouselExpansion(reduced: false) != Motion.contentReflow(reduced: false))
}
}
// MARK: - Reduce Motion
@@ -141,7 +131,6 @@ struct ReduceMotionVariantTests {
#expect(Motion.delete(reduced: true) == nil)
#expect(Motion.laneResize(reduced: true) == nil)
#expect(Motion.contentReflow(reduced: true) == nil)
#expect(Motion.carouselExpansion(reduced: true) == nil)
}
/// "Appear/disappear is scale + fade (cards scale from ~0.8, lanes ~0.9, combined with opacity)."
@@ -156,16 +145,6 @@ struct ReduceMotionVariantTests {
#expect(Motion.cardAppearance(reduced: true) == .crossfade)
#expect(Motion.laneAppearance(reduced: true) == .crossfade)
}
/// **The carousel is the one appearance with no reduced variant, because it is already the
/// reduced form.** 03 fixes an appear/disappear scale for cards and lanes and for nothing else,
/// and the carousel needs none: the movement is the card growing, which `carouselExpansion`
/// animates and Reduce Motion makes instant, so the content only has to fade in over it. This
/// asserts the parameterless accessor is deliberate rather than an omission.
@Test func theCarouselAppearanceIsTheCrossfadeInBothVariants() {
#expect(Motion.carouselAppearance == .crossfade)
#expect(Motion.carouselAppearance != Motion.cardAppearance(reduced: false))
}
}
// MARK: - The create handoff