@buoy-gg/time-machine 7.0.21 → 7.0.23
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.
- package/lib/commonjs/components/TimeMachineBody.js +148 -583
- package/lib/commonjs/components/TimeMachineBody.js.map +1 -1
- package/lib/commonjs/components/TimeMachineModal.js +10 -11
- package/lib/commonjs/components/TimeMachineModal.js.map +1 -1
- package/lib/commonjs/components/confirm.js +39 -0
- package/lib/commonjs/components/confirm.js.map +1 -0
- package/lib/commonjs/components/format.js +24 -0
- package/lib/commonjs/components/format.js.map +1 -0
- package/lib/commonjs/components/list/CaptureBar.js +95 -0
- package/lib/commonjs/components/list/CaptureBar.js.map +1 -0
- package/lib/commonjs/components/list/RestoreOutcomeCard.js +162 -0
- package/lib/commonjs/components/list/RestoreOutcomeCard.js.map +1 -0
- package/lib/commonjs/components/list/SnapshotRow.js +141 -0
- package/lib/commonjs/components/list/SnapshotRow.js.map +1 -0
- package/lib/commonjs/components/list/SourcesStrip.js +126 -0
- package/lib/commonjs/components/list/SourcesStrip.js.map +1 -0
- package/lib/commonjs/components/list/UndoDeleteBar.js +81 -0
- package/lib/commonjs/components/list/UndoDeleteBar.js.map +1 -0
- package/lib/commonjs/components/list/WipeAllRow.js +46 -0
- package/lib/commonjs/components/list/WipeAllRow.js.map +1 -0
- package/lib/commonjs/components/preview/ItemDiffPanel.js +77 -0
- package/lib/commonjs/components/preview/ItemDiffPanel.js.map +1 -0
- package/lib/commonjs/components/preview/PreviewFlow.js +51 -0
- package/lib/commonjs/components/preview/PreviewFlow.js.map +1 -0
- package/lib/commonjs/components/preview/RestorePointNameRow.js +83 -0
- package/lib/commonjs/components/preview/RestorePointNameRow.js.map +1 -0
- package/lib/commonjs/components/preview/RestorePreviewScreen.js +549 -635
- package/lib/commonjs/components/preview/RestorePreviewScreen.js.map +1 -1
- package/lib/commonjs/components/preview/verdictMeta.js +8 -8
- package/lib/commonjs/components/preview/verdictMeta.js.map +1 -1
- package/lib/commonjs/components/sources.js +72 -0
- package/lib/commonjs/components/sources.js.map +1 -0
- package/lib/commonjs/index.js +55 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/store/routeRestore.js +188 -0
- package/lib/commonjs/store/routeRestore.js.map +1 -0
- package/lib/commonjs/store/snapshotVault.js +93 -9
- package/lib/commonjs/store/snapshotVault.js.map +1 -1
- package/lib/commonjs/store/timeMachineStore.js +164 -7
- package/lib/commonjs/store/timeMachineStore.js.map +1 -1
- package/lib/commonjs/sync/timeMachineSyncAdapter.js +59 -4
- package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +1 -1
- package/lib/commonjs/ui/SwipeActionsRow.js +167 -0
- package/lib/commonjs/ui/SwipeActionsRow.js.map +1 -0
- package/lib/commonjs/ui/primitives.js +164 -0
- package/lib/commonjs/ui/primitives.js.map +1 -0
- package/lib/commonjs/ui/tmTheme.js +103 -0
- package/lib/commonjs/ui/tmTheme.js.map +1 -0
- package/lib/module/components/TimeMachineBody.js +151 -586
- package/lib/module/components/TimeMachineBody.js.map +1 -1
- package/lib/module/components/TimeMachineModal.js +11 -12
- package/lib/module/components/TimeMachineModal.js.map +1 -1
- package/lib/module/components/confirm.js +34 -0
- package/lib/module/components/confirm.js.map +1 -0
- package/lib/module/components/format.js +19 -0
- package/lib/module/components/format.js.map +1 -0
- package/lib/module/components/list/CaptureBar.js +90 -0
- package/lib/module/components/list/CaptureBar.js.map +1 -0
- package/lib/module/components/list/RestoreOutcomeCard.js +156 -0
- package/lib/module/components/list/RestoreOutcomeCard.js.map +1 -0
- package/lib/module/components/list/SnapshotRow.js +138 -0
- package/lib/module/components/list/SnapshotRow.js.map +1 -0
- package/lib/module/components/list/SourcesStrip.js +121 -0
- package/lib/module/components/list/SourcesStrip.js.map +1 -0
- package/lib/module/components/list/UndoDeleteBar.js +78 -0
- package/lib/module/components/list/UndoDeleteBar.js.map +1 -0
- package/lib/module/components/list/WipeAllRow.js +41 -0
- package/lib/module/components/list/WipeAllRow.js.map +1 -0
- package/lib/module/components/preview/ItemDiffPanel.js +72 -0
- package/lib/module/components/preview/ItemDiffPanel.js.map +1 -0
- package/lib/module/components/preview/PreviewFlow.js +46 -0
- package/lib/module/components/preview/PreviewFlow.js.map +1 -0
- package/lib/module/components/preview/RestorePointNameRow.js +78 -0
- package/lib/module/components/preview/RestorePointNameRow.js.map +1 -0
- package/lib/module/components/preview/RestorePreviewScreen.js +552 -638
- package/lib/module/components/preview/RestorePreviewScreen.js.map +1 -1
- package/lib/module/components/preview/verdictMeta.js +9 -8
- package/lib/module/components/preview/verdictMeta.js.map +1 -1
- package/lib/module/components/sources.js +66 -0
- package/lib/module/components/sources.js.map +1 -0
- package/lib/module/index.js +11 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/store/routeRestore.js +176 -0
- package/lib/module/store/routeRestore.js.map +1 -0
- package/lib/module/store/snapshotVault.js +87 -9
- package/lib/module/store/snapshotVault.js.map +1 -1
- package/lib/module/store/timeMachineStore.js +164 -8
- package/lib/module/store/timeMachineStore.js.map +1 -1
- package/lib/module/sync/timeMachineSyncAdapter.js +59 -4
- package/lib/module/sync/timeMachineSyncAdapter.js.map +1 -1
- package/lib/module/ui/SwipeActionsRow.js +162 -0
- package/lib/module/ui/SwipeActionsRow.js.map +1 -0
- package/lib/module/ui/primitives.js +156 -0
- package/lib/module/ui/primitives.js.map +1 -0
- package/lib/module/ui/tmTheme.js +99 -0
- package/lib/module/ui/tmTheme.js.map +1 -0
- package/lib/typescript/components/TimeMachineBody.d.ts.map +1 -1
- package/lib/typescript/components/TimeMachineModal.d.ts.map +1 -1
- package/lib/typescript/components/confirm.d.ts +2 -0
- package/lib/typescript/components/confirm.d.ts.map +1 -0
- package/lib/typescript/components/format.d.ts +4 -0
- package/lib/typescript/components/format.d.ts.map +1 -0
- package/lib/typescript/components/list/CaptureBar.d.ts +6 -0
- package/lib/typescript/components/list/CaptureBar.d.ts.map +1 -0
- package/lib/typescript/components/list/RestoreOutcomeCard.d.ts +11 -0
- package/lib/typescript/components/list/RestoreOutcomeCard.d.ts.map +1 -0
- package/lib/typescript/components/list/SnapshotRow.d.ts +12 -0
- package/lib/typescript/components/list/SnapshotRow.d.ts.map +1 -0
- package/lib/typescript/components/list/SourcesStrip.d.ts +5 -0
- package/lib/typescript/components/list/SourcesStrip.d.ts.map +1 -0
- package/lib/typescript/components/list/UndoDeleteBar.d.ts +8 -0
- package/lib/typescript/components/list/UndoDeleteBar.d.ts.map +1 -0
- package/lib/typescript/components/list/WipeAllRow.d.ts +5 -0
- package/lib/typescript/components/list/WipeAllRow.d.ts.map +1 -0
- package/lib/typescript/components/preview/ItemDiffPanel.d.ts +5 -0
- package/lib/typescript/components/preview/ItemDiffPanel.d.ts.map +1 -0
- package/lib/typescript/components/preview/PreviewFlow.d.ts +9 -0
- package/lib/typescript/components/preview/PreviewFlow.d.ts.map +1 -0
- package/lib/typescript/components/preview/RestorePointNameRow.d.ts +5 -0
- package/lib/typescript/components/preview/RestorePointNameRow.d.ts.map +1 -0
- package/lib/typescript/components/preview/RestorePreviewScreen.d.ts +1 -5
- package/lib/typescript/components/preview/RestorePreviewScreen.d.ts.map +1 -1
- package/lib/typescript/components/preview/verdictMeta.d.ts +5 -0
- package/lib/typescript/components/preview/verdictMeta.d.ts.map +1 -1
- package/lib/typescript/components/sources.d.ts +36 -0
- package/lib/typescript/components/sources.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/store/routeRestore.d.ts +51 -0
- package/lib/typescript/store/routeRestore.d.ts.map +1 -0
- package/lib/typescript/store/snapshotVault.d.ts +55 -0
- package/lib/typescript/store/snapshotVault.d.ts.map +1 -1
- package/lib/typescript/store/timeMachineStore.d.ts +64 -2
- package/lib/typescript/store/timeMachineStore.d.ts.map +1 -1
- package/lib/typescript/sync/timeMachineSyncAdapter.d.ts +25 -0
- package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/ui/SwipeActionsRow.d.ts +32 -0
- package/lib/typescript/ui/SwipeActionsRow.d.ts.map +1 -0
- package/lib/typescript/ui/primitives.d.ts +57 -0
- package/lib/typescript/ui/primitives.d.ts.map +1 -0
- package/lib/typescript/ui/tmTheme.d.ts +67 -0
- package/lib/typescript/ui/tmTheme.d.ts.map +1 -0
- package/package.json +12 -8
- package/src/components/TimeMachineBody.tsx +184 -682
- package/src/components/TimeMachineModal.tsx +10 -11
- package/src/components/confirm.ts +35 -0
- package/src/components/format.ts +17 -0
- package/src/components/list/CaptureBar.tsx +100 -0
- package/src/components/list/RestoreOutcomeCard.tsx +159 -0
- package/src/components/list/SnapshotRow.tsx +155 -0
- package/src/components/list/SourcesStrip.tsx +124 -0
- package/src/components/list/UndoDeleteBar.tsx +76 -0
- package/src/components/list/WipeAllRow.tsx +42 -0
- package/src/components/preview/ItemDiffPanel.tsx +77 -0
- package/src/components/preview/PreviewFlow.tsx +70 -0
- package/src/components/preview/RestorePointNameRow.tsx +80 -0
- package/src/components/preview/RestorePreviewScreen.tsx +535 -535
- package/src/components/preview/verdictMeta.ts +8 -8
- package/src/components/sources.ts +92 -0
- package/src/index.tsx +22 -0
- package/src/store/routeRestore.ts +194 -0
- package/src/store/snapshotVault.ts +138 -9
- package/src/store/timeMachineStore.ts +195 -5
- package/src/sync/timeMachineSyncAdapter.ts +57 -4
- package/src/ui/SwipeActionsRow.tsx +199 -0
- package/src/ui/primitives.tsx +157 -0
- package/src/ui/tmTheme.ts +107 -0
- package/lib/commonjs/components/preview/ItemDetailScreen.js +0 -189
- package/lib/commonjs/components/preview/ItemDetailScreen.js.map +0 -1
- package/lib/module/components/preview/ItemDetailScreen.js +0 -184
- package/lib/module/components/preview/ItemDetailScreen.js.map +0 -1
- package/lib/typescript/components/preview/ItemDetailScreen.d.ts +0 -11
- package/lib/typescript/components/preview/ItemDetailScreen.d.ts.map +0 -1
- package/src/components/preview/ItemDetailScreen.tsx +0 -172
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Restore Preview — "what will restoring this snapshot actually change?"
|
|
5
5
|
*
|
|
6
|
-
* ONE virtualized FlatList is the scrollable area (
|
|
7
|
-
* source
|
|
8
|
-
* footer — no nested scroll regions, thousands of
|
|
6
|
+
* ONE virtualized FlatList is the scrollable area (the section header, the
|
|
7
|
+
* source rows, their item rows and the inline diffs are all list rows) with
|
|
8
|
+
* a sticky Restore CTA footer — no nested scroll regions, so thousands of
|
|
9
|
+
* storage keys stay cheap.
|
|
9
10
|
*
|
|
10
11
|
* Layout follows the Time Machine mockup: source sections are card
|
|
11
12
|
* dropdowns with tool-icon tiles, a "N keys · M changed" detection subtitle,
|
|
@@ -16,20 +17,16 @@
|
|
|
16
17
|
* items are passed to restore as exclusions and left untouched.
|
|
17
18
|
*/
|
|
18
19
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
19
|
-
import { ActivityIndicator, FlatList, StyleSheet, Text,
|
|
20
|
-
import {
|
|
21
|
-
import { JotaiIcon, QueryIcon, ReduxIcon, StorageIcon, TimeMachineIcon, ZustandIcon } from "@buoy-gg/floating-tools-core";
|
|
20
|
+
import { ActivityIndicator, FlatList, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
21
|
+
import { ChevronUp, AlertTriangle, RefreshCw, useSafeAreaInsets } from "@buoy-gg/shared-ui";
|
|
22
22
|
import { useToolHeader, useToolHost } from "@buoy-gg/core";
|
|
23
|
-
import {
|
|
23
|
+
import { DisclosureChevron, Group, SectionLabel } from "../../ui/primitives";
|
|
24
|
+
import { TM_MONO, tm } from "../../ui/tmTheme";
|
|
25
|
+
import { timeMachineStore, willRestoreRoute } from "../../store/timeMachineStore";
|
|
26
|
+
import { ItemDiffPanel } from "./ItemDiffPanel";
|
|
27
|
+
import { RestorePointNameRow } from "./RestorePointNameRow";
|
|
24
28
|
import { VERDICT_META } from "./verdictMeta";
|
|
25
29
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
26
|
-
const SOURCE_ICONS = {
|
|
27
|
-
storage: StorageIcon,
|
|
28
|
-
redux: ReduxIcon,
|
|
29
|
-
zustand: ZustandIcon,
|
|
30
|
-
jotai: JotaiIcon,
|
|
31
|
-
query: QueryIcon
|
|
32
|
-
};
|
|
33
30
|
const SOURCE_NOUNS = {
|
|
34
31
|
storage: "keys",
|
|
35
32
|
redux: "slices",
|
|
@@ -37,6 +34,29 @@ const SOURCE_NOUNS = {
|
|
|
37
34
|
jotai: "atoms",
|
|
38
35
|
query: "queries"
|
|
39
36
|
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Where a row sits in the one card that holds the whole list. The list has
|
|
40
|
+
* to stay flat to stay virtualized, so each row paints its own share of the
|
|
41
|
+
* card: the first row rounds its top, the last rounds its bottom, and every
|
|
42
|
+
* other row draws the hairline above it.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The route leg, as a row in the same card as the sources. It is not a
|
|
47
|
+
* source — nothing diffs it — but "will this also take me back to
|
|
48
|
+
* /checkout?" is a decision about THIS restore, so it belongs beside the
|
|
49
|
+
* other things the restore is about to do rather than on the list screen.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/** Shared empty selection — a stable identity for "nothing is excluded". */
|
|
53
|
+
const EMPTY_SELECTION = new Set();
|
|
54
|
+
function sameSelection(a, b) {
|
|
55
|
+
if (a.size !== b.size) return false;
|
|
56
|
+
for (const key of a) if (!b.has(key)) return false;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
/** Compound exclusion key — what timeMachineStore.restore expects. */
|
|
41
61
|
function cKey(sourceId, itemKey) {
|
|
42
62
|
return `${sourceId}::${itemKey}`;
|
|
@@ -51,95 +71,120 @@ function shortPreview(value) {
|
|
|
51
71
|
return "[complex]";
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The grouped-card shell one row paints. Rounds the ends, insets the card
|
|
77
|
+
* from the sheet, and draws the hairline that separates it from the row
|
|
78
|
+
* above — the same shape the home list's <Group> makes, rebuilt per row so
|
|
79
|
+
* the list stays virtualized.
|
|
80
|
+
*/
|
|
81
|
+
function CardRow({
|
|
82
|
+
position,
|
|
83
|
+
children
|
|
84
|
+
}) {
|
|
85
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
86
|
+
style: [styles.cardRow, position.first ? styles.cardRowFirst : null, position.last ? styles.cardRowLast : null],
|
|
87
|
+
children: [position.first ? null : /*#__PURE__*/_jsx(View, {
|
|
88
|
+
style: styles.cardSeparator
|
|
89
|
+
}), children]
|
|
90
|
+
});
|
|
57
91
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
function CountPill({
|
|
93
|
+
glyph,
|
|
94
|
+
count,
|
|
95
|
+
color
|
|
96
|
+
}) {
|
|
97
|
+
return /*#__PURE__*/_jsx(View, {
|
|
98
|
+
style: [styles.countPill, {
|
|
99
|
+
backgroundColor: `${color}26`
|
|
100
|
+
}],
|
|
101
|
+
children: /*#__PURE__*/_jsxs(Text, {
|
|
102
|
+
style: [styles.countPillText, {
|
|
103
|
+
color
|
|
104
|
+
}],
|
|
105
|
+
children: [glyph, count]
|
|
106
|
+
})
|
|
107
|
+
});
|
|
65
108
|
}
|
|
66
109
|
function Checkbox({
|
|
67
110
|
state
|
|
68
111
|
}) {
|
|
69
|
-
return /*#__PURE__*/
|
|
112
|
+
return /*#__PURE__*/_jsx(View, {
|
|
70
113
|
style: [styles.checkbox, state !== "none" && styles.checkboxChecked],
|
|
71
|
-
children:
|
|
72
|
-
style: styles.checkboxGlyph,
|
|
73
|
-
children: "\u2713"
|
|
74
|
-
}), state === "some" && /*#__PURE__*/_jsx(Text, {
|
|
114
|
+
children: state === "none" ? null : /*#__PURE__*/_jsx(Text, {
|
|
75
115
|
style: styles.checkboxGlyph,
|
|
76
|
-
children: "
|
|
77
|
-
})
|
|
116
|
+
children: state === "all" ? "✓" : "−"
|
|
117
|
+
})
|
|
78
118
|
});
|
|
79
119
|
}
|
|
80
120
|
const ItemRow = /*#__PURE__*/memo(function ItemRow({
|
|
81
121
|
row,
|
|
122
|
+
expanded,
|
|
82
123
|
onOpen,
|
|
83
124
|
onToggle
|
|
84
125
|
}) {
|
|
85
126
|
const meta = VERDICT_META[row.item.verdict];
|
|
86
127
|
const dimmed = !meta.applies || row.excluded;
|
|
87
128
|
const openable = row.item.verdict === "changed" || row.item.verdict === "added" || row.item.verdict === "removed";
|
|
88
|
-
return /*#__PURE__*/
|
|
89
|
-
|
|
90
|
-
children:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}],
|
|
111
|
-
children: meta.glyph
|
|
112
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
113
|
-
style: styles.itemBody,
|
|
129
|
+
return /*#__PURE__*/_jsx(CardRow, {
|
|
130
|
+
position: row,
|
|
131
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
132
|
+
style: [styles.itemRow, dimmed && styles.dimmed],
|
|
133
|
+
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
134
|
+
onPress: onToggle,
|
|
135
|
+
disabled: !meta.applies,
|
|
136
|
+
hitSlop: {
|
|
137
|
+
top: 10,
|
|
138
|
+
bottom: 10,
|
|
139
|
+
left: 8,
|
|
140
|
+
right: 8
|
|
141
|
+
},
|
|
142
|
+
testID: `tm-preview-toggle-${row.sourceId}-${row.item.key}`,
|
|
143
|
+
children: /*#__PURE__*/_jsx(Checkbox, {
|
|
144
|
+
state: !meta.applies || row.excluded ? "none" : "all"
|
|
145
|
+
})
|
|
146
|
+
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
147
|
+
style: styles.itemBodyTouch,
|
|
148
|
+
onPress: () => openable && onOpen(),
|
|
149
|
+
disabled: !openable,
|
|
150
|
+
testID: `tm-preview-item-${row.sourceId}-${row.item.key}`,
|
|
114
151
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
115
|
-
style: styles.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
children: [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
style: [styles.itemGlyph, {
|
|
153
|
+
color: meta.color
|
|
154
|
+
}],
|
|
155
|
+
children: meta.glyph
|
|
156
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
157
|
+
style: styles.itemBody,
|
|
158
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
159
|
+
style: styles.itemLabel,
|
|
160
|
+
numberOfLines: 1,
|
|
161
|
+
children: row.item.label
|
|
162
|
+
}), row.item.verdict === "changed" && /*#__PURE__*/_jsxs(Text, {
|
|
163
|
+
style: styles.itemPreview,
|
|
164
|
+
numberOfLines: 1,
|
|
165
|
+
children: [shortPreview(row.item.oldValue), " \u2192", " ", shortPreview(row.item.newValue)]
|
|
166
|
+
}), row.item.verdict === "added" && /*#__PURE__*/_jsx(Text, {
|
|
167
|
+
style: styles.itemPreview,
|
|
168
|
+
numberOfLines: 1,
|
|
169
|
+
children: shortPreview(row.item.newValue)
|
|
170
|
+
}), row.item.verdict === "removed" && /*#__PURE__*/_jsx(Text, {
|
|
171
|
+
style: [styles.itemPreview, styles.itemPreviewRemoved],
|
|
172
|
+
numberOfLines: 1,
|
|
173
|
+
children: shortPreview(row.item.oldValue)
|
|
174
|
+
}), row.excluded ? /*#__PURE__*/_jsx(Text, {
|
|
175
|
+
style: styles.itemExcluded,
|
|
176
|
+
children: "excluded \u2014 restore leaves this untouched"
|
|
177
|
+
}) : row.item.reason && /*#__PURE__*/_jsx(Text, {
|
|
178
|
+
style: styles.itemReason,
|
|
179
|
+
numberOfLines: 2,
|
|
180
|
+
children: row.item.reason
|
|
181
|
+
})]
|
|
182
|
+
}), openable ? expanded ? /*#__PURE__*/_jsx(ChevronUp, {
|
|
183
|
+
size: 16,
|
|
184
|
+
color: tm.color.labelTertiary
|
|
185
|
+
}) : /*#__PURE__*/_jsx(DisclosureChevron, {}) : null]
|
|
141
186
|
})]
|
|
142
|
-
})
|
|
187
|
+
})
|
|
143
188
|
});
|
|
144
189
|
});
|
|
145
190
|
export function RestorePreviewScreen({
|
|
@@ -147,43 +192,65 @@ export function RestorePreviewScreen({
|
|
|
147
192
|
snapshots,
|
|
148
193
|
busy,
|
|
149
194
|
onBack,
|
|
150
|
-
|
|
151
|
-
onRestore,
|
|
152
|
-
onPickSide,
|
|
153
|
-
onRefresh
|
|
195
|
+
onRestore
|
|
154
196
|
}) {
|
|
155
197
|
const host = useToolHost();
|
|
156
198
|
const hosted = host.instanceId !== undefined;
|
|
157
|
-
|
|
199
|
+
// `minBottom` is a floor, not a preference: the dimension map can't know
|
|
200
|
+
// about a device that ships next year, and an unmapped one falls through to
|
|
201
|
+
// `bottom: 0`. This keeps the CTA off the home indicator either way.
|
|
202
|
+
const insets = useSafeAreaInsets({
|
|
203
|
+
minBottom: 16
|
|
204
|
+
});
|
|
158
205
|
const [collapsed, setCollapsed] = useState({});
|
|
159
|
-
const [allCollapsed, setAllCollapsed] = useState(false);
|
|
160
206
|
const [excluded, setExcluded] = useState(new Set());
|
|
161
|
-
|
|
207
|
+
/** Set by every checkbox interaction — see the auto-save effect below. */
|
|
208
|
+
const userEditedRef = useRef(false);
|
|
209
|
+
/** Item rows whose diff is expanded inline, by compound key. */
|
|
210
|
+
const [openDiffs, setOpenDiffs] = useState(EMPTY_SELECTION);
|
|
211
|
+
const toggleDiff = useCallback(key => {
|
|
212
|
+
setOpenDiffs(prev => {
|
|
213
|
+
const next = new Set(prev);
|
|
214
|
+
if (next.has(key)) next.delete(key);else next.add(key);
|
|
215
|
+
return next;
|
|
216
|
+
});
|
|
217
|
+
}, []);
|
|
218
|
+
// Filtering lives in the shared modal header (the magnifier), same as the
|
|
219
|
+
// restore-point list and every other Buoy tool — one place to look for it.
|
|
220
|
+
const search = host.search;
|
|
162
221
|
const rightMeta = preview.right === "current" ? undefined : snapshots.find(s => s.id === preview.right.snapshotId);
|
|
222
|
+
// Latest-ref: `renderRow` is memoized on its handlers, so this one has to
|
|
223
|
+
// keep a stable identity while still reading the current snapshot.
|
|
224
|
+
const rightMetaRef = useRef(rightMeta);
|
|
225
|
+
rightMetaRef.current = rightMeta;
|
|
226
|
+
|
|
227
|
+
/** Flip whether this restore also navigates back to the captured route. */
|
|
228
|
+
const toggleRoute = useCallback(() => {
|
|
229
|
+
if (!rightMetaRef.current) return;
|
|
230
|
+
const meta = rightMetaRef.current;
|
|
231
|
+
void timeMachineStore.setRestoreRoute(meta.id, !willRestoreRoute(meta));
|
|
232
|
+
}, []);
|
|
163
233
|
const isBaseline = rightMeta?.baseline === true;
|
|
164
234
|
const liveLeft = preview.left === "current";
|
|
165
235
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
setExcluded(next);
|
|
177
|
-
}, [preview.sources]);
|
|
236
|
+
/**
|
|
237
|
+
* The selection this restore point already remembers, as exclusions. Null
|
|
238
|
+
* until there is a snapshot on the right-hand side to have one.
|
|
239
|
+
*/
|
|
240
|
+
const savedExcluded = useMemo(() => {
|
|
241
|
+
if (!rightMeta) return null;
|
|
242
|
+
return rightMeta.excludedKeys?.length ? new Set(rightMeta.excludedKeys) : EMPTY_SELECTION;
|
|
243
|
+
}, [rightMeta]);
|
|
178
244
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
245
|
+
/**
|
|
246
|
+
* A snapshot still carrying the older `scope` include-list ("only ever
|
|
247
|
+
* touch these", set via MCP). It seeds the checkboxes and is converted to
|
|
248
|
+
* exclusions on read — a restore of it would otherwise still apply include
|
|
249
|
+
* semantics, and the two forms would disagree about what one tap does.
|
|
250
|
+
*/
|
|
251
|
+
const legacyScoped = !!rightMeta?.scope?.length && !rightMeta?.excludedKeys?.length;
|
|
252
|
+
const legacySeed = useMemo(() => {
|
|
253
|
+
if (!legacyScoped || !rightMeta?.scope) return EMPTY_SELECTION;
|
|
187
254
|
const scopeSet = new Set(rightMeta.scope);
|
|
188
255
|
const next = new Set();
|
|
189
256
|
for (const src of preview.sources) {
|
|
@@ -194,28 +261,48 @@ export function RestorePreviewScreen({
|
|
|
194
261
|
}
|
|
195
262
|
}
|
|
196
263
|
}
|
|
197
|
-
|
|
198
|
-
}, [
|
|
264
|
+
return next;
|
|
265
|
+
}, [legacyScoped, rightMeta, preview.sources]);
|
|
199
266
|
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!rightMeta) return;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
267
|
+
// Open with the saved selection already applied — once per snapshot, and
|
|
268
|
+
// only once there is a diff to apply it to.
|
|
269
|
+
const seededFor = useRef(null);
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
if (!rightMeta || preview.building || !savedExcluded) return;
|
|
272
|
+
if (seededFor.current === rightMeta.id) return;
|
|
273
|
+
seededFor.current = rightMeta.id;
|
|
274
|
+
userEditedRef.current = false;
|
|
275
|
+
const seed = savedExcluded.size > 0 ? savedExcluded : legacySeed;
|
|
276
|
+
if (seed.size > 0) setExcluded(new Set(seed));
|
|
277
|
+
// Convert an older include-list scope the moment it's read, so the two
|
|
278
|
+
// forms never disagree about what a restore will do.
|
|
279
|
+
if (legacyScoped && seed.size > 0) {
|
|
280
|
+
void timeMachineStore.setExclusions(rightMeta.id, Array.from(seed));
|
|
213
281
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
282
|
+
}, [rightMeta, preview.building, savedExcluded, legacySeed, legacyScoped]);
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Auto-save. The checkboxes ARE the restore point's setting, so there is
|
|
286
|
+
* nothing to confirm — every edit persists as EXCLUSIONS (the items to
|
|
287
|
+
* leave alone), and every later restore of this point honors them, one-tap
|
|
288
|
+
* RESTORE and MCP included.
|
|
289
|
+
*
|
|
290
|
+
* Never an include list: unchecking one of ten keys must keep restoring the
|
|
291
|
+
* other nine AND anything that only starts differing tomorrow, which a list
|
|
292
|
+
* built from today's diff cannot do.
|
|
293
|
+
*
|
|
294
|
+
* Gated on a ref rather than on comparing to what's stored, because on the
|
|
295
|
+
* frames before the seed lands `excluded` is legitimately empty — writing
|
|
296
|
+
* that would erase the saved selection we are about to load.
|
|
297
|
+
*/
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
if (!rightMeta || !savedExcluded || !userEditedRef.current) return;
|
|
300
|
+
if (sameSelection(excluded, savedExcluded)) return;
|
|
301
|
+
const keys = Array.from(excluded);
|
|
302
|
+
void timeMachineStore.setExclusions(rightMeta.id, keys.length ? keys : null);
|
|
303
|
+
}, [excluded, rightMeta, savedExcluded]);
|
|
218
304
|
const toggleItem = useCallback(key => {
|
|
305
|
+
userEditedRef.current = true;
|
|
219
306
|
setExcluded(prev => {
|
|
220
307
|
const next = new Set(prev);
|
|
221
308
|
if (next.has(key)) next.delete(key);else next.add(key);
|
|
@@ -223,6 +310,7 @@ export function RestorePreviewScreen({
|
|
|
223
310
|
});
|
|
224
311
|
}, []);
|
|
225
312
|
const toggleSource = useCallback((source, includeState) => {
|
|
313
|
+
userEditedRef.current = true;
|
|
226
314
|
setExcluded(prev => {
|
|
227
315
|
const next = new Set(prev);
|
|
228
316
|
for (const item of source.items) {
|
|
@@ -243,13 +331,6 @@ export function RestorePreviewScreen({
|
|
|
243
331
|
[id]: !currentlyCollapsed
|
|
244
332
|
}));
|
|
245
333
|
}, []);
|
|
246
|
-
const toggleAllCollapsed = useCallback(() => {
|
|
247
|
-
setAllCollapsed(prev => {
|
|
248
|
-
const next = !prev;
|
|
249
|
-
setCollapsed(Object.fromEntries(preview.sources.map(s => [s.id, next])));
|
|
250
|
-
return next;
|
|
251
|
-
});
|
|
252
|
-
}, [preview.sources]);
|
|
253
334
|
const totals = useMemo(() => {
|
|
254
335
|
let applies = 0;
|
|
255
336
|
let excludedCount = 0;
|
|
@@ -280,23 +361,31 @@ export function RestorePreviewScreen({
|
|
|
280
361
|
// bottom CTA; the preset's labeled RELOAD APP action stays available.
|
|
281
362
|
useToolHeader(hosted ? {
|
|
282
363
|
title: "Restore Preview",
|
|
283
|
-
subtitle: rightMeta?.name ?? "
|
|
284
|
-
onBack
|
|
285
|
-
searchable: false // the in-body "Filter keys…" field covers it
|
|
364
|
+
subtitle: rightMeta?.name ?? "preview",
|
|
365
|
+
onBack
|
|
286
366
|
} : null,
|
|
287
367
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
288
368
|
[hosted, rightMeta?.name]);
|
|
289
|
-
const
|
|
369
|
+
const {
|
|
370
|
+
rows,
|
|
371
|
+
blockedSources
|
|
372
|
+
} = useMemo(() => {
|
|
290
373
|
const q = search.trim().toLowerCase();
|
|
291
374
|
const out = [];
|
|
375
|
+
// Sources the restore would not touch at all get no card: "no changes"
|
|
376
|
+
// rows are noise on a screen about what WILL change.
|
|
377
|
+
/** Sources holding a won't-apply item, so the note can name where to look. */
|
|
378
|
+
const blocked = [];
|
|
292
379
|
for (const src of preview.sources) {
|
|
293
380
|
// Every detected source gets a card whose subtitle carries the
|
|
294
381
|
// detection proof ("60 keys · 7 changed" / "2 slices · no changes"),
|
|
295
382
|
// so clean sources need no expansion. Unchanged ITEMS never render.
|
|
296
383
|
const relevant = src.items.some(i => i.verdict !== "unchanged");
|
|
384
|
+
if (!relevant && !src.error) continue;
|
|
297
385
|
const expandable = relevant || src.notes.length > 0 || !!src.error;
|
|
298
386
|
const applicable = src.items.filter(i => VERDICT_META[i.verdict].applies);
|
|
299
387
|
const wontApplyCount = src.items.filter(i => i.verdict === "wont-apply").length;
|
|
388
|
+
if (wontApplyCount > 0) blocked.push(src.label);
|
|
300
389
|
const noun = src.items.length === 1 ? (SOURCE_NOUNS[src.id] ?? "items").replace(/ies$/, "y").replace(/s$/, "") : SOURCE_NOUNS[src.id] ?? "items";
|
|
301
390
|
const summary = `${src.items.length} ${noun} · ` + (applicable.length > 0 ? `${applicable.length} changed` : wontApplyCount > 0 ? `${wontApplyCount} won't apply` : "no changes");
|
|
302
391
|
const excludedInSource = applicable.filter(i => excluded.has(cKey(src.id, i.key))).length;
|
|
@@ -317,7 +406,10 @@ export function RestorePreviewScreen({
|
|
|
317
406
|
if (q && !item.label.toLowerCase().includes(q)) return false;
|
|
318
407
|
return true;
|
|
319
408
|
});
|
|
320
|
-
|
|
409
|
+
// Everything starts collapsed. The screen's job on open is "which
|
|
410
|
+
// sources would this touch, and how much" — expanding a source with
|
|
411
|
+
// 58 keys before you asked buries that under its own contents.
|
|
412
|
+
const isCollapsed = expandable ? collapsed[src.id] ?? true : true;
|
|
321
413
|
out.push({
|
|
322
414
|
kind: "source",
|
|
323
415
|
source: src,
|
|
@@ -326,35 +418,73 @@ export function RestorePreviewScreen({
|
|
|
326
418
|
applicableCount: applicable.length,
|
|
327
419
|
counts,
|
|
328
420
|
expandable,
|
|
329
|
-
summary
|
|
421
|
+
summary,
|
|
422
|
+
first: false,
|
|
423
|
+
last: false
|
|
330
424
|
});
|
|
331
425
|
if (!isCollapsed) {
|
|
332
426
|
for (const note of src.notes) {
|
|
333
427
|
out.push({
|
|
334
428
|
kind: "note",
|
|
335
429
|
id: `${src.id}:${note}`,
|
|
336
|
-
text: note
|
|
430
|
+
text: note,
|
|
431
|
+
first: false,
|
|
432
|
+
last: false
|
|
337
433
|
});
|
|
338
434
|
}
|
|
339
435
|
if (src.error) {
|
|
340
436
|
out.push({
|
|
341
437
|
kind: "note",
|
|
342
438
|
id: `${src.id}:err`,
|
|
343
|
-
text: `⚠ ${src.error}
|
|
439
|
+
text: `⚠ ${src.error}`,
|
|
440
|
+
first: false,
|
|
441
|
+
last: false
|
|
344
442
|
});
|
|
345
443
|
}
|
|
346
444
|
for (const item of visible) {
|
|
445
|
+
const key = cKey(src.id, item.key);
|
|
347
446
|
out.push({
|
|
348
447
|
kind: "item",
|
|
349
448
|
sourceId: src.id,
|
|
350
449
|
item,
|
|
351
|
-
excluded: excluded.has(
|
|
450
|
+
excluded: excluded.has(key),
|
|
451
|
+
first: false,
|
|
452
|
+
last: false
|
|
352
453
|
});
|
|
454
|
+
if (openDiffs.has(key)) {
|
|
455
|
+
out.push({
|
|
456
|
+
kind: "diff",
|
|
457
|
+
sourceId: src.id,
|
|
458
|
+
item,
|
|
459
|
+
first: false,
|
|
460
|
+
last: false
|
|
461
|
+
});
|
|
462
|
+
}
|
|
353
463
|
}
|
|
354
464
|
}
|
|
355
465
|
}
|
|
356
|
-
|
|
357
|
-
|
|
466
|
+
if (rightMeta?.route) {
|
|
467
|
+
out.push({
|
|
468
|
+
kind: "route",
|
|
469
|
+
href: rightMeta.route.href,
|
|
470
|
+
on: willRestoreRoute(rightMeta),
|
|
471
|
+
first: false,
|
|
472
|
+
last: false
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
// ONE card holds every source — the ends of the LIST round, and every
|
|
476
|
+
// other boundary is just a hairline. Per-source cards would read as
|
|
477
|
+
// separate groups, which they aren't: this is one list of what a restore
|
|
478
|
+
// would touch.
|
|
479
|
+
if (out.length > 0) {
|
|
480
|
+
out[0].first = true;
|
|
481
|
+
out[out.length - 1].last = true;
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
rows: out,
|
|
485
|
+
blockedSources: blocked
|
|
486
|
+
};
|
|
487
|
+
}, [preview.sources, search, collapsed, excluded, openDiffs, rightMeta]);
|
|
358
488
|
const renderRow = useCallback(({
|
|
359
489
|
item: row
|
|
360
490
|
}) => {
|
|
@@ -363,86 +493,119 @@ export function RestorePreviewScreen({
|
|
|
363
493
|
const {
|
|
364
494
|
counts
|
|
365
495
|
} = row;
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
383
|
-
style: styles.sourceCardTouch,
|
|
384
|
-
onPress: () => row.expandable && toggleCollapse(src.id, row.collapsed),
|
|
385
|
-
disabled: !row.expandable,
|
|
386
|
-
testID: `tm-preview-source-${src.id}`,
|
|
387
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
388
|
-
style: styles.sourceIconTile,
|
|
389
|
-
children: /*#__PURE__*/_jsx(IconComp, {
|
|
390
|
-
size: 18
|
|
496
|
+
return /*#__PURE__*/_jsx(CardRow, {
|
|
497
|
+
position: row,
|
|
498
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
499
|
+
style: styles.sourceCard,
|
|
500
|
+
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
501
|
+
onPress: () => toggleSource(src, row.includeState),
|
|
502
|
+
disabled: row.applicableCount === 0,
|
|
503
|
+
hitSlop: {
|
|
504
|
+
top: 10,
|
|
505
|
+
bottom: 10,
|
|
506
|
+
left: 8,
|
|
507
|
+
right: 8
|
|
508
|
+
},
|
|
509
|
+
testID: `tm-preview-source-toggle-${src.id}`,
|
|
510
|
+
children: /*#__PURE__*/_jsx(Checkbox, {
|
|
511
|
+
state: row.applicableCount === 0 ? "none" : row.includeState
|
|
391
512
|
})
|
|
513
|
+
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
514
|
+
style: styles.sourceCardTouch,
|
|
515
|
+
onPress: () => row.expandable && toggleCollapse(src.id, row.collapsed),
|
|
516
|
+
disabled: !row.expandable,
|
|
517
|
+
testID: `tm-preview-source-${src.id}`,
|
|
518
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
519
|
+
style: styles.sourceNameBlock,
|
|
520
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
521
|
+
style: styles.sourceName,
|
|
522
|
+
numberOfLines: 1,
|
|
523
|
+
children: src.label
|
|
524
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
525
|
+
style: styles.sourceSummary,
|
|
526
|
+
numberOfLines: 1,
|
|
527
|
+
children: row.includeState === "none" && row.applicableCount > 0 ? `${row.summary} · excluded` : row.summary
|
|
528
|
+
})]
|
|
529
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
530
|
+
style: styles.sourceBadges,
|
|
531
|
+
children: [counts.removed > 0 ? /*#__PURE__*/_jsx(CountPill, {
|
|
532
|
+
glyph: "\u2212",
|
|
533
|
+
count: counts.removed,
|
|
534
|
+
color: tm.color.danger
|
|
535
|
+
}) : null, counts.added > 0 ? /*#__PURE__*/_jsx(CountPill, {
|
|
536
|
+
glyph: "+",
|
|
537
|
+
count: counts.added,
|
|
538
|
+
color: tm.color.accent
|
|
539
|
+
}) : null, counts.changed > 0 ? /*#__PURE__*/_jsx(CountPill, {
|
|
540
|
+
glyph: "\u2248",
|
|
541
|
+
count: counts.changed,
|
|
542
|
+
color: tm.color.info
|
|
543
|
+
}) : null]
|
|
544
|
+
}), row.expandable ? row.collapsed ? /*#__PURE__*/_jsx(DisclosureChevron, {}) : /*#__PURE__*/_jsx(ChevronUp, {
|
|
545
|
+
size: 16,
|
|
546
|
+
color: tm.color.labelTertiary
|
|
547
|
+
}) : null]
|
|
548
|
+
})]
|
|
549
|
+
})
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (row.kind === "note") {
|
|
553
|
+
return /*#__PURE__*/_jsx(CardRow, {
|
|
554
|
+
position: row,
|
|
555
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
556
|
+
style: styles.noteRow,
|
|
557
|
+
children: row.text
|
|
558
|
+
})
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
if (row.kind === "route") {
|
|
562
|
+
return /*#__PURE__*/_jsx(CardRow, {
|
|
563
|
+
position: row,
|
|
564
|
+
children: /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
565
|
+
style: styles.routeRow,
|
|
566
|
+
onPress: toggleRoute,
|
|
567
|
+
accessibilityRole: "switch",
|
|
568
|
+
accessibilityState: {
|
|
569
|
+
checked: row.on
|
|
570
|
+
},
|
|
571
|
+
accessibilityLabel: `Also return to ${row.href}`,
|
|
572
|
+
testID: "tm-preview-route",
|
|
573
|
+
children: [/*#__PURE__*/_jsx(Checkbox, {
|
|
574
|
+
state: row.on ? "all" : "none"
|
|
392
575
|
}), /*#__PURE__*/_jsxs(View, {
|
|
393
|
-
style: styles.
|
|
576
|
+
style: styles.routeText,
|
|
394
577
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
395
|
-
style: styles.
|
|
396
|
-
|
|
397
|
-
children: src.label
|
|
578
|
+
style: styles.routeTitle,
|
|
579
|
+
children: "Return to this screen"
|
|
398
580
|
}), /*#__PURE__*/_jsx(Text, {
|
|
399
|
-
style: styles.
|
|
581
|
+
style: styles.routeHref,
|
|
400
582
|
numberOfLines: 1,
|
|
401
|
-
children: row.
|
|
402
|
-
})]
|
|
403
|
-
}), !clean && /*#__PURE__*/_jsxs(View, {
|
|
404
|
-
style: styles.sourceBadges,
|
|
405
|
-
children: [counts.removed > 0 && /*#__PURE__*/_jsxs(Text, {
|
|
406
|
-
style: [styles.sourceBadge, {
|
|
407
|
-
color: buoyColors.error
|
|
408
|
-
}],
|
|
409
|
-
children: ["\u2212", counts.removed]
|
|
410
|
-
}), counts.added > 0 && /*#__PURE__*/_jsxs(Text, {
|
|
411
|
-
style: [styles.sourceBadge, {
|
|
412
|
-
color: buoyColors.success
|
|
413
|
-
}],
|
|
414
|
-
children: ["+", counts.added]
|
|
415
|
-
}), counts.changed > 0 && /*#__PURE__*/_jsxs(Text, {
|
|
416
|
-
style: [styles.sourceBadge, {
|
|
417
|
-
color: buoyColors.info
|
|
418
|
-
}],
|
|
419
|
-
children: ["\u2248", counts.changed]
|
|
583
|
+
children: row.href
|
|
420
584
|
})]
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
color: buoyColors.textMuted
|
|
424
|
-
}) : /*#__PURE__*/_jsx(ChevronUp, {
|
|
425
|
-
size: 16,
|
|
426
|
-
color: buoyColors.textMuted
|
|
427
|
-
}) : null]
|
|
428
|
-
})]
|
|
585
|
+
})]
|
|
586
|
+
})
|
|
429
587
|
});
|
|
430
588
|
}
|
|
431
|
-
if (row.kind === "
|
|
432
|
-
return /*#__PURE__*/_jsx(
|
|
433
|
-
|
|
434
|
-
children:
|
|
589
|
+
if (row.kind === "diff") {
|
|
590
|
+
return /*#__PURE__*/_jsx(CardRow, {
|
|
591
|
+
position: row,
|
|
592
|
+
children: /*#__PURE__*/_jsx(ItemDiffPanel, {
|
|
593
|
+
item: row.item
|
|
594
|
+
})
|
|
435
595
|
});
|
|
436
596
|
}
|
|
437
597
|
return /*#__PURE__*/_jsx(ItemRow, {
|
|
438
598
|
row: row,
|
|
439
|
-
|
|
599
|
+
expanded: openDiffs.has(cKey(row.sourceId, row.item.key)),
|
|
600
|
+
onOpen: () => toggleDiff(cKey(row.sourceId, row.item.key)),
|
|
440
601
|
onToggle: () => toggleItem(cKey(row.sourceId, row.item.key))
|
|
441
602
|
});
|
|
442
|
-
}, [
|
|
603
|
+
}, [openDiffs, toggleDiff, toggleCollapse, toggleItem, toggleSource, toggleRoute]);
|
|
443
604
|
const keyExtractor = useCallback(row => {
|
|
444
605
|
if (row.kind === "source") return `s:${row.source.id}`;
|
|
445
606
|
if (row.kind === "note") return `n:${row.id}`;
|
|
607
|
+
if (row.kind === "route") return "route";
|
|
608
|
+
if (row.kind === "diff") return `d:${row.sourceId}:${row.item.key}`;
|
|
446
609
|
return `i:${row.sourceId}:${row.item.key}`;
|
|
447
610
|
}, []);
|
|
448
611
|
const header = /*#__PURE__*/_jsxs(View, {
|
|
@@ -461,179 +624,17 @@ export function RestorePreviewScreen({
|
|
|
461
624
|
numberOfLines: 1,
|
|
462
625
|
children: "Restore Preview"
|
|
463
626
|
})]
|
|
464
|
-
}), /*#__PURE__*/_jsxs(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
style: styles.compareTagRow,
|
|
472
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
473
|
-
style: [styles.stateDot, {
|
|
474
|
-
backgroundColor: buoyColors.info
|
|
475
|
-
}]
|
|
476
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
477
|
-
style: styles.compareTag,
|
|
478
|
-
children: liveLeft ? "CURRENT STATE" : "SNAPSHOT"
|
|
479
|
-
})]
|
|
480
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
481
|
-
style: styles.compareName,
|
|
482
|
-
numberOfLines: 1,
|
|
483
|
-
children: sideName(preview.left, snapshots)
|
|
484
|
-
}), liveLeft ? /*#__PURE__*/_jsxs(View, {
|
|
485
|
-
style: styles.compareMetaRow,
|
|
486
|
-
children: [/*#__PURE__*/_jsxs(Text, {
|
|
487
|
-
style: styles.compareMeta,
|
|
488
|
-
children: ["built ", timeAgo(preview.builtAt), " ago"]
|
|
489
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
490
|
-
onPress: onRefresh,
|
|
491
|
-
disabled: preview.building,
|
|
492
|
-
hitSlop: {
|
|
493
|
-
top: 8,
|
|
494
|
-
bottom: 8,
|
|
495
|
-
left: 6,
|
|
496
|
-
right: 8
|
|
497
|
-
},
|
|
498
|
-
accessibilityLabel: "Rebuild preview",
|
|
499
|
-
testID: "tm-preview-refresh",
|
|
500
|
-
children: /*#__PURE__*/_jsx(RefreshCw, {
|
|
501
|
-
size: 11,
|
|
502
|
-
color: preview.building ? macOSColors.text.disabled : buoyColors.textSecondary
|
|
503
|
-
})
|
|
504
|
-
})]
|
|
505
|
-
}) : /*#__PURE__*/_jsx(Text, {
|
|
506
|
-
style: styles.compareMeta,
|
|
507
|
-
children: "snapshot"
|
|
508
|
-
})]
|
|
509
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
510
|
-
style: styles.swapBadge,
|
|
511
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
512
|
-
style: styles.swapGlyph,
|
|
513
|
-
children: "\u21C4"
|
|
514
|
-
})
|
|
515
|
-
}), /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
516
|
-
style: [styles.compareSide, styles.compareSideRight],
|
|
517
|
-
onPress: () => onPickSide("right"),
|
|
518
|
-
testID: "tm-preview-pick-right",
|
|
519
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
520
|
-
style: styles.compareTagRow,
|
|
521
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
522
|
-
style: styles.compareTag,
|
|
523
|
-
children: isBaseline ? "BASELINE" : "SNAPSHOT"
|
|
524
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
525
|
-
style: [styles.stateDot, {
|
|
526
|
-
backgroundColor: buoyColors.success
|
|
527
|
-
}]
|
|
528
|
-
})]
|
|
529
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
530
|
-
style: [styles.compareName, styles.compareNameRight],
|
|
531
|
-
numberOfLines: 1,
|
|
532
|
-
children: sideName(preview.right, snapshots)
|
|
533
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
534
|
-
style: [styles.compareMeta, styles.compareMetaRight],
|
|
535
|
-
children: rightMeta ? `${timeAgo(rightMeta.createdAt)} ago` : ""
|
|
536
|
-
})]
|
|
537
|
-
})]
|
|
538
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
539
|
-
style: styles.changesRow,
|
|
540
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
541
|
-
style: styles.changesTitleRow,
|
|
542
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
543
|
-
style: styles.changesTitle,
|
|
544
|
-
children: "Changes to restore"
|
|
545
|
-
}), totals.applies + totals.excludedCount > 1 && /*#__PURE__*/_jsxs(View, {
|
|
546
|
-
style: styles.selectLinks,
|
|
547
|
-
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
|
548
|
-
onPress: selectAll,
|
|
549
|
-
hitSlop: {
|
|
550
|
-
top: 8,
|
|
551
|
-
bottom: 8,
|
|
552
|
-
left: 4,
|
|
553
|
-
right: 4
|
|
554
|
-
},
|
|
555
|
-
testID: "tm-preview-select-all",
|
|
556
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
557
|
-
style: [styles.selectLink, totals.excludedCount === 0 && styles.selectLinkActive],
|
|
558
|
-
children: "all"
|
|
559
|
-
})
|
|
560
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
561
|
-
style: styles.selectLinkSep,
|
|
562
|
-
children: "\xB7"
|
|
563
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
564
|
-
onPress: selectNone,
|
|
565
|
-
hitSlop: {
|
|
566
|
-
top: 8,
|
|
567
|
-
bottom: 8,
|
|
568
|
-
left: 4,
|
|
569
|
-
right: 4
|
|
570
|
-
},
|
|
571
|
-
testID: "tm-preview-select-none",
|
|
572
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
573
|
-
style: [styles.selectLink, totals.applies === 0 && totals.excludedCount > 0 && styles.selectLinkActive],
|
|
574
|
-
children: "none"
|
|
575
|
-
})
|
|
576
|
-
}), liveLeft && rightMeta && /*#__PURE__*/_jsxs(_Fragment, {
|
|
577
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
578
|
-
style: styles.selectLinkSep,
|
|
579
|
-
children: "\xB7"
|
|
580
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
581
|
-
onPress: saveScope,
|
|
582
|
-
hitSlop: {
|
|
583
|
-
top: 8,
|
|
584
|
-
bottom: 8,
|
|
585
|
-
left: 4,
|
|
586
|
-
right: 4
|
|
587
|
-
},
|
|
588
|
-
testID: "tm-preview-save-scope",
|
|
589
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
590
|
-
style: [styles.selectLink, !!rightMeta.scope?.length && styles.selectLinkActive],
|
|
591
|
-
children: "save scope"
|
|
592
|
-
})
|
|
593
|
-
})]
|
|
594
|
-
})]
|
|
595
|
-
})]
|
|
596
|
-
}), preview.building ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
597
|
-
size: "small",
|
|
598
|
-
color: buoyColors.primary
|
|
599
|
-
}) : /*#__PURE__*/_jsx(View, {
|
|
600
|
-
style: styles.changesChip,
|
|
601
|
-
children: /*#__PURE__*/_jsxs(Text, {
|
|
602
|
-
style: styles.changesChipText,
|
|
603
|
-
numberOfLines: 1,
|
|
604
|
-
children: [totals.applies === 0 ? totals.excludedCount > 0 ? "everything excluded" : "no changes" : totals.perSource.join(" · "), totals.excludedCount > 0 && totals.applies > 0 ? ` · ${totals.excludedCount} excluded` : ""]
|
|
605
|
-
})
|
|
606
|
-
})]
|
|
607
|
-
}), totals.wontApply > 0 && /*#__PURE__*/_jsxs(Text, {
|
|
608
|
-
style: styles.wontApplyLine,
|
|
609
|
-
children: ["\u2298 ", totals.wontApply, " item", totals.wontApply === 1 ? "" : "s", " won't apply \u2014 shown inline with the reason"]
|
|
610
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
611
|
-
style: styles.searchRow,
|
|
612
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
613
|
-
style: styles.searchField,
|
|
614
|
-
children: [/*#__PURE__*/_jsx(Search, {
|
|
615
|
-
size: 14,
|
|
616
|
-
color: buoyColors.textMuted
|
|
617
|
-
}), /*#__PURE__*/_jsx(TextInput, {
|
|
618
|
-
style: styles.searchInput,
|
|
619
|
-
value: localSearch,
|
|
620
|
-
onChangeText: setLocalSearch,
|
|
621
|
-
placeholder: "Filter keys\u2026",
|
|
622
|
-
placeholderTextColor: buoyColors.textMuted,
|
|
623
|
-
autoCorrect: false,
|
|
624
|
-
autoCapitalize: "none",
|
|
625
|
-
testID: "tm-preview-search"
|
|
626
|
-
})]
|
|
627
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
628
|
-
style: styles.collapseAllButton,
|
|
629
|
-
onPress: toggleAllCollapsed,
|
|
630
|
-
accessibilityLabel: allCollapsed ? "Expand all" : "Collapse all",
|
|
631
|
-
testID: "tm-preview-collapse-all",
|
|
632
|
-
children: /*#__PURE__*/_jsx(Filter, {
|
|
633
|
-
size: 15,
|
|
634
|
-
color: buoyColors.textSecondary
|
|
627
|
+
}), rightMeta ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
628
|
+
children: [/*#__PURE__*/_jsx(SectionLabel, {
|
|
629
|
+
children: "Restore Point"
|
|
630
|
+
}), /*#__PURE__*/_jsx(Group, {
|
|
631
|
+
children: /*#__PURE__*/_jsx(RestorePointNameRow, {
|
|
632
|
+
name: rightMeta.name,
|
|
633
|
+
onRename: next => void timeMachineStore.rename(rightMeta.id, next)
|
|
635
634
|
})
|
|
636
635
|
})]
|
|
636
|
+
}) : null, /*#__PURE__*/_jsx(SectionLabel, {
|
|
637
|
+
children: "Changes to Restore"
|
|
637
638
|
})]
|
|
638
639
|
});
|
|
639
640
|
const ctaDisabled = busy || preview.building || totals.applies === 0;
|
|
@@ -644,16 +645,34 @@ export function RestorePreviewScreen({
|
|
|
644
645
|
renderItem: renderRow,
|
|
645
646
|
keyExtractor: keyExtractor,
|
|
646
647
|
ListHeaderComponent: header,
|
|
647
|
-
ListEmptyComponent: preview.building ?
|
|
648
|
+
ListEmptyComponent: preview.building ? /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
649
|
+
style: styles.buildingSpinner,
|
|
650
|
+
size: "small",
|
|
651
|
+
color: tm.color.accent
|
|
652
|
+
}) : /*#__PURE__*/_jsx(Text, {
|
|
648
653
|
style: styles.emptyText,
|
|
649
654
|
children: search ? "Nothing matches this filter." : "No changes — restoring would change nothing."
|
|
650
655
|
}),
|
|
656
|
+
ListFooterComponent: /*#__PURE__*/_jsx(View, {
|
|
657
|
+
children: totals.wontApply > 0 ? /*#__PURE__*/_jsxs(View, {
|
|
658
|
+
style: styles.warnNote,
|
|
659
|
+
children: [/*#__PURE__*/_jsx(AlertTriangle, {
|
|
660
|
+
size: 12,
|
|
661
|
+
color: tm.color.warning
|
|
662
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
663
|
+
style: styles.warnNoteText,
|
|
664
|
+
children: [totals.wontApply, " item", totals.wontApply === 1 ? "" : "s", " ", "won't apply \u2014 expand", " ", blockedSources.length > 0 ? blockedSources.join(" and ") : "the source", " ", "to see the reason."]
|
|
665
|
+
})]
|
|
666
|
+
}) : null
|
|
667
|
+
}),
|
|
651
668
|
removeClippedSubviews: true,
|
|
652
669
|
initialNumToRender: 20,
|
|
653
670
|
windowSize: 7,
|
|
654
671
|
contentContainerStyle: styles.listContent
|
|
655
672
|
}), liveLeft && rightMeta && /*#__PURE__*/_jsxs(View, {
|
|
656
|
-
style: styles.footer,
|
|
673
|
+
style: [styles.footer, {
|
|
674
|
+
paddingBottom: insets.bottom
|
|
675
|
+
}],
|
|
657
676
|
children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
658
677
|
style: [styles.cta, isBaseline && styles.ctaDanger, ctaDisabled && styles.ctaDisabled],
|
|
659
678
|
onPress: () => onRestore(isBaseline ? "reload" : "live", Array.from(excluded)),
|
|
@@ -661,9 +680,9 @@ export function RestorePreviewScreen({
|
|
|
661
680
|
testID: "tm-preview-restore",
|
|
662
681
|
children: [/*#__PURE__*/_jsx(RefreshCw, {
|
|
663
682
|
size: 16,
|
|
664
|
-
color:
|
|
683
|
+
color: isBaseline ? tm.color.onDanger : tm.color.onAccent
|
|
665
684
|
}), /*#__PURE__*/_jsx(Text, {
|
|
666
|
-
style: styles.ctaText,
|
|
685
|
+
style: [styles.ctaText, isBaseline ? styles.ctaTextDanger : null],
|
|
667
686
|
children: isBaseline ? "Wipe & Reload" : "Restore State"
|
|
668
687
|
})]
|
|
669
688
|
}), /*#__PURE__*/_jsx(Text, {
|
|
@@ -681,226 +700,133 @@ const HIT = {
|
|
|
681
700
|
};
|
|
682
701
|
const styles = StyleSheet.create({
|
|
683
702
|
container: {
|
|
684
|
-
flex: 1
|
|
703
|
+
flex: 1,
|
|
704
|
+
backgroundColor: tm.color.sheet
|
|
685
705
|
},
|
|
686
706
|
fallbackHeader: {
|
|
687
707
|
flexDirection: "row",
|
|
688
708
|
alignItems: "center",
|
|
689
|
-
|
|
709
|
+
paddingHorizontal: tm.gutter,
|
|
710
|
+
paddingVertical: 10,
|
|
690
711
|
gap: 10
|
|
691
712
|
},
|
|
692
713
|
backText: {
|
|
693
|
-
color:
|
|
694
|
-
fontSize:
|
|
714
|
+
color: tm.color.accent,
|
|
715
|
+
fontSize: tm.font.control
|
|
695
716
|
},
|
|
696
717
|
screenTitle: {
|
|
697
|
-
color:
|
|
698
|
-
fontSize:
|
|
718
|
+
color: tm.color.label,
|
|
719
|
+
fontSize: 20,
|
|
699
720
|
fontWeight: "700",
|
|
721
|
+
letterSpacing: tm.tracking.row,
|
|
700
722
|
flex: 1
|
|
701
723
|
},
|
|
702
|
-
toolbarButton: {
|
|
703
|
-
padding: 4,
|
|
704
|
-
justifyContent: "center",
|
|
705
|
-
alignItems: "center"
|
|
706
|
-
},
|
|
707
724
|
dimmed: {
|
|
708
|
-
opacity: 0.
|
|
725
|
+
opacity: 0.5
|
|
709
726
|
},
|
|
710
727
|
listContent: {
|
|
711
|
-
|
|
712
|
-
paddingTop: 8,
|
|
728
|
+
paddingTop: 14,
|
|
713
729
|
paddingBottom: 12
|
|
714
730
|
},
|
|
715
|
-
//
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
backgroundColor: buoyColors.card,
|
|
720
|
-
borderColor: buoyColors.border,
|
|
721
|
-
borderWidth: 1,
|
|
722
|
-
borderRadius: 14,
|
|
723
|
-
padding: 14,
|
|
724
|
-
gap: 10
|
|
725
|
-
},
|
|
726
|
-
compareSide: {
|
|
727
|
-
flex: 1,
|
|
728
|
-
minWidth: 0,
|
|
729
|
-
gap: 3
|
|
730
|
-
},
|
|
731
|
-
compareSideRight: {
|
|
732
|
-
alignItems: "flex-end"
|
|
733
|
-
},
|
|
734
|
-
compareTagRow: {
|
|
735
|
-
flexDirection: "row",
|
|
736
|
-
alignItems: "center",
|
|
737
|
-
gap: 5
|
|
738
|
-
},
|
|
739
|
-
stateDot: {
|
|
740
|
-
width: 6,
|
|
741
|
-
height: 6,
|
|
742
|
-
borderRadius: 3
|
|
743
|
-
},
|
|
744
|
-
compareTag: {
|
|
745
|
-
color: buoyColors.textSecondary,
|
|
746
|
-
fontSize: 10,
|
|
747
|
-
fontWeight: "700",
|
|
748
|
-
letterSpacing: 0.6
|
|
749
|
-
},
|
|
750
|
-
compareName: {
|
|
751
|
-
color: buoyColors.text,
|
|
752
|
-
fontSize: 15,
|
|
753
|
-
fontWeight: "700"
|
|
754
|
-
},
|
|
755
|
-
compareNameRight: {
|
|
756
|
-
textAlign: "right"
|
|
757
|
-
},
|
|
758
|
-
compareMeta: {
|
|
759
|
-
color: buoyColors.textMuted,
|
|
760
|
-
fontSize: 10
|
|
761
|
-
},
|
|
762
|
-
compareMetaRight: {
|
|
763
|
-
textAlign: "right"
|
|
764
|
-
},
|
|
765
|
-
compareMetaRow: {
|
|
766
|
-
flexDirection: "row",
|
|
767
|
-
alignItems: "center",
|
|
768
|
-
gap: 6
|
|
731
|
+
// ── The grouped card each list row paints its own share of ──────────────
|
|
732
|
+
cardRow: {
|
|
733
|
+
marginHorizontal: tm.gutter,
|
|
734
|
+
backgroundColor: tm.color.group
|
|
769
735
|
},
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
borderRadius: 17,
|
|
774
|
-
backgroundColor: buoyColors.hover,
|
|
775
|
-
borderColor: buoyColors.border,
|
|
776
|
-
borderWidth: 1,
|
|
777
|
-
alignItems: "center",
|
|
778
|
-
justifyContent: "center"
|
|
736
|
+
cardRowFirst: {
|
|
737
|
+
borderTopLeftRadius: tm.radius.group,
|
|
738
|
+
borderTopRightRadius: tm.radius.group
|
|
779
739
|
},
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
740
|
+
cardRowLast: {
|
|
741
|
+
borderBottomLeftRadius: tm.radius.group,
|
|
742
|
+
borderBottomRightRadius: tm.radius.group,
|
|
743
|
+
marginBottom: 10
|
|
783
744
|
},
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
justifyContent: "space-between",
|
|
789
|
-
marginTop: 14,
|
|
790
|
-
gap: 10
|
|
791
|
-
},
|
|
792
|
-
changesTitle: {
|
|
793
|
-
color: buoyColors.text,
|
|
794
|
-
fontSize: 14,
|
|
795
|
-
fontWeight: "700"
|
|
745
|
+
cardSeparator: {
|
|
746
|
+
height: StyleSheet.hairlineWidth,
|
|
747
|
+
marginLeft: 51,
|
|
748
|
+
backgroundColor: tm.color.separator
|
|
796
749
|
},
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
},
|
|
802
|
-
selectLinks: {
|
|
750
|
+
// ── Selection card ──────────────────────────────────────────────────────
|
|
751
|
+
|
|
752
|
+
// ── The "won't apply" heads-up under the card ───────────────────────────
|
|
753
|
+
warnNote: {
|
|
803
754
|
flexDirection: "row",
|
|
804
|
-
alignItems: "
|
|
805
|
-
gap:
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
color: buoyColors.textMuted,
|
|
809
|
-
fontSize: 12
|
|
755
|
+
alignItems: "flex-start",
|
|
756
|
+
gap: 6,
|
|
757
|
+
paddingHorizontal: tm.gutter + tm.textInset,
|
|
758
|
+
paddingTop: 8
|
|
810
759
|
},
|
|
811
|
-
|
|
812
|
-
|
|
760
|
+
warnNoteText: {
|
|
761
|
+
flex: 1,
|
|
762
|
+
color: tm.color.labelSecondary,
|
|
763
|
+
fontSize: tm.font.footnote,
|
|
764
|
+
lineHeight: tm.font.footnote * 1.35
|
|
765
|
+
},
|
|
766
|
+
// ── Count pills on a source row ─────────────────────────────────────────
|
|
767
|
+
countPill: {
|
|
768
|
+
borderRadius: tm.radius.pill,
|
|
769
|
+
paddingHorizontal: 9,
|
|
770
|
+
paddingVertical: 3
|
|
771
|
+
},
|
|
772
|
+
countPillText: {
|
|
773
|
+
fontFamily: TM_MONO,
|
|
774
|
+
fontSize: tm.font.footnote,
|
|
813
775
|
fontWeight: "600"
|
|
814
776
|
},
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
borderColor:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
paddingHorizontal: 10,
|
|
825
|
-
paddingVertical: 5,
|
|
826
|
-
maxWidth: "60%"
|
|
827
|
-
},
|
|
828
|
-
changesChipText: {
|
|
829
|
-
color: buoyColors.textSecondary,
|
|
830
|
-
fontSize: 11
|
|
777
|
+
// ── Include checkbox ────────────────────────────────────────────────────
|
|
778
|
+
checkbox: {
|
|
779
|
+
width: 23,
|
|
780
|
+
height: 23,
|
|
781
|
+
borderRadius: tm.radius.pill,
|
|
782
|
+
borderWidth: 1.7,
|
|
783
|
+
borderColor: tm.color.labelTertiary,
|
|
784
|
+
alignItems: "center",
|
|
785
|
+
justifyContent: "center"
|
|
831
786
|
},
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
marginTop: 6
|
|
787
|
+
checkboxChecked: {
|
|
788
|
+
borderColor: tm.color.accent,
|
|
789
|
+
backgroundColor: tm.color.accent
|
|
836
790
|
},
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
marginTop: 10,
|
|
843
|
-
marginBottom: 6
|
|
791
|
+
checkboxGlyph: {
|
|
792
|
+
color: tm.color.onAccent,
|
|
793
|
+
fontSize: 13,
|
|
794
|
+
fontWeight: "700",
|
|
795
|
+
lineHeight: 15
|
|
844
796
|
},
|
|
845
|
-
|
|
846
|
-
|
|
797
|
+
// ── Route row ───────────────────────────────────────────────────────────
|
|
798
|
+
routeRow: {
|
|
847
799
|
flexDirection: "row",
|
|
848
800
|
alignItems: "center",
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
borderRadius: 10,
|
|
854
|
-
paddingHorizontal: 10
|
|
801
|
+
minHeight: 58,
|
|
802
|
+
paddingHorizontal: tm.textInset,
|
|
803
|
+
paddingVertical: 9,
|
|
804
|
+
gap: 12
|
|
855
805
|
},
|
|
856
|
-
|
|
806
|
+
routeText: {
|
|
857
807
|
flex: 1,
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
paddingVertical: 8
|
|
861
|
-
},
|
|
862
|
-
collapseAllButton: {
|
|
863
|
-
width: 36,
|
|
864
|
-
height: 36,
|
|
865
|
-
borderRadius: 10,
|
|
866
|
-
backgroundColor: buoyColors.card,
|
|
867
|
-
borderColor: buoyColors.border,
|
|
868
|
-
borderWidth: 1,
|
|
869
|
-
alignItems: "center",
|
|
870
|
-
justifyContent: "center"
|
|
871
|
-
},
|
|
872
|
-
// Checkbox
|
|
873
|
-
checkbox: {
|
|
874
|
-
width: 18,
|
|
875
|
-
height: 18,
|
|
876
|
-
borderRadius: 4,
|
|
877
|
-
borderWidth: 1.5,
|
|
878
|
-
borderColor: buoyColors.textMuted,
|
|
879
|
-
alignItems: "center",
|
|
880
|
-
justifyContent: "center"
|
|
808
|
+
minWidth: 0,
|
|
809
|
+
gap: 2
|
|
881
810
|
},
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
811
|
+
routeTitle: {
|
|
812
|
+
color: tm.color.label,
|
|
813
|
+
fontSize: tm.font.row,
|
|
814
|
+
fontWeight: "600",
|
|
815
|
+
letterSpacing: tm.tracking.row
|
|
885
816
|
},
|
|
886
|
-
|
|
887
|
-
color:
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
lineHeight: 13
|
|
817
|
+
routeHref: {
|
|
818
|
+
color: tm.color.labelSecondary,
|
|
819
|
+
fontFamily: TM_MONO,
|
|
820
|
+
fontSize: tm.font.mono
|
|
891
821
|
},
|
|
892
|
-
// Source
|
|
822
|
+
// ── Source header row (first row of its card) ───────────────────────────
|
|
893
823
|
sourceCard: {
|
|
894
824
|
flexDirection: "row",
|
|
895
825
|
alignItems: "center",
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
paddingHorizontal: 12,
|
|
901
|
-
paddingVertical: 10,
|
|
902
|
-
marginTop: 8,
|
|
903
|
-
gap: 10
|
|
826
|
+
minHeight: 58,
|
|
827
|
+
paddingHorizontal: tm.textInset,
|
|
828
|
+
paddingVertical: 9,
|
|
829
|
+
gap: 12
|
|
904
830
|
},
|
|
905
831
|
sourceCardTouch: {
|
|
906
832
|
flex: 1,
|
|
@@ -909,67 +835,51 @@ const styles = StyleSheet.create({
|
|
|
909
835
|
gap: 12,
|
|
910
836
|
minWidth: 0
|
|
911
837
|
},
|
|
912
|
-
sourceIconTile: {
|
|
913
|
-
width: 36,
|
|
914
|
-
height: 36,
|
|
915
|
-
borderRadius: 10,
|
|
916
|
-
backgroundColor: buoyColors.hover,
|
|
917
|
-
alignItems: "center",
|
|
918
|
-
justifyContent: "center"
|
|
919
|
-
},
|
|
920
838
|
sourceNameBlock: {
|
|
921
839
|
flex: 1,
|
|
922
840
|
minWidth: 0,
|
|
923
841
|
gap: 2
|
|
924
842
|
},
|
|
925
843
|
sourceName: {
|
|
926
|
-
color:
|
|
927
|
-
fontSize:
|
|
928
|
-
fontWeight: "
|
|
844
|
+
color: tm.color.label,
|
|
845
|
+
fontSize: tm.font.row,
|
|
846
|
+
fontWeight: "600",
|
|
847
|
+
letterSpacing: tm.tracking.row
|
|
929
848
|
},
|
|
930
849
|
sourceSummary: {
|
|
931
|
-
color:
|
|
932
|
-
fontSize:
|
|
850
|
+
color: tm.color.labelSecondary,
|
|
851
|
+
fontSize: tm.font.footnote,
|
|
933
852
|
fontVariant: ["tabular-nums"]
|
|
934
853
|
},
|
|
935
854
|
sourceBadges: {
|
|
936
855
|
flexDirection: "row",
|
|
937
|
-
gap:
|
|
938
|
-
},
|
|
939
|
-
sourceBadge: {
|
|
940
|
-
fontSize: 13,
|
|
941
|
-
fontWeight: "700",
|
|
942
|
-
fontVariant: ["tabular-nums"]
|
|
856
|
+
gap: 6
|
|
943
857
|
},
|
|
944
858
|
noteRow: {
|
|
945
|
-
color:
|
|
946
|
-
fontSize:
|
|
947
|
-
|
|
948
|
-
|
|
859
|
+
color: tm.color.labelTertiary,
|
|
860
|
+
fontSize: tm.font.footnote,
|
|
861
|
+
lineHeight: tm.font.footnote * 1.35,
|
|
862
|
+
paddingHorizontal: tm.textInset,
|
|
863
|
+
paddingVertical: 9
|
|
949
864
|
},
|
|
950
|
-
// Item rows (
|
|
865
|
+
// ── Item rows (further rows of the same card) ───────────────────────────
|
|
951
866
|
itemRow: {
|
|
952
867
|
flexDirection: "row",
|
|
953
868
|
alignItems: "center",
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
paddingHorizontal: 10,
|
|
959
|
-
paddingVertical: 9,
|
|
960
|
-
marginTop: 6,
|
|
961
|
-
marginLeft: 14,
|
|
962
|
-
gap: 10
|
|
869
|
+
paddingLeft: tm.textInset,
|
|
870
|
+
paddingRight: tm.textInset,
|
|
871
|
+
paddingVertical: 10,
|
|
872
|
+
gap: 12
|
|
963
873
|
},
|
|
964
874
|
itemBodyTouch: {
|
|
965
875
|
flex: 1,
|
|
966
876
|
flexDirection: "row",
|
|
967
877
|
alignItems: "center",
|
|
968
|
-
gap:
|
|
878
|
+
gap: 10,
|
|
969
879
|
minWidth: 0
|
|
970
880
|
},
|
|
971
881
|
itemGlyph: {
|
|
972
|
-
fontSize:
|
|
882
|
+
fontSize: 14,
|
|
973
883
|
fontWeight: "700",
|
|
974
884
|
width: 14,
|
|
975
885
|
textAlign: "center"
|
|
@@ -979,72 +889,76 @@ const styles = StyleSheet.create({
|
|
|
979
889
|
minWidth: 0
|
|
980
890
|
},
|
|
981
891
|
itemLabel: {
|
|
982
|
-
color:
|
|
983
|
-
fontSize:
|
|
892
|
+
color: tm.color.label,
|
|
893
|
+
fontSize: tm.font.control,
|
|
894
|
+
letterSpacing: tm.tracking.control
|
|
984
895
|
},
|
|
985
896
|
itemPreview: {
|
|
986
|
-
color:
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
897
|
+
color: tm.color.labelSecondary,
|
|
898
|
+
fontFamily: TM_MONO,
|
|
899
|
+
fontSize: tm.font.mono,
|
|
900
|
+
marginTop: 3
|
|
990
901
|
},
|
|
991
902
|
itemPreviewRemoved: {
|
|
992
903
|
textDecorationLine: "line-through"
|
|
993
904
|
},
|
|
994
905
|
itemReason: {
|
|
995
|
-
color:
|
|
996
|
-
fontSize:
|
|
997
|
-
marginTop:
|
|
906
|
+
color: tm.color.warning,
|
|
907
|
+
fontSize: tm.font.mono,
|
|
908
|
+
marginTop: 3
|
|
998
909
|
},
|
|
999
910
|
itemExcluded: {
|
|
1000
|
-
color:
|
|
1001
|
-
fontSize:
|
|
1002
|
-
marginTop:
|
|
911
|
+
color: tm.color.labelTertiary,
|
|
912
|
+
fontSize: tm.font.mono,
|
|
913
|
+
marginTop: 3,
|
|
1003
914
|
fontStyle: "italic"
|
|
1004
915
|
},
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
fontSize: 16
|
|
916
|
+
buildingSpinner: {
|
|
917
|
+
marginTop: 28
|
|
1008
918
|
},
|
|
1009
919
|
emptyText: {
|
|
1010
|
-
color:
|
|
1011
|
-
fontSize:
|
|
920
|
+
color: tm.color.labelSecondary,
|
|
921
|
+
fontSize: tm.font.footnote,
|
|
1012
922
|
textAlign: "center",
|
|
1013
|
-
marginTop:
|
|
923
|
+
marginTop: 28
|
|
1014
924
|
},
|
|
1015
|
-
//
|
|
925
|
+
// ── Sticky restore CTA ──────────────────────────────────────────────────
|
|
1016
926
|
footer: {
|
|
1017
|
-
paddingHorizontal:
|
|
1018
|
-
paddingTop:
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
backgroundColor: buoyColors.base
|
|
927
|
+
paddingHorizontal: tm.gutter,
|
|
928
|
+
paddingTop: 10,
|
|
929
|
+
gap: 8,
|
|
930
|
+
borderTopColor: tm.color.separator,
|
|
931
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
932
|
+
backgroundColor: tm.color.sheet
|
|
1024
933
|
},
|
|
1025
934
|
cta: {
|
|
1026
935
|
flexDirection: "row",
|
|
1027
936
|
alignItems: "center",
|
|
1028
937
|
justifyContent: "center",
|
|
1029
938
|
gap: 8,
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
939
|
+
height: 50,
|
|
940
|
+
backgroundColor: tm.color.accent,
|
|
941
|
+
borderRadius: tm.radius.group
|
|
1033
942
|
},
|
|
1034
943
|
ctaDanger: {
|
|
1035
|
-
backgroundColor:
|
|
944
|
+
backgroundColor: tm.color.danger
|
|
945
|
+
},
|
|
946
|
+
ctaTextDanger: {
|
|
947
|
+
color: tm.color.onDanger
|
|
1036
948
|
},
|
|
1037
949
|
ctaDisabled: {
|
|
1038
950
|
opacity: 0.4
|
|
1039
951
|
},
|
|
1040
952
|
ctaText: {
|
|
1041
|
-
color:
|
|
1042
|
-
fontSize:
|
|
1043
|
-
fontWeight: "
|
|
953
|
+
color: tm.color.onAccent,
|
|
954
|
+
fontSize: tm.font.row,
|
|
955
|
+
fontWeight: "600",
|
|
956
|
+
letterSpacing: tm.tracking.row
|
|
1044
957
|
},
|
|
1045
958
|
ctaCaption: {
|
|
1046
|
-
color:
|
|
1047
|
-
fontSize:
|
|
959
|
+
color: tm.color.labelSecondary,
|
|
960
|
+
fontSize: tm.font.footnote,
|
|
961
|
+
lineHeight: tm.font.footnote * 1.35,
|
|
1048
962
|
textAlign: "center"
|
|
1049
963
|
}
|
|
1050
964
|
});
|