@buoy-gg/jotai 7.0.41 → 7.0.43

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 (54) hide show
  1. package/README.md +11 -80
  2. package/lib/commonjs/index.js +1 -112
  3. package/lib/commonjs/jotai/components/JotaiAtomBrowser.js +1 -199
  4. package/lib/commonjs/jotai/components/JotaiAtomChangeItem.js +1 -95
  5. package/lib/commonjs/jotai/components/JotaiAtomDetailContent.js +1 -715
  6. package/lib/commonjs/jotai/components/JotaiAtomValueDetailContent.js +1 -444
  7. package/lib/commonjs/jotai/components/JotaiEventFilterView.js +1 -43
  8. package/lib/commonjs/jotai/components/JotaiIcon.js +1 -18
  9. package/lib/commonjs/jotai/components/JotaiModal.js +1 -743
  10. package/lib/commonjs/jotai/hooks/useJotaiAtomChanges.js +1 -69
  11. package/lib/commonjs/jotai/sync/jotaiSyncAdapter.js +1 -306
  12. package/lib/commonjs/jotai/types/index.js +1 -1
  13. package/lib/commonjs/jotai/utils/jotaiStateStore.js +1 -321
  14. package/lib/commonjs/jotai/utils/snapshotProvider.js +1 -215
  15. package/lib/commonjs/jotai/utils/testIDs.js +1 -90
  16. package/lib/commonjs/jotai/utils/watchAtoms.js +1 -143
  17. package/lib/commonjs/jotai/utils/writeSemantics.js +1 -47
  18. package/lib/commonjs/preset.js +1 -90
  19. package/lib/module/index.js +1 -85
  20. package/lib/module/jotai/components/JotaiAtomBrowser.js +1 -195
  21. package/lib/module/jotai/components/JotaiAtomChangeItem.js +1 -91
  22. package/lib/module/jotai/components/JotaiAtomDetailContent.js +1 -710
  23. package/lib/module/jotai/components/JotaiAtomValueDetailContent.js +1 -442
  24. package/lib/module/jotai/components/JotaiEventFilterView.js +1 -39
  25. package/lib/module/jotai/components/JotaiIcon.js +1 -11
  26. package/lib/module/jotai/components/JotaiModal.js +1 -738
  27. package/lib/module/jotai/hooks/useJotaiAtomChanges.js +1 -65
  28. package/lib/module/jotai/sync/jotaiSyncAdapter.js +1 -303
  29. package/lib/module/jotai/types/index.js +1 -1
  30. package/lib/module/jotai/utils/jotaiStateStore.js +1 -317
  31. package/lib/module/jotai/utils/snapshotProvider.js +1 -214
  32. package/lib/module/jotai/utils/testIDs.js +1 -82
  33. package/lib/module/jotai/utils/watchAtoms.js +1 -138
  34. package/lib/module/jotai/utils/writeSemantics.js +1 -43
  35. package/lib/module/preset.js +1 -87
  36. package/lib/web/index.mjs +2 -3
  37. package/package.json +4 -7
  38. package/lib/typescript/index.d.ts.map +0 -1
  39. package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts.map +0 -1
  40. package/lib/typescript/jotai/components/JotaiAtomChangeItem.d.ts.map +0 -1
  41. package/lib/typescript/jotai/components/JotaiAtomDetailContent.d.ts.map +0 -1
  42. package/lib/typescript/jotai/components/JotaiAtomValueDetailContent.d.ts.map +0 -1
  43. package/lib/typescript/jotai/components/JotaiEventFilterView.d.ts.map +0 -1
  44. package/lib/typescript/jotai/components/JotaiIcon.d.ts.map +0 -1
  45. package/lib/typescript/jotai/components/JotaiModal.d.ts.map +0 -1
  46. package/lib/typescript/jotai/hooks/useJotaiAtomChanges.d.ts.map +0 -1
  47. package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts.map +0 -1
  48. package/lib/typescript/jotai/types/index.d.ts.map +0 -1
  49. package/lib/typescript/jotai/utils/jotaiStateStore.d.ts.map +0 -1
  50. package/lib/typescript/jotai/utils/snapshotProvider.d.ts.map +0 -1
  51. package/lib/typescript/jotai/utils/testIDs.d.ts.map +0 -1
  52. package/lib/typescript/jotai/utils/watchAtoms.d.ts.map +0 -1
  53. package/lib/typescript/jotai/utils/writeSemantics.d.ts.map +0 -1
  54. package/lib/typescript/preset.d.ts.map +0 -1
@@ -1,444 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.JotaiAtomValueDetailContent = JotaiAtomValueDetailContent;
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 _jotaiStateStore = require("../utils/jotaiStateStore");
12
- var _testIDs = require("../utils/testIDs");
13
- var _jsxRuntime = require("react/jsx-runtime");
14
- /**
15
- * One atom, on a screen of its own — the React Query detail-screen shape
16
- * pointed at a live jotai atom. The jotai counterpart of zustand's
17
- * `ZustandStoreDetailContent`; tapping an atom card opens this instead of
18
- * expanding in place.
19
- *
20
- * 1. VALUE EDITOR — the shared LiveExplorer. No draft, no Save: an edit
21
- * writes into the atom as you make it and the app reacts immediately.
22
- * Typing commits on a short debounce (~300ms, flushed on blur) so a word
23
- * is one recorded change, not one per keystroke — atom writes land in
24
- * the change log and count against the free tier's cap.
25
- * 2. ATOM DETAILS — what the expanded card used to say: type, write
26
- * semantics, change count (with the road to history).
27
- * 3. VALUE EXPLORER — the read-only tree of the same value.
28
- *
29
- * THE WRITE RULE — whole-value replace, jotai's own semantics. An atom is one
30
- * value; there is no merge. However deep the edit, the writer rebuilds the
31
- * whole value (`updateNestedDataByPath`) and hands it to `store.set(atom, …)`
32
- * — so nested deletes are fine and only the ROOT itself can't be removed.
33
- *
34
- * Write-kind honesty (classified from jotai's own `defaultWrite`, see
35
- * writeSemantics.ts):
36
- * - "value" — primitive atom: `store.set` stores the edited value. Clean.
37
- * - "action" — custom write (`atomWithStorage`, `atomWithReducer`, …): the
38
- * edited value is passed to that write as its argument. For most
39
- * (`atomWithStorage`) that IS a set; for a reducer it's an action. Still
40
- * editable — the MCP's `setAtom` and Time Machine restore already write
41
- * these — but the editor says what the write really does.
42
- * - read-only derived — no setter exists; the editor section explains and
43
- * the info/explorer sections still render.
44
- */
45
-
46
- /** How long typed edits coalesce before the single write fires. */const TYPING_COMMIT_MS = 300;
47
-
48
- /** `buoy-jotai-atom-detail-…` — this screen's own handle root. */
49
- const DETAIL_ID = `${_testIDs.JOTAI_TEST_ID}-atom-detail`;
50
- function detailExplorerTestID(dataPath) {
51
- const slug = part => part.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
52
- return dataPath.length === 0 ? `${DETAIL_ID}-explorer-root` : `${DETAIL_ID}-explorer-${dataPath.map(slug).filter(Boolean).join("-")}`;
53
- }
54
-
55
- /**
56
- * Why this atom's value can't be live-edited, or null when it can.
57
- *
58
- * The path utils handle scalars, arrays, plain objects, Map and Set. What
59
- * they can't faithfully rebuild is a class instance (a leaf edit would spread
60
- * it into a plain object, dropping its prototype) — and a mirror's
61
- * on-device marker or a function value aren't editable data at all.
62
- */
63
- function editBlockedReasonFor(atom, value) {
64
- if (!atom.setValue) {
65
- return "Derived atom — its value recomputes from other atoms and has no setter. Edit the atoms it reads from instead.";
66
- }
67
- if (typeof value === "function") {
68
- return "This atom holds a function — nothing here to edit as data.";
69
- }
70
- if (value && typeof value === "object" && value.__buoyValueOnDevice === true) {
71
- return "The value stayed on the device (too large for the wire) — open the tool on-device to edit it.";
72
- }
73
- if (value && typeof value === "object") {
74
- const proto = Object.getPrototypeOf(value);
75
- const isPlain = proto === Object.prototype || proto === null || Array.isArray(value) || value instanceof Map || value instanceof Set;
76
- if (!isPlain) {
77
- const name = value.constructor?.name ?? "class";
78
- return `This atom holds a ${name} instance — rebuilding it from edited fields would drop its prototype. Edit via app code.`;
79
- }
80
- }
81
- return null;
82
- }
83
- function valueTypeLabel(value) {
84
- if (value === null) return "null";
85
- if (value === undefined) return "undefined";
86
- if (Array.isArray(value)) return `array · ${value.length}`;
87
- return typeof value;
88
- }
89
- function JotaiAtomValueDetailContent({
90
- atom,
91
- onViewHistory
92
- }) {
93
- const rowID = (0, _testIDs.atomRowTestID)(atom.label);
94
-
95
- // Re-render on every recorded change — our own edits included — so the
96
- // editor, the read-only explorer, and the change count all track the live
97
- // atom. Changes and registry updates arrive on separate listener sets, so
98
- // subscribe to both. On a desktop mirror the snapshot refresh drives the
99
- // same listeners.
100
- const [version, setVersion] = (0, _react.useState)(0);
101
- (0, _react.useEffect)(() => {
102
- const bump = () => setVersion(v => v + 1);
103
- const unsubChanges = _jotaiStateStore.jotaiStateStore.subscribe(bump);
104
- const unsubAtoms = _jotaiStateStore.jotaiStateStore.subscribeToAtoms(bump);
105
- return () => {
106
- unsubChanges();
107
- unsubAtoms();
108
- };
109
- }, []);
110
-
111
- // Recomputed per change on purpose — this screen edits the LIVE atom, so
112
- // every view of it derives from the atom's current answer, never a seed.
113
- const value = (0, _react.useMemo)(() => {
114
- try {
115
- return atom.getValue();
116
- } catch {
117
- return undefined;
118
- }
119
- },
120
- // eslint-disable-next-line react-hooks/exhaustive-deps
121
- [atom, version]);
122
- const blockedReason = editBlockedReasonFor(atom, value);
123
- const [rawOpen, setRawOpen] = (0, _react.useState)(false);
124
- const writer = (0, _react.useMemo)(() => {
125
- if (blockedReason !== null) return undefined;
126
- return {
127
- update: (path, next) => {
128
- const current = atom.getValue();
129
- atom.setValue?.(path.length === 0 ? next : (0, _dataViewer.updateNestedDataByPath)(current, path, next));
130
- },
131
- remove: path => {
132
- // Depth 0 is vetoed by canRemove; an atom is one value — you can
133
- // empty it, not remove it.
134
- if (path.length === 0) return;
135
- const current = atom.getValue();
136
- atom.setValue?.((0, _dataViewer.deleteNestedDataByPath)(current, path));
137
- },
138
- canRemove: path => path.length > 0
139
- };
140
- }, [atom, blockedReason]);
141
- const displayValue = (0, _react.useMemo)(() => {
142
- if (value && typeof value === "object") {
143
- const filtered = {};
144
- for (const [key, v] of Object.entries(value)) {
145
- if (typeof v !== "function") filtered[key] = v;
146
- }
147
- return (0, _sharedUi.parseValue)(filtered);
148
- }
149
- return (0, _sharedUi.parseValue)(value);
150
- // eslint-disable-next-line react-hooks/exhaustive-deps
151
- }, [value, version]);
152
-
153
- /** Raw-hatch save: whole-value edit, one write. */
154
- const applyRaw = (0, _react.useCallback)(next => {
155
- atom.setValue?.(next);
156
- setRawOpen(false);
157
- }, [atom]);
158
- const writePill = atom.setValue === undefined ? {
159
- color: _sharedUi.night.color.textTertiary,
160
- label: "READ-ONLY (DERIVED)"
161
- } : atom.writeKind === "action" ? {
162
- color: _sharedUi.buoyColors.warning,
163
- label: "CUSTOM WRITE"
164
- } : {
165
- color: _sharedUi.buoyColors.success,
166
- label: "PRIMITIVE"
167
- };
168
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
169
- style: styles.root,
170
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
171
- testID: `${DETAIL_ID}-scroll`,
172
- style: styles.scroll,
173
- contentContainerStyle: styles.scrollContent,
174
- keyboardShouldPersistTaps: "handled",
175
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
176
- style: styles.section,
177
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
178
- style: styles.editorContainer,
179
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
180
- style: styles.sectionHeaderRow,
181
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
182
- testID: `${DETAIL_ID}-editor-header`,
183
- style: styles.sectionHeaderText,
184
- children: "Value Editor"
185
- }), blockedReason === null ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
186
- onPress: () => setRawOpen(true),
187
- testID: `${DETAIL_ID}-raw`,
188
- accessibilityRole: "button",
189
- accessibilityLabel: "Edit raw JSON",
190
- style: styles.rawButton,
191
- hitSlop: {
192
- top: 6,
193
- bottom: 6,
194
- left: 6,
195
- right: 6
196
- },
197
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileJson, {
198
- size: 13,
199
- color: _sharedUi.night.color.accent
200
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
201
- style: styles.rawButtonText,
202
- children: "Raw"
203
- })]
204
- }) : null]
205
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
206
- style: styles.sectionBody,
207
- children: blockedReason === null ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
208
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.LiveExplorer, {
209
- editable: true,
210
- label: atom.label,
211
- value: value,
212
- defaultExpanded: [atom.label],
213
- writer: writer,
214
- dataVersion: version,
215
- testIDForPath: detailExplorerTestID,
216
- debounceMs: TYPING_COMMIT_MS
217
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
218
- style: styles.editorHint,
219
- children: atom.writeKind === "action" ? "This atom has a custom write — the edited value is handed to it as its argument (a set for atomWithStorage, an ACTION for a reducer atom)." : "Edits write into the live atom as you make them — the whole value is replaced, jotai's own semantics."
220
- })]
221
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
222
- style: styles.blockedText,
223
- testID: `${DETAIL_ID}-edit-blocked`,
224
- children: blockedReason
225
- })
226
- })]
227
- })
228
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
229
- style: styles.section,
230
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
231
- style: styles.infoContainer,
232
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
233
- style: styles.sectionHeaderTextPlain,
234
- children: "Atom Details"
235
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
236
- style: styles.infoBody,
237
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ExpandedInfoRow, {
238
- label: "Type",
239
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PillBadge, {
240
- color: atom.color,
241
- testID: `${rowID}-type`,
242
- children: "JOTAI"
243
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
244
- style: styles.valueTypeText,
245
- testID: `${rowID}-valuetype`,
246
- children: valueTypeLabel(value)
247
- })]
248
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ExpandedInfoRow, {
249
- label: "Write",
250
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PillBadge, {
251
- color: writePill.color,
252
- testID: `${DETAIL_ID}-write`,
253
- children: writePill.label
254
- })
255
- }), atom.changeCount > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ExpandedInfoRow, {
256
- label: "Changes",
257
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PillBadge, {
258
- color: _sharedUi.buoyColors.warning,
259
- testID: `${rowID}-changes`,
260
- children: String(atom.changeCount)
261
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
262
- onPress: () => onViewHistory(atom.label),
263
- style: styles.inlineLinkButton,
264
- hitSlop: {
265
- top: 6,
266
- bottom: 6,
267
- left: 6,
268
- right: 6
269
- },
270
- testID: `${rowID}-history`,
271
- accessibilityRole: "button",
272
- accessibilityLabel: `View ${atom.label} history`,
273
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
274
- style: [styles.inlineLinkText, {
275
- color: atom.color
276
- }],
277
- children: "view history \u2192"
278
- })
279
- })]
280
- })]
281
- })]
282
- })
283
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
284
- style: styles.section,
285
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
286
- style: styles.infoContainer,
287
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
288
- style: styles.sectionHeaderTextPlain,
289
- children: "Value Explorer"
290
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
291
- style: styles.viewerBody,
292
- testID: `${rowID}-value`,
293
- children: displayValue && typeof displayValue === "object" ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
294
- title: "",
295
- data: displayValue,
296
- showTypeFilter: true,
297
- rawMode: true,
298
- initialExpanded: false
299
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
300
- style: styles.primitiveText,
301
- testID: `${rowID}-primitive`,
302
- children: String(value ?? "undefined")
303
- })
304
- })]
305
- })
306
- })]
307
- }), rawOpen && blockedReason === null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.ValueEditModal, {
308
- path: [],
309
- value: value,
310
- onSave: applyRaw,
311
- onCancel: () => setRawOpen(false),
312
- saveLabel: "Apply",
313
- testIDPrefix: `${_testIDs.JOTAI_TEST_ID}-value`
314
- }) : null]
315
- });
316
- }
317
- const styles = _reactNative.StyleSheet.create({
318
- root: {
319
- flex: 1,
320
- minHeight: 0
321
- },
322
- scroll: {
323
- flex: 1
324
- },
325
- scrollContent: {
326
- paddingHorizontal: 8,
327
- paddingTop: 8,
328
- paddingBottom: 24
329
- },
330
- section: {
331
- marginBottom: 16
332
- },
333
- // The editable section wears the accent, like RQ's Data Editor — the one
334
- // box on this screen that changes things should read differently from the
335
- // two that don't.
336
- editorContainer: {
337
- minWidth: 200,
338
- backgroundColor: _sharedUi.night.color.surface,
339
- borderRadius: 6,
340
- borderWidth: 1,
341
- borderColor: _sharedUi.night.color.accent + "4D",
342
- overflow: "hidden",
343
- shadowColor: _sharedUi.night.color.accent,
344
- shadowOffset: {
345
- width: 0,
346
- height: 0
347
- },
348
- shadowOpacity: 0.1,
349
- shadowRadius: 6
350
- },
351
- sectionHeaderRow: {
352
- flexDirection: "row",
353
- alignItems: "center",
354
- justifyContent: "space-between",
355
- backgroundColor: _sharedUi.night.color.accent + "1A",
356
- paddingHorizontal: 12,
357
- paddingVertical: 8,
358
- borderBottomWidth: 1,
359
- borderBottomColor: _sharedUi.night.color.accent + "33"
360
- },
361
- sectionHeaderText: {
362
- fontWeight: "600",
363
- fontSize: 12,
364
- color: _sharedUi.night.color.accent,
365
- letterSpacing: 0.5,
366
- textTransform: "uppercase",
367
- fontFamily: _sharedUi.monoFont
368
- },
369
- rawButton: {
370
- flexDirection: "row",
371
- alignItems: "center",
372
- gap: 4
373
- },
374
- rawButtonText: {
375
- color: _sharedUi.night.color.accent,
376
- fontSize: 11,
377
- fontWeight: "700"
378
- },
379
- sectionBody: {
380
- padding: 8
381
- },
382
- editorHint: {
383
- color: _sharedUi.night.color.textTertiary,
384
- fontSize: 10,
385
- paddingTop: 8,
386
- paddingHorizontal: 4,
387
- lineHeight: 14
388
- },
389
- blockedText: {
390
- color: _sharedUi.night.color.textSecondary,
391
- fontSize: 12,
392
- padding: 12,
393
- lineHeight: 18
394
- },
395
- infoContainer: {
396
- minWidth: 200,
397
- backgroundColor: _sharedUi.night.color.surface,
398
- borderRadius: 6,
399
- borderWidth: 1,
400
- borderColor: _sharedUi.night.color.border,
401
- overflow: "hidden"
402
- },
403
- sectionHeaderTextPlain: {
404
- backgroundColor: _sharedUi.night.color.surfaceElevated,
405
- paddingHorizontal: 12,
406
- paddingVertical: 8,
407
- fontWeight: "600",
408
- fontSize: 12,
409
- color: _sharedUi.night.color.textSecondary,
410
- borderBottomWidth: 1,
411
- borderBottomColor: _sharedUi.night.color.border,
412
- letterSpacing: 0.5,
413
- textTransform: "uppercase",
414
- fontFamily: _sharedUi.monoFont
415
- },
416
- infoBody: {
417
- padding: 12,
418
- gap: 10
419
- },
420
- viewerBody: {
421
- backgroundColor: _sharedUi.night.color.bg,
422
- minHeight: 60
423
- },
424
- primitiveText: {
425
- color: _sharedUi.night.color.text,
426
- fontSize: 12,
427
- fontFamily: _sharedUi.monoFont,
428
- padding: 14
429
- },
430
- valueTypeText: {
431
- color: _sharedUi.night.color.textTertiary,
432
- fontSize: 10,
433
- fontFamily: _sharedUi.monoFont,
434
- marginLeft: 6
435
- },
436
- inlineLinkButton: {
437
- marginLeft: 4
438
- },
439
- inlineLinkText: {
440
- fontSize: 10,
441
- fontWeight: "600",
442
- fontFamily: _sharedUi.monoFont
443
- }
444
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.JotaiAtomValueDetailContent=JotaiAtomValueDetailContent;var _react=require("react");var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _dataViewer=require("@buoy-gg/shared-ui/dataViewer");var _jotaiStateStore=require("../utils/jotaiStateStore");var _testIDs=require("../utils/testIDs");var _jsxRuntime=require("react/jsx-runtime");const TYPING_COMMIT_MS=300;const DETAIL_ID=`${_testIDs.JOTAI_TEST_ID}-atom-detail`;function detailExplorerTestID(e){const t=r=>r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return e.length===0?`${DETAIL_ID}-explorer-root`:`${DETAIL_ID}-explorer-${e.map(t).filter(Boolean).join("-")}`}function editBlockedReasonFor(e,t){if(!e.setValue){return"Derived atom \u2014 its value recomputes from other atoms and has no setter. Edit the atoms it reads from instead."}if(typeof t==="function"){return"This atom holds a function \u2014 nothing here to edit as data."}if(t&&typeof t==="object"&&t.__buoyValueOnDevice===true){return"The value stayed on the device (too large for the wire) \u2014 open the tool on-device to edit it."}if(t&&typeof t==="object"){const r=Object.getPrototypeOf(t);const a=r===Object.prototype||r===null||Array.isArray(t)||t instanceof Map||t instanceof Set;if(!a){const c=t.constructor?.name??"class";return`This atom holds a ${c} instance \u2014 rebuilding it from edited fields would drop its prototype. Edit via app code.`}}return null}function valueTypeLabel(e){if(e===null)return"null";if(e===void 0)return"undefined";if(Array.isArray(e))return`array \xB7 ${e.length}`;return typeof e}function JotaiAtomValueDetailContent({atom:e,onViewHistory:t}){const r=(0,_testIDs.atomRowTestID)(e.label);const[a,c]=(0,_react.useState)(0);(0,_react.useEffect)(()=>{const o=()=>c(p=>p+1);const n=_jotaiStateStore.jotaiStateStore.subscribe(o);const s=_jotaiStateStore.jotaiStateStore.subscribeToAtoms(o);return()=>{n();s()}},[]);const i=(0,_react.useMemo)(()=>{try{return e.getValue()}catch{return void 0}},[e,a]);const l=editBlockedReasonFor(e,i);const[f,d]=(0,_react.useState)(false);const g=(0,_react.useMemo)(()=>{if(l!==null)return void 0;return{update:(o,n)=>{const s=e.getValue();e.setValue?.(o.length===0?n:(0,_dataViewer.updateNestedDataByPath)(s,o,n))},remove:o=>{if(o.length===0)return;const n=e.getValue();e.setValue?.((0,_dataViewer.deleteNestedDataByPath)(n,o))},canRemove:o=>o.length>0}},[e,l]);const u=(0,_react.useMemo)(()=>{if(i&&typeof i==="object"){const o={};for(const[n,s]of Object.entries(i)){if(typeof s!=="function")o[n]=s}return(0,_sharedUi.parseValue)(o)}return(0,_sharedUi.parseValue)(i)},[i,a]);const y=(0,_react.useCallback)(o=>{e.setValue?.(o);d(false)},[e]);const h=e.setValue===void 0?{color:_sharedUi.night.color.textTertiary,label:"READ-ONLY (DERIVED)"}:e.writeKind==="action"?{color:_sharedUi.buoyColors.warning,label:"CUSTOM WRITE"}:{color:_sharedUi.buoyColors.success,label:"PRIMITIVE"};return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.root,children:[(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{testID:`${DETAIL_ID}-scroll`,style:styles.scroll,contentContainerStyle:styles.scrollContent,keyboardShouldPersistTaps:"handled",children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.section,children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.editorContainer,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.sectionHeaderRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{testID:`${DETAIL_ID}-editor-header`,style:styles.sectionHeaderText,children:"Value Editor"}),l===null?(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{onPress:()=>d(true),testID:`${DETAIL_ID}-raw`,accessibilityRole:"button",accessibilityLabel:"Edit raw JSON",style:styles.rawButton,hitSlop:{top:6,bottom:6,left:6,right:6},children:[(0,_jsxRuntime.jsx)(_sharedUi.FileJson,{size:13,color:_sharedUi.night.color.accent}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rawButtonText,children:"Raw"})]}):null]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.sectionBody,children:l===null?(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[(0,_jsxRuntime.jsx)(_dataViewer.LiveExplorer,{editable:true,label:e.label,value:i,defaultExpanded:[e.label],writer:g,dataVersion:a,testIDForPath:detailExplorerTestID,debounceMs:TYPING_COMMIT_MS}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.editorHint,children:e.writeKind==="action"?"This atom has a custom write \u2014 the edited value is handed to it as its argument (a set for atomWithStorage, an ACTION for a reducer atom).":"Edits write into the live atom as you make them \u2014 the whole value is replaced, jotai's own semantics."})]}):(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.blockedText,testID:`${DETAIL_ID}-edit-blocked`,children:l})})]})}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.section,children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.infoContainer,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.sectionHeaderTextPlain,children:"Atom Details"}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.infoBody,children:[(0,_jsxRuntime.jsxs)(_sharedUi.ExpandedInfoRow,{label:"Type",children:[(0,_jsxRuntime.jsx)(_sharedUi.PillBadge,{color:e.color,testID:`${r}-type`,children:"JOTAI"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.valueTypeText,testID:`${r}-valuetype`,children:valueTypeLabel(i)})]}),(0,_jsxRuntime.jsx)(_sharedUi.ExpandedInfoRow,{label:"Write",children:(0,_jsxRuntime.jsx)(_sharedUi.PillBadge,{color:h.color,testID:`${DETAIL_ID}-write`,children:h.label})}),e.changeCount>0&&(0,_jsxRuntime.jsxs)(_sharedUi.ExpandedInfoRow,{label:"Changes",children:[(0,_jsxRuntime.jsx)(_sharedUi.PillBadge,{color:_sharedUi.buoyColors.warning,testID:`${r}-changes`,children:String(e.changeCount)}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:()=>t(e.label),style:styles.inlineLinkButton,hitSlop:{top:6,bottom:6,left:6,right:6},testID:`${r}-history`,accessibilityRole:"button",accessibilityLabel:`View ${e.label} history`,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.inlineLinkText,{color:e.color}],children:"view history \u2192"})})]})]})]})}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.section,children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.infoContainer,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.sectionHeaderTextPlain,children:"Value Explorer"}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.viewerBody,testID:`${r}-value`,children:u&&typeof u==="object"?(0,_jsxRuntime.jsx)(_dataViewer.DataViewer,{title:"",data:u,showTypeFilter:true,rawMode:true,initialExpanded:false}):(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.primitiveText,testID:`${r}-primitive`,children:String(i??"undefined")})})]})})]}),f&&l===null?(0,_jsxRuntime.jsx)(_dataViewer.ValueEditModal,{path:[],value:i,onSave:y,onCancel:()=>d(false),saveLabel:"Apply",testIDPrefix:`${_testIDs.JOTAI_TEST_ID}-value`}):null]})}const styles=_reactNative.StyleSheet.create({root:{flex:1,minHeight:0},scroll:{flex:1},scrollContent:{paddingHorizontal:8,paddingTop:8,paddingBottom:24},section:{marginBottom:16},editorContainer:{minWidth:200,backgroundColor:_sharedUi.night.color.surface,borderRadius:6,borderWidth:1,borderColor:_sharedUi.night.color.accent+"4D",overflow:"hidden",shadowColor:_sharedUi.night.color.accent,shadowOffset:{width:0,height:0},shadowOpacity:.1,shadowRadius:6},sectionHeaderRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",backgroundColor:_sharedUi.night.color.accent+"1A",paddingHorizontal:12,paddingVertical:8,borderBottomWidth:1,borderBottomColor:_sharedUi.night.color.accent+"33"},sectionHeaderText:{fontWeight:"600",fontSize:12,color:_sharedUi.night.color.accent,letterSpacing:.5,textTransform:"uppercase",fontFamily:_sharedUi.monoFont},rawButton:{flexDirection:"row",alignItems:"center",gap:4},rawButtonText:{color:_sharedUi.night.color.accent,fontSize:11,fontWeight:"700"},sectionBody:{padding:8},editorHint:{color:_sharedUi.night.color.textTertiary,fontSize:10,paddingTop:8,paddingHorizontal:4,lineHeight:14},blockedText:{color:_sharedUi.night.color.textSecondary,fontSize:12,padding:12,lineHeight:18},infoContainer:{minWidth:200,backgroundColor:_sharedUi.night.color.surface,borderRadius:6,borderWidth:1,borderColor:_sharedUi.night.color.border,overflow:"hidden"},sectionHeaderTextPlain:{backgroundColor:_sharedUi.night.color.surfaceElevated,paddingHorizontal:12,paddingVertical:8,fontWeight:"600",fontSize:12,color:_sharedUi.night.color.textSecondary,borderBottomWidth:1,borderBottomColor:_sharedUi.night.color.border,letterSpacing:.5,textTransform:"uppercase",fontFamily:_sharedUi.monoFont},infoBody:{padding:12,gap:10},viewerBody:{backgroundColor:_sharedUi.night.color.bg,minHeight:60},primitiveText:{color:_sharedUi.night.color.text,fontSize:12,fontFamily:_sharedUi.monoFont,padding:14},valueTypeText:{color:_sharedUi.night.color.textTertiary,fontSize:10,fontFamily:_sharedUi.monoFont,marginLeft:6},inlineLinkButton:{marginLeft:4},inlineLinkText:{fontSize:10,fontWeight:"600",fontFamily:_sharedUi.monoFont}});
@@ -1,43 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.JotaiEventFilterView = JotaiEventFilterView;
7
- var _react = require("react");
8
- var _sharedUi = require("@buoy-gg/shared-ui");
9
- var _testIDs = require("../utils/testIDs");
10
- var _jsxRuntime = require("react/jsx-runtime");
11
- /**
12
- * JotaiEventFilterView
13
- *
14
- * The shared `EntityFilterView` with jotai's nouns: watched atoms you can
15
- * tap to hide, active atom-name patterns, and how it works.
16
- */
17
-
18
- const EXAMPLES = ["• count -> hides countAtom from atoms & events", "• auth -> hides authAtom, authStatusAtom", "• user -> hides userAtom from both tabs"];
19
- function JotaiEventFilterView({
20
- ignoredPatterns,
21
- onTogglePattern,
22
- onAddPattern,
23
- atoms
24
- }) {
25
- const entities = (0, _react.useMemo)(() => atoms.map(atom => ({
26
- name: atom.label,
27
- color: atom.color
28
- })), [atoms]);
29
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.EntityFilterView, {
30
- entities: entities,
31
- ignoredPatterns: ignoredPatterns,
32
- onTogglePattern: onTogglePattern,
33
- onAddPattern: onAddPattern,
34
- entityNoun: "ATOMS",
35
- entitySingular: "atom",
36
- entityKey: "atom",
37
- testIDPrefix: _testIDs.JOTAI_TEST_ID,
38
- slug: _testIDs.slugForTestID,
39
- howItWorks: "Patterns hide matching atoms from both the Atoms tab and the Events tab. Tap an atom above to quickly add it as a filter.",
40
- examples: EXAMPLES,
41
- emptyMessage: "No atoms are being watched yet."
42
- });
43
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.JotaiEventFilterView=JotaiEventFilterView;var _react=require("react");var _sharedUi=require("@buoy-gg/shared-ui");var _testIDs=require("../utils/testIDs");var _jsxRuntime=require("react/jsx-runtime");const EXAMPLES=["\u2022 count -> hides countAtom from atoms & events","\u2022 auth -> hides authAtom, authStatusAtom","\u2022 user -> hides userAtom from both tabs"];function JotaiEventFilterView({ignoredPatterns:r,onTogglePattern:a,onAddPattern:s,atoms:t}){const o=(0,_react.useMemo)(()=>t.map(e=>({name:e.label,color:e.color})),[t]);return(0,_jsxRuntime.jsx)(_sharedUi.EntityFilterView,{entities:o,ignoredPatterns:r,onTogglePattern:a,onAddPattern:s,entityNoun:"ATOMS",entitySingular:"atom",entityKey:"atom",testIDPrefix:_testIDs.JOTAI_TEST_ID,slug:_testIDs.slugForTestID,howItWorks:"Patterns hide matching atoms from both the Atoms tab and the Events tab. Tap an atom above to quickly add it as a filter.",examples:EXAMPLES,emptyMessage:"No atoms are being watched yet."})}
@@ -1,18 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "JOTAI_ICON_COLOR", {
7
- enumerable: true,
8
- get: function () {
9
- return _sharedUi.JOTAI_ICON_COLOR;
10
- }
11
- });
12
- Object.defineProperty(exports, "JotaiIcon", {
13
- enumerable: true,
14
- get: function () {
15
- return _sharedUi.JotaiIcon;
16
- }
17
- });
18
- var _sharedUi = require("@buoy-gg/shared-ui");
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"JOTAI_ICON_COLOR",{enumerable:true,get:function(){return _sharedUi.JOTAI_ICON_COLOR}});Object.defineProperty(exports,"JotaiIcon",{enumerable:true,get:function(){return _sharedUi.JotaiIcon}});var _sharedUi=require("@buoy-gg/shared-ui");