@buoy-gg/zustand 7.0.36 → 7.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +27 -5
  2. package/lib/commonjs/index.js +1 -105
  3. package/lib/commonjs/preset.js +1 -102
  4. package/lib/commonjs/zustand/components/ZustandActionButton.js +1 -118
  5. package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +1 -48
  6. package/lib/commonjs/zustand/components/ZustandEventFilterView.js +1 -43
  7. package/lib/commonjs/zustand/components/ZustandIcon.js +1 -18
  8. package/lib/commonjs/zustand/components/ZustandModal.js +1 -772
  9. package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +1 -117
  10. package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +1 -372
  11. package/lib/commonjs/zustand/components/ZustandStateInfoView.js +1 -488
  12. package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +1 -204
  13. package/lib/commonjs/zustand/components/ZustandStoreDetailContent.js +1 -471
  14. package/lib/commonjs/zustand/components/ZustandStoreEditorScreen.js +1 -393
  15. package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +1 -72
  16. package/lib/commonjs/zustand/sync/zustandSyncAdapter.js +1 -382
  17. package/lib/commonjs/zustand/utils/buoyZustandMiddleware.js +1 -284
  18. package/lib/commonjs/zustand/utils/editableState.js +1 -139
  19. package/lib/commonjs/zustand/utils/snapshotProvider.js +1 -209
  20. package/lib/commonjs/zustand/utils/testIDs.js +1 -82
  21. package/lib/commonjs/zustand/utils/zustandStateStore.js +1 -375
  22. package/lib/module/index.js +1 -90
  23. package/lib/module/preset.js +1 -98
  24. package/lib/module/zustand/components/ZustandActionButton.js +1 -114
  25. package/lib/module/zustand/components/ZustandDetailViewToggle.js +1 -43
  26. package/lib/module/zustand/components/ZustandEventFilterView.js +1 -39
  27. package/lib/module/zustand/components/ZustandIcon.js +1 -11
  28. package/lib/module/zustand/components/ZustandModal.js +1 -767
  29. package/lib/module/zustand/components/ZustandStateChangeItem.js +1 -113
  30. package/lib/module/zustand/components/ZustandStateDetailContent.js +1 -368
  31. package/lib/module/zustand/components/ZustandStateInfoView.js +1 -483
  32. package/lib/module/zustand/components/ZustandStoreBrowser.js +1 -200
  33. package/lib/module/zustand/components/ZustandStoreDetailContent.js +1 -469
  34. package/lib/module/zustand/components/ZustandStoreEditorScreen.js +1 -389
  35. package/lib/module/zustand/hooks/useZustandStateChanges.js +1 -68
  36. package/lib/module/zustand/sync/zustandSyncAdapter.js +1 -380
  37. package/lib/module/zustand/utils/buoyZustandMiddleware.js +1 -278
  38. package/lib/module/zustand/utils/editableState.js +1 -131
  39. package/lib/module/zustand/utils/snapshotProvider.js +1 -209
  40. package/lib/module/zustand/utils/testIDs.js +1 -74
  41. package/lib/module/zustand/utils/zustandStateStore.js +1 -371
  42. package/package.json +4 -4
  43. package/lib/typescript/index.d.ts.map +0 -1
  44. package/lib/typescript/preset.d.ts.map +0 -1
  45. package/lib/typescript/zustand/components/ZustandActionButton.d.ts.map +0 -1
  46. package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts.map +0 -1
  47. package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts.map +0 -1
  48. package/lib/typescript/zustand/components/ZustandIcon.d.ts.map +0 -1
  49. package/lib/typescript/zustand/components/ZustandModal.d.ts.map +0 -1
  50. package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts.map +0 -1
  51. package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts.map +0 -1
  52. package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts.map +0 -1
  53. package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts.map +0 -1
  54. package/lib/typescript/zustand/components/ZustandStoreDetailContent.d.ts.map +0 -1
  55. package/lib/typescript/zustand/components/ZustandStoreEditorScreen.d.ts.map +0 -1
  56. package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts.map +0 -1
  57. package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts.map +0 -1
  58. package/lib/typescript/zustand/types/index.d.ts.map +0 -1
  59. package/lib/typescript/zustand/utils/buoyZustandMiddleware.d.ts.map +0 -1
  60. package/lib/typescript/zustand/utils/editableState.d.ts.map +0 -1
  61. package/lib/typescript/zustand/utils/snapshotProvider.d.ts.map +0 -1
  62. package/lib/typescript/zustand/utils/testIDs.d.ts.map +0 -1
  63. package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +0 -1
