@buoy-gg/time-machine 7.0.35 → 7.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +40 -35
  2. package/lib/commonjs/components/HeaderActions.js +91 -0
  3. package/lib/commonjs/components/HeaderReloadButton.js +22 -25
  4. package/lib/commonjs/components/TimeMachineBody.js +68 -13
  5. package/lib/commonjs/components/floating/NamePromptModal.js +193 -0
  6. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js +715 -0
  7. package/lib/commonjs/components/floating/armedPoint.js +92 -0
  8. package/lib/commonjs/components/floating/barSettings.js +66 -0
  9. package/lib/commonjs/components/list/CaptureBar.js +2 -0
  10. package/lib/commonjs/components/list/RestoreOutcomeCard.js +2 -0
  11. package/lib/commonjs/components/list/SnapshotRow.js +37 -13
  12. package/lib/commonjs/components/list/SourcesStrip.js +127 -83
  13. package/lib/commonjs/components/list/UndoDeleteBar.js +3 -1
  14. package/lib/commonjs/components/preview/RestorePreviewScreen.js +7 -2
  15. package/lib/commonjs/components/sources.js +19 -8
  16. package/lib/commonjs/index.js +42 -0
  17. package/lib/commonjs/preset.js +6 -4
  18. package/lib/commonjs/store/sourceIds.js +9 -0
  19. package/lib/commonjs/store/sourceSettings.js +79 -0
  20. package/lib/commonjs/store/timeMachineStore.js +75 -10
  21. package/lib/commonjs/sync/timeMachineSyncAdapter.js +5 -1
  22. package/lib/commonjs/ui/SwipeActionsRow.js +14 -3
  23. package/lib/commonjs/ui/primitives.js +2 -0
  24. package/lib/commonjs/ui/tmTheme.js +18 -5
  25. package/lib/module/components/HeaderActions.js +85 -0
  26. package/lib/module/components/HeaderReloadButton.js +24 -27
  27. package/lib/module/components/TimeMachineBody.js +71 -16
  28. package/lib/module/components/floating/NamePromptModal.js +188 -0
  29. package/lib/module/components/floating/TimeMachineFloatingTool.js +708 -0
  30. package/lib/module/components/floating/armedPoint.js +82 -0
  31. package/lib/module/components/floating/barSettings.js +57 -0
  32. package/lib/module/components/list/CaptureBar.js +2 -0
  33. package/lib/module/components/list/RestoreOutcomeCard.js +2 -0
  34. package/lib/module/components/list/SnapshotRow.js +37 -13
  35. package/lib/module/components/list/SourcesStrip.js +130 -84
  36. package/lib/module/components/list/UndoDeleteBar.js +3 -1
  37. package/lib/module/components/preview/RestorePreviewScreen.js +7 -2
  38. package/lib/module/components/sources.js +13 -6
  39. package/lib/module/index.js +7 -1
  40. package/lib/module/preset.js +6 -4
  41. package/lib/module/store/sourceIds.js +5 -0
  42. package/lib/module/store/sourceSettings.js +69 -0
  43. package/lib/module/store/timeMachineStore.js +74 -9
  44. package/lib/module/sync/timeMachineSyncAdapter.js +5 -1
  45. package/lib/module/ui/SwipeActionsRow.js +14 -3
  46. package/lib/module/ui/primitives.js +2 -0
  47. package/lib/module/ui/tmTheme.js +18 -5
  48. package/lib/typescript/components/HeaderActions.d.ts +7 -0
  49. package/lib/typescript/components/floating/NamePromptModal.d.ts +9 -0
  50. package/lib/typescript/components/floating/TimeMachineFloatingTool.d.ts +6 -0
  51. package/lib/typescript/components/floating/armedPoint.d.ts +14 -0
  52. package/lib/typescript/components/floating/barSettings.d.ts +7 -0
  53. package/lib/typescript/components/list/SnapshotRow.d.ts +5 -1
  54. package/lib/typescript/components/list/SourcesStrip.d.ts +3 -1
  55. package/lib/typescript/components/sources.d.ts +7 -2
  56. package/lib/typescript/index.d.ts +3 -1
  57. package/lib/typescript/preset.d.ts +4 -2
  58. package/lib/typescript/store/snapshotVault.d.ts +6 -0
  59. package/lib/typescript/store/sourceIds.d.ts +3 -0
  60. package/lib/typescript/store/sourceSettings.d.ts +9 -0
  61. package/lib/typescript/store/timeMachineStore.d.ts +16 -1
  62. package/lib/typescript/sync/timeMachineSyncAdapter.d.ts +4 -0
  63. package/lib/typescript/ui/tmTheme.d.ts +17 -4
  64. package/package.json +11 -8
  65. package/lib/commonjs/components/HeaderReloadButton.js.map +0 -1
  66. package/lib/commonjs/components/TimeMachineBody.js.map +0 -1
  67. package/lib/commonjs/components/TimeMachineModal.js.map +0 -1
  68. package/lib/commonjs/components/confirm.js.map +0 -1
  69. package/lib/commonjs/components/format.js.map +0 -1
  70. package/lib/commonjs/components/list/CaptureBar.js.map +0 -1
  71. package/lib/commonjs/components/list/RestoreOutcomeCard.js.map +0 -1
  72. package/lib/commonjs/components/list/SnapshotRow.js.map +0 -1
  73. package/lib/commonjs/components/list/SourcesStrip.js.map +0 -1
  74. package/lib/commonjs/components/list/UndoDeleteBar.js.map +0 -1
  75. package/lib/commonjs/components/list/WipeAllRow.js.map +0 -1
  76. package/lib/commonjs/components/preview/ItemDiffPanel.js.map +0 -1
  77. package/lib/commonjs/components/preview/PreviewFlow.js.map +0 -1
  78. package/lib/commonjs/components/preview/RestorePointNameRow.js.map +0 -1
  79. package/lib/commonjs/components/preview/RestorePreviewScreen.js.map +0 -1
  80. package/lib/commonjs/components/preview/verdictMeta.js.map +0 -1
  81. package/lib/commonjs/components/sources.js.map +0 -1
  82. package/lib/commonjs/hooks/useTimeMachine.js.map +0 -1
  83. package/lib/commonjs/index.js.map +0 -1
  84. package/lib/commonjs/preset.js.map +0 -1
  85. package/lib/commonjs/store/routeRestore.js.map +0 -1
  86. package/lib/commonjs/store/snapshotVault.js.map +0 -1
  87. package/lib/commonjs/store/timeMachineStore.js.map +0 -1
  88. package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +0 -1
  89. package/lib/commonjs/ui/SwipeActionsRow.js.map +0 -1
  90. package/lib/commonjs/ui/primitives.js.map +0 -1
  91. package/lib/commonjs/ui/tmTheme.js.map +0 -1
  92. package/lib/module/components/HeaderReloadButton.js.map +0 -1
  93. package/lib/module/components/TimeMachineBody.js.map +0 -1
  94. package/lib/module/components/TimeMachineModal.js.map +0 -1
  95. package/lib/module/components/confirm.js.map +0 -1
  96. package/lib/module/components/format.js.map +0 -1
  97. package/lib/module/components/list/CaptureBar.js.map +0 -1
  98. package/lib/module/components/list/RestoreOutcomeCard.js.map +0 -1
  99. package/lib/module/components/list/SnapshotRow.js.map +0 -1
  100. package/lib/module/components/list/SourcesStrip.js.map +0 -1
  101. package/lib/module/components/list/UndoDeleteBar.js.map +0 -1
  102. package/lib/module/components/list/WipeAllRow.js.map +0 -1
  103. package/lib/module/components/preview/ItemDiffPanel.js.map +0 -1
  104. package/lib/module/components/preview/PreviewFlow.js.map +0 -1
  105. package/lib/module/components/preview/RestorePointNameRow.js.map +0 -1
  106. package/lib/module/components/preview/RestorePreviewScreen.js.map +0 -1
  107. package/lib/module/components/preview/verdictMeta.js.map +0 -1
  108. package/lib/module/components/sources.js.map +0 -1
  109. package/lib/module/hooks/useTimeMachine.js.map +0 -1
  110. package/lib/module/index.js.map +0 -1
  111. package/lib/module/preset.js.map +0 -1
  112. package/lib/module/store/routeRestore.js.map +0 -1
  113. package/lib/module/store/snapshotVault.js.map +0 -1
  114. package/lib/module/store/timeMachineStore.js.map +0 -1
  115. package/lib/module/sync/timeMachineSyncAdapter.js.map +0 -1
  116. package/lib/module/ui/SwipeActionsRow.js.map +0 -1
  117. package/lib/module/ui/primitives.js.map +0 -1
  118. package/lib/module/ui/tmTheme.js.map +0 -1
  119. package/lib/typescript/components/HeaderReloadButton.d.ts.map +0 -1
  120. package/lib/typescript/components/TimeMachineBody.d.ts.map +0 -1
  121. package/lib/typescript/components/TimeMachineModal.d.ts.map +0 -1
  122. package/lib/typescript/components/confirm.d.ts.map +0 -1
  123. package/lib/typescript/components/format.d.ts.map +0 -1
  124. package/lib/typescript/components/list/CaptureBar.d.ts.map +0 -1
  125. package/lib/typescript/components/list/RestoreOutcomeCard.d.ts.map +0 -1
  126. package/lib/typescript/components/list/SnapshotRow.d.ts.map +0 -1
  127. package/lib/typescript/components/list/SourcesStrip.d.ts.map +0 -1
  128. package/lib/typescript/components/list/UndoDeleteBar.d.ts.map +0 -1
  129. package/lib/typescript/components/list/WipeAllRow.d.ts.map +0 -1
  130. package/lib/typescript/components/preview/ItemDiffPanel.d.ts.map +0 -1
  131. package/lib/typescript/components/preview/PreviewFlow.d.ts.map +0 -1
  132. package/lib/typescript/components/preview/RestorePointNameRow.d.ts.map +0 -1
  133. package/lib/typescript/components/preview/RestorePreviewScreen.d.ts.map +0 -1
  134. package/lib/typescript/components/preview/verdictMeta.d.ts.map +0 -1
  135. package/lib/typescript/components/sources.d.ts.map +0 -1
  136. package/lib/typescript/hooks/useTimeMachine.d.ts.map +0 -1
  137. package/lib/typescript/index.d.ts.map +0 -1
  138. package/lib/typescript/preset.d.ts.map +0 -1
  139. package/lib/typescript/store/routeRestore.d.ts.map +0 -1
  140. package/lib/typescript/store/snapshotVault.d.ts.map +0 -1
  141. package/lib/typescript/store/timeMachineStore.d.ts.map +0 -1
  142. package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +0 -1
  143. package/lib/typescript/ui/SwipeActionsRow.d.ts.map +0 -1
  144. package/lib/typescript/ui/primitives.d.ts.map +0 -1
  145. package/lib/typescript/ui/tmTheme.d.ts.map +0 -1
@@ -9,6 +9,18 @@ Object.defineProperty(exports, "RESTORE_ORDER", {
9
9
  return _timeMachineStore.RESTORE_ORDER;
10
10
  }
11
11
  });
12
+ Object.defineProperty(exports, "SAFETY_SNAPSHOT_NAME", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _timeMachineStore.SAFETY_SNAPSHOT_NAME;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "TIME_MACHINE_FLOATING_TOOL_ID", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _TimeMachineFloatingTool.TIME_MACHINE_FLOATING_TOOL_ID;
22
+ }
23
+ });
12
24
  Object.defineProperty(exports, "TimeMachineModal", {
13
25
  enumerable: true,
14
26
  get: function () {
@@ -45,12 +57,24 @@ Object.defineProperty(exports, "createTimeMachineTool", {
45
57
  return _preset.createTimeMachineTool;
46
58
  }
47
59
  });
60
+ Object.defineProperty(exports, "isSourceEnabled", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _sourceSettings.isSourceEnabled;
64
+ }
65
+ });
48
66
  Object.defineProperty(exports, "navigateToRoute", {
49
67
  enumerable: true,
50
68
  get: function () {
51
69
  return _routeRestore.navigateToRoute;
52
70
  }
53
71
  });
72
+ Object.defineProperty(exports, "openTimeMachineFloatingTool", {
73
+ enumerable: true,
74
+ get: function () {
75
+ return _TimeMachineFloatingTool.openTimeMachineFloatingTool;
76
+ }
77
+ });
54
78
  Object.defineProperty(exports, "replaceTimeMachineFromSnapshot", {
55
79
  enumerable: true,
56
80
  get: function () {
@@ -63,6 +87,18 @@ Object.defineProperty(exports, "setRemoteTimeMachineHandler", {
63
87
  return _timeMachineStore.setRemoteTimeMachineHandler;
64
88
  }
65
89
  });
90
+ Object.defineProperty(exports, "setSourceEnabled", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _sourceSettings.setSourceEnabled;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "subscribeSourceSettings", {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _sourceSettings.subscribeSourceSettings;
100
+ }
101
+ });
66
102
  Object.defineProperty(exports, "timeMachineStore", {
67
103
  enumerable: true,
68
104
  get: function () {
@@ -84,8 +120,10 @@ Object.defineProperty(exports, "timeMachineToolPreset", {
84
120
  var _routeRestore = require("./store/routeRestore");
85
121
  var _preset = require("./preset");
86
122
  var _TimeMachineModal = require("./components/TimeMachineModal");
123
+ var _TimeMachineFloatingTool = require("./components/floating/TimeMachineFloatingTool");
87
124
  var _timeMachineSyncAdapter = require("./sync/timeMachineSyncAdapter");
88
125
  var _timeMachineStore = require("./store/timeMachineStore");
126
+ var _sourceSettings = require("./store/sourceSettings");
89
127
  /**
90
128
  * @buoy-gg/time-machine
91
129
  *
@@ -102,12 +140,16 @@ var _timeMachineStore = require("./store/timeMachineStore");
102
140
 
103
141
  // Components
104
142
 
143
+ // The floating restore bar (one armed point, one tap to restore)
144
+
105
145
  // External-sync adapter (auto-registered by @buoy-gg/core)
106
146
 
107
147
  // Programmatic snapshot control (also used by tests / power users)
108
148
 
109
149
  // Route capture / restore ("also take me back to the screen I was on")
110
150
 
151
+ // Per-source on/off switches (the Sources strip); off = never captured or restored
152
+
111
153
  // Buoy Desktop remote-mirror seam
112
154
 
113
155
  // A reload-mode restore that also restores the route can't navigate itself —
@@ -5,9 +5,10 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.createTimeMachineTool = createTimeMachineTool;
7
7
  exports.timeMachineToolPreset = void 0;
8
+ var _reactNative = require("react-native");
8
9
  var _sharedUi = require("@buoy-gg/shared-ui");
9
10
  var _TimeMachineBody = require("./components/TimeMachineBody");
10
- var _HeaderReloadButton = require("./components/HeaderReloadButton");
11
+ var _HeaderActions = require("./components/HeaderActions");
11
12
  var _jsxRuntime = require("react/jsx-runtime");
12
13
  /**
13
14
  * Pre-configured Time Machine tool for FloatingDevTools.
@@ -27,9 +28,10 @@ const timeMachineToolPreset = exports.timeMachineToolPreset = {
27
28
  slot: "both",
28
29
  icon: ({
29
30
  size,
30
- color
31
+ color,
32
+ slot
31
33
  }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TimeMachineIcon, {
32
- size: size,
34
+ size: slot === "dial" && _reactNative.Platform.OS !== "web" ? size * 1.5 : size,
33
35
  color: color || _sharedUi.TIME_MACHINE_ICON_COLOR
34
36
  }),
35
37
  component: _TimeMachineBody.TimeMachineBody,
@@ -39,7 +41,7 @@ const timeMachineToolPreset = exports.timeMachineToolPreset = {
39
41
  subtitle: "snapshot & restore app state",
40
42
  searchable: true,
41
43
  searchPlaceholder: "Filter…",
42
- headerActions: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HeaderReloadButton.HeaderReloadButton, {})
44
+ headerActions: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HeaderActions.TimeMachineHeaderActions, {})
43
45
  }
44
46
  };
45
47
 
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ROUTE_SOURCE_ID = void 0;
7
+ /** Id of the route "source": a device capability, not a snapshot provider. */
8
+ const ROUTE_SOURCE_ID = exports.ROUTE_SOURCE_ID = "route";
9
+ //# sourceMappingURL=sourceIds.js.map
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ensureSourceSettingsLoaded = ensureSourceSettingsLoaded;
7
+ exports.getDisabledSources = getDisabledSources;
8
+ exports.isSourceEnabled = isSourceEnabled;
9
+ exports.resetSourceSettingsForTests = resetSourceSettingsForTests;
10
+ exports.setSourceEnabled = setSourceEnabled;
11
+ exports.subscribeSourceSettings = subscribeSourceSettings;
12
+ var _sharedUi = require("@buoy-gg/shared-ui");
13
+ /**
14
+ * Which sources Time Machine is allowed to touch.
15
+ *
16
+ * A source switched off here is left out of every capture and every restore,
17
+ * from the tool, the bar, the desktop and MCP alike: "off" has to mean off
18
+ * everywhere, or the one path that forgets it is the one that breaks the app.
19
+ * The case this exists for is a store whose full replace the app cannot
20
+ * survive (a Zustand store holding live handles, say): leave it off and the
21
+ * rest of the state still round-trips.
22
+ *
23
+ * Persisted as the set of ids that are OFF, so a source that registers for
24
+ * the first time tomorrow starts on, and the default is "everything".
25
+ */
26
+
27
+ const setting = (0, _sharedUi.definePersistedSetting)(_sharedUi.devToolsStorageKeys.timeMachine.disabledSources(), [], {
28
+ sanitize: value => Array.isArray(value) ? value.filter(v => typeof v === "string") : []
29
+ });
30
+ let disabled = new Set();
31
+ let loaded = false;
32
+ let loadPromise = null;
33
+ const listeners = new Set();
34
+ const notify = () => listeners.forEach(listener => listener());
35
+ function ensureSourceSettingsLoaded() {
36
+ if (loaded) return Promise.resolve();
37
+ if (!loadPromise) {
38
+ loadPromise = setting.load().then(ids => {
39
+ // A toggle made while the read was in flight wins over the disk copy.
40
+ if (disabled.size === 0) {
41
+ disabled = new Set(ids);
42
+ notify();
43
+ }
44
+ }).finally(() => {
45
+ loaded = true;
46
+ });
47
+ }
48
+ return loadPromise;
49
+ }
50
+ function subscribeSourceSettings(listener) {
51
+ listeners.add(listener);
52
+ return () => {
53
+ listeners.delete(listener);
54
+ };
55
+ }
56
+
57
+ /** Stable across renders until something changes, for useSyncExternalStore. */
58
+ function getDisabledSources() {
59
+ return disabled;
60
+ }
61
+ function isSourceEnabled(id) {
62
+ return !disabled.has(id);
63
+ }
64
+ function setSourceEnabled(id, enabled) {
65
+ if (enabled === !disabled.has(id)) return;
66
+ const next = new Set(disabled);
67
+ if (enabled) next.delete(id);else next.add(id);
68
+ disabled = next;
69
+ notify();
70
+ void setting.save([...next]);
71
+ }
72
+
73
+ /** Test seam: forget everything without touching storage. */
74
+ function resetSourceSettingsForTests() {
75
+ disabled = new Set();
76
+ loaded = false;
77
+ loadPromise = null;
78
+ }
79
+ //# sourceMappingURL=sourceSettings.js.map
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.RESTORE_ORDER = void 0;
6
+ exports.SAFETY_SNAPSHOT_NAME = exports.RESTORE_ORDER = void 0;
7
7
  exports.replaceTimeMachineFromSnapshot = replaceTimeMachineFromSnapshot;
8
8
  exports.setRemoteTimeMachineHandler = setRemoteTimeMachineHandler;
9
9
  exports.timeMachineStore = void 0;
@@ -12,6 +12,8 @@ var _sharedUi = require("@buoy-gg/shared-ui");
12
12
  var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
13
13
  var _snapshotVault = require("./snapshotVault");
14
14
  var _routeRestore = require("./routeRestore");
15
+ var _sourceSettings = require("./sourceSettings");
16
+ var _sourceIds = require("./sourceIds");
15
17
  /**
16
18
  * Time Machine orchestrator — snapshot CRUD + capture/restore across every
17
19
  * registered StateSnapshotProvider (storage, redux, zustand, jotai, query,
@@ -39,7 +41,22 @@ const RESTORE_ORDER = exports.RESTORE_ORDER = ["storage", "redux", "zustand", "j
39
41
  function defaultSnapshotName(route) {
40
42
  const now = new Date();
41
43
  const clock = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
42
- return route ? `${route.pathname} · ${clock}` : `Snapshot · ${clock}`;
44
+ const screen = route ? route.pathname === "/" ? "Home" : route.pathname : null;
45
+ return screen ? `${screen} · ${clock}` : `Snapshot · ${clock}`;
46
+ }
47
+
48
+ /**
49
+ * Two captures in the same minute on the same screen would get the same
50
+ * default name and be told apart by nothing. Number the repeats.
51
+ */
52
+ function dedupeName(name, existing) {
53
+ const taken = new Set(existing.map(m => m.name));
54
+ if (!taken.has(name)) return name;
55
+ for (let n = 2; n < 1000; n++) {
56
+ const candidate = `${name} (${n})`;
57
+ if (!taken.has(candidate)) return candidate;
58
+ }
59
+ return name;
43
60
  }
44
61
 
45
62
  /**
@@ -53,6 +70,8 @@ function willRestoreRoute(meta) {
53
70
 
54
71
  /** Extra knobs a restore accepts beyond mode + exclusions. */
55
72
 
73
+ const SAFETY_SNAPSHOT_NAME = exports.SAFETY_SNAPSHOT_NAME = "Before last restore";
74
+
56
75
  /** Provider capability row as mirrored to a remote dashboard. */
57
76
 
58
77
  /** One side of a preview comparison. */
@@ -144,7 +163,7 @@ class TimeMachineStore {
144
163
  }
145
164
 
146
165
  /** Capture all capturable sources (or a subset) into a named snapshot. */
147
- async capture(name, sourceIds) {
166
+ async capture(name, sourceIds, options) {
148
167
  if (remoteHandler) {
149
168
  const result = await remoteHandler("capture", {
150
169
  name,
@@ -153,11 +172,12 @@ class TimeMachineStore {
153
172
  return result.snapshot;
154
173
  }
155
174
  await this.ensureLoaded();
175
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
156
176
  this.setState({
157
177
  busy: "capture"
158
178
  });
159
179
  try {
160
- const providers = (0, _sharedUi.getStateSnapshotProviders)().filter(p => p.status().canCapture && (!sourceIds || sourceIds.includes(p.id)));
180
+ const providers = (0, _sharedUi.getStateSnapshotProviders)().filter(p => p.status().canCapture && (0, _sourceSettings.isSourceEnabled)(p.id) && (!sourceIds || sourceIds.includes(p.id)));
161
181
  const data = {};
162
182
  const sources = {};
163
183
  for (const provider of providers) {
@@ -174,21 +194,31 @@ class TimeMachineStore {
174
194
  }
175
195
  }
176
196
  // Where the app was when this state was true — restoring can return here.
177
- const route = (0, _routeRestore.captureCurrentRoute)();
197
+ const route = (0, _sourceSettings.isSourceEnabled)(_sourceIds.ROUTE_SOURCE_ID) ? (0, _routeRestore.captureCurrentRoute)() : null;
198
+ // The safety slot is rolling: the previous one goes before this one lands.
199
+ let current = this.state.snapshots;
200
+ if (options?.safety) {
201
+ for (const stale of current.filter(m => m.safety)) {
202
+ current = await (0, _snapshotVault.deleteSnapshot)(stale.id, current);
203
+ }
204
+ }
178
205
  const snapshot = {
179
206
  meta: {
180
207
  id: nextId(),
181
- name: name.trim() || defaultSnapshotName(route),
208
+ name: options?.safety ? SAFETY_SNAPSHOT_NAME : dedupeName(name.trim() || defaultSnapshotName(route), current),
182
209
  createdAt: Date.now(),
183
210
  sizeBytes: 0,
184
211
  ...(route ? {
185
212
  route
186
213
  } : {}),
214
+ ...(options?.safety ? {
215
+ safety: true
216
+ } : {}),
187
217
  sources
188
218
  },
189
219
  data
190
220
  };
191
- const snapshots = await (0, _snapshotVault.saveSnapshot)(snapshot, this.state.snapshots);
221
+ const snapshots = await (0, _snapshotVault.saveSnapshot)(snapshot, current);
192
222
  this.setState({
193
223
  snapshots
194
224
  });
@@ -211,6 +241,7 @@ class TimeMachineStore {
211
241
  return result.snapshot;
212
242
  }
213
243
  await this.ensureLoaded();
244
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
214
245
  const snapshot = {
215
246
  meta: {
216
247
  id: nextId(),
@@ -248,6 +279,16 @@ class TimeMachineStore {
248
279
  return outcome;
249
280
  }
250
281
  await this.ensureLoaded();
282
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
283
+ // The way back from a mis-tap: capture what is about to be replaced.
284
+ // Restoring the safety slot itself never rolls it, or Undo would eat
285
+ // its own target.
286
+ let safetySnapshotId;
287
+ if (options?.safetyCapture && !this.state.snapshots.find(m => m.id === id)?.safety) {
288
+ safetySnapshotId = (await this.capture(SAFETY_SNAPSHOT_NAME, undefined, {
289
+ safety: true
290
+ })).id;
291
+ }
251
292
  this.setState({
252
293
  busy: "restore"
253
294
  });
@@ -260,7 +301,10 @@ class TimeMachineStore {
260
301
  mode,
261
302
  at: Date.now(),
262
303
  willReload: false,
263
- results: {}
304
+ results: {},
305
+ ...(safetySnapshotId ? {
306
+ safetySnapshotId
307
+ } : {})
264
308
  };
265
309
  if (stored.meta.baseline) {
266
310
  // Fresh install: wipe every clearable source, then reload so all
@@ -308,10 +352,10 @@ class TimeMachineStore {
308
352
  // No prompt any more: the point carries its own answer, and having a
309
353
  // route means wanting it back unless you said otherwise. An explicit
310
354
  // option still wins, so MCP and the desktop can force either way.
311
- const wantsRoute = options?.restoreRoute ?? willRestoreRoute(stored.meta);
355
+ const wantsRoute = (0, _sourceSettings.isSourceEnabled)(_sourceIds.ROUTE_SOURCE_ID) && (options?.restoreRoute ?? willRestoreRoute(stored.meta));
312
356
  if (mode === "reload") {
313
357
  // Storage only; everything in-memory rebuilds after the reload.
314
- if (stored.data.storage && (!includeBySource || includeBySource.has("storage"))) {
358
+ if (stored.data.storage && (0, _sourceSettings.isSourceEnabled)("storage") && (!includeBySource || includeBySource.has("storage"))) {
315
359
  outcome.results.storage = await this.restoreSource("storage", stored.data.storage, excludeBySource.get("storage"), includeBySource?.get("storage"));
316
360
  }
317
361
  // Hand the route to the NEXT boot — this realm is about to die, so the
@@ -343,6 +387,17 @@ class TimeMachineStore {
343
387
  for (const sid of sourceIds) {
344
388
  // Sources entirely outside the scope are untouched and unreported.
345
389
  if (includeBySource && !includeBySource.has(sid)) continue;
390
+ // A source switched off in Sources is left exactly as it is. Reported
391
+ // as a warning, not a failure: this is what you asked for.
392
+ if (!(0, _sourceSettings.isSourceEnabled)(sid)) {
393
+ outcome.results[sid] = {
394
+ ok: true,
395
+ applied: 0,
396
+ skipped: [],
397
+ warnings: ["Switched off in Sources; left as it is"]
398
+ };
399
+ continue;
400
+ }
346
401
  outcome.results[sid] = await this.restoreSource(sid, stored.data[sid], excludeBySource.get(sid), includeBySource?.get(sid));
347
402
  }
348
403
  // Navigate LAST — the screen we land on should read the restored state.
@@ -409,6 +464,7 @@ class TimeMachineStore {
409
464
  return;
410
465
  }
411
466
  await this.ensureLoaded();
467
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
412
468
  const stored = await (0, _snapshotVault.readSnapshot)(id);
413
469
  const snapshots = await (0, _snapshotVault.deleteSnapshot)(id, this.state.snapshots);
414
470
  this.setState({
@@ -448,6 +504,7 @@ class TimeMachineStore {
448
504
  return;
449
505
  }
450
506
  await this.ensureLoaded();
507
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
451
508
  const snapshots = await (0, _snapshotVault.setSnapshotRestoreRoute)(id, restoreRoute, this.state.snapshots);
452
509
  this.setState({
453
510
  snapshots
@@ -463,6 +520,7 @@ class TimeMachineStore {
463
520
  return result?.snapshot ?? null;
464
521
  }
465
522
  await this.ensureLoaded();
523
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
466
524
  const source = this.state.snapshots.find(s => s.id === id);
467
525
  if (!source) return null;
468
526
  const result = await (0, _snapshotVault.duplicateSnapshot)(id, `${source.name} copy`, this.state.snapshots);
@@ -481,6 +539,7 @@ class TimeMachineStore {
481
539
  return;
482
540
  }
483
541
  await this.ensureLoaded();
542
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
484
543
  const snapshots = await (0, _snapshotVault.renameSnapshot)(id, name, this.state.snapshots);
485
544
  this.setState({
486
545
  snapshots
@@ -501,6 +560,7 @@ class TimeMachineStore {
501
560
  return;
502
561
  }
503
562
  await this.ensureLoaded();
563
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
504
564
  const snapshots = await (0, _snapshotVault.setSnapshotScope)(id, scope, this.state.snapshots);
505
565
  this.setState({
506
566
  snapshots
@@ -521,6 +581,7 @@ class TimeMachineStore {
521
581
  return;
522
582
  }
523
583
  await this.ensureLoaded();
584
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
524
585
  const snapshots = await (0, _snapshotVault.setSnapshotExclusions)(id, excludedKeys, this.state.snapshots);
525
586
  this.setState({
526
587
  snapshots
@@ -638,6 +699,7 @@ class TimeMachineStore {
638
699
  return;
639
700
  }
640
701
  await this.ensureLoaded();
702
+ await (0, _sourceSettings.ensureSourceSettingsLoaded)();
641
703
  const runId = ++this.previewRunId;
642
704
  this.setState({
643
705
  preview: {
@@ -669,6 +731,9 @@ class TimeMachineStore {
669
731
  const sourceIds = isBaseline ? (0, _sharedUi.getStateSnapshotProviders)().filter(p => p.clear).map(p => p.id) : [...RESTORE_ORDER.filter(sid => sid in rightData), ...Object.keys(rightData).filter(sid => !RESTORE_ORDER.includes(sid))];
670
732
  const sources = [];
671
733
  for (const sid of sourceIds) {
734
+ // Off in Sources: a restore will not touch it, so there is nothing
735
+ // to preview.
736
+ if (!(0, _sourceSettings.isSourceEnabled)(sid)) continue;
672
737
  // Yield a frame between sources — verdict passes are bounded but a
673
738
  // five-source build shouldn't run as one synchronous block.
674
739
  await new Promise(r => setTimeout(r, 0));
@@ -9,6 +9,8 @@ var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
9
9
  var _timeMachineStore = require("../store/timeMachineStore");
10
10
  var _snapshotVault = require("../store/snapshotVault");
11
11
  var _routeRestore = require("../store/routeRestore");
12
+ var _sourceSettings = require("../store/sourceSettings");
13
+ var _sourceIds = require("../store/sourceIds");
12
14
  /**
13
15
  * Sync adapter for the Time Machine tool, consumed by @buoy-gg/external-sync's
14
16
  * `useExternalSync` (structurally matches its ToolSyncAdapter interface so
@@ -31,7 +33,8 @@ function providerStatuses() {
31
33
  label: p.label,
32
34
  canCapture: s.canCapture,
33
35
  canRestore: s.canRestore,
34
- detail: s.detail
36
+ detail: s.detail,
37
+ enabled: (0, _sourceSettings.isSourceEnabled)(p.id)
35
38
  };
36
39
  });
37
40
  }
@@ -51,6 +54,7 @@ function metadata() {
51
54
  // a dashboard has no router of its own to probe, so it's mirrored here.
52
55
  route: {
53
56
  available: (0, _routeRestore.canNavigate)(),
57
+ enabled: (0, _sourceSettings.isSourceEnabled)(_sourceIds.ROUTE_SOURCE_ID),
54
58
  ...(current ? {
55
59
  current: current.href
56
60
  } : {})
@@ -96,11 +96,20 @@ function SwipeActionsRow({
96
96
  (0, _react.useEffect)(() => () => {
97
97
  if (closeOpenRow === close) closeOpenRow = null;
98
98
  }, [close]);
99
+
100
+ // The row is glass now, so the actions can't hide under it: they fade in
101
+ // with the first points of the drag instead of sitting there at rest.
102
+ const actionOpacity = translateX.interpolate({
103
+ inputRange: [-24, 0],
104
+ outputRange: [1, 0],
105
+ extrapolate: "clamp"
106
+ });
99
107
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
100
108
  style: styles.clip,
101
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
109
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
102
110
  style: [styles.actionLayer, {
103
- width: openWidth
111
+ width: openWidth,
112
+ opacity: actionOpacity
104
113
  }],
105
114
  children: actions.map(action => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
106
115
  style: [styles.action, action.background ? {
@@ -159,7 +168,9 @@ const styles = _reactNative.StyleSheet.create({
159
168
  fontWeight: "600",
160
169
  letterSpacing: _tmTheme.tm.tracking.control
161
170
  },
162
- // Opaque so the red action never bleeds through the row it sits under.
171
+ // Glass, like the card it sits in. The actions underneath are faded out
172
+ // at rest (see actionOpacity), which is what keeps them from bleeding
173
+ // through a translucent row.
163
174
  content: {
164
175
  backgroundColor: _tmTheme.tm.color.group
165
176
  }
@@ -147,6 +147,8 @@ const styles = _reactNative.StyleSheet.create({
147
147
  marginHorizontal: _tmTheme.tm.gutter,
148
148
  backgroundColor: _tmTheme.tm.color.group,
149
149
  borderRadius: _tmTheme.tm.radius.group,
150
+ borderWidth: 1,
151
+ borderColor: _tmTheme.tm.color.groupBorder,
150
152
  overflow: "hidden"
151
153
  },
152
154
  separator: {
@@ -9,7 +9,7 @@ var _sharedUi = require("@buoy-gg/shared-ui");
9
9
  * Time Machine design tokens — the "modern" surface language.
10
10
  *
11
11
  * A grouped-inset list system (iOS Settings shape): one dark sheet, rounded
12
- * grouped cards holding hairline-separated rows, uppercase section labels
12
+ * glass cards holding hairline-separated rows, uppercase section labels
13
13
  * outside the cards, and explanatory footnotes under them.
14
14
  *
15
15
  * COLOR COMES FROM the shared `night` palette (ui/night/nightTheme.ts) — not
@@ -39,15 +39,28 @@ const tm = exports.tm = {
39
39
  * without the list reading through the CTA.
40
40
  */
41
41
  sheet: "rgba(5, 5, 5, 0.92)",
42
- /** A grouped card. */
43
- group: _sharedUi.night.color.surface,
42
+ /**
43
+ * A grouped card: night glass, so the star field behind the sheet shows
44
+ * through every card the way it does through the header chips. Cards
45
+ * take `groupBorder` as a hairline, or the glass reads as a smudge.
46
+ */
47
+ group: _sharedUi.night.color.glass,
48
+ groupBorder: _sharedUi.night.color.glassBorder,
44
49
  /** Row press / hover wash on top of a group. */
45
- rowPressed: _sharedUi.night.color.surfaceElevated,
50
+ rowPressed: _sharedUi.night.color.glassElevated,
46
51
  /** Hairline between rows, inset from the left like iOS. */
47
52
  separator: _sharedUi.night.color.separator,
48
53
  /** Neutral control fill (icon discs, chips, round buttons). */
49
- fill: _sharedUi.night.color.surfaceElevated,
54
+ fill: _sharedUi.night.color.glassElevated,
50
55
  fillStrong: _sharedUi.night.color.fillTertiary,
56
+ /** Opaque ring around a badge on a glass card (the sheet's own color). */
57
+ ring: _sharedUi.night.color.bg,
58
+ /**
59
+ * Opaque disc behind a Buoy icon. The icons paint their knock-outs in
60
+ * the color they are handed, so a translucent fill turns a floppy disk
61
+ * into a yellow square. Stays solid on purpose.
62
+ */
63
+ disc: _sharedUi.night.color.surfaceElevated,
51
64
  label: _sharedUi.night.color.text,
52
65
  labelSecondary: _sharedUi.night.color.textSecondary,
53
66
  labelTertiary: _sharedUi.night.color.textTertiary,
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * The tool's header actions, in Network's header language: labeled
5
+ * dark-glass chips. "Action bar" is named for the thing it opens, the
6
+ * floating action bar over the app, and takes the accent ink as the one
7
+ * action a newcomer has to find. Reload is the plain JS reload.
8
+ *
9
+ * Action bar puts a restore point on the floating bar and gets the modal out
10
+ * of the way. The preview screen passes the point it is showing as `armId`,
11
+ * so opening it from there arms that point; the list leaves it to the
12
+ * default rule.
13
+ */
14
+ import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
15
+ import { night } from "@buoy-gg/shared-ui";
16
+ import { useToolHost } from "@buoy-gg/core";
17
+ import { HeaderReloadButton } from "./HeaderReloadButton";
18
+ import { openTimeMachineFloatingTool } from "./floating/TimeMachineFloatingTool";
19
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
+ export function HeaderBarButton({
21
+ armId
22
+ }) {
23
+ const host = useToolHost();
24
+ const onPress = () => {
25
+ openTimeMachineFloatingTool(armId);
26
+ // Same move as Network: the bar is for using the app, so the modal
27
+ // minimizes to the dock instead of covering the screen the bar controls.
28
+ host.minimize();
29
+ };
30
+ return /*#__PURE__*/_jsxs(TouchableOpacity, {
31
+ style: styles.labeledChip,
32
+ onPress: onPress,
33
+ accessibilityLabel: "Open the floating action bar over the app",
34
+ accessibilityRole: "button",
35
+ testID: "time-machine-header-bar",
36
+ children: [/*#__PURE__*/_jsx(View, {
37
+ style: styles.glyph
38
+ }), /*#__PURE__*/_jsx(Text, {
39
+ style: styles.label,
40
+ children: "Action bar"
41
+ })]
42
+ });
43
+ }
44
+ export function TimeMachineHeaderActions({
45
+ armId
46
+ }) {
47
+ return /*#__PURE__*/_jsxs(View, {
48
+ style: styles.row,
49
+ children: [/*#__PURE__*/_jsx(HeaderBarButton, {
50
+ armId: armId
51
+ }), /*#__PURE__*/_jsx(HeaderReloadButton, {})]
52
+ });
53
+ }
54
+ const styles = StyleSheet.create({
55
+ row: {
56
+ flexDirection: "row",
57
+ alignItems: "center",
58
+ gap: 8
59
+ },
60
+ labeledChip: {
61
+ height: 32,
62
+ flexDirection: "row",
63
+ alignItems: "center",
64
+ gap: 6,
65
+ paddingHorizontal: 10,
66
+ borderRadius: 8,
67
+ borderWidth: 1,
68
+ borderColor: night.color.border,
69
+ backgroundColor: night.color.buttonSurface
70
+ },
71
+ // A small strip outline: the thing the button opens.
72
+ glyph: {
73
+ width: 14,
74
+ height: 8,
75
+ borderRadius: 3,
76
+ borderWidth: 1.5,
77
+ borderColor: night.color.accent
78
+ },
79
+ label: {
80
+ fontSize: 11,
81
+ fontWeight: "700",
82
+ color: night.color.accent
83
+ }
84
+ });
85
+ //# sourceMappingURL=HeaderActions.js.map