@buoy-gg/time-machine 7.0.41 → 7.0.42

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 (172) hide show
  1. package/lib/commonjs/components/HeaderActions.js +1 -91
  2. package/lib/commonjs/components/HeaderReloadButton.js +1 -58
  3. package/lib/commonjs/components/TimeMachineBody.js +1 -282
  4. package/lib/commonjs/components/TimeMachineModal.js +1 -74
  5. package/lib/commonjs/components/confirm.js +2 -38
  6. package/lib/commonjs/components/floating/NamePromptModal.js +1 -193
  7. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js +1 -715
  8. package/lib/commonjs/components/floating/armedPoint.js +1 -92
  9. package/lib/commonjs/components/floating/barSettings.js +1 -66
  10. package/lib/commonjs/components/format.js +1 -24
  11. package/lib/commonjs/components/list/CaptureBar.js +1 -97
  12. package/lib/commonjs/components/list/RestoreOutcomeCard.js +1 -175
  13. package/lib/commonjs/components/list/SnapshotRow.js +1 -170
  14. package/lib/commonjs/components/list/SourcesStrip.js +1 -172
  15. package/lib/commonjs/components/list/UndoDeleteBar.js +1 -85
  16. package/lib/commonjs/components/list/WipeAllRow.js +1 -46
  17. package/lib/commonjs/components/preview/ItemDiffPanel.js +1 -79
  18. package/lib/commonjs/components/preview/PreviewFlow.js +1 -51
  19. package/lib/commonjs/components/preview/RestorePointNameRow.js +1 -83
  20. package/lib/commonjs/components/preview/RestorePreviewScreen.js +1 -1010
  21. package/lib/commonjs/components/preview/verdictMeta.js +1 -58
  22. package/lib/commonjs/components/sources.js +1 -83
  23. package/lib/commonjs/hooks/useTimeMachine.js +1 -24
  24. package/lib/commonjs/index.js +1 -161
  25. package/lib/commonjs/preset.js +1 -56
  26. package/lib/commonjs/store/routeRestore.js +1 -188
  27. package/lib/commonjs/store/snapshotVault.js +1 -186
  28. package/lib/commonjs/store/sourceIds.js +1 -9
  29. package/lib/commonjs/store/sourceSettings.js +1 -79
  30. package/lib/commonjs/store/timeMachineStore.js +1 -848
  31. package/lib/commonjs/sync/timeMachineSyncAdapter.js +1 -258
  32. package/lib/commonjs/ui/SwipeActionsRow.js +1 -178
  33. package/lib/commonjs/ui/primitives.js +1 -172
  34. package/lib/commonjs/ui/tmTheme.js +1 -118
  35. package/lib/module/components/HeaderActions.js +1 -85
  36. package/lib/module/components/HeaderReloadButton.js +1 -53
  37. package/lib/module/components/TimeMachineBody.js +1 -277
  38. package/lib/module/components/TimeMachineModal.js +1 -68
  39. package/lib/module/components/confirm.js +2 -33
  40. package/lib/module/components/floating/NamePromptModal.js +1 -188
  41. package/lib/module/components/floating/TimeMachineFloatingTool.js +1 -708
  42. package/lib/module/components/floating/armedPoint.js +1 -82
  43. package/lib/module/components/floating/barSettings.js +1 -57
  44. package/lib/module/components/format.js +1 -19
  45. package/lib/module/components/list/CaptureBar.js +1 -92
  46. package/lib/module/components/list/RestoreOutcomeCard.js +1 -169
  47. package/lib/module/components/list/SnapshotRow.js +1 -167
  48. package/lib/module/components/list/SourcesStrip.js +1 -169
  49. package/lib/module/components/list/UndoDeleteBar.js +1 -82
  50. package/lib/module/components/list/WipeAllRow.js +1 -41
  51. package/lib/module/components/preview/ItemDiffPanel.js +1 -74
  52. package/lib/module/components/preview/PreviewFlow.js +1 -46
  53. package/lib/module/components/preview/RestorePointNameRow.js +1 -78
  54. package/lib/module/components/preview/RestorePreviewScreen.js +1 -1005
  55. package/lib/module/components/preview/verdictMeta.js +1 -54
  56. package/lib/module/components/sources.js +1 -73
  57. package/lib/module/hooks/useTimeMachine.js +1 -19
  58. package/lib/module/index.js +1 -46
  59. package/lib/module/preset.js +1 -51
  60. package/lib/module/store/routeRestore.js +1 -176
  61. package/lib/module/store/snapshotVault.js +1 -170
  62. package/lib/module/store/sourceIds.js +1 -5
  63. package/lib/module/store/sourceSettings.js +1 -69
  64. package/lib/module/store/timeMachineStore.js +1 -839
  65. package/lib/module/sync/timeMachineSyncAdapter.js +1 -253
  66. package/lib/module/ui/SwipeActionsRow.js +1 -173
  67. package/lib/module/ui/primitives.js +1 -164
  68. package/lib/module/ui/tmTheme.js +1 -114
  69. package/lib/web/index.mjs +2 -3
  70. package/package.json +7 -7
  71. package/lib/commonjs/components/HeaderActions.js.map +0 -1
  72. package/lib/commonjs/components/HeaderReloadButton.js.map +0 -1
  73. package/lib/commonjs/components/TimeMachineBody.js.map +0 -1
  74. package/lib/commonjs/components/TimeMachineModal.js.map +0 -1
  75. package/lib/commonjs/components/confirm.js.map +0 -1
  76. package/lib/commonjs/components/floating/NamePromptModal.js.map +0 -1
  77. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js.map +0 -1
  78. package/lib/commonjs/components/floating/armedPoint.js.map +0 -1
  79. package/lib/commonjs/components/floating/barSettings.js.map +0 -1
  80. package/lib/commonjs/components/format.js.map +0 -1
  81. package/lib/commonjs/components/list/CaptureBar.js.map +0 -1
  82. package/lib/commonjs/components/list/RestoreOutcomeCard.js.map +0 -1
  83. package/lib/commonjs/components/list/SnapshotRow.js.map +0 -1
  84. package/lib/commonjs/components/list/SourcesStrip.js.map +0 -1
  85. package/lib/commonjs/components/list/UndoDeleteBar.js.map +0 -1
  86. package/lib/commonjs/components/list/WipeAllRow.js.map +0 -1
  87. package/lib/commonjs/components/preview/ItemDiffPanel.js.map +0 -1
  88. package/lib/commonjs/components/preview/PreviewFlow.js.map +0 -1
  89. package/lib/commonjs/components/preview/RestorePointNameRow.js.map +0 -1
  90. package/lib/commonjs/components/preview/RestorePreviewScreen.js.map +0 -1
  91. package/lib/commonjs/components/preview/verdictMeta.js.map +0 -1
  92. package/lib/commonjs/components/sources.js.map +0 -1
  93. package/lib/commonjs/hooks/useTimeMachine.js.map +0 -1
  94. package/lib/commonjs/index.js.map +0 -1
  95. package/lib/commonjs/preset.js.map +0 -1
  96. package/lib/commonjs/store/routeRestore.js.map +0 -1
  97. package/lib/commonjs/store/snapshotVault.js.map +0 -1
  98. package/lib/commonjs/store/sourceIds.js.map +0 -1
  99. package/lib/commonjs/store/sourceSettings.js.map +0 -1
  100. package/lib/commonjs/store/timeMachineStore.js.map +0 -1
  101. package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +0 -1
  102. package/lib/commonjs/ui/SwipeActionsRow.js.map +0 -1
  103. package/lib/commonjs/ui/primitives.js.map +0 -1
  104. package/lib/commonjs/ui/tmTheme.js.map +0 -1
  105. package/lib/module/components/HeaderActions.js.map +0 -1
  106. package/lib/module/components/HeaderReloadButton.js.map +0 -1
  107. package/lib/module/components/TimeMachineBody.js.map +0 -1
  108. package/lib/module/components/TimeMachineModal.js.map +0 -1
  109. package/lib/module/components/confirm.js.map +0 -1
  110. package/lib/module/components/floating/NamePromptModal.js.map +0 -1
  111. package/lib/module/components/floating/TimeMachineFloatingTool.js.map +0 -1
  112. package/lib/module/components/floating/armedPoint.js.map +0 -1
  113. package/lib/module/components/floating/barSettings.js.map +0 -1
  114. package/lib/module/components/format.js.map +0 -1
  115. package/lib/module/components/list/CaptureBar.js.map +0 -1
  116. package/lib/module/components/list/RestoreOutcomeCard.js.map +0 -1
  117. package/lib/module/components/list/SnapshotRow.js.map +0 -1
  118. package/lib/module/components/list/SourcesStrip.js.map +0 -1
  119. package/lib/module/components/list/UndoDeleteBar.js.map +0 -1
  120. package/lib/module/components/list/WipeAllRow.js.map +0 -1
  121. package/lib/module/components/preview/ItemDiffPanel.js.map +0 -1
  122. package/lib/module/components/preview/PreviewFlow.js.map +0 -1
  123. package/lib/module/components/preview/RestorePointNameRow.js.map +0 -1
  124. package/lib/module/components/preview/RestorePreviewScreen.js.map +0 -1
  125. package/lib/module/components/preview/verdictMeta.js.map +0 -1
  126. package/lib/module/components/sources.js.map +0 -1
  127. package/lib/module/hooks/useTimeMachine.js.map +0 -1
  128. package/lib/module/index.js.map +0 -1
  129. package/lib/module/preset.js.map +0 -1
  130. package/lib/module/store/routeRestore.js.map +0 -1
  131. package/lib/module/store/snapshotVault.js.map +0 -1
  132. package/lib/module/store/sourceIds.js.map +0 -1
  133. package/lib/module/store/sourceSettings.js.map +0 -1
  134. package/lib/module/store/timeMachineStore.js.map +0 -1
  135. package/lib/module/sync/timeMachineSyncAdapter.js.map +0 -1
  136. package/lib/module/ui/SwipeActionsRow.js.map +0 -1
  137. package/lib/module/ui/primitives.js.map +0 -1
  138. package/lib/module/ui/tmTheme.js.map +0 -1
  139. package/lib/typescript/components/HeaderActions.d.ts.map +0 -1
  140. package/lib/typescript/components/HeaderReloadButton.d.ts.map +0 -1
  141. package/lib/typescript/components/TimeMachineBody.d.ts.map +0 -1
  142. package/lib/typescript/components/TimeMachineModal.d.ts.map +0 -1
  143. package/lib/typescript/components/confirm.d.ts.map +0 -1
  144. package/lib/typescript/components/floating/NamePromptModal.d.ts.map +0 -1
  145. package/lib/typescript/components/floating/TimeMachineFloatingTool.d.ts.map +0 -1
  146. package/lib/typescript/components/floating/armedPoint.d.ts.map +0 -1
  147. package/lib/typescript/components/floating/barSettings.d.ts.map +0 -1
  148. package/lib/typescript/components/format.d.ts.map +0 -1
  149. package/lib/typescript/components/list/CaptureBar.d.ts.map +0 -1
  150. package/lib/typescript/components/list/RestoreOutcomeCard.d.ts.map +0 -1
  151. package/lib/typescript/components/list/SnapshotRow.d.ts.map +0 -1
  152. package/lib/typescript/components/list/SourcesStrip.d.ts.map +0 -1
  153. package/lib/typescript/components/list/UndoDeleteBar.d.ts.map +0 -1
  154. package/lib/typescript/components/list/WipeAllRow.d.ts.map +0 -1
  155. package/lib/typescript/components/preview/ItemDiffPanel.d.ts.map +0 -1
  156. package/lib/typescript/components/preview/PreviewFlow.d.ts.map +0 -1
  157. package/lib/typescript/components/preview/RestorePointNameRow.d.ts.map +0 -1
  158. package/lib/typescript/components/preview/RestorePreviewScreen.d.ts.map +0 -1
  159. package/lib/typescript/components/preview/verdictMeta.d.ts.map +0 -1
  160. package/lib/typescript/components/sources.d.ts.map +0 -1
  161. package/lib/typescript/hooks/useTimeMachine.d.ts.map +0 -1
  162. package/lib/typescript/index.d.ts.map +0 -1
  163. package/lib/typescript/preset.d.ts.map +0 -1
  164. package/lib/typescript/store/routeRestore.d.ts.map +0 -1
  165. package/lib/typescript/store/snapshotVault.d.ts.map +0 -1
  166. package/lib/typescript/store/sourceIds.d.ts.map +0 -1
  167. package/lib/typescript/store/sourceSettings.d.ts.map +0 -1
  168. package/lib/typescript/store/timeMachineStore.d.ts.map +0 -1
  169. package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +0 -1
  170. package/lib/typescript/ui/SwipeActionsRow.d.ts.map +0 -1
  171. package/lib/typescript/ui/primitives.d.ts.map +0 -1
  172. package/lib/typescript/ui/tmTheme.d.ts.map +0 -1
@@ -1,715 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.TIME_MACHINE_FLOATING_TOOL_ID = void 0;
7
- exports.TimeMachineFloatingTool = TimeMachineFloatingTool;
8
- exports.openTimeMachineFloatingTool = openTimeMachineFloatingTool;
9
- var _react = require("react");
10
- var _reactNative = require("react-native");
11
- var _sharedUi = require("@buoy-gg/shared-ui");
12
- var _useTimeMachine = require("../../hooks/useTimeMachine");
13
- var _timeMachineStore = require("../../store/timeMachineStore");
14
- var _routeRestore = require("../../store/routeRestore");
15
- var _sourceSettings = require("../../store/sourceSettings");
16
- var _RestoreOutcomeCard = require("../list/RestoreOutcomeCard");
17
- var _format = require("../format");
18
- var _armedPoint = require("./armedPoint");
19
- var _NamePromptModal = require("./NamePromptModal");
20
- var _barSettings = require("./barSettings");
21
- var _jsxRuntime = require("react/jsx-runtime");
22
- /**
23
- * The restore bar: a floating strip that stays over the app with one restore
24
- * point armed, so "go back to A" is one tap instead of dial → tool → RESTORE.
25
- *
26
- * Same shell and shape as Network's throttling strip. Left to right: a
27
- * "Restore" button, the armed point's name over a status line (the drag
28
- * surface), a chevron for the picker, a capture button, and ×. Close only
29
- * dismisses; an armed point is not a running effect, so there is nothing to
30
- * switch off.
31
- *
32
- * Safety: every restore from here first writes the rolling "Before last
33
- * restore" snapshot, and the status line offers Undo for ten seconds. A
34
- * control meant to be tapped without looking has to be safe to mis-tap.
35
- *
36
- * Feedback follows the list's rule: a clean restore gets no toast, because
37
- * the restored app is the confirmation. The button flashes a check so a tap
38
- * on an already-matching screen still visibly did something. Only a partial
39
- * restore, an error, or the tier cap opens the accessory panel under the
40
- * strip.
41
- */
42
-
43
- const TIME_MACHINE_FLOATING_TOOL_ID = exports.TIME_MACHINE_FLOATING_TOOL_ID = "time-machine-restore";
44
-
45
- /** How long "just now" and Undo stay on offer. */
46
- const RECENT_MS = 10_000;
47
- const FLASH_MS = 900;
48
- const PICKER_ROWS = 6;
49
- const registration = {
50
- id: TIME_MACHINE_FLOATING_TOOL_ID,
51
- render: context => /*#__PURE__*/(0, _jsxRuntime.jsx)(TimeMachineFloatingTool, {
52
- ...context
53
- })
54
- };
55
-
56
- // Restore an open bar on the next boot. Dev bundles on a real RN runtime
57
- // only: a production build must never re-open a devtool at launch, and the
58
- // desktop dashboard and web mirrors host this module without a device.
59
- if (typeof __DEV__ !== "undefined" && __DEV__ && typeof navigator !== "undefined" && navigator.product === "ReactNative") {
60
- (0, _sharedUi.registerPersistentFloatingTool)(registration, _sharedUi.persistentStorage);
61
- }
62
-
63
- /** Open (or reveal) the restore bar, optionally arming a specific point. */
64
- function openTimeMachineFloatingTool(armId) {
65
- if (armId) (0, _armedPoint.setPickedPointId)(armId);
66
- (0, _sharedUi.openFloatingTool)(registration);
67
- }
68
- /** "3 of 5 sources failed", counting the route leg when it was asked for. */
69
- function failureSummary(outcome) {
70
- const results = Object.values(outcome.results);
71
- const failed = results.filter(r => !r.ok || r.skipped.length > 0).length;
72
- const total = results.length;
73
- const route = outcome.route;
74
- const routeFailed = route?.status === "failed" || route?.status === "unavailable";
75
- if (failed === 0 && routeFailed) return "Route did not restore";
76
- return `${failed} of ${total} source${total === 1 ? "" : "s"} failed`;
77
- }
78
- function failureLines(outcome) {
79
- const lines = [];
80
- for (const [id, r] of Object.entries(outcome.results)) {
81
- if (r.ok && r.skipped.length === 0) continue;
82
- const reason = r.skipped[0]?.reason ?? r.warnings[0] ?? "did not apply";
83
- lines.push(`${id}: ${reason}`);
84
- }
85
- const route = outcome.route;
86
- if (route?.status === "failed" || route?.status === "unavailable") lines.push(`route: ${route.status}`);
87
- return lines;
88
- }
89
-
90
- /** A short-lived flag: set it, and it clears itself after `ms`. */
91
- function useFlash(ms) {
92
- const [token, setToken] = (0, _react.useState)(0);
93
- (0, _react.useEffect)(() => {
94
- if (!token) return;
95
- const timer = setTimeout(() => setToken(0), ms);
96
- return () => clearTimeout(timer);
97
- }, [token, ms]);
98
- return [token > 0, () => setToken(n => n + 1)];
99
- }
100
- function TimeMachineFloatingTool({
101
- id,
102
- revealToken,
103
- dismiss
104
- }) {
105
- const {
106
- snapshots,
107
- hasLoaded,
108
- busy
109
- } = (0, _useTimeMachine.useTimeMachineState)();
110
- const providers = (0, _useTimeMachine.useSnapshotProviders)();
111
- const pickedId = (0, _react.useSyncExternalStore)(_armedPoint.subscribePickedPoint, _armedPoint.getPickedPointId, _armedPoint.getPickedPointId);
112
- const snapshotLimit = (0, _sharedUi.useLimit)("timeMachine.snapshots");
113
- const reloadMode = (0, _react.useSyncExternalStore)(_barSettings.subscribeBarSettings, _barSettings.getBarReloadMode, _barSettings.getBarReloadMode);
114
- const {
115
- width
116
- } = (0, _reactNative.useWindowDimensions)();
117
- const [panel, setPanel] = (0, _react.useState)(null);
118
- const [failed, setFailed] = (0, _react.useState)(null);
119
- const [error, setError] = (0, _react.useState)();
120
- const [naming, setNaming] = (0, _react.useState)(false);
121
- const [restoredFlash, flashRestored] = useFlash(FLASH_MS);
122
- const [capturedFlash, flashCaptured] = useFlash(FLASH_MS);
123
- // The way back after a bar restore: the safety snapshot's id, for a while.
124
- const [undo, setUndo] = (0, _react.useState)(null);
125
- const [undoneAt, setUndoneAt] = (0, _react.useState)(null);
126
- // Re-render on a slow clock so "just now" and "restored 5s ago" move on.
127
- const [, setTick] = (0, _react.useState)(0);
128
- const mounted = (0, _react.useRef)(true);
129
- (0, _react.useEffect)(() => {
130
- mounted.current = true;
131
- void (0, _armedPoint.ensurePickedPointLoaded)();
132
- void (0, _barSettings.ensureBarSettingsLoaded)();
133
- void _timeMachineStore.timeMachineStore.ensureLoaded();
134
- return () => {
135
- mounted.current = false;
136
- };
137
- }, []);
138
- const armed = (0, _armedPoint.resolveArmedPoint)(snapshots, pickedId);
139
- const armedId = armed?.id;
140
- const lastRestoredAt = armed?.lastRestoredAt;
141
- const createdAt = armed?.createdAt;
142
- (0, _react.useEffect)(() => {
143
- if (lastRestoredAt === undefined && createdAt === undefined && !undo && undoneAt === null) return;
144
- const timer = setInterval(() => setTick(n => n + 1), 2_500);
145
- return () => clearInterval(timer);
146
- }, [lastRestoredAt, createdAt, undo, undoneAt]);
147
-
148
- // A failure belongs to the point it happened on; arming another clears it.
149
- (0, _react.useEffect)(() => {
150
- if (failed && failed.snapshotId !== armedId) {
151
- setFailed(null);
152
- setPanel(p => p === "outcome" ? null : p);
153
- }
154
- }, [armedId, failed]);
155
- const now = Date.now();
156
- const undoAvailable = undo !== null && now - undo.at < RECENT_MS && snapshots.some(meta => meta.id === undo.id);
157
- // The tier cap counts the points you chose to keep, not the safety slot.
158
- const kept = snapshots.filter(meta => !meta.safety);
159
- const atCaptureLimit = kept.length >= snapshotLimit;
160
- const restoreDisabled = !armed || busy !== null;
161
- const captureDisabled = busy !== null;
162
- const runRestore = async (targetId, options) => {
163
- if (busy) return;
164
- setPanel(null);
165
- setError(undefined);
166
- try {
167
- const outcome = await _timeMachineStore.timeMachineStore.restore(targetId,
168
- // Reload mode restores storage, parks the route for the next boot
169
- // and reloads: the only way to reach screens that read storage once.
170
- reloadMode && options.safetyCapture ? "reload" : "live", undefined, options);
171
- if (!mounted.current) return;
172
- if ((0, _RestoreOutcomeCard.restoreNeedsAttention)(outcome)) {
173
- setFailed(outcome);
174
- setPanel("outcome");
175
- } else {
176
- setFailed(null);
177
- flashRestored();
178
- }
179
- setUndo(outcome.safetySnapshotId ? {
180
- id: outcome.safetySnapshotId,
181
- at: Date.now()
182
- } : null);
183
- if (outcome.safetySnapshotId) setUndoneAt(null);
184
- } catch (failure) {
185
- if (!mounted.current) return;
186
- setError(failure instanceof Error ? failure.message : String(failure));
187
- setPanel("error");
188
- }
189
- };
190
-
191
- /** Tap: the point's own route setting applies. Long-press: state only. */
192
- const restore = (stateOnly = false) => {
193
- if (!armed) return;
194
- void runRestore(armed.id, {
195
- safetyCapture: true,
196
- ...(stateOnly ? {
197
- restoreRoute: false
198
- } : {})
199
- });
200
- };
201
- const undoRestore = () => {
202
- if (!undo) return;
203
- // Back to where the app was, state only: you have not moved since.
204
- void runRestore(undo.id, {
205
- safetyCapture: false,
206
- restoreRoute: false
207
- }).then(() => {
208
- if (mounted.current) setUndoneAt(Date.now());
209
- });
210
- };
211
-
212
- // `+` asks for a name first, in a dialog over the app. An empty name
213
- // keeps the default (screen and clock).
214
- const askForName = () => {
215
- if (busy) return;
216
- setError(undefined);
217
- if (atCaptureLimit) {
218
- setPanel("cap");
219
- return;
220
- }
221
- setPanel(null);
222
- setNaming(true);
223
- };
224
- const capture = async typed => {
225
- setNaming(false);
226
- if (busy) return;
227
- try {
228
- const meta = await _timeMachineStore.timeMachineStore.capture(typed);
229
- if (!mounted.current) return;
230
- // Captured from the bar means "loop back to here": arm it.
231
- (0, _armedPoint.setPickedPointId)(meta.id);
232
- setUndo(null);
233
- flashCaptured();
234
- } catch (failure) {
235
- if (!mounted.current) return;
236
- setError(failure instanceof Error ? failure.message : String(failure));
237
- setPanel("error");
238
- }
239
- };
240
- const pick = meta => {
241
- (0, _armedPoint.setPickedPointId)(meta.id);
242
- setPanel(null);
243
- };
244
- const name = armed?.name ?? (hasLoaded ? "No restore points" : "Loading…");
245
- let status;
246
- let statusTone = "normal";
247
- let idle = false;
248
- if (busy === "restore") status = "Restoring…";else if (busy === "capture") status = "Capturing…";else if (error) {
249
- status = error;
250
- statusTone = "danger";
251
- } else if (failed && armed && failed.snapshotId === armed.id) {
252
- status = failureSummary(failed);
253
- statusTone = "danger";
254
- } else if (!armed) status = hasLoaded ? "Capture one with +" : "";else {
255
- idle = true;
256
- const restoredAgo = armed.lastRestoredAt === undefined ? null : now - armed.lastRestoredAt;
257
- const capturedAgo = now - armed.createdAt;
258
- if (undoneAt !== null && now - undoneAt < RECENT_MS) status = "undone, state only";else if (restoredAgo !== null && restoredAgo < RECENT_MS) status = "restored just now";else if (capturedAgo < RECENT_MS && restoredAgo === null) status = "captured just now";else if (armed.lastRestoredAt !== undefined) status = `restored ${(0, _format.timeAgo)(armed.lastRestoredAt)}`;else status = `${Object.keys(armed.sources).length} sources`;
259
- if (reloadMode) status += " · reloads";
260
- }
261
- // Say where a restore will take you before it does.
262
- const destination = idle && !undoAvailable && armed && armed.route && (0, _timeMachineStore.willRestoreRoute)(armed) ? armed.route.pathname : null;
263
- const pickable = snapshots.filter(meta => !meta.baseline && !meta.safety);
264
- const captureSummary = (() => {
265
- const count = providers.filter(p => p.status().canCapture && (0, _sourceSettings.isSourceEnabled)(p.id)).length;
266
- const route = (0, _routeRestore.captureCurrentRoute)();
267
- const parts = [`${count} source${count === 1 ? "" : "s"}`];
268
- if (route) parts.push(route.pathname);
269
- return parts.join(" · ");
270
- })();
271
- let accessory;
272
- if (panel === "picker") {
273
- accessory = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
274
- style: styles.menu,
275
- testID: "time-machine-bar-picker",
276
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
277
- style: styles.pickerList,
278
- bounces: false,
279
- children: [pickable.map(meta => {
280
- const isArmed = meta.id === armed?.id;
281
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
282
- testID: `time-machine-bar-pick-${meta.id}`,
283
- accessibilityRole: "button",
284
- accessibilityState: {
285
- selected: isArmed
286
- },
287
- accessibilityLabel: `Arm ${meta.name}`,
288
- onPress: () => pick(meta),
289
- style: styles.pickRow,
290
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
291
- style: styles.pickText,
292
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
293
- numberOfLines: 1,
294
- style: styles.pickName,
295
- children: meta.name
296
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
297
- numberOfLines: 1,
298
- style: styles.pickDetail,
299
- children: [(0, _format.timeAgo)(meta.createdAt), meta.route ? ` · ${meta.route.pathname}` : "", meta.lastRestoredAt !== undefined ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
300
- style: styles.pickRestored,
301
- children: ` · restored ${(0, _format.timeAgo)(meta.lastRestoredAt)}`
302
- }) : null]
303
- })]
304
- }), isArmed ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
305
- size: 14,
306
- color: _sharedUi.night.color.accent
307
- }) : null]
308
- }, meta.id);
309
- }), pickable.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
310
- style: styles.menuText,
311
- children: "No restore points yet."
312
- }) : null]
313
- }), pickable.length > PICKER_ROWS ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
314
- style: styles.pickerMore,
315
- children: ["Scroll for ", pickable.length - PICKER_ROWS, " more"]
316
- }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
317
- style: styles.settingRow,
318
- testID: "time-machine-bar-reload-mode",
319
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
320
- style: styles.settingText,
321
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
322
- style: styles.settingLabel,
323
- children: "Reload the app after restoring"
324
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
325
- style: styles.settingHint,
326
- children: "For screens that read their state from storage only when they open, like a cart kept in a React context."
327
- })]
328
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.NightSwitch, {
329
- value: reloadMode,
330
- onValueChange: _barSettings.setBarReloadMode
331
- })]
332
- })]
333
- });
334
- } else if (panel === "outcome" && failed) {
335
- accessory = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
336
- style: styles.menu,
337
- testID: "time-machine-bar-outcome",
338
- children: [failureLines(failed).map(line => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
339
- style: styles.menuDanger,
340
- children: line
341
- }, line)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
342
- style: styles.menuText,
343
- children: "The full report is on Time Machine's outcome card."
344
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(DismissLink, {
345
- onPress: () => setPanel(null)
346
- })]
347
- });
348
- } else if (panel === "cap") {
349
- accessory = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
350
- style: styles.menu,
351
- testID: "time-machine-bar-cap",
352
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
353
- style: styles.menuText,
354
- children: ["Free plan keeps ", snapshotLimit, " restore point", snapshotLimit === 1 ? "" : "s", ". Delete one in Time Machine, or upgrade to Pro for unlimited snapshots."]
355
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(DismissLink, {
356
- onPress: () => setPanel(null)
357
- })]
358
- });
359
- } else if (panel === "error" && error) {
360
- accessory = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
361
- style: styles.menu,
362
- testID: "time-machine-bar-error",
363
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
364
- accessibilityRole: "alert",
365
- style: styles.menuDanger,
366
- children: error
367
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(DismissLink, {
368
- onPress: () => {
369
- setPanel(null);
370
- setError(undefined);
371
- }
372
- })]
373
- });
374
- }
375
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
376
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_NamePromptModal.NamePromptModal, {
377
- visible: naming,
378
- summary: captureSummary,
379
- onCapture: typed => void capture(typed),
380
- onCancel: () => setNaming(false)
381
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FloatingTool, {
382
- id: id,
383
- revealToken: revealToken,
384
- variant: "strip",
385
- dragFromSurface: true,
386
- defaultPlacement: "bottom",
387
- accessibilityLabel: `Move or hide the action bar. ${armed ? `${armed.name} armed` : "Nothing armed"}`,
388
- accessory: accessory
389
- // The hidden tab keeps the tool's icon, so it is not one more
390
- // anonymous grip on the edge next to the minimized-tools dock.
391
- ,
392
- handleAdornment: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TimeMachineIcon, {
393
- size: 12,
394
- color: _sharedUi.night.color.accent
395
- }),
396
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
397
- style: [styles.row, {
398
- width: Math.max(260, Math.min(320, width - 64))
399
- }],
400
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
401
- testID: "time-machine-bar-restore",
402
- accessibilityRole: "button",
403
- accessibilityLabel: armed ? `Restore ${armed.name}` : "Restore, nothing armed",
404
- accessibilityHint: destination ? `Also returns to ${destination}. Long press to restore state only.` : undefined,
405
- accessibilityState: {
406
- disabled: restoreDisabled
407
- },
408
- disabled: restoreDisabled,
409
- onPress: () => restore(false),
410
- onLongPress: () => restore(true),
411
- style: [styles.restoreButton, restoreDisabled && styles.disabled],
412
- children: busy === "restore" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
413
- size: "small",
414
- color: _sharedUi.night.color.accent
415
- }) : restoredFlash ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
416
- style: styles.restoreText,
417
- testID: "time-machine-bar-restored",
418
- children: "Restored"
419
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
420
- style: styles.restoreText,
421
- children: "Restore"
422
- })
423
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
424
- style: styles.identity,
425
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
426
- testID: "time-machine-bar-name",
427
- accessibilityLabel: `Armed restore point: ${name}`,
428
- numberOfLines: 1
429
- // Default names are "screen · clock"; the clock is what tells
430
- // two captures apart, so the end must survive truncation.
431
- ,
432
- ellipsizeMode: "middle",
433
- style: styles.name,
434
- children: name
435
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
436
- style: styles.statusRow,
437
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
438
- testID: "time-machine-bar-status",
439
- numberOfLines: 1,
440
- accessibilityLiveRegion: "polite",
441
- style: [styles.status, statusTone === "danger" && styles.danger],
442
- children: [status, destination ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
443
- style: styles.destination,
444
- children: ` · → ${destination}`
445
- }) : null]
446
- }), undoAvailable && !busy ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
447
- testID: "time-machine-bar-undo",
448
- accessibilityRole: "button",
449
- accessibilityLabel: "Undo the last restore",
450
- onPress: undoRestore,
451
- hitSlop: {
452
- top: 8,
453
- bottom: 8,
454
- left: 6,
455
- right: 6
456
- },
457
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
458
- style: styles.undo,
459
- children: "Undo"
460
- })
461
- }) : null]
462
- })]
463
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
464
- testID: "time-machine-bar-pick",
465
- accessibilityRole: "button",
466
- accessibilityLabel: "Choose another restore point",
467
- accessibilityState: {
468
- expanded: panel === "picker"
469
- },
470
- onPress: () => setPanel(p => p === "picker" ? null : "picker"),
471
- style: styles.smallButton,
472
- children: panel === "picker" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronUp, {
473
- size: 14,
474
- color: _sharedUi.night.color.textSecondary
475
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
476
- size: 14,
477
- color: _sharedUi.night.color.textSecondary
478
- })
479
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
480
- testID: "time-machine-bar-capture",
481
- accessibilityRole: "button",
482
- accessibilityLabel: "Capture a new restore point here and arm it",
483
- accessibilityState: {
484
- disabled: captureDisabled
485
- },
486
- disabled: captureDisabled,
487
- onPress: askForName,
488
- style: [styles.smallButton, captureDisabled && styles.disabled],
489
- children: busy === "capture" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
490
- size: "small",
491
- color: _sharedUi.night.color.accent
492
- }) : capturedFlash ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
493
- testID: "time-machine-bar-captured",
494
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
495
- size: 18,
496
- color: _sharedUi.night.color.accent
497
- })
498
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Plus, {
499
- size: 18,
500
- color: atCaptureLimit ? _sharedUi.night.color.textTertiary : _sharedUi.night.color.text
501
- })
502
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
503
- style: styles.separator
504
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
505
- testID: "time-machine-bar-close",
506
- accessibilityRole: "button",
507
- accessibilityLabel: "Close the action bar",
508
- onPress: dismiss,
509
- style: styles.closeButton,
510
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
511
- size: 16,
512
- color: _sharedUi.night.color.textSecondary
513
- })
514
- })]
515
- })
516
- })]
517
- });
518
- }
519
- function DismissLink({
520
- onPress,
521
- label = "Dismiss"
522
- }) {
523
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
524
- testID: "time-machine-bar-dismiss",
525
- accessibilityRole: "button",
526
- onPress: onPress,
527
- style: styles.dismiss,
528
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
529
- style: styles.dismissText,
530
- children: label
531
- })
532
- });
533
- }
534
- const styles = _reactNative.StyleSheet.create({
535
- row: {
536
- flexDirection: "row",
537
- alignItems: "center",
538
- gap: 4,
539
- paddingVertical: 4,
540
- minHeight: 48
541
- },
542
- // The primary action is a word, not a glyph: "Restore" needs no legend.
543
- // Same pill as Network's strip action: accent ink on a tinted, outlined
544
- // dark chip, so it belongs to the strip instead of shouting over it.
545
- restoreButton: {
546
- height: 32,
547
- minWidth: 72,
548
- paddingHorizontal: 12,
549
- borderRadius: 8,
550
- borderWidth: 1,
551
- borderColor: _sharedUi.night.color.accentBorderStrong,
552
- backgroundColor: _sharedUi.night.color.accentSoft,
553
- alignItems: "center",
554
- justifyContent: "center",
555
- marginLeft: 2
556
- },
557
- restoreText: {
558
- color: _sharedUi.night.color.accent,
559
- fontSize: 12,
560
- fontWeight: "700"
561
- },
562
- smallButton: {
563
- width: 32,
564
- minHeight: 44,
565
- alignItems: "center",
566
- justifyContent: "center"
567
- },
568
- closeButton: {
569
- width: 28,
570
- minHeight: 44,
571
- alignItems: "center",
572
- justifyContent: "center"
573
- },
574
- identity: {
575
- flex: 1,
576
- minHeight: 44,
577
- justifyContent: "center",
578
- gap: 1
579
- },
580
- name: {
581
- color: _sharedUi.night.color.text,
582
- fontSize: 13,
583
- fontWeight: "500"
584
- },
585
- statusRow: {
586
- flexDirection: "row",
587
- alignItems: "center",
588
- gap: 8
589
- },
590
- status: {
591
- color: _sharedUi.night.color.textSecondary,
592
- fontSize: 10,
593
- lineHeight: 14,
594
- flexShrink: 1
595
- },
596
- destination: {
597
- color: _sharedUi.night.color.textTertiary
598
- },
599
- danger: {
600
- color: _sharedUi.night.color.danger
601
- },
602
- undo: {
603
- color: _sharedUi.night.color.accent,
604
- fontSize: 10,
605
- lineHeight: 14,
606
- fontWeight: "700"
607
- },
608
- separator: {
609
- height: 20,
610
- width: 1,
611
- backgroundColor: _sharedUi.night.color.separator,
612
- marginHorizontal: 2
613
- },
614
- disabled: {
615
- opacity: 0.4
616
- },
617
- menu: {
618
- marginVertical: 8,
619
- padding: 6,
620
- borderRadius: _sharedUi.night.radius.card,
621
- borderWidth: 1,
622
- borderColor: _sharedUi.night.color.separator,
623
- backgroundColor: _sharedUi.night.color.surface,
624
- shadowColor: "#000",
625
- shadowOffset: {
626
- width: 0,
627
- height: 6
628
- },
629
- shadowOpacity: 0.25,
630
- shadowRadius: 16,
631
- elevation: 8
632
- },
633
- menuText: {
634
- color: _sharedUi.night.color.textSecondary,
635
- fontSize: 12,
636
- lineHeight: 18,
637
- padding: 10
638
- },
639
- menuDanger: {
640
- color: _sharedUi.night.color.danger,
641
- fontSize: 12,
642
- lineHeight: 18,
643
- paddingHorizontal: 10,
644
- paddingTop: 6
645
- },
646
- pickerList: {
647
- maxHeight: PICKER_ROWS * 44
648
- },
649
- pickerMore: {
650
- color: _sharedUi.night.color.textTertiary,
651
- fontSize: 10,
652
- lineHeight: 14,
653
- textAlign: "center",
654
- paddingVertical: 4
655
- },
656
- pickRow: {
657
- flexDirection: "row",
658
- alignItems: "center",
659
- gap: 8,
660
- minHeight: 44,
661
- paddingHorizontal: 10,
662
- borderRadius: _sharedUi.night.radius.row
663
- },
664
- pickText: {
665
- flex: 1
666
- },
667
- settingRow: {
668
- flexDirection: "row",
669
- alignItems: "center",
670
- gap: 10,
671
- paddingHorizontal: 10,
672
- paddingVertical: 8,
673
- borderTopWidth: _reactNative.StyleSheet.hairlineWidth,
674
- borderTopColor: _sharedUi.night.color.separator,
675
- marginTop: 4
676
- },
677
- settingText: {
678
- flex: 1,
679
- gap: 2
680
- },
681
- settingLabel: {
682
- color: _sharedUi.night.color.text,
683
- fontSize: 12,
684
- fontWeight: "500"
685
- },
686
- settingHint: {
687
- color: _sharedUi.night.color.textTertiary,
688
- fontSize: 10,
689
- lineHeight: 13
690
- },
691
- pickName: {
692
- color: _sharedUi.night.color.text,
693
- fontSize: 13,
694
- fontWeight: "500"
695
- },
696
- pickDetail: {
697
- color: _sharedUi.night.color.textSecondary,
698
- fontSize: 10,
699
- lineHeight: 14
700
- },
701
- pickRestored: {
702
- color: _sharedUi.night.color.accent
703
- },
704
- dismiss: {
705
- alignSelf: "flex-end",
706
- paddingHorizontal: 10,
707
- paddingVertical: 8
708
- },
709
- dismissText: {
710
- color: _sharedUi.night.color.accent,
711
- fontSize: 12,
712
- fontWeight: "600"
713
- }
714
- });
715
- //# sourceMappingURL=TimeMachineFloatingTool.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.TIME_MACHINE_FLOATING_TOOL_ID=void 0;exports.TimeMachineFloatingTool=TimeMachineFloatingTool;exports.openTimeMachineFloatingTool=openTimeMachineFloatingTool;var _react=require("react");var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _useTimeMachine=require("../../hooks/useTimeMachine");var _timeMachineStore=require("../../store/timeMachineStore");var _routeRestore=require("../../store/routeRestore");var _sourceSettings=require("../../store/sourceSettings");var _RestoreOutcomeCard=require("../list/RestoreOutcomeCard");var _format=require("../format");var _armedPoint=require("./armedPoint");var _NamePromptModal=require("./NamePromptModal");var _barSettings=require("./barSettings");var _jsxRuntime=require("react/jsx-runtime");const TIME_MACHINE_FLOATING_TOOL_ID=exports.TIME_MACHINE_FLOATING_TOOL_ID="time-machine-restore";const RECENT_MS=1e4;const FLASH_MS=900;const PICKER_ROWS=6;const registration={id:TIME_MACHINE_FLOATING_TOOL_ID,render:r=>(0,_jsxRuntime.jsx)(TimeMachineFloatingTool,{...r})};if(typeof __DEV__!=="undefined"&&__DEV__&&typeof navigator!=="undefined"&&navigator.product==="ReactNative"){(0,_sharedUi.registerPersistentFloatingTool)(registration,_sharedUi.persistentStorage)}function openTimeMachineFloatingTool(r){if(r)(0,_armedPoint.setPickedPointId)(r);(0,_sharedUi.openFloatingTool)(registration)}function failureSummary(r){const n=Object.values(r.results);const d=n.filter(y=>!y.ok||y.skipped.length>0).length;const s=n.length;const u=r.route;const o=u?.status==="failed"||u?.status==="unavailable";if(d===0&&o)return"Route did not restore";return`${d} of ${s} source${s===1?"":"s"} failed`}function failureLines(r){const n=[];for(const[s,u]of Object.entries(r.results)){if(u.ok&&u.skipped.length===0)continue;const o=u.skipped[0]?.reason??u.warnings[0]??"did not apply";n.push(`${s}: ${o}`)}const d=r.route;if(d?.status==="failed"||d?.status==="unavailable")n.push(`route: ${d.status}`);return n}function useFlash(r){const[n,d]=(0,_react.useState)(0);(0,_react.useEffect)(()=>{if(!n)return;const s=setTimeout(()=>d(0),r);return()=>clearTimeout(s)},[n,r]);return[n>0,()=>d(s=>s+1)]}function TimeMachineFloatingTool({id:r,revealToken:n,dismiss:d}){const{snapshots:s,hasLoaded:u,busy:o}=(0,_useTimeMachine.useTimeMachineState)();const y=(0,_useTimeMachine.useSnapshotProviders)();const q=(0,_react.useSyncExternalStore)(_armedPoint.subscribePickedPoint,_armedPoint.getPickedPointId,_armedPoint.getPickedPointId);const k=(0,_sharedUi.useLimit)("timeMachine.snapshots");const w=(0,_react.useSyncExternalStore)(_barSettings.subscribeBarSettings,_barSettings.getBarReloadMode,_barSettings.getBarReloadMode);const{width:B}=(0,_reactNative.useWindowDimensions)();const[f,a]=(0,_react.useState)(null);const[h,I]=(0,_react.useState)(null);const[b,p]=(0,_react.useState)();const[W,v]=(0,_react.useState)(false);const[U,G]=useFlash(FLASH_MS);const[K,X]=useFlash(FLASH_MS);const[g,M]=(0,_react.useState)(null);const[T,A]=(0,_react.useState)(null);const[,J]=(0,_react.useState)(0);const m=(0,_react.useRef)(true);(0,_react.useEffect)(()=>{m.current=true;void(0,_armedPoint.ensurePickedPointLoaded)();void(0,_barSettings.ensureBarSettingsLoaded)();void _timeMachineStore.timeMachineStore.ensureLoaded();return()=>{m.current=false}},[]);const t=(0,_armedPoint.resolveArmedPoint)(s,q);const C=t?.id;const L=t?.lastRestoredAt;const O=t?.createdAt;(0,_react.useEffect)(()=>{if(L===void 0&&O===void 0&&!g&&T===null)return;const e=setInterval(()=>J(i=>i+1),2500);return()=>clearInterval(e)},[L,O,g,T]);(0,_react.useEffect)(()=>{if(h&&h.snapshotId!==C){I(null);a(e=>e==="outcome"?null:e)}},[C,h]);const S=Date.now();const z=g!==null&&S-g.at<RECENT_MS&&s.some(e=>e.id===g.id);const Q=s.filter(e=>!e.safety);const H=Q.length>=k;const D=!t||o!==null;const _=o!==null;const E=async(e,i)=>{if(o)return;a(null);p(void 0);try{const c=await _timeMachineStore.timeMachineStore.restore(e,w&&i.safetyCapture?"reload":"live",void 0,i);if(!m.current)return;if((0,_RestoreOutcomeCard.restoreNeedsAttention)(c)){I(c);a("outcome")}else{I(null);G()}M(c.safetySnapshotId?{id:c.safetySnapshotId,at:Date.now()}:null);if(c.safetySnapshotId)A(null)}catch(c){if(!m.current)return;p(c instanceof Error?c.message:String(c));a("error")}};const F=(e=false)=>{if(!t)return;void E(t.id,{safetyCapture:true,...e?{restoreRoute:false}:{}})};const Y=()=>{if(!g)return;void E(g.id,{safetyCapture:false,restoreRoute:false}).then(()=>{if(m.current)A(Date.now())})};const Z=()=>{if(o)return;p(void 0);if(H){a("cap");return}a(null);v(true)};const ee=async e=>{v(false);if(o)return;try{const i=await _timeMachineStore.timeMachineStore.capture(e);if(!m.current)return;(0,_armedPoint.setPickedPointId)(i.id);M(null);X()}catch(i){if(!m.current)return;p(i instanceof Error?i.message:String(i));a("error")}};const te=e=>{(0,_armedPoint.setPickedPointId)(e.id);a(null)};const N=t?.name??(u?"No restore points":"Loading\u2026");let l;let P="normal";let V=false;if(o==="restore")l="Restoring\u2026";else if(o==="capture")l="Capturing\u2026";else if(b){l=b;P="danger"}else if(h&&t&&h.snapshotId===t.id){l=failureSummary(h);P="danger"}else if(!t)l=u?"Capture one with +":"";else{V=true;const e=t.lastRestoredAt===void 0?null:S-t.lastRestoredAt;const i=S-t.createdAt;if(T!==null&&S-T<RECENT_MS)l="undone, state only";else if(e!==null&&e<RECENT_MS)l="restored just now";else if(i<RECENT_MS&&e===null)l="captured just now";else if(t.lastRestoredAt!==void 0)l=`restored ${(0,_format.timeAgo)(t.lastRestoredAt)}`;else l=`${Object.keys(t.sources).length} sources`;if(w)l+=" \xB7 reloads"}const j=V&&!z&&t&&t.route&&(0,_timeMachineStore.willRestoreRoute)(t)?t.route.pathname:null;const R=s.filter(e=>!e.baseline&&!e.safety);const ie=(()=>{const e=y.filter($=>$.status().canCapture&&(0,_sourceSettings.isSourceEnabled)($.id)).length;const i=(0,_routeRestore.captureCurrentRoute)();const c=[`${e} source${e===1?"":"s"}`];if(i)c.push(i.pathname);return c.join(" \xB7 ")})();let x;if(f==="picker"){x=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.menu,testID:"time-machine-bar-picker",children:[(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{style:styles.pickerList,bounces:false,children:[R.map(e=>{const i=e.id===t?.id;return(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{testID:`time-machine-bar-pick-${e.id}`,accessibilityRole:"button",accessibilityState:{selected:i},accessibilityLabel:`Arm ${e.name}`,onPress:()=>te(e),style:styles.pickRow,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.pickText,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{numberOfLines:1,style:styles.pickName,children:e.name}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{numberOfLines:1,style:styles.pickDetail,children:[(0,_format.timeAgo)(e.createdAt),e.route?` \xB7 ${e.route.pathname}`:"",e.lastRestoredAt!==void 0?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.pickRestored,children:` \xB7 restored ${(0,_format.timeAgo)(e.lastRestoredAt)}`}):null]})]}),i?(0,_jsxRuntime.jsx)(_sharedUi.Check,{size:14,color:_sharedUi.night.color.accent}):null]},e.id)}),R.length===0?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.menuText,children:"No restore points yet."}):null]}),R.length>PICKER_ROWS?(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.pickerMore,children:["Scroll for ",R.length-PICKER_ROWS," more"]}):null,(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.settingRow,testID:"time-machine-bar-reload-mode",children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.settingText,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.settingLabel,children:"Reload the app after restoring"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.settingHint,children:"For screens that read their state from storage only when they open, like a cart kept in a React context."})]}),(0,_jsxRuntime.jsx)(_sharedUi.NightSwitch,{value:w,onValueChange:_barSettings.setBarReloadMode})]})]})}else if(f==="outcome"&&h){x=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.menu,testID:"time-machine-bar-outcome",children:[failureLines(h).map(e=>(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.menuDanger,children:e},e)),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.menuText,children:"The full report is on Time Machine's outcome card."}),(0,_jsxRuntime.jsx)(DismissLink,{onPress:()=>a(null)})]})}else if(f==="cap"){x=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.menu,testID:"time-machine-bar-cap",children:[(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.menuText,children:["Free plan keeps ",k," restore point",k===1?"":"s",". Delete one in Time Machine, or upgrade to Pro for unlimited snapshots."]}),(0,_jsxRuntime.jsx)(DismissLink,{onPress:()=>a(null)})]})}else if(f==="error"&&b){x=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.menu,testID:"time-machine-bar-error",children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{accessibilityRole:"alert",style:styles.menuDanger,children:b}),(0,_jsxRuntime.jsx)(DismissLink,{onPress:()=>{a(null);p(void 0)}})]})}return(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsx)(_NamePromptModal.NamePromptModal,{visible:W,summary:ie,onCapture:e=>void ee(e),onCancel:()=>v(false)}),(0,_jsxRuntime.jsx)(_sharedUi.FloatingTool,{id:r,revealToken:n,variant:"strip",dragFromSurface:true,defaultPlacement:"bottom",accessibilityLabel:`Move or hide the action bar. ${t?`${t.name} armed`:"Nothing armed"}`,accessory:x,handleAdornment:(0,_jsxRuntime.jsx)(_sharedUi.TimeMachineIcon,{size:12,color:_sharedUi.night.color.accent}),children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:[styles.row,{width:Math.max(260,Math.min(320,B-64))}],children:[(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"time-machine-bar-restore",accessibilityRole:"button",accessibilityLabel:t?`Restore ${t.name}`:"Restore, nothing armed",accessibilityHint:j?`Also returns to ${j}. Long press to restore state only.`:void 0,accessibilityState:{disabled:D},disabled:D,onPress:()=>F(false),onLongPress:()=>F(true),style:[styles.restoreButton,D&&styles.disabled],children:o==="restore"?(0,_jsxRuntime.jsx)(_reactNative.ActivityIndicator,{size:"small",color:_sharedUi.night.color.accent}):U?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.restoreText,testID:"time-machine-bar-restored",children:"Restored"}):(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.restoreText,children:"Restore"})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.identity,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:"time-machine-bar-name",accessibilityLabel:`Armed restore point: ${N}`,numberOfLines:1,ellipsizeMode:"middle",style:styles.name,children:N}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.statusRow,children:[(0,_jsxRuntime.jsxs)(_reactNative.Text,{testID:"time-machine-bar-status",numberOfLines:1,accessibilityLiveRegion:"polite",style:[styles.status,P==="danger"&&styles.danger],children:[l,j?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.destination,children:` \xB7 \u2192 ${j}`}):null]}),z&&!o?(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"time-machine-bar-undo",accessibilityRole:"button",accessibilityLabel:"Undo the last restore",onPress:Y,hitSlop:{top:8,bottom:8,left:6,right:6},children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.undo,children:"Undo"})}):null]})]}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"time-machine-bar-pick",accessibilityRole:"button",accessibilityLabel:"Choose another restore point",accessibilityState:{expanded:f==="picker"},onPress:()=>a(e=>e==="picker"?null:"picker"),style:styles.smallButton,children:f==="picker"?(0,_jsxRuntime.jsx)(_sharedUi.ChevronUp,{size:14,color:_sharedUi.night.color.textSecondary}):(0,_jsxRuntime.jsx)(_sharedUi.ChevronDown,{size:14,color:_sharedUi.night.color.textSecondary})}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"time-machine-bar-capture",accessibilityRole:"button",accessibilityLabel:"Capture a new restore point here and arm it",accessibilityState:{disabled:_},disabled:_,onPress:Z,style:[styles.smallButton,_&&styles.disabled],children:o==="capture"?(0,_jsxRuntime.jsx)(_reactNative.ActivityIndicator,{size:"small",color:_sharedUi.night.color.accent}):K?(0,_jsxRuntime.jsx)(_reactNative.View,{testID:"time-machine-bar-captured",children:(0,_jsxRuntime.jsx)(_sharedUi.Check,{size:18,color:_sharedUi.night.color.accent})}):(0,_jsxRuntime.jsx)(_sharedUi.Plus,{size:18,color:H?_sharedUi.night.color.textTertiary:_sharedUi.night.color.text})}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.separator}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"time-machine-bar-close",accessibilityRole:"button",accessibilityLabel:"Close the action bar",onPress:d,style:styles.closeButton,children:(0,_jsxRuntime.jsx)(_sharedUi.X,{size:16,color:_sharedUi.night.color.textSecondary})})]})})]})}function DismissLink({onPress:r,label:n="Dismiss"}){return(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"time-machine-bar-dismiss",accessibilityRole:"button",onPress:r,style:styles.dismiss,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.dismissText,children:n})})}const styles=_reactNative.StyleSheet.create({row:{flexDirection:"row",alignItems:"center",gap:4,paddingVertical:4,minHeight:48},restoreButton:{height:32,minWidth:72,paddingHorizontal:12,borderRadius:8,borderWidth:1,borderColor:_sharedUi.night.color.accentBorderStrong,backgroundColor:_sharedUi.night.color.accentSoft,alignItems:"center",justifyContent:"center",marginLeft:2},restoreText:{color:_sharedUi.night.color.accent,fontSize:12,fontWeight:"700"},smallButton:{width:32,minHeight:44,alignItems:"center",justifyContent:"center"},closeButton:{width:28,minHeight:44,alignItems:"center",justifyContent:"center"},identity:{flex:1,minHeight:44,justifyContent:"center",gap:1},name:{color:_sharedUi.night.color.text,fontSize:13,fontWeight:"500"},statusRow:{flexDirection:"row",alignItems:"center",gap:8},status:{color:_sharedUi.night.color.textSecondary,fontSize:10,lineHeight:14,flexShrink:1},destination:{color:_sharedUi.night.color.textTertiary},danger:{color:_sharedUi.night.color.danger},undo:{color:_sharedUi.night.color.accent,fontSize:10,lineHeight:14,fontWeight:"700"},separator:{height:20,width:1,backgroundColor:_sharedUi.night.color.separator,marginHorizontal:2},disabled:{opacity:.4},menu:{marginVertical:8,padding:6,borderRadius:_sharedUi.night.radius.card,borderWidth:1,borderColor:_sharedUi.night.color.separator,backgroundColor:_sharedUi.night.color.surface,shadowColor:"#000",shadowOffset:{width:0,height:6},shadowOpacity:.25,shadowRadius:16,elevation:8},menuText:{color:_sharedUi.night.color.textSecondary,fontSize:12,lineHeight:18,padding:10},menuDanger:{color:_sharedUi.night.color.danger,fontSize:12,lineHeight:18,paddingHorizontal:10,paddingTop:6},pickerList:{maxHeight:PICKER_ROWS*44},pickerMore:{color:_sharedUi.night.color.textTertiary,fontSize:10,lineHeight:14,textAlign:"center",paddingVertical:4},pickRow:{flexDirection:"row",alignItems:"center",gap:8,minHeight:44,paddingHorizontal:10,borderRadius:_sharedUi.night.radius.row},pickText:{flex:1},settingRow:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:10,paddingVertical:8,borderTopWidth:_reactNative.StyleSheet.hairlineWidth,borderTopColor:_sharedUi.night.color.separator,marginTop:4},settingText:{flex:1,gap:2},settingLabel:{color:_sharedUi.night.color.text,fontSize:12,fontWeight:"500"},settingHint:{color:_sharedUi.night.color.textTertiary,fontSize:10,lineHeight:13},pickName:{color:_sharedUi.night.color.text,fontSize:13,fontWeight:"500"},pickDetail:{color:_sharedUi.night.color.textSecondary,fontSize:10,lineHeight:14},pickRestored:{color:_sharedUi.night.color.accent},dismiss:{alignSelf:"flex-end",paddingHorizontal:10,paddingVertical:8},dismissText:{color:_sharedUi.night.color.accent,fontSize:12,fontWeight:"600"}});