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