@@ -1,393 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ZustandStoreEditorScreen = ZustandStoreEditorScreen;
7
- var _react = require("react");
8
- var _reactNative = require("react-native");
9
- var _sharedUi = require("@buoy-gg/shared-ui");
10
- var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
11
- var _editableState = require("../utils/editableState");
12
- var _testIDs = require("../utils/testIDs");
13
- var _jsxRuntime = require("react/jsx-runtime");
14
- /**
15
- * Editing a store, on a screen of its own — the Storage key editor's shape,
16
- * pointed at a live zustand store.
17
- *
18
- * Same reasoning as storage's editor screen: a tree, six structural actions
19
- * and a keyboard don't fit inside an expanded card, so the card offers ONE
20
- * edit button and the work happens here with the whole viewport. The tree,
21
- * dock, draft and value modal are the shared-ui editor — one implementation,
22
- * two tools.
23
- *
24
- * What is zustand-specific is the WRITE. Storage saves by replacing one key's
25
- * serialized value; a store has no such luxury — its state holds the app's
26
- * action functions, and a full `setState(draft, true)` replace would delete
27
- * them (see `editableState.ts`). So Save diffs the draft against the state it
28
- * was seeded from and writes ONE top-level merge:
29
- *
30
- * store.api.setState(changedTopLevelKeys, false)
31
- *
32
- * which by construction cannot remove a key or touch a function. The two
33
- * edits a merge cannot express — removing or renaming a top-level key — are
34
- * disabled in the dock (`rootEntriesLocked`) and refused at save if they
35
- * arrive through the raw-JSON escape hatch.
36
- *
37
- * On a desktop mirror the same line goes over the wire: the mirrored store's
38
- * `api.setState` forwards to the device's sync adapter (the time-travel
39
- * path). That write has no ack, so a remote save resolves optimistically and
40
- * the next device snapshot is the confirmation.
41
- */
42
-
43
- const EDITOR_ID = `${_testIDs.ZUSTAND_TEST_ID}-editor`;
44
- function ZustandStoreEditorScreen({
45
- store,
46
- onClose,
47
- applySafeAreaInset
48
- }) {
49
- const insets = (0, _sharedUi.useSafeAreaInsets)();
50
- const bottomInset = applySafeAreaInset ? insets.bottom : 0;
51
-
52
- // Seeded ONCE, when the editor opens — the draft is diffed against this at
53
- // save, so the partial carries exactly what the user changed and a key the
54
- // app wrote meanwhile (untouched here) keeps the app's value.
55
- const [seed] = (0, _react.useState)(() => (0, _editableState.editableStateOf)(store.api.getState()));
56
- const save = (0, _react.useCallback)(async draft => {
57
- if (seed.data === null) throw new Error("Store is not editable.");
58
- const partial = (0, _editableState.computeMergePartial)(seed.data, draft);
59
- // A merge, never a replace — the entire reason this editor can exist.
60
- // Empty partial = the draft circled back to the seed; nothing to write.
61
- if (Object.keys(partial).length > 0) {
62
- store.api.setState(partial, false);
63
- }
64
- }, [seed, store]);
65
- const editor = (0, _dataViewer.useTreeEditor)({
66
- value: seed.data,
67
- save,
68
- onClose,
69
- confirmDiscard: choices => _reactNative.Alert.alert("Unsaved changes", `"${store.name}" has edits that haven't been written to the store.`, [{
70
- text: "Keep editing",
71
- style: "cancel"
72
- }, {
73
- text: "Discard",
74
- style: "destructive",
75
- onPress: choices.discard
76
- }, {
77
- text: "Save",
78
- onPress: choices.save
79
- }])
80
- });
81
- const {
82
- draft,
83
- selection,
84
- valuePath
85
- } = editor;
86
- const keyCount = (0, _react.useMemo)(() => {
87
- const v = draft.value;
88
- return v !== null && typeof v === "object" ? Object.keys(v).length : 0;
89
- }, [draft.value]);
90
-
91
- // The card's gating means this can't normally be reached un-editable, but a
92
- // remote mirror can theoretically drop hydration between tap and mount.
93
- if (seed.data === null) {
94
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
95
- style: styles.screen,
96
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolBackground, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
97
- style: styles.blockedText,
98
- children: seed.blockedReason
99
- })]
100
- });
101
- }
102
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
103
- style: styles.screen,
104
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolBackground, {}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
105
- style: styles.header,
106
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
107
- onPress: editor.requestClose,
108
- testID: `${EDITOR_ID}-close`,
109
- accessibilityRole: "button",
110
- accessibilityLabel: "Close editor",
111
- style: styles.headerButton,
112
- hitSlop: {
113
- top: 8,
114
- bottom: 8,
115
- left: 8,
116
- right: 8
117
- },
118
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
119
- size: 16,
120
- color: _sharedUi.night.color.textSecondary
121
- })
122
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
123
- style: styles.headerTitle,
124
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text
125
- // Which store this editor is pointed at — opening it on the wrong
126
- // one looks exactly like the right one until a write lands
127
- // somewhere unexpected.
128
- , {
129
- testID: `${EDITOR_ID}-store`,
130
- style: styles.headerKey,
131
- numberOfLines: 1,
132
- children: store.name
133
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
134
- style: [styles.headerState, draft.isDirty && styles.headerDirty],
135
- children: draft.isSaving ? "Saving…" : draft.isDirty ? "Draft · unsaved" : "In sync with store"
136
- })]
137
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
138
- onPress: editor.openRawEditor,
139
- testID: `${EDITOR_ID}-raw`,
140
- accessibilityRole: "button",
141
- accessibilityLabel: "Edit raw JSON",
142
- style: styles.headerButton,
143
- hitSlop: {
144
- top: 8,
145
- bottom: 8,
146
- left: 8,
147
- right: 8
148
- },
149
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileJson, {
150
- size: 16,
151
- color: _sharedUi.night.color.accent
152
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
153
- style: styles.headerRawText,
154
- children: "Raw"
155
- })]
156
- })]
157
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
158
- style: styles.metaRow,
159
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PillBadge, {
160
- color: _sharedUi.buoyColors.info,
161
- icon: store.isPersisted ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
162
- size: 9,
163
- color: _sharedUi.buoyColors.info
164
- }) : undefined,
165
- children: store.isPersisted ? store.persistName || "persisted" : "in-memory"
166
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
167
- style: styles.metaText,
168
- children: [`object · ${keyCount} key${keyCount === 1 ? "" : "s"}`, seed.fnPaths.length > 0 ? ` · ${seed.fnPaths.length} action${seed.fnPaths.length === 1 ? "" : "s"} untouched` : ""]
169
- })]
170
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
171
- testID: `${EDITOR_ID}-scroll`,
172
- style: styles.treeScroll,
173
- contentContainerStyle: styles.treeContent,
174
- keyboardShouldPersistTaps: "handled",
175
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
176
- style: styles.treeCard,
177
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
178
- data: draft.value,
179
- title: "",
180
- rawMode: true,
181
- initialExpanded: true
182
- // Type filtering is a READING aid: the filtered view is a
183
- // projection whose row paths don't address the real document, so
184
- // the explorer stops reporting selection while one is active. An
185
- // editor whose actions silently go dead isn't worth the filter.
186
- ,
187
- showTypeFilter: false,
188
- onSelectionChange: editor.setSelection,
189
- selectPath: draft.selectPath
190
- // Its own handle root: the card that opened this editor is still
191
- // mounted underneath, rendering the same value in its preview tree.
192
- ,
193
- testIDPrefix: `${EDITOR_ID}-row`,
194
- onRequestInlineEdit: sel => editor.openValueEditor(sel.path)
195
- })
196
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
197
- style: styles.treeHint,
198
- children: "Tap a row to select it \xB7 double-tap to edit its value \xB7 saves merge into the live store, so actions and untouched keys survive"
199
- })]
200
- }), draft.error ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
201
- style: styles.error,
202
- numberOfLines: 3,
203
- testID: `${EDITOR_ID}-error`,
204
- children: draft.error
205
- }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
206
- style: [styles.footer, {
207
- paddingBottom: bottomInset
208
- }],
209
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataTreeActionDock, {
210
- root: draft.value,
211
- selection: selection,
212
- onEdit: draft.apply,
213
- onSelectPath: draft.selectAt
214
- // Booleans never reach this — the dock applies their flip directly.
215
- ,
216
- onEditValue: () => editor.openValueEditor(selection?.path ?? [])
217
- // A merge write can rewrite a top-level key but never delete one, so
218
- // Remove is disabled at depth 1 — see `editableState.ts`.
219
- ,
220
- rootEntriesLocked: true
221
- }), draft.isDirty ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
222
- style: styles.commitRow,
223
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
224
- onPress: draft.discard,
225
- disabled: draft.isSaving,
226
- testID: `${EDITOR_ID}-discard`,
227
- accessibilityRole: "button",
228
- accessibilityLabel: "Discard changes",
229
- style: styles.discardButton,
230
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
231
- style: styles.discardText,
232
- children: "Discard"
233
- })
234
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
235
- onPress: editor.saveAndClose,
236
- disabled: draft.isSaving,
237
- testID: `${EDITOR_ID}-save`,
238
- accessibilityRole: "button",
239
- accessibilityLabel: "Save changes",
240
- style: [styles.saveButton, draft.isSaving && styles.saveDisabled],
241
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
242
- style: styles.saveText,
243
- children: draft.isSaving ? "Saving…" : "Save changes"
244
- })
245
- })]
246
- }) : null]
247
- }), valuePath ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.ValueEditModal, {
248
- path: valuePath,
249
- value: (0, _dataViewer.getAtPath)(draft.value, valuePath),
250
- onSave: editor.applyValue,
251
- onCancel: editor.closeValueEditor
252
- // It only reaches the draft here — the single write happens on Save.
253
- ,
254
- saveLabel: "Apply",
255
- testIDPrefix: `${_testIDs.ZUSTAND_TEST_ID}-value`
256
- }) : null]
257
- });
258
- }
259
- const styles = _reactNative.StyleSheet.create({
260
- screen: {
261
- flex: 1,
262
- // Opaque on purpose: transparency here showed the whole store list
263
- // through the editor, not just the sheet's backdrop. The screen paints
264
- // its own ToolBackground instead — see the mount site above.
265
- backgroundColor: _sharedUi.night.color.bg
266
- },
267
- blockedText: {
268
- color: _sharedUi.night.color.textTertiary,
269
- fontSize: 12,
270
- padding: 20,
271
- textAlign: "center",
272
- lineHeight: 18
273
- },
274
- header: {
275
- flexDirection: "row",
276
- alignItems: "center",
277
- gap: 8,
278
- paddingHorizontal: 10,
279
- paddingVertical: 8,
280
- borderBottomWidth: 1,
281
- borderBottomColor: _sharedUi.night.color.border
282
- },
283
- headerButton: {
284
- flexDirection: "row",
285
- alignItems: "center",
286
- gap: 4,
287
- paddingVertical: 6,
288
- paddingHorizontal: 4
289
- },
290
- headerRawText: {
291
- color: _sharedUi.night.color.accent,
292
- fontSize: 12,
293
- fontWeight: "700"
294
- },
295
- headerTitle: {
296
- flex: 1,
297
- alignItems: "center",
298
- gap: 2
299
- },
300
- headerKey: {
301
- color: _sharedUi.night.color.text,
302
- fontSize: 13,
303
- fontWeight: "600",
304
- fontFamily: _sharedUi.monoFont
305
- },
306
- headerState: {
307
- color: _sharedUi.night.color.textTertiary,
308
- fontSize: 10
309
- },
310
- headerDirty: {
311
- color: _sharedUi.night.color.warning
312
- },
313
- metaRow: {
314
- flexDirection: "row",
315
- alignItems: "center",
316
- gap: 8,
317
- paddingHorizontal: 12,
318
- paddingVertical: 8
319
- },
320
- metaText: {
321
- color: _sharedUi.night.color.textTertiary,
322
- fontSize: 11,
323
- fontFamily: _sharedUi.monoFont
324
- },
325
- treeScroll: {
326
- flex: 1
327
- },
328
- treeContent: {
329
- paddingHorizontal: 12,
330
- paddingBottom: 16
331
- },
332
- treeCard: {
333
- backgroundColor: _sharedUi.night.color.surface,
334
- borderRadius: 10,
335
- borderWidth: 1,
336
- borderColor: _sharedUi.night.color.border,
337
- padding: 6
338
- },
339
- treeHint: {
340
- color: _sharedUi.night.color.textTertiary,
341
- fontSize: 10,
342
- paddingTop: 8,
343
- paddingHorizontal: 4
344
- },
345
- error: {
346
- color: _sharedUi.night.color.danger,
347
- fontSize: 11,
348
- fontFamily: _sharedUi.monoFont,
349
- paddingHorizontal: 12,
350
- paddingBottom: 6
351
- },
352
- footer: {
353
- backgroundColor: _sharedUi.night.color.surface
354
- },
355
- commitRow: {
356
- flexDirection: "row",
357
- gap: 8,
358
- paddingHorizontal: 12,
359
- paddingVertical: 10,
360
- borderTopWidth: 1,
361
- borderTopColor: _sharedUi.night.color.border
362
- },
363
- discardButton: {
364
- flex: 1,
365
- minHeight: 44,
366
- alignItems: "center",
367
- justifyContent: "center",
368
- borderRadius: 10,
369
- borderWidth: 1,
370
- borderColor: _sharedUi.night.color.border
371
- },
372
- discardText: {
373
- color: _sharedUi.night.color.textSecondary,
374
- fontSize: 14,
375
- fontWeight: "600"
376
- },
377
- saveButton: {
378
- flex: 1.4,
379
- minHeight: 44,
380
- alignItems: "center",
381
- justifyContent: "center",
382
- borderRadius: 10,
383
- backgroundColor: _sharedUi.night.color.accent
384
- },
385
- saveDisabled: {
386
- opacity: 0.5
387
- },
388
- saveText: {
389
- color: _sharedUi.night.color.onAccent,
390
- fontSize: 15,
391
- fontWeight: "700"
392
- }
393
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ZustandStoreEditorScreen=ZustandStoreEditorScreen;var _react=require("react"),_reactNative=require("react-native"),_sharedUi=require("@buoy-gg/shared-ui"),_dataViewer=require("@buoy-gg/shared-ui/dataViewer"),_editableState=require("../utils/editableState"),_testIDs=require("../utils/testIDs"),_jsxRuntime=require("react/jsx-runtime");const EDITOR_ID=`${_testIDs.ZUSTAND_TEST_ID}-editor`;function ZustandStoreEditorScreen({store:e,onClose:t,applySafeAreaInset:i}){const r=(0,_sharedUi.useSafeAreaInsets)(),s=i?r.bottom:0,[a]=(0,_react.useState)(()=>(0,_editableState.editableStateOf)(e.api.getState())),o=(0,_react.useCallback)(async t=>{if(null===a.data)throw new Error("Store is not editable.");const i=(0,_editableState.computeMergePartial)(a.data,t);Object.keys(i).length>0&&e.api.setState(i,!1)},[a,e]),n=(0,_dataViewer.useTreeEditor)({value:a.data,save:o,onClose:t,confirmDiscard:t=>_reactNative.Alert.alert("Unsaved changes",`"${e.name}" has edits that haven't been written to the store.`,[{text:"Keep editing",style:"cancel"},{text:"Discard",style:"destructive",onPress:t.discard},{text:"Save",onPress:t.save}])}),{draft:l,selection:d,valuePath:c}=n,h=(0,_react.useMemo)(()=>{const e=l.value;return null!==e&&"object"==typeof e?Object.keys(e).length:0},[l.value]);return null===a.data?(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.screen,children:[(0,_jsxRuntime.jsx)(_sharedUi.ToolBackground,{}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.blockedText,children:a.blockedReason})]}):(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.screen,children:[(0,_jsxRuntime.jsx)(_sharedUi.ToolBackground,{}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.header,children:[(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:n.requestClose,testID:`${EDITOR_ID}-close`,accessibilityRole:"button",accessibilityLabel:"Close editor",style:styles.headerButton,hitSlop:{top:8,bottom:8,left:8,right:8},children:(0,_jsxRuntime.jsx)(_sharedUi.X,{size:16,color:_sharedUi.night.color.textSecondary})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.headerTitle,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:`${EDITOR_ID}-store`,style:styles.headerKey,numberOfLines:1,children:e.name}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.headerState,l.isDirty&&styles.headerDirty],children:l.isSaving?"Saving…":l.isDirty?"Draft · unsaved":"In sync with store"})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{onPress:n.openRawEditor,testID:`${EDITOR_ID}-raw`,accessibilityRole:"button",accessibilityLabel:"Edit raw JSON",style:styles.headerButton,hitSlop:{top:8,bottom:8,left:8,right:8},children:[(0,_jsxRuntime.jsx)(_sharedUi.FileJson,{size:16,color:_sharedUi.night.color.accent}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.headerRawText,children:"Raw"})]})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.metaRow,children:[(0,_jsxRuntime.jsx)(_sharedUi.PillBadge,{color:_sharedUi.buoyColors.info,icon:e.isPersisted?(0,_jsxRuntime.jsx)(_sharedUi.Database,{size:9,color:_sharedUi.buoyColors.info}):void 0,children:e.isPersisted?e.persistName||"persisted":"in-memory"}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.metaText,children:[`object · ${h} key${1===h?"":"s"}`,a.fnPaths.length>0?` · ${a.fnPaths.length} action${1===a.fnPaths.length?"":"s"} untouched`:""]})]}),(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{testID:`${EDITOR_ID}-scroll`,style:styles.treeScroll,contentContainerStyle:styles.treeContent,keyboardShouldPersistTaps:"handled",children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.treeCard,children:(0,_jsxRuntime.jsx)(_dataViewer.DataViewer,{data:l.value,title:"",rawMode:!0,initialExpanded:!0,showTypeFilter:!1,onSelectionChange:n.setSelection,selectPath:l.selectPath,testIDPrefix:`${EDITOR_ID}-row`,onRequestInlineEdit:e=>n.openValueEditor(e.path)})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.treeHint,children:"Tap a row to select it · double-tap to edit its value · saves merge into the live store, so actions and untouched keys survive"})]}),l.error?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.error,numberOfLines:3,testID:`${EDITOR_ID}-error`,children:l.error}):null,(0,_jsxRuntime.jsxs)(_reactNative.View,{style:[styles.footer,{paddingBottom:s}],children:[(0,_jsxRuntime.jsx)(_dataViewer.DataTreeActionDock,{root:l.value,selection:d,onEdit:l.apply,onSelectPath:l.selectAt,onEditValue:()=>n.openValueEditor(d?.path??[]),rootEntriesLocked:!0}),l.isDirty?(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.commitRow,children:[(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:l.discard,disabled:l.isSaving,testID:`${EDITOR_ID}-discard`,accessibilityRole:"button",accessibilityLabel:"Discard changes",style:styles.discardButton,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.discardText,children:"Discard"})}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:n.saveAndClose,disabled:l.isSaving,testID:`${EDITOR_ID}-save`,accessibilityRole:"button",accessibilityLabel:"Save changes",style:[styles.saveButton,l.isSaving&&styles.saveDisabled],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.saveText,children:l.isSaving?"Saving…":"Save changes"})})]}):null]}),c?(0,_jsxRuntime.jsx)(_dataViewer.ValueEditModal,{path:c,value:(0,_dataViewer.getAtPath)(l.value,c),onSave:n.applyValue,onCancel:n.closeValueEditor,saveLabel:"Apply",testIDPrefix:`${_testIDs.ZUSTAND_TEST_ID}-value`}):null]})}const styles=_reactNative.StyleSheet.create({screen:{flex:1,backgroundColor:_sharedUi.night.color.bg},blockedText:{color:_sharedUi.night.color.textTertiary,fontSize:12,padding:20,textAlign:"center",lineHeight:18},header:{flexDirection:"row",alignItems:"center",gap:8,paddingHorizontal:10,paddingVertical:8,borderBottomWidth:1,borderBottomColor:_sharedUi.night.color.border},headerButton:{flexDirection:"row",alignItems:"center",gap:4,paddingVertical:6,paddingHorizontal:4},headerRawText:{color:_sharedUi.night.color.accent,fontSize:12,fontWeight:"700"},headerTitle:{flex:1,alignItems:"center",gap:2},headerKey:{color:_sharedUi.night.color.text,fontSize:13,fontWeight:"600",fontFamily:_sharedUi.monoFont},headerState:{color:_sharedUi.night.color.textTertiary,fontSize:10},headerDirty:{color:_sharedUi.night.color.warning},metaRow:{flexDirection:"row",alignItems:"center",gap:8,paddingHorizontal:12,paddingVertical:8},metaText:{color:_sharedUi.night.color.textTertiary,fontSize:11,fontFamily:_sharedUi.monoFont},treeScroll:{flex:1},treeContent:{paddingHorizontal:12,paddingBottom:16},treeCard:{backgroundColor:_sharedUi.night.color.surface,borderRadius:10,borderWidth:1,borderColor:_sharedUi.night.color.border,padding:6},treeHint:{color:_sharedUi.night.color.textTertiary,fontSize:10,paddingTop:8,paddingHorizontal:4},error:{color:_sharedUi.night.color.danger,fontSize:11,fontFamily:_sharedUi.monoFont,paddingHorizontal:12,paddingBottom:6},footer:{backgroundColor:_sharedUi.night.color.surface},commitRow:{flexDirection:"row",gap:8,paddingHorizontal:12,paddingVertical:10,borderTopWidth:1,borderTopColor:_sharedUi.night.color.border},discardButton:{flex:1,minHeight:44,alignItems:"center",justifyContent:"center",borderRadius:10,borderWidth:1,borderColor:_sharedUi.night.color.border},discardText:{color:_sharedUi.night.color.textSecondary,fontSize:14,fontWeight:"600"},saveButton:{flex:1.4,minHeight:44,alignItems:"center",justifyContent:"center",borderRadius:10,backgroundColor:_sharedUi.night.color.accent},saveDisabled:{opacity:.5},saveText:{color:_sharedUi.night.color.onAccent,fontSize:15,fontWeight:"700"}});
@@ -1,72 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.useZustandStateChanges = useZustandStateChanges;
7
- var _react = require("react");
8
- var _sharedUi = require("@buoy-gg/shared-ui");
9
- var _zustandStateStore = require("../utils/zustandStateStore");
10
- /**
11
- * Hook for consuming Zustand state changes from the store
12
- *
13
- * The subscribe / filter / pause / clear plumbing is shared `useChangeLog`;
14
- * this file owns the zustand-specific filter, stats and store registry.
15
- */
16
-
17
- function filterChanges(changes, filter) {
18
- let filtered = changes;
19
- if (filter.searchText) {
20
- const search = filter.searchText.toLowerCase();
21
- filtered = filtered.filter(c => c.storeName.toLowerCase().includes(search) || c.partialPreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
22
- }
23
- if (filter.storeNames && filter.storeNames.length > 0) {
24
- filtered = filtered.filter(c => filter.storeNames.includes(c.storeName));
25
- }
26
- if (filter.onlyWithChanges) {
27
- filtered = filtered.filter(c => c.hasStateChange);
28
- }
29
- return filtered;
30
- }
31
- const readStores = () => _zustandStateStore.zustandStateStore.getStores();
32
- const subscribeToStores = listener => _zustandStateStore.zustandStateStore.subscribeToStores(listener);
33
- function useZustandStateChanges() {
34
- const log = (0, _sharedUi.useChangeLog)(_zustandStateStore.zustandStateStore, {
35
- filterItems: filterChanges
36
- });
37
- const stores = (0, _sharedUi.useSubscribedValue)(readStores, subscribeToStores);
38
-
39
- // Get stats — derive directly from state
40
- const stats = (0, _react.useMemo)(() => {
41
- const total = log.items.length;
42
- const withChanges = log.items.filter(c => c.hasStateChange).length;
43
- return {
44
- totalChanges: total,
45
- changesWithStateChange: withChanges,
46
- changesWithoutStateChange: total - withChanges,
47
- storeCount: stores.length,
48
- averageDuration: 0
49
- };
50
- }, [log.items, stores]);
51
-
52
- // Get unique store names
53
- const storeNames = (0, _react.useMemo)(() => {
54
- return _zustandStateStore.zustandStateStore.getUniqueStoreNames();
55
- }, [stores]);
56
- const getChangeById = (0, _react.useCallback)(id => {
57
- return _zustandStateStore.zustandStateStore.getStateChangeById(id);
58
- }, []);
59
- return {
60
- stateChanges: log.items,
61
- filteredChanges: log.filteredItems,
62
- filter: log.filter,
63
- setFilter: log.setFilter,
64
- stats,
65
- stores,
66
- clearChanges: log.clear,
67
- isEnabled: log.isEnabled,
68
- toggleCapture: log.toggleCapture,
69
- storeNames,
70
- getChangeById
71
- };
72
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useZustandStateChanges=useZustandStateChanges;var _react=require("react"),_sharedUi=require("@buoy-gg/shared-ui"),_zustandStateStore=require("../utils/zustandStateStore");function filterChanges(e,t){let a=e;if(t.searchText){const e=t.searchText.toLowerCase();a=a.filter(t=>t.storeName.toLowerCase().includes(e)||t.partialPreview.toLowerCase().includes(e)||t.changedKeys.some(t=>t.toLowerCase().includes(e)))}return t.storeNames&&t.storeNames.length>0&&(a=a.filter(e=>t.storeNames.includes(e.storeName))),t.onlyWithChanges&&(a=a.filter(e=>e.hasStateChange)),a}const readStores=()=>_zustandStateStore.zustandStateStore.getStores(),subscribeToStores=e=>_zustandStateStore.zustandStateStore.subscribeToStores(e);function useZustandStateChanges(){const e=(0,_sharedUi.useChangeLog)(_zustandStateStore.zustandStateStore,{filterItems:filterChanges}),t=(0,_sharedUi.useSubscribedValue)(readStores,subscribeToStores),a=(0,_react.useMemo)(()=>{const a=e.items.length,s=e.items.filter(e=>e.hasStateChange).length;return{totalChanges:a,changesWithStateChange:s,changesWithoutStateChange:a-s,storeCount:t.length,averageDuration:0}},[e.items,t]),s=(0,_react.useMemo)(()=>_zustandStateStore.zustandStateStore.getUniqueStoreNames(),[t]),r=(0,_react.useCallback)(e=>_zustandStateStore.zustandStateStore.getStateChangeById(e),[]);return{stateChanges:e.items,filteredChanges:e.filteredItems,filter:e.filter,setFilter:e.setFilter,stats:a,stores:t,clearChanges:_zustandStateStore.zustandStateStore.clearStateChanges.bind(_zustandStateStore.zustandStateStore),isEnabled:e.isEnabled,toggleCapture:e.toggleCapture,storeNames:s,getChangeById:r}}