@buoy-gg/time-machine 7.0.40 → 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 (173) hide show
  1. package/README.md +4 -0
  2. package/lib/commonjs/components/HeaderActions.js +1 -91
  3. package/lib/commonjs/components/HeaderReloadButton.js +1 -58
  4. package/lib/commonjs/components/TimeMachineBody.js +1 -282
  5. package/lib/commonjs/components/TimeMachineModal.js +1 -74
  6. package/lib/commonjs/components/confirm.js +2 -38
  7. package/lib/commonjs/components/floating/NamePromptModal.js +1 -193
  8. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js +1 -715
  9. package/lib/commonjs/components/floating/armedPoint.js +1 -92
  10. package/lib/commonjs/components/floating/barSettings.js +1 -66
  11. package/lib/commonjs/components/format.js +1 -24
  12. package/lib/commonjs/components/list/CaptureBar.js +1 -97
  13. package/lib/commonjs/components/list/RestoreOutcomeCard.js +1 -175
  14. package/lib/commonjs/components/list/SnapshotRow.js +1 -170
  15. package/lib/commonjs/components/list/SourcesStrip.js +1 -172
  16. package/lib/commonjs/components/list/UndoDeleteBar.js +1 -85
  17. package/lib/commonjs/components/list/WipeAllRow.js +1 -46
  18. package/lib/commonjs/components/preview/ItemDiffPanel.js +1 -79
  19. package/lib/commonjs/components/preview/PreviewFlow.js +1 -51
  20. package/lib/commonjs/components/preview/RestorePointNameRow.js +1 -83
  21. package/lib/commonjs/components/preview/RestorePreviewScreen.js +1 -1010
  22. package/lib/commonjs/components/preview/verdictMeta.js +1 -58
  23. package/lib/commonjs/components/sources.js +1 -83
  24. package/lib/commonjs/hooks/useTimeMachine.js +1 -24
  25. package/lib/commonjs/index.js +1 -161
  26. package/lib/commonjs/preset.js +1 -56
  27. package/lib/commonjs/store/routeRestore.js +1 -188
  28. package/lib/commonjs/store/snapshotVault.js +1 -186
  29. package/lib/commonjs/store/sourceIds.js +1 -9
  30. package/lib/commonjs/store/sourceSettings.js +1 -79
  31. package/lib/commonjs/store/timeMachineStore.js +1 -848
  32. package/lib/commonjs/sync/timeMachineSyncAdapter.js +1 -258
  33. package/lib/commonjs/ui/SwipeActionsRow.js +1 -178
  34. package/lib/commonjs/ui/primitives.js +1 -172
  35. package/lib/commonjs/ui/tmTheme.js +1 -118
  36. package/lib/module/components/HeaderActions.js +1 -85
  37. package/lib/module/components/HeaderReloadButton.js +1 -53
  38. package/lib/module/components/TimeMachineBody.js +1 -277
  39. package/lib/module/components/TimeMachineModal.js +1 -68
  40. package/lib/module/components/confirm.js +2 -33
  41. package/lib/module/components/floating/NamePromptModal.js +1 -188
  42. package/lib/module/components/floating/TimeMachineFloatingTool.js +1 -708
  43. package/lib/module/components/floating/armedPoint.js +1 -82
  44. package/lib/module/components/floating/barSettings.js +1 -57
  45. package/lib/module/components/format.js +1 -19
  46. package/lib/module/components/list/CaptureBar.js +1 -92
  47. package/lib/module/components/list/RestoreOutcomeCard.js +1 -169
  48. package/lib/module/components/list/SnapshotRow.js +1 -167
  49. package/lib/module/components/list/SourcesStrip.js +1 -169
  50. package/lib/module/components/list/UndoDeleteBar.js +1 -82
  51. package/lib/module/components/list/WipeAllRow.js +1 -41
  52. package/lib/module/components/preview/ItemDiffPanel.js +1 -74
  53. package/lib/module/components/preview/PreviewFlow.js +1 -46
  54. package/lib/module/components/preview/RestorePointNameRow.js +1 -78
  55. package/lib/module/components/preview/RestorePreviewScreen.js +1 -1005
  56. package/lib/module/components/preview/verdictMeta.js +1 -54
  57. package/lib/module/components/sources.js +1 -73
  58. package/lib/module/hooks/useTimeMachine.js +1 -19
  59. package/lib/module/index.js +1 -46
  60. package/lib/module/preset.js +1 -51
  61. package/lib/module/store/routeRestore.js +1 -176
  62. package/lib/module/store/snapshotVault.js +1 -170
  63. package/lib/module/store/sourceIds.js +1 -5
  64. package/lib/module/store/sourceSettings.js +1 -69
  65. package/lib/module/store/timeMachineStore.js +1 -839
  66. package/lib/module/sync/timeMachineSyncAdapter.js +1 -253
  67. package/lib/module/ui/SwipeActionsRow.js +1 -173
  68. package/lib/module/ui/primitives.js +1 -164
  69. package/lib/module/ui/tmTheme.js +1 -114
  70. package/lib/web/index.mjs +3 -0
  71. package/package.json +43 -10
  72. package/lib/commonjs/components/HeaderActions.js.map +0 -1
  73. package/lib/commonjs/components/HeaderReloadButton.js.map +0 -1
  74. package/lib/commonjs/components/TimeMachineBody.js.map +0 -1
  75. package/lib/commonjs/components/TimeMachineModal.js.map +0 -1
  76. package/lib/commonjs/components/confirm.js.map +0 -1
  77. package/lib/commonjs/components/floating/NamePromptModal.js.map +0 -1
  78. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js.map +0 -1
  79. package/lib/commonjs/components/floating/armedPoint.js.map +0 -1
  80. package/lib/commonjs/components/floating/barSettings.js.map +0 -1
  81. package/lib/commonjs/components/format.js.map +0 -1
  82. package/lib/commonjs/components/list/CaptureBar.js.map +0 -1
  83. package/lib/commonjs/components/list/RestoreOutcomeCard.js.map +0 -1
  84. package/lib/commonjs/components/list/SnapshotRow.js.map +0 -1
  85. package/lib/commonjs/components/list/SourcesStrip.js.map +0 -1
  86. package/lib/commonjs/components/list/UndoDeleteBar.js.map +0 -1
  87. package/lib/commonjs/components/list/WipeAllRow.js.map +0 -1
  88. package/lib/commonjs/components/preview/ItemDiffPanel.js.map +0 -1
  89. package/lib/commonjs/components/preview/PreviewFlow.js.map +0 -1
  90. package/lib/commonjs/components/preview/RestorePointNameRow.js.map +0 -1
  91. package/lib/commonjs/components/preview/RestorePreviewScreen.js.map +0 -1
  92. package/lib/commonjs/components/preview/verdictMeta.js.map +0 -1
  93. package/lib/commonjs/components/sources.js.map +0 -1
  94. package/lib/commonjs/hooks/useTimeMachine.js.map +0 -1
  95. package/lib/commonjs/index.js.map +0 -1
  96. package/lib/commonjs/preset.js.map +0 -1
  97. package/lib/commonjs/store/routeRestore.js.map +0 -1
  98. package/lib/commonjs/store/snapshotVault.js.map +0 -1
  99. package/lib/commonjs/store/sourceIds.js.map +0 -1
  100. package/lib/commonjs/store/sourceSettings.js.map +0 -1
  101. package/lib/commonjs/store/timeMachineStore.js.map +0 -1
  102. package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +0 -1
  103. package/lib/commonjs/ui/SwipeActionsRow.js.map +0 -1
  104. package/lib/commonjs/ui/primitives.js.map +0 -1
  105. package/lib/commonjs/ui/tmTheme.js.map +0 -1
  106. package/lib/module/components/HeaderActions.js.map +0 -1
  107. package/lib/module/components/HeaderReloadButton.js.map +0 -1
  108. package/lib/module/components/TimeMachineBody.js.map +0 -1
  109. package/lib/module/components/TimeMachineModal.js.map +0 -1
  110. package/lib/module/components/confirm.js.map +0 -1
  111. package/lib/module/components/floating/NamePromptModal.js.map +0 -1
  112. package/lib/module/components/floating/TimeMachineFloatingTool.js.map +0 -1
  113. package/lib/module/components/floating/armedPoint.js.map +0 -1
  114. package/lib/module/components/floating/barSettings.js.map +0 -1
  115. package/lib/module/components/format.js.map +0 -1
  116. package/lib/module/components/list/CaptureBar.js.map +0 -1
  117. package/lib/module/components/list/RestoreOutcomeCard.js.map +0 -1
  118. package/lib/module/components/list/SnapshotRow.js.map +0 -1
  119. package/lib/module/components/list/SourcesStrip.js.map +0 -1
  120. package/lib/module/components/list/UndoDeleteBar.js.map +0 -1
  121. package/lib/module/components/list/WipeAllRow.js.map +0 -1
  122. package/lib/module/components/preview/ItemDiffPanel.js.map +0 -1
  123. package/lib/module/components/preview/PreviewFlow.js.map +0 -1
  124. package/lib/module/components/preview/RestorePointNameRow.js.map +0 -1
  125. package/lib/module/components/preview/RestorePreviewScreen.js.map +0 -1
  126. package/lib/module/components/preview/verdictMeta.js.map +0 -1
  127. package/lib/module/components/sources.js.map +0 -1
  128. package/lib/module/hooks/useTimeMachine.js.map +0 -1
  129. package/lib/module/index.js.map +0 -1
  130. package/lib/module/preset.js.map +0 -1
  131. package/lib/module/store/routeRestore.js.map +0 -1
  132. package/lib/module/store/snapshotVault.js.map +0 -1
  133. package/lib/module/store/sourceIds.js.map +0 -1
  134. package/lib/module/store/sourceSettings.js.map +0 -1
  135. package/lib/module/store/timeMachineStore.js.map +0 -1
  136. package/lib/module/sync/timeMachineSyncAdapter.js.map +0 -1
  137. package/lib/module/ui/SwipeActionsRow.js.map +0 -1
  138. package/lib/module/ui/primitives.js.map +0 -1
  139. package/lib/module/ui/tmTheme.js.map +0 -1
  140. package/lib/typescript/components/HeaderActions.d.ts.map +0 -1
  141. package/lib/typescript/components/HeaderReloadButton.d.ts.map +0 -1
  142. package/lib/typescript/components/TimeMachineBody.d.ts.map +0 -1
  143. package/lib/typescript/components/TimeMachineModal.d.ts.map +0 -1
  144. package/lib/typescript/components/confirm.d.ts.map +0 -1
  145. package/lib/typescript/components/floating/NamePromptModal.d.ts.map +0 -1
  146. package/lib/typescript/components/floating/TimeMachineFloatingTool.d.ts.map +0 -1
  147. package/lib/typescript/components/floating/armedPoint.d.ts.map +0 -1
  148. package/lib/typescript/components/floating/barSettings.d.ts.map +0 -1
  149. package/lib/typescript/components/format.d.ts.map +0 -1
  150. package/lib/typescript/components/list/CaptureBar.d.ts.map +0 -1
  151. package/lib/typescript/components/list/RestoreOutcomeCard.d.ts.map +0 -1
  152. package/lib/typescript/components/list/SnapshotRow.d.ts.map +0 -1
  153. package/lib/typescript/components/list/SourcesStrip.d.ts.map +0 -1
  154. package/lib/typescript/components/list/UndoDeleteBar.d.ts.map +0 -1
  155. package/lib/typescript/components/list/WipeAllRow.d.ts.map +0 -1
  156. package/lib/typescript/components/preview/ItemDiffPanel.d.ts.map +0 -1
  157. package/lib/typescript/components/preview/PreviewFlow.d.ts.map +0 -1
  158. package/lib/typescript/components/preview/RestorePointNameRow.d.ts.map +0 -1
  159. package/lib/typescript/components/preview/RestorePreviewScreen.d.ts.map +0 -1
  160. package/lib/typescript/components/preview/verdictMeta.d.ts.map +0 -1
  161. package/lib/typescript/components/sources.d.ts.map +0 -1
  162. package/lib/typescript/hooks/useTimeMachine.d.ts.map +0 -1
  163. package/lib/typescript/index.d.ts.map +0 -1
  164. package/lib/typescript/preset.d.ts.map +0 -1
  165. package/lib/typescript/store/routeRestore.d.ts.map +0 -1
  166. package/lib/typescript/store/snapshotVault.d.ts.map +0 -1
  167. package/lib/typescript/store/sourceIds.d.ts.map +0 -1
  168. package/lib/typescript/store/sourceSettings.d.ts.map +0 -1
  169. package/lib/typescript/store/timeMachineStore.d.ts.map +0 -1
  170. package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +0 -1
  171. package/lib/typescript/ui/SwipeActionsRow.d.ts.map +0 -1
  172. package/lib/typescript/ui/primitives.d.ts.map +0 -1
  173. package/lib/typescript/ui/tmTheme.d.ts.map +0 -1
@@ -1,82 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Which restore point the floating restore bar restores on tap.
5
- *
6
- * The bar needs an answer before the user picks anything, or its first tap is
7
- * a picker instead of a restore. So the armed point is resolved, not just
8
- * stored: an explicit pick wins, then the point restored most recently, then
9
- * the newest capture. Baselines never resolve; restoring one wipes storage
10
- * and reloads behind a confirm dialog, which is not a one-tap action. The
11
- * safety slot never resolves either: Undo is its way in.
12
- *
13
- * The explicit pick is persisted so it survives the reloads the loop itself
14
- * causes (RELOAD APP, reload-mode restores).
15
- */
16
- import { devToolsStorageKeys, persistentStorage } from "@buoy-gg/shared-ui";
17
- const KEY = devToolsStorageKeys.timeMachine.armedPoint();
18
- let pickedId = null;
19
- let loaded = false;
20
- let loadPromise = null;
21
- const listeners = new Set();
22
- const notify = () => listeners.forEach(listener => listener());
23
- export function getPickedPointId() {
24
- return pickedId;
25
- }
26
- export function subscribePickedPoint(listener) {
27
- listeners.add(listener);
28
- return () => {
29
- listeners.delete(listener);
30
- };
31
- }
32
-
33
- /** Read the saved pick once. Later calls return the same promise. */
34
- export function ensurePickedPointLoaded() {
35
- if (loaded) return Promise.resolve();
36
- if (!loadPromise) {
37
- loadPromise = persistentStorage.getItem(KEY).then(saved => {
38
- // A pick made while the read was in flight wins over the disk copy.
39
- if (pickedId === null && saved) {
40
- pickedId = saved;
41
- notify();
42
- }
43
- }).catch(() => {}).finally(() => {
44
- loaded = true;
45
- });
46
- }
47
- return loadPromise;
48
- }
49
- export function setPickedPointId(id) {
50
- if (pickedId === id) return;
51
- pickedId = id;
52
- notify();
53
- void (id === null ? persistentStorage.removeItem(KEY) : persistentStorage.setItem(KEY, id)).catch(() => {});
54
- }
55
-
56
- /** Test seam: forget the pick and the load, without touching storage. */
57
- export function resetPickedPointForTests() {
58
- pickedId = null;
59
- loaded = false;
60
- loadPromise = null;
61
- }
62
-
63
- /**
64
- * The point the bar restores: the saved pick if it still exists, else the
65
- * most recently restored point, else the newest capture. Never a baseline.
66
- */
67
- export function resolveArmedPoint(snapshots, pickedId) {
68
- const candidates = snapshots.filter(meta => !meta.baseline && !meta.safety);
69
- if (candidates.length === 0) return null;
70
- if (pickedId) {
71
- const picked = candidates.find(meta => meta.id === pickedId);
72
- if (picked) return picked;
73
- }
74
- let best = null;
75
- for (const meta of candidates) {
76
- if (meta.lastRestoredAt === undefined) continue;
77
- if (!best || meta.lastRestoredAt > (best.lastRestoredAt ?? 0)) best = meta;
78
- }
79
- if (best) return best;
80
- return candidates.reduce((newest, meta) => meta.createdAt > newest.createdAt ? meta : newest);
81
- }
82
- //# sourceMappingURL=armedPoint.js.map
1
+ "use strict";import{devToolsStorageKeys as P,persistentStorage as s}from"@buoy-gg/shared-ui";const c=P.timeMachine.armedPoint();let n=null;let u=false;let l=null;const d=new Set;const a=()=>d.forEach(e=>e());export function getPickedPointId(){return n}export function subscribePickedPoint(e){d.add(e);return()=>{d.delete(e)}}export function ensurePickedPointLoaded(){if(u)return Promise.resolve();if(!l){l=s.getItem(c).then(e=>{if(n===null&&e){n=e;a()}}).catch(()=>{}).finally(()=>{u=true})}return l}export function setPickedPointId(e){if(n===e)return;n=e;a();void(e===null?s.removeItem(c):s.setItem(c,e)).catch(()=>{})}export function resetPickedPointForTests(){n=null;u=false;l=null}export function resolveArmedPoint(e,f){const o=e.filter(t=>!t.baseline&&!t.safety);if(o.length===0)return null;if(f){const t=o.find(i=>i.id===f);if(t)return t}let r=null;for(const t of o){if(t.lastRestoredAt===void 0)continue;if(!r||t.lastRestoredAt>(r.lastRestoredAt??0))r=t}if(r)return r;return o.reduce((t,i)=>i.createdAt>t.createdAt?i:t)}
@@ -1,57 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * The action bar's one setting: restore live, or restore and reload.
5
- *
6
- * Live restore replaces stores in place and is right for state that lives in
7
- * Redux, Zustand, Jotai or the query cache. It cannot reach a React context
8
- * or `useState` that hydrates itself from storage once at mount: the
9
- * storage key comes back, the screen keeps showing what it already had, and
10
- * "restore did nothing" is the report. A restore that reloads the app makes
11
- * those screens rebuild from the restored storage. Persisted, because the
12
- * reload itself is what brings the bar back.
13
- */
14
- import { devToolsStorageKeys, persistentStorage } from "@buoy-gg/shared-ui";
15
- const KEY = devToolsStorageKeys.timeMachine.barReloadMode();
16
- let reloadMode = false;
17
- let loaded = false;
18
- let loadPromise = null;
19
- const listeners = new Set();
20
- const notify = () => listeners.forEach(listener => listener());
21
- export function getBarReloadMode() {
22
- return reloadMode;
23
- }
24
- export function subscribeBarSettings(listener) {
25
- listeners.add(listener);
26
- return () => {
27
- listeners.delete(listener);
28
- };
29
- }
30
- export function ensureBarSettingsLoaded() {
31
- if (loaded) return Promise.resolve();
32
- if (!loadPromise) {
33
- loadPromise = persistentStorage.getItem(KEY).then(saved => {
34
- if (!reloadMode && saved === "1") {
35
- reloadMode = true;
36
- notify();
37
- }
38
- }).catch(() => {}).finally(() => {
39
- loaded = true;
40
- });
41
- }
42
- return loadPromise;
43
- }
44
- export function setBarReloadMode(on) {
45
- if (on === reloadMode) return;
46
- reloadMode = on;
47
- notify();
48
- void persistentStorage.setItem(KEY, on ? "1" : "0").catch(() => {});
49
- }
50
-
51
- /** Test seam. */
52
- export function resetBarSettingsForTests() {
53
- reloadMode = false;
54
- loaded = false;
55
- loadPromise = null;
56
- }
57
- //# sourceMappingURL=barSettings.js.map
1
+ "use strict";import{devToolsStorageKeys as l,persistentStorage as s}from"@buoy-gg/shared-ui";const a=l.timeMachine.barReloadMode();let t=false;let o=false;let r=null;const n=new Set;const i=()=>n.forEach(e=>e());export function getBarReloadMode(){return t}export function subscribeBarSettings(e){n.add(e);return()=>{n.delete(e)}}export function ensureBarSettingsLoaded(){if(o)return Promise.resolve();if(!r){r=s.getItem(a).then(e=>{if(!t&&e==="1"){t=true;i()}}).catch(()=>{}).finally(()=>{o=true})}return r}export function setBarReloadMode(e){if(e===t)return;t=e;i();void s.setItem(a,e?"1":"0").catch(()=>{})}export function resetBarSettingsForTests(){t=false;o=false;r=null}
@@ -1,19 +1 @@
1
- "use strict";
2
-
3
- /** Display formatting shared by the Time Machine screens. */
4
-
5
- export function timeAgo(ts) {
6
- const s = Math.max(0, Math.round((Date.now() - ts) / 1000));
7
- if (s < 60) return `${s}s ago`;
8
- const m = Math.round(s / 60);
9
- if (m < 60) return `${m}m ago`;
10
- const h = Math.round(m / 60);
11
- if (h < 24) return `${h}h ago`;
12
- return `${Math.round(h / 24)}d ago`;
13
- }
14
- export function formatBytes(bytes) {
15
- if (bytes < 1024) return `${bytes} B`;
16
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
17
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
18
- }
19
- //# sourceMappingURL=format.js.map
1
+ "use strict";export function timeAgo(t){const r=Math.max(0,Math.round((Date.now()-t)/1e3));if(r<60)return`${r}s ago`;const o=Math.round(r/60);if(o<60)return`${o}m ago`;const n=Math.round(o/60);if(n<24)return`${n}h ago`;return`${Math.round(n/24)}d ago`}export function formatBytes(t){if(t<1024)return`${t} B`;if(t<1024*1024)return`${(t/1024).toFixed(1)} KB`;return`${(t/(1024*1024)).toFixed(1)} MB`}
@@ -1,92 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Capture bar — the single field-plus-pill control at the top of the list.
5
- *
6
- * Owns the draft name itself so keystrokes re-render this component and not
7
- * the snapshot list underneath it. The parent only hears about a finished
8
- * capture.
9
- */
10
- import { useState } from "react";
11
- import { StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
12
- import { tm } from "../../ui/tmTheme";
13
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
- export function CaptureBar({
15
- disabled,
16
- busy,
17
- onCapture
18
- }) {
19
- const [name, setName] = useState("");
20
- const capture = () => {
21
- if (disabled) return;
22
- onCapture(name);
23
- setName("");
24
- };
25
- return /*#__PURE__*/_jsxs(View, {
26
- style: styles.bar,
27
- children: [/*#__PURE__*/_jsx(TextInput, {
28
- style: styles.input,
29
- value: name,
30
- onChangeText: setName,
31
- placeholder: "Name this restore point\u2026",
32
- placeholderTextColor: tm.color.labelTertiary,
33
- editable: !disabled,
34
- onSubmitEditing: capture,
35
- returnKeyType: "done",
36
- testID: "time-machine-name-input"
37
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
38
- style: [styles.button, disabled ? styles.buttonDisabled : null],
39
- onPress: capture,
40
- disabled: disabled,
41
- accessibilityRole: "button",
42
- accessibilityLabel: "Capture restore point",
43
- testID: "time-machine-capture",
44
- children: /*#__PURE__*/_jsx(Text, {
45
- style: styles.buttonText,
46
- children: busy ? "Capturing…" : "Capture"
47
- })
48
- })]
49
- });
50
- }
51
- const styles = StyleSheet.create({
52
- bar: {
53
- flexDirection: "row",
54
- alignItems: "center",
55
- gap: 8,
56
- marginHorizontal: tm.gutter,
57
- backgroundColor: tm.color.group,
58
- borderRadius: tm.radius.group,
59
- borderWidth: 1,
60
- borderColor: tm.color.groupBorder,
61
- paddingLeft: tm.textInset,
62
- padding: 6
63
- },
64
- input: {
65
- flex: 1,
66
- minWidth: 0,
67
- color: tm.color.label,
68
- fontSize: tm.font.row,
69
- letterSpacing: tm.tracking.row,
70
- // Zero out the platform chrome so the field reads as part of the card.
71
- padding: 0,
72
- backgroundColor: "transparent"
73
- },
74
- button: {
75
- height: 34,
76
- paddingHorizontal: 16,
77
- borderRadius: tm.radius.pill,
78
- backgroundColor: tm.color.accent,
79
- alignItems: "center",
80
- justifyContent: "center"
81
- },
82
- buttonDisabled: {
83
- opacity: 0.4
84
- },
85
- buttonText: {
86
- color: tm.color.onAccent,
87
- fontSize: tm.font.control,
88
- fontWeight: "600",
89
- letterSpacing: tm.tracking.control
90
- }
91
- });
92
- //# sourceMappingURL=CaptureBar.js.map
1
+ "use strict";import{useState as s}from"react";import{StyleSheet as u,Text as p,TextInput as d,TouchableOpacity as b,View as g}from"react-native";import{tm as t}from"../../ui/tmTheme";import{jsx as r,jsxs as m}from"react/jsx-runtime";export function CaptureBar({disabled:o,busy:l,onCapture:c}){const[n,i]=s("");const a=()=>{if(o)return;c(n);i("")};return m(g,{style:e.bar,children:[r(d,{style:e.input,value:n,onChangeText:i,placeholder:"Name this restore point\u2026",placeholderTextColor:t.color.labelTertiary,editable:!o,onSubmitEditing:a,returnKeyType:"done",testID:"time-machine-name-input"}),r(b,{style:[e.button,o?e.buttonDisabled:null],onPress:a,disabled:o,accessibilityRole:"button",accessibilityLabel:"Capture restore point",testID:"time-machine-capture",children:r(p,{style:e.buttonText,children:l?"Capturing\u2026":"Capture"})})]})}const e=u.create({bar:{flexDirection:"row",alignItems:"center",gap:8,marginHorizontal:t.gutter,backgroundColor:t.color.group,borderRadius:t.radius.group,borderWidth:1,borderColor:t.color.groupBorder,paddingLeft:t.textInset,padding:6},input:{flex:1,minWidth:0,color:t.color.label,fontSize:t.font.row,letterSpacing:t.tracking.row,padding:0,backgroundColor:"transparent"},button:{height:34,paddingHorizontal:16,borderRadius:t.radius.pill,backgroundColor:t.color.accent,alignItems:"center",justifyContent:"center"},buttonDisabled:{opacity:.4},buttonText:{color:t.color.onAccent,fontSize:t.font.control,fontWeight:"600",letterSpacing:t.tracking.control}});
@@ -1,169 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * What the last restore did, shown ONLY when something went wrong.
5
- *
6
- * A clean restore says so with a toast and then gets out of the way — the
7
- * restored app is the proof, and a wall of green ticks on top of the list
8
- * every single time was noise. A restore that skipped or failed a source is
9
- * the opposite: nothing else on screen would ever tell you, so it stays until
10
- * dismissed.
11
- */
12
- import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
13
- import { X } from "@buoy-gg/shared-ui";
14
- import { tm } from "../../ui/tmTheme";
15
- import { timeMachineStore } from "../../store/timeMachineStore";
16
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
17
- const HIT = {
18
- top: 10,
19
- bottom: 10,
20
- left: 10,
21
- right: 10
22
- };
23
-
24
- /**
25
- * Did this restore do anything the app itself won't show you? Warnings alone
26
- * don't count — "1 derived atom recomputes" is true of every restore and
27
- * would put the card back on screen permanently.
28
- */
29
- export function restoreNeedsAttention(outcome) {
30
- if (Object.values(outcome.results).some(r => !r.ok || r.skipped.length > 0)) return true;
31
- const route = outcome.route;
32
- return route?.status === "failed" || route?.status === "unavailable";
33
- }
34
-
35
- /** ✓ / ✕ / ↻ line — the glyph carries the meaning, the color reinforces it. */
36
- function ResultLine({
37
- glyph,
38
- text,
39
- ok,
40
- testID
41
- }) {
42
- return /*#__PURE__*/_jsxs(Text, {
43
- style: [styles.line, {
44
- color: ok ? tm.color.accent : tm.color.warning
45
- }],
46
- numberOfLines: 2,
47
- testID: testID,
48
- children: [/*#__PURE__*/_jsxs(Text, {
49
- style: styles.glyph,
50
- children: [glyph, " "]
51
- }), text]
52
- });
53
- }
54
- const ROUTE_TEST_ID = "time-machine-outcome-route";
55
- function RouteResult({
56
- route
57
- }) {
58
- if (route.status === "navigated") return /*#__PURE__*/_jsx(ResultLine, {
59
- glyph: "\u2713",
60
- ok: true,
61
- text: `route — ${route.href}`,
62
- testID: ROUTE_TEST_ID
63
- });
64
- if (route.status === "deferred") return /*#__PURE__*/_jsx(ResultLine, {
65
- glyph: "\u21BB",
66
- ok: true,
67
- text: `route — ${route.href} after reload`,
68
- testID: ROUTE_TEST_ID
69
- });
70
- return /*#__PURE__*/_jsx(ResultLine, {
71
- glyph: "\u2715",
72
- ok: false,
73
- text: `route — ${route.error ?? route.status}`,
74
- testID: ROUTE_TEST_ID
75
- });
76
- }
77
- export function RestoreOutcomeCard({
78
- outcome
79
- }) {
80
- const entries = Object.entries(outcome.results);
81
- const reloading = outcome.mode === "reload" || outcome.willReload;
82
- return /*#__PURE__*/_jsxs(View, {
83
- style: styles.card,
84
- testID: "time-machine-outcome",
85
- children: [/*#__PURE__*/_jsxs(View, {
86
- style: styles.headerRow,
87
- children: [/*#__PURE__*/_jsxs(Text, {
88
- style: styles.title,
89
- numberOfLines: 1,
90
- testID: "time-machine-outcome-title",
91
- children: ["Restored \u201C", outcome.snapshotName, "\u201D", reloading ? " · reloading" : ""]
92
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
93
- onPress: () => timeMachineStore.clearLastOutcome(),
94
- hitSlop: HIT,
95
- accessibilityRole: "button",
96
- accessibilityLabel: "Dismiss restore result",
97
- testID: "time-machine-outcome-dismiss",
98
- children: /*#__PURE__*/_jsx(X, {
99
- size: 13,
100
- color: tm.color.labelSecondary
101
- })
102
- })]
103
- }), entries.length === 0 && !outcome.route ? /*#__PURE__*/_jsx(Text, {
104
- style: styles.empty,
105
- testID: "time-machine-outcome-empty",
106
- children: "No sources restored."
107
- }) : null, outcome.route ? /*#__PURE__*/_jsx(RouteResult, {
108
- route: outcome.route
109
- }) : null, entries.map(([sourceId, result]) => /*#__PURE__*/_jsxs(View, {
110
- children: [/*#__PURE__*/_jsx(ResultLine, {
111
- testID: `time-machine-outcome-${sourceId}`,
112
- glyph: result.ok ? "✓" : "✕",
113
- ok: result.ok,
114
- text: `${sourceId} — applied ${result.applied}${result.skipped.length > 0 ? `, skipped ${result.skipped.length}` : ""}`
115
- }), result.skipped.slice(0, 3).map(skip => /*#__PURE__*/_jsxs(Text, {
116
- style: styles.detail,
117
- numberOfLines: 2,
118
- children: [skip.id, ": ", skip.reason]
119
- }, skip.id)), result.warnings.slice(0, 3).map(warning => /*#__PURE__*/_jsxs(Text, {
120
- style: styles.detail,
121
- numberOfLines: 2,
122
- children: ["\u26A0 ", warning]
123
- }, warning))]
124
- }, sourceId))]
125
- });
126
- }
127
- const styles = StyleSheet.create({
128
- card: {
129
- marginHorizontal: tm.gutter,
130
- marginTop: 12,
131
- backgroundColor: tm.color.group,
132
- borderRadius: tm.radius.group,
133
- borderWidth: 1,
134
- borderColor: tm.color.groupBorder,
135
- padding: 14,
136
- gap: 4
137
- },
138
- headerRow: {
139
- flexDirection: "row",
140
- alignItems: "center",
141
- gap: 10,
142
- marginBottom: 2
143
- },
144
- title: {
145
- flex: 1,
146
- color: tm.color.label,
147
- fontSize: 15,
148
- fontWeight: "600",
149
- letterSpacing: tm.tracking.control
150
- },
151
- line: {
152
- fontSize: tm.font.footnote,
153
- lineHeight: tm.font.footnote * 1.4
154
- },
155
- glyph: {
156
- fontWeight: "700"
157
- },
158
- detail: {
159
- color: tm.color.labelTertiary,
160
- fontSize: tm.font.mono,
161
- lineHeight: tm.font.mono * 1.4,
162
- paddingLeft: 14
163
- },
164
- empty: {
165
- color: tm.color.labelSecondary,
166
- fontSize: tm.font.footnote
167
- }
168
- });
169
- //# sourceMappingURL=RestoreOutcomeCard.js.map
1
+ "use strict";import{StyleSheet as h,Text as c,TouchableOpacity as f,View as u}from"react-native";import{X as g}from"@buoy-gg/shared-ui";import{tm as t}from"../../ui/tmTheme";import{timeMachineStore as y}from"../../store/timeMachineStore";import{jsxs as o,jsx as r}from"react/jsx-runtime";const b={top:10,bottom:10,left:10,right:10};export function restoreNeedsAttention(e){if(Object.values(e.results).some(s=>!s.ok||s.skipped.length>0))return true;const n=e.route;return n?.status==="failed"||n?.status==="unavailable"}function m({glyph:e,text:n,ok:s,testID:d}){return o(c,{style:[i.line,{color:s?t.color.accent:t.color.warning}],numberOfLines:2,testID:d,children:[o(c,{style:i.glyph,children:[e," "]}),n]})}const p="time-machine-outcome-route";function x({route:e}){if(e.status==="navigated")return r(m,{glyph:"\u2713",ok:true,text:`route \u2014 ${e.href}`,testID:p});if(e.status==="deferred")return r(m,{glyph:"\u21BB",ok:true,text:`route \u2014 ${e.href} after reload`,testID:p});return r(m,{glyph:"\u2715",ok:false,text:`route \u2014 ${e.error??e.status}`,testID:p})}export function RestoreOutcomeCard({outcome:e}){const n=Object.entries(e.results);const s=e.mode==="reload"||e.willReload;return o(u,{style:i.card,testID:"time-machine-outcome",children:[o(u,{style:i.headerRow,children:[o(c,{style:i.title,numberOfLines:1,testID:"time-machine-outcome-title",children:["Restored \u201C",e.snapshotName,"\u201D",s?" \xB7 reloading":""]}),r(f,{onPress:()=>y.clearLastOutcome(),hitSlop:b,accessibilityRole:"button",accessibilityLabel:"Dismiss restore result",testID:"time-machine-outcome-dismiss",children:r(g,{size:13,color:t.color.labelSecondary})})]}),n.length===0&&!e.route?r(c,{style:i.empty,testID:"time-machine-outcome-empty",children:"No sources restored."}):null,e.route?r(x,{route:e.route}):null,n.map(([d,l])=>o(u,{children:[r(m,{testID:`time-machine-outcome-${d}`,glyph:l.ok?"\u2713":"\u2715",ok:l.ok,text:`${d} \u2014 applied ${l.applied}${l.skipped.length>0?`, skipped ${l.skipped.length}`:""}`}),l.skipped.slice(0,3).map(a=>o(c,{style:i.detail,numberOfLines:2,children:[a.id,": ",a.reason]},a.id)),l.warnings.slice(0,3).map(a=>o(c,{style:i.detail,numberOfLines:2,children:["\u26A0 ",a]},a))]},d))]})}const i=h.create({card:{marginHorizontal:t.gutter,marginTop:12,backgroundColor:t.color.group,borderRadius:t.radius.group,borderWidth:1,borderColor:t.color.groupBorder,padding:14,gap:4},headerRow:{flexDirection:"row",alignItems:"center",gap:10,marginBottom:2},title:{flex:1,color:t.color.label,fontSize:15,fontWeight:"600",letterSpacing:t.tracking.control},line:{fontSize:t.font.footnote,lineHeight:t.font.footnote*1.4},glyph:{fontWeight:"700"},detail:{color:t.color.labelTertiary,fontSize:t.font.mono,lineHeight:t.font.mono*1.4,paddingLeft:14},empty:{color:t.color.labelSecondary,fontSize:t.font.footnote}});
@@ -1,167 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * One restore point in the list: what it is, when it was taken, how big it
5
- * is, where it was captured — and the single action worth having at arm's
6
- * reach. Tapping the row opens the restore preview; the pill restores now.
7
- *
8
- * The route is shown as context — where this state was true. Whether a
9
- * restore also navigates there is decided in the restore preview, beside the
10
- * other changes it is about to make.
11
- *
12
- * Duplicate and delete live behind a swipe. A destructive target beside
13
- * "Restore" is a mis-tap that costs a snapshot, and the two-step gesture is
14
- * deliberate enough — recovery is the undo the list offers after, not a
15
- * dialog before. Renaming is NOT here: it belongs with the restore point's
16
- * other settings, on the preview screen.
17
- */
18
- import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
19
- import { PillLabel } from "../../ui/primitives";
20
- import { SwipeActionsRow } from "../../ui/SwipeActionsRow";
21
- import { TM_MONO, tm } from "../../ui/tmTheme";
22
- import { formatBytes, timeAgo } from "../format";
23
-
24
- /** "15h ago · 9.5 KB · 5 sources · skips 2" */
25
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
26
- function describe(meta) {
27
- if (meta.baseline) return "Wipes to fresh install";
28
- const parts = [timeAgo(meta.createdAt), formatBytes(meta.sizeBytes), `${Object.keys(meta.sources).length} sources`];
29
- // A saved selection is worth stating on the row — this restore point no
30
- // longer does the obvious thing, and one tap on RESTORE honors it.
31
- if (meta.excludedKeys?.length) parts.push(`skips ${meta.excludedKeys.length}`);else if (meta.scope?.length) parts.push(`scoped (${meta.scope.length})`);
32
- // The bar's own undo slot: kept for one restore, not a point you chose.
33
- if (meta.safety) parts.unshift("safety copy");
34
- return parts.join(" · ");
35
- }
36
- export function SnapshotRow({
37
- meta,
38
- busy,
39
- armed = false,
40
- onOpen,
41
- onRestore,
42
- onDelete,
43
- onDuplicate,
44
- onArm
45
- }) {
46
- const testID = `time-machine-snap-${meta.id}`;
47
- return /*#__PURE__*/_jsx(SwipeActionsRow, {
48
- testID: testID,
49
- actions: [
50
- // "Bar": put this point on the floating action bar.
51
- {
52
- label: "Bar",
53
- onPress: onArm
54
- },
55
- // "Dup" over "Copy": on a row full of text, "Copy" reads as
56
- // copy-to-clipboard.
57
- {
58
- label: "Dup",
59
- onPress: onDuplicate
60
- }, {
61
- label: "Delete",
62
- onPress: onDelete,
63
- background: tm.color.danger,
64
- color: tm.color.onDanger
65
- }],
66
- children: /*#__PURE__*/_jsxs(View, {
67
- style: styles.row,
68
- children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
69
- style: styles.text,
70
- onPress: onOpen,
71
- activeOpacity: 0.7,
72
- accessibilityRole: "button",
73
- accessibilityLabel: `${meta.name}. ${describe(meta)}. Open restore preview`,
74
- testID: testID,
75
- children: [/*#__PURE__*/_jsx(Text, {
76
- style: styles.name,
77
- numberOfLines: 1,
78
- testID: `${testID}-name`,
79
- children: meta.name
80
- }), armed ? /*#__PURE__*/_jsx(Text, {
81
- style: styles.armed,
82
- numberOfLines: 1,
83
- testID: `${testID}-armed`,
84
- children: "On the action bar"
85
- }) : null, /*#__PURE__*/_jsx(Text, {
86
- style: styles.meta,
87
- numberOfLines: 1,
88
- testID: `${testID}-meta`,
89
- children: describe(meta)
90
- }), meta.lastRestoredAt ? /*#__PURE__*/_jsxs(Text, {
91
- style: styles.restored,
92
- numberOfLines: 1,
93
- testID: `${testID}-restored`,
94
- children: ["Restored ", timeAgo(meta.lastRestoredAt)]
95
- }) : null, meta.route ? /*#__PURE__*/_jsx(Text, {
96
- style: styles.route,
97
- numberOfLines: 1,
98
- testID: `${testID}-route`,
99
- children: meta.route.href
100
- }) : null]
101
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
102
- onPress: onRestore,
103
- disabled: busy,
104
- style: [styles.restore, busy ? styles.actionDisabled : null],
105
- accessibilityRole: "button",
106
- accessibilityLabel: meta.baseline ? "Wipe and reload" : "Restore",
107
- testID: `${testID}-restore`,
108
- children: /*#__PURE__*/_jsx(PillLabel, {
109
- testID: `${testID}-restore-label`,
110
- label: meta.baseline ? "Wipe" : "Restore",
111
- color: meta.baseline ? tm.color.danger : tm.color.accent,
112
- background: meta.baseline ? tm.color.dangerSoft : tm.color.accentSoft
113
- })
114
- })]
115
- })
116
- });
117
- }
118
- const styles = StyleSheet.create({
119
- row: {
120
- flexDirection: "row",
121
- alignItems: "center",
122
- gap: 10,
123
- paddingHorizontal: tm.textInset
124
- },
125
- // The text block is the tallest child, so its padding sets the row height
126
- // and the open target spans the full row.
127
- text: {
128
- flex: 1,
129
- minWidth: 0,
130
- paddingVertical: 11
131
- },
132
- restore: {
133
- justifyContent: "center"
134
- },
135
- name: {
136
- color: tm.color.label,
137
- fontSize: tm.font.row,
138
- fontWeight: "600",
139
- letterSpacing: tm.tracking.row
140
- },
141
- meta: {
142
- color: tm.color.labelSecondary,
143
- fontSize: tm.font.footnote,
144
- marginTop: 2,
145
- fontVariant: ["tabular-nums"]
146
- },
147
- restored: {
148
- color: tm.color.accent,
149
- fontSize: tm.font.footnote,
150
- marginTop: 2
151
- },
152
- armed: {
153
- color: tm.color.accent,
154
- fontSize: tm.font.footnote,
155
- marginTop: 2
156
- },
157
- route: {
158
- color: tm.color.labelTertiary,
159
- fontFamily: TM_MONO,
160
- fontSize: tm.font.mono,
161
- marginTop: 4
162
- },
163
- actionDisabled: {
164
- opacity: 0.4
165
- }
166
- });
167
- //# sourceMappingURL=SnapshotRow.js.map
1
+ "use strict";import{StyleSheet as g,Text as l,TouchableOpacity as c,View as m}from"react-native";import{PillLabel as D}from"../../ui/primitives";import{SwipeActionsRow as x}from"../../ui/SwipeActionsRow";import{TM_MONO as S,tm as o}from"../../ui/tmTheme";import{formatBytes as $,timeAgo as a}from"../format";import{jsx as s,jsxs as i}from"react/jsx-runtime";function f(e){if(e.baseline)return"Wipes to fresh install";const n=[a(e.createdAt),$(e.sizeBytes),`${Object.keys(e.sources).length} sources`];if(e.excludedKeys?.length)n.push(`skips ${e.excludedKeys.length}`);else if(e.scope?.length)n.push(`scoped (${e.scope.length})`);if(e.safety)n.unshift("safety copy");return n.join(" \xB7 ")}export function SnapshotRow({meta:e,busy:n,armed:d=false,onOpen:b,onRestore:p,onDelete:u,onDuplicate:y,onArm:h}){const t=`time-machine-snap-${e.id}`;return s(x,{testID:t,actions:[{label:"Bar",onPress:h},{label:"Dup",onPress:y},{label:"Delete",onPress:u,background:o.color.danger,color:o.color.onDanger}],children:i(m,{style:r.row,children:[i(c,{style:r.text,onPress:b,activeOpacity:.7,accessibilityRole:"button",accessibilityLabel:`${e.name}. ${f(e)}. Open restore preview`,testID:t,children:[s(l,{style:r.name,numberOfLines:1,testID:`${t}-name`,children:e.name}),d?s(l,{style:r.armed,numberOfLines:1,testID:`${t}-armed`,children:"On the action bar"}):null,s(l,{style:r.meta,numberOfLines:1,testID:`${t}-meta`,children:f(e)}),e.lastRestoredAt?i(l,{style:r.restored,numberOfLines:1,testID:`${t}-restored`,children:["Restored ",a(e.lastRestoredAt)]}):null,e.route?s(l,{style:r.route,numberOfLines:1,testID:`${t}-route`,children:e.route.href}):null]}),s(c,{onPress:p,disabled:n,style:[r.restore,n?r.actionDisabled:null],accessibilityRole:"button",accessibilityLabel:e.baseline?"Wipe and reload":"Restore",testID:`${t}-restore`,children:s(D,{testID:`${t}-restore-label`,label:e.baseline?"Wipe":"Restore",color:e.baseline?o.color.danger:o.color.accent,background:e.baseline?o.color.dangerSoft:o.color.accentSoft})})]})})}const r=g.create({row:{flexDirection:"row",alignItems:"center",gap:10,paddingHorizontal:o.textInset},text:{flex:1,minWidth:0,paddingVertical:11},restore:{justifyContent:"center"},name:{color:o.color.label,fontSize:o.font.row,fontWeight:"600",letterSpacing:o.tracking.row},meta:{color:o.color.labelSecondary,fontSize:o.font.footnote,marginTop:2,fontVariant:["tabular-nums"]},restored:{color:o.color.accent,fontSize:o.font.footnote,marginTop:2},armed:{color:o.color.accent,fontSize:o.font.footnote,marginTop:2},route:{color:o.color.labelTertiary,fontFamily:S,fontSize:o.font.mono,marginTop:4},actionDisabled:{opacity:.4}});