Skip to content

Commit 7e7aa81

Browse files
illixionclaude
andcommitted
Restore always-on thumbnail diorama and gate behind a new setting
The gaze-only thumbnail diorama experiment never read right — the parallax pop only registered while the cell was hovered, which lost the overall sense of depth in the grid. Restore the always-on layout from before that series of commits (foreground at z=24, ScaleHoverEffect on the container) and introduce a Diorama Thumbnails toggle that's shown only when Reduce Motion is off, so users who want the flat look without disabling all motion have an opt-out. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9df0a44 commit 7e7aa81

4 files changed

Lines changed: 32 additions & 31 deletions

File tree

SpatialStash/SpatialStash/Model/AppModel.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,23 @@ class AppModel {
562562
reduceMotion || systemReduceMotion
563563
}
564564

565+
/// When true, gallery thumbnails render the two-layer diorama with the
566+
/// foreground popped forward in z. Independent from Reduce Motion: a
567+
/// user may want the parallax-free look without disabling other motion.
568+
/// Forced off when `effectiveReduceMotion` is true — read
569+
/// `effectiveThumbnailDiorama` at use sites.
570+
var thumbnailDiorama: Bool {
571+
didSet {
572+
if thumbnailDiorama != oldValue {
573+
UserDefaults.standard.set(thumbnailDiorama, forKey: "thumbnailDiorama")
574+
}
575+
}
576+
}
577+
578+
var effectiveThumbnailDiorama: Bool {
579+
thumbnailDiorama && !effectiveReduceMotion
580+
}
581+
565582
/// When true, image viewer windows have rounded corners.
566583
var roundedCorners: Bool {
567584
didSet {
@@ -827,6 +844,11 @@ class AppModel {
827844
// Accessibility setting overrides via effectiveReduceMotion)
828845
let loadedReduceMotion = UserDefaults.standard.bool(forKey: "reduceMotion")
829846

847+
// Load thumbnail diorama preference (default: true)
848+
let loadedThumbnailDiorama = UserDefaults.standard.object(forKey: "thumbnailDiorama") != nil
849+
? UserDefaults.standard.bool(forKey: "thumbnailDiorama")
850+
: true
851+
830852
// Load rounded corners (default: true)
831853
let loadedRoundedCorners = UserDefaults.standard.object(forKey: "roundedCorners") != nil
832854
? UserDefaults.standard.bool(forKey: "roundedCorners")
@@ -907,6 +929,7 @@ class AppModel {
907929
self.spatial3DMaxResolution = loadedSpatial3DMaxResolution
908930
self.dioramaDistance = loadedDioramaDistance
909931
self.reduceMotion = loadedReduceMotion
932+
self.thumbnailDiorama = loadedThumbnailDiorama
910933
self.roundedCorners = loadedRoundedCorners
911934
self.openMediaInNewWindows = loadedOpenMediaInNewWindows
912935
self.rememberImageEnhancements = loadedRememberImageEnhancements

SpatialStash/SpatialStash/Views/DioramaForegroundHoverEffect.swift

Lines changed: 0 additions & 23 deletions
This file was deleted.

SpatialStash/SpatialStash/Views/Pictures/GalleryThumbnailView.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,16 @@ struct GalleryThumbnailView: View {
2222
// Background
2323
Color.secondary.opacity(0.2)
2424

25-
if let dioramaPair, !appModel.effectiveReduceMotion {
26-
// Both layers always rendered, both at z=0 — looks flat at
27-
// rest. On gaze the foreground scales beyond the container's
28-
// hover scale and tilts slightly, reading as forward motion
29-
// without needing dynamic z-offset (which the hover-effect
30-
// transform vocabulary doesn't expose).
25+
if let dioramaPair, appModel.effectiveThumbnailDiorama {
26+
// Two-layer diorama: blurred-subject backdrop, masked foreground
27+
// popped forward in z for an Apple TV-style parallax pop.
3128
Image(uiImage: dioramaPair.backdrop)
3229
.resizable()
3330
.scaledToFill()
3431
Image(uiImage: dioramaPair.foreground)
3532
.resizable()
3633
.scaledToFill()
37-
.hoverEffect(DioramaForegroundHoverEffect())
34+
.offset(z: 24)
3835
} else if let loadedImage {
3936
Image(uiImage: loadedImage)
4037
.resizable()
@@ -81,7 +78,7 @@ struct GalleryThumbnailView: View {
8178
/// any in-flight or completed result without re-running Vision.
8279
private func generateDioramaIfPossible() async {
8380
guard let loadedImage else { return }
84-
guard !appModel.effectiveReduceMotion else { return }
81+
guard appModel.effectiveThumbnailDiorama else { return }
8582
let key = image.thumbnailURL
8683
if let cached = ThumbnailDioramaCache.shared.cached(for: key) {
8784
dioramaPair = cached

SpatialStash/SpatialStash/Views/Settings/SettingsTabView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ struct SettingsTabView: View {
5151
.foregroundColor(.secondary)
5252
}
5353

54+
if !appModel.effectiveReduceMotion {
55+
Toggle("Diorama Thumbnails", isOn: $appModel.thumbnailDiorama)
56+
}
57+
5458
Toggle("Rounded Corners", isOn: $appModel.roundedCorners)
5559

5660
Toggle("Always Open In New Window", isOn: $appModel.openMediaInNewWindows)

0 commit comments

Comments
 (0)