@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,710 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * JotaiAtomDetailContent
5
- *
6
- * Detail view for a Jotai atom change with 3 view modes:
7
- * - CHANGE: Atom label, timestamp, category, changed keys
8
- * - VALUE: Full atom value after change
9
- * - DIFF: Compare prev vs next value
10
- *
11
- * Mirrors ZustandStateDetailContent.tsx from @buoy-gg/zustand
12
- */
13
-
14
- import React, { memo, useState, useCallback, useMemo } from "react";
15
- import { View, Text, StyleSheet, ScrollView } from "react-native";
16
- import { buoyColors, night, EventStepperFooter, formatRelativeTime, parseValue, Info, Zap, FileText, Database, GitBranch, copyToClipboard, ProUpgradeModal, DiffModeTabs, CompareBar, DetailViewToggle, monoFont } from "@buoy-gg/shared-ui";
17
- import { DataViewer, SplitDiffViewer, TreeDiffViewer, diffThemes } from "@buoy-gg/shared-ui/dataViewer";
18
- import { useIsPro } from "@buoy-gg/license";
19
- import { jotaiStateStore } from "../utils/jotaiStateStore";
20
- import { JOTAI_TEST_ID, slugForTestID } from "../utils/testIDs";
21
-
22
- // ---- View Toggle ----
23
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
24
- const VIEW_CONFIGS = [{
25
- key: "change",
26
- label: "CHANGE",
27
- icon: FileText
28
- }, {
29
- key: "value",
30
- label: "VALUE",
31
- icon: Database
32
- }, {
33
- key: "diff",
34
- label: "DIFF",
35
- icon: GitBranch
36
- }];
37
- const DIFF_DISABLED = ["diff"];
38
-
39
- // ---- Helpers ----
40
-
41
- function getCategoryInfo(category) {
42
- switch (category) {
43
- case "initial":
44
- return {
45
- label: "INITIAL",
46
- color: night.color.textTertiary,
47
- bgColor: night.color.textTertiary + "26"
48
- };
49
- default:
50
- return {
51
- label: "WRITE",
52
- color: buoyColors.success,
53
- bgColor: buoyColors.success + "26"
54
- };
55
- }
56
- }
57
- function formatTime(timestamp) {
58
- const date = new Date(timestamp);
59
- const h = date.getHours().toString().padStart(2, "0");
60
- const m = date.getMinutes().toString().padStart(2, "0");
61
- const s = date.getSeconds().toString().padStart(2, "0");
62
- const ms = date.getMilliseconds().toString().padStart(3, "0");
63
- return `${h}:${m}:${s}.${ms}`;
64
- }
65
- function formatTimestamp(timestamp) {
66
- return new Date(timestamp).toLocaleTimeString("en-US", {
67
- hour12: false,
68
- hour: "2-digit",
69
- minute: "2-digit",
70
- second: "2-digit"
71
- });
72
- }
73
- function getValueType(value) {
74
- if (value === null) return "null";
75
- if (value === undefined) return "undefined";
76
- if (Array.isArray(value)) return "array";
77
- return typeof value;
78
- }
79
- function getChangeLabel(category) {
80
- return category === "initial" ? "INIT" : "WRITE";
81
- }
82
- const DIFF_MODE_TABS = [{
83
- key: "tree",
84
- label: "TREE VIEW"
85
- }, {
86
- key: "split",
87
- label: "SPLIT VIEW"
88
- }];
89
-
90
- // ---- Change Info View (CHANGE tab) ----
91
-
92
- const JotaiAtomInfoView = /*#__PURE__*/memo(function JotaiAtomInfoView({
93
- change
94
- }) {
95
- const isPro = useIsPro();
96
- const [showProModal, setShowProModal] = useState(false);
97
- const categoryInfo = getCategoryInfo(change.category);
98
- const atomColor = jotaiStateStore.getAtomColor(change.atomLabel);
99
- const handleCopyValue = useCallback(async () => {
100
- if (!isPro) {
101
- setShowProModal(true);
102
- return;
103
- }
104
- await copyToClipboard(change.nextValue);
105
- }, [change.nextValue, isPro]);
106
- return /*#__PURE__*/_jsxs(ScrollView, {
107
- style: infoStyles.container,
108
- contentContainerStyle: infoStyles.content,
109
- showsVerticalScrollIndicator: false,
110
- testID: `${JOTAI_TEST_ID}-detail-scroll`,
111
- children: [/*#__PURE__*/_jsxs(View, {
112
- style: infoStyles.card,
113
- testID: `${JOTAI_TEST_ID}-info-card`,
114
- children: [/*#__PURE__*/_jsx(View, {
115
- style: infoStyles.sectionHeader,
116
- children: /*#__PURE__*/_jsxs(View, {
117
- style: infoStyles.sectionHeaderLeft,
118
- children: [/*#__PURE__*/_jsx(Info, {
119
- size: 14,
120
- color: night.color.accent
121
- }), /*#__PURE__*/_jsx(Text, {
122
- style: infoStyles.sectionTitle,
123
- children: "CHANGE INFO"
124
- })]
125
- })
126
- }), /*#__PURE__*/_jsxs(View, {
127
- style: infoStyles.cardContent,
128
- children: [/*#__PURE__*/_jsxs(View, {
129
- style: infoStyles.infoRow,
130
- children: [/*#__PURE__*/_jsx(Text, {
131
- style: infoStyles.infoLabel,
132
- children: "Atom"
133
- }), /*#__PURE__*/_jsx(Text, {
134
- style: [infoStyles.infoValue, infoStyles.infoValueMono, {
135
- color: atomColor
136
- }],
137
- numberOfLines: 1,
138
- ellipsizeMode: "middle",
139
- testID: `${JOTAI_TEST_ID}-info-atom`,
140
- children: change.atomLabel
141
- })]
142
- }), /*#__PURE__*/_jsxs(View, {
143
- style: infoStyles.infoRow,
144
- children: [/*#__PURE__*/_jsx(Text, {
145
- style: infoStyles.infoLabel,
146
- children: "Time"
147
- }), /*#__PURE__*/_jsx(Text, {
148
- style: [infoStyles.infoValue, infoStyles.infoValueMono],
149
- testID: `${JOTAI_TEST_ID}-info-time`,
150
- children: formatTime(change.timestamp)
151
- })]
152
- }), /*#__PURE__*/_jsxs(View, {
153
- style: infoStyles.badgeRow,
154
- children: [/*#__PURE__*/_jsx(View, {
155
- style: [infoStyles.categoryBadge, {
156
- backgroundColor: categoryInfo.bgColor
157
- }],
158
- children: /*#__PURE__*/_jsx(Text, {
159
- style: [infoStyles.categoryText, {
160
- color: categoryInfo.color
161
- }],
162
- testID: `${JOTAI_TEST_ID}-info-category`,
163
- children: categoryInfo.label
164
- })
165
- }), change.hasValueChange ? /*#__PURE__*/_jsxs(View, {
166
- style: infoStyles.changesBadge,
167
- children: [/*#__PURE__*/_jsx(Zap, {
168
- size: 10,
169
- color: buoyColors.success
170
- }), /*#__PURE__*/_jsx(Text, {
171
- style: infoStyles.changesText,
172
- testID: `${JOTAI_TEST_ID}-info-changes`,
173
- children: change.diffSummary || "changed"
174
- })]
175
- }) : /*#__PURE__*/_jsx(View, {
176
- style: infoStyles.noChangesBadge,
177
- children: /*#__PURE__*/_jsx(Text, {
178
- style: infoStyles.noChangesText,
179
- testID: `${JOTAI_TEST_ID}-info-no-changes`,
180
- children: "no change"
181
- })
182
- })]
183
- })]
184
- })]
185
- }), change.changedKeys.length > 0 && /*#__PURE__*/_jsxs(View, {
186
- style: infoStyles.card,
187
- testID: `${JOTAI_TEST_ID}-keys-card`,
188
- children: [/*#__PURE__*/_jsxs(View, {
189
- style: infoStyles.sectionHeader,
190
- children: [/*#__PURE__*/_jsxs(View, {
191
- style: infoStyles.sectionHeaderLeft,
192
- children: [/*#__PURE__*/_jsx(Zap, {
193
- size: 14,
194
- color: night.color.accent
195
- }), /*#__PURE__*/_jsx(Text, {
196
- style: infoStyles.sectionTitle,
197
- children: "CHANGED KEYS"
198
- })]
199
- }), /*#__PURE__*/_jsx(View, {
200
- style: infoStyles.countBadge,
201
- children: /*#__PURE__*/_jsx(Text, {
202
- style: infoStyles.countText,
203
- testID: `${JOTAI_TEST_ID}-keys-count`,
204
- children: change.changedKeys.length
205
- })
206
- })]
207
- }), /*#__PURE__*/_jsx(View, {
208
- style: infoStyles.cardContent,
209
- children: /*#__PURE__*/_jsx(View, {
210
- style: infoStyles.keysContainer,
211
- children: change.changedKeys.map(key => /*#__PURE__*/_jsx(View, {
212
- style: infoStyles.keyBadge,
213
- children: /*#__PURE__*/_jsx(Text, {
214
- style: infoStyles.keyText,
215
- testID: `${JOTAI_TEST_ID}-key-${slugForTestID(key)}`,
216
- children: key
217
- })
218
- }, key))
219
- })
220
- })]
221
- }), /*#__PURE__*/_jsx(View, {
222
- style: infoStyles.actionsRow,
223
- children: /*#__PURE__*/_jsx(View, {
224
- style: [infoStyles.actionButton, infoStyles.actionButtonCopy],
225
- onTouchEnd: handleCopyValue,
226
- testID: `${JOTAI_TEST_ID}-copy-value`,
227
- accessibilityRole: "button",
228
- accessibilityLabel: "Copy value",
229
- children: /*#__PURE__*/_jsx(Text, {
230
- style: [infoStyles.actionButtonText, {
231
- color: night.color.accent
232
- }],
233
- children: "COPY VALUE"
234
- })
235
- })
236
- }), /*#__PURE__*/_jsx(ProUpgradeModal, {
237
- visible: showProModal,
238
- onClose: () => setShowProModal(false),
239
- featureName: "Copy"
240
- })]
241
- });
242
- });
243
-
244
- // ---- Main Detail Content ----
245
-
246
- export function JotaiAtomDetailContent({
247
- change,
248
- changes,
249
- selectedIndex,
250
- onIndexChange,
251
- disableInternalFooter = false,
252
- initialView = "change"
253
- }) {
254
- // View state (initialView seeds it once; taps own it afterwards)
255
- const [activeView, setActiveView] = useState(initialView);
256
- const [diffMode, setDiffMode] = useState("tree");
257
- const totalChanges = changes.length;
258
- const atomColor = jotaiStateStore.getAtomColor(change.atomLabel);
259
- const changeLabel = getChangeLabel(change.category);
260
- const valueType = getValueType(parseValue(change.nextValue));
261
- const chronologicalNumber = totalChanges - selectedIndex;
262
- const prevChangeIndex = selectedIndex < totalChanges - 1 ? selectedIndex + 1 : null;
263
- const prevChange = prevChangeIndex !== null ? changes[prevChangeIndex] : null;
264
- const prevChronologicalNumber = chronologicalNumber - 1;
265
- const leftEvent = useMemo(() => ({
266
- index: prevChangeIndex ?? 0,
267
- label: prevChronologicalNumber > 0 ? `#${prevChronologicalNumber}` : "Initial",
268
- timestamp: prevChange ? formatTimestamp(prevChange.timestamp) : "",
269
- relativeTime: prevChange ? formatRelativeTime(new Date(prevChange.timestamp)) : "value",
270
- badge: prevChange ? /*#__PURE__*/_jsx(View, {
271
- style: [styles.changeBadgeSmall, {
272
- backgroundColor: `${jotaiStateStore.getAtomColor(prevChange.atomLabel)}20`
273
- }],
274
- children: /*#__PURE__*/_jsx(Text, {
275
- style: [styles.changeTextSmall, {
276
- color: jotaiStateStore.getAtomColor(prevChange.atomLabel)
277
- }],
278
- children: getChangeLabel(prevChange.category)
279
- })
280
- }) : undefined
281
- }), [prevChangeIndex, prevChange, prevChronologicalNumber]);
282
- const rightEvent = useMemo(() => ({
283
- index: selectedIndex,
284
- label: `#${chronologicalNumber}`,
285
- timestamp: formatTimestamp(change.timestamp),
286
- relativeTime: formatRelativeTime(new Date(change.timestamp)),
287
- badge: /*#__PURE__*/_jsx(View, {
288
- style: [styles.changeBadgeSmall, {
289
- backgroundColor: `${atomColor}20`
290
- }],
291
- children: /*#__PURE__*/_jsx(Text, {
292
- style: [styles.changeTextSmall, {
293
- color: atomColor
294
- }],
295
- children: changeLabel
296
- })
297
- })
298
- }), [selectedIndex, change.timestamp, atomColor, changeLabel, chronologicalNumber]);
299
- const renderValueView = useCallback(() => {
300
- const parsed = parseValue(change.nextValue);
301
- return /*#__PURE__*/_jsxs(View, {
302
- style: styles.contentCard,
303
- testID: `${JOTAI_TEST_ID}-value-card`,
304
- children: [/*#__PURE__*/_jsxs(View, {
305
- style: styles.valueHeader,
306
- children: [/*#__PURE__*/_jsx(Text, {
307
- style: styles.valueLabel,
308
- children: "VALUE AFTER CHANGE"
309
- }), /*#__PURE__*/_jsxs(View, {
310
- style: styles.valueHeaderBadges,
311
- children: [/*#__PURE__*/_jsx(View, {
312
- style: [styles.changeBadge, {
313
- backgroundColor: `${atomColor}20`
314
- }],
315
- children: /*#__PURE__*/_jsx(Text, {
316
- style: [styles.changeText, {
317
- color: atomColor
318
- }],
319
- children: changeLabel
320
- })
321
- }), /*#__PURE__*/_jsx(View, {
322
- style: styles.typeBadge,
323
- children: /*#__PURE__*/_jsx(Text, {
324
- style: styles.typeText,
325
- testID: `${JOTAI_TEST_ID}-value-type`,
326
- children: valueType.toUpperCase()
327
- })
328
- })]
329
- })]
330
- }), /*#__PURE__*/_jsx(View, {
331
- style: infoStyles.dataViewerContainer,
332
- children: /*#__PURE__*/_jsx(DataViewer, {
333
- title: "",
334
- data: parsed,
335
- showTypeFilter: true,
336
- rawMode: true,
337
- initialExpanded: true
338
- })
339
- })]
340
- });
341
- }, [change.nextValue, atomColor, changeLabel, valueType]);
342
- const renderDiffContent = useCallback(() => {
343
- const prevVal = parseValue(change.prevValue);
344
- const nextVal = parseValue(change.nextValue);
345
- if (diffMode === "split") {
346
- return /*#__PURE__*/_jsx(ScrollView, {
347
- style: {
348
- flex: 1
349
- },
350
- showsVerticalScrollIndicator: true,
351
- children: /*#__PURE__*/_jsx(SplitDiffViewer, {
352
- oldValue: prevVal,
353
- newValue: nextVal,
354
- theme: diffThemes.devToolsDefault,
355
- options: {
356
- hideLineNumbers: false,
357
- disableWordDiff: false,
358
- showDiffOnly: false,
359
- compareMethod: "words",
360
- contextLines: 3
361
- },
362
- showThemeName: false
363
- })
364
- });
365
- }
366
- return /*#__PURE__*/_jsx(TreeDiffViewer, {
367
- oldValue: prevVal,
368
- newValue: nextVal
369
- });
370
- }, [change.prevValue, change.nextValue, diffMode]);
371
- const handleFooterPrevious = useCallback(() => {
372
- onIndexChange(Math.min(totalChanges - 1, selectedIndex + 1));
373
- }, [selectedIndex, totalChanges, onIndexChange]);
374
- const handleFooterNext = useCallback(() => {
375
- onIndexChange(Math.max(0, selectedIndex - 1));
376
- }, [selectedIndex, onIndexChange]);
377
-
378
- // The diff compares THIS change's prevValue/nextValue — no siblings needed.
379
- // Gating on `changes.length` greyed DIFF out for every jotai event opened
380
- // from the Events timeline (a one-element array). Only a change with no
381
- // captured previous value has nothing to compare.
382
- const diffDisabled = change.prevValue === undefined;
383
- const showInternalFooter = !disableInternalFooter && totalChanges > 1;
384
- return /*#__PURE__*/_jsxs(View, {
385
- style: styles.container,
386
- children: [/*#__PURE__*/_jsx(DetailViewToggle, {
387
- views: VIEW_CONFIGS,
388
- activeView: activeView,
389
- onViewChange: setActiveView,
390
- disabledKeys: diffDisabled ? DIFF_DISABLED : undefined,
391
- testIDPrefix: JOTAI_TEST_ID
392
- }), /*#__PURE__*/_jsxs(View, {
393
- style: [styles.contentArea, showInternalFooter && styles.contentAreaFooterPad],
394
- children: [activeView === "change" && /*#__PURE__*/_jsx(JotaiAtomInfoView, {
395
- change: change
396
- }), activeView === "value" && /*#__PURE__*/_jsx(View, {
397
- style: styles.stateContainer,
398
- children: renderValueView()
399
- }), activeView === "diff" && /*#__PURE__*/_jsxs(View, {
400
- style: styles.diffContainer,
401
- testID: `${JOTAI_TEST_ID}-diff`,
402
- children: [/*#__PURE__*/_jsx(DiffModeTabs, {
403
- tabs: DIFF_MODE_TABS,
404
- activeTab: diffMode,
405
- onTabChange: setDiffMode,
406
- testIDPrefix: `${JOTAI_TEST_ID}-diff-tab`
407
- }), /*#__PURE__*/_jsx(CompareBar, {
408
- leftEvent: leftEvent,
409
- rightEvent: rightEvent
410
- }), /*#__PURE__*/_jsx(View, {
411
- style: styles.diffContent,
412
- testID: `${JOTAI_TEST_ID}-diff-content`,
413
- children: renderDiffContent()
414
- })]
415
- })]
416
- }), showInternalFooter && /*#__PURE__*/_jsx(EventStepperFooter, {
417
- currentIndex: chronologicalNumber - 1,
418
- totalItems: totalChanges,
419
- onPrevious: handleFooterPrevious,
420
- onNext: handleFooterNext,
421
- itemLabel: "Change",
422
- subtitle: formatRelativeTime(new Date(change.timestamp)),
423
- absolute: true
424
- })]
425
- });
426
- }
427
- export function JotaiAtomDetailFooter({
428
- change,
429
- changes,
430
- selectedIndex,
431
- onIndexChange
432
- }) {
433
- const totalChanges = changes.length;
434
- if (totalChanges <= 1) return null;
435
- const chronologicalNumber = totalChanges - selectedIndex;
436
- return /*#__PURE__*/_jsx(EventStepperFooter, {
437
- currentIndex: chronologicalNumber - 1,
438
- totalItems: totalChanges,
439
- onPrevious: () => onIndexChange(Math.min(totalChanges - 1, selectedIndex + 1)),
440
- onNext: () => onIndexChange(Math.max(0, selectedIndex - 1)),
441
- itemLabel: "Change",
442
- subtitle: formatRelativeTime(new Date(change.timestamp))
443
- });
444
- }
445
- const styles = StyleSheet.create({
446
- container: {
447
- flex: 1,
448
- // Transparent so the night sheet's star field shows through.
449
- backgroundColor: "transparent"
450
- },
451
- contentArea: {
452
- flex: 1
453
- },
454
- // Clearance for the absolute EventStepperFooter — only when it renders,
455
- // otherwise hosts like the Events tool get a dead band at the sheet bottom.
456
- contentAreaFooterPad: {
457
- paddingBottom: 96
458
- },
459
- stateContainer: {
460
- flex: 1,
461
- padding: 14
462
- },
463
- contentCard: {
464
- backgroundColor: night.color.surface,
465
- borderRadius: 14,
466
- padding: 14,
467
- borderWidth: 1,
468
- borderColor: night.color.border
469
- },
470
- valueHeader: {
471
- flexDirection: "row",
472
- alignItems: "center",
473
- justifyContent: "space-between",
474
- marginBottom: 8
475
- },
476
- valueLabel: {
477
- fontSize: 10,
478
- color: night.color.textSecondary,
479
- fontFamily: monoFont,
480
- letterSpacing: 0.5,
481
- fontWeight: "600"
482
- },
483
- valueHeaderBadges: {
484
- flexDirection: "row",
485
- alignItems: "center",
486
- gap: 6
487
- },
488
- changeBadge: {
489
- paddingHorizontal: 8,
490
- paddingVertical: 2,
491
- borderRadius: 4
492
- },
493
- changeText: {
494
- fontSize: 9,
495
- fontWeight: "700",
496
- fontFamily: monoFont,
497
- letterSpacing: 0.3
498
- },
499
- typeBadge: {
500
- paddingHorizontal: 8,
501
- paddingVertical: 2,
502
- borderRadius: 4,
503
- backgroundColor: night.color.surfaceElevated
504
- },
505
- typeText: {
506
- fontSize: 9,
507
- fontWeight: "600",
508
- color: night.color.textTertiary,
509
- fontFamily: monoFont
510
- },
511
- valueBox: {
512
- // Inset code box — darker than the card, the night track color.
513
- backgroundColor: night.color.bg,
514
- borderRadius: 6,
515
- borderWidth: 1,
516
- borderColor: night.color.border,
517
- padding: 8
518
- },
519
- valueText: {
520
- fontSize: 12,
521
- color: night.color.text,
522
- fontFamily: monoFont,
523
- lineHeight: 18
524
- },
525
- diffContainer: {
526
- flex: 1
527
- },
528
- diffContent: {
529
- flex: 1,
530
- paddingHorizontal: 14
531
- },
532
- changeBadgeSmall: {
533
- paddingHorizontal: 6,
534
- paddingVertical: 1,
535
- borderRadius: 3
536
- },
537
- changeTextSmall: {
538
- fontSize: 8,
539
- fontWeight: "700",
540
- fontFamily: monoFont
541
- }
542
- });
543
- const infoStyles = StyleSheet.create({
544
- container: {
545
- flex: 1
546
- },
547
- content: {
548
- padding: 8,
549
- paddingBottom: 100,
550
- gap: 16
551
- },
552
- card: {
553
- backgroundColor: night.color.surface,
554
- borderRadius: 6,
555
- borderWidth: 1,
556
- borderColor: night.color.accentBorderStrong,
557
- overflow: "hidden",
558
- shadowColor: night.color.accent,
559
- shadowOffset: {
560
- width: 0,
561
- height: 0
562
- },
563
- shadowOpacity: 0.1,
564
- shadowRadius: 6
565
- },
566
- sectionHeader: {
567
- flexDirection: "row",
568
- alignItems: "center",
569
- justifyContent: "space-between",
570
- paddingHorizontal: 12,
571
- paddingVertical: 10,
572
- borderBottomWidth: 1,
573
- borderBottomColor: night.color.accentBorder,
574
- backgroundColor: night.color.accentSoft
575
- },
576
- sectionHeaderLeft: {
577
- flexDirection: "row",
578
- alignItems: "center",
579
- gap: 6
580
- },
581
- sectionTitle: {
582
- fontSize: 12,
583
- fontWeight: "600",
584
- letterSpacing: 0.5,
585
- color: night.color.accent,
586
- fontFamily: monoFont
587
- },
588
- cardContent: {
589
- padding: 14,
590
- gap: 10
591
- },
592
- infoRow: {
593
- flexDirection: "row",
594
- alignItems: "center",
595
- justifyContent: "space-between"
596
- },
597
- infoLabel: {
598
- fontSize: 11,
599
- color: night.color.textTertiary,
600
- fontWeight: "500"
601
- },
602
- infoValue: {
603
- fontSize: 11,
604
- color: night.color.text,
605
- fontWeight: "500",
606
- flex: 1,
607
- textAlign: "right",
608
- marginLeft: 12
609
- },
610
- infoValueMono: {
611
- fontFamily: monoFont
612
- },
613
- badgeRow: {
614
- flexDirection: "row",
615
- flexWrap: "wrap",
616
- gap: 8,
617
- marginTop: 6
618
- },
619
- categoryBadge: {
620
- paddingHorizontal: 8,
621
- paddingVertical: 3,
622
- borderRadius: 4
623
- },
624
- categoryText: {
625
- fontSize: 9,
626
- fontWeight: "700",
627
- letterSpacing: 0.3
628
- },
629
- changesBadge: {
630
- flexDirection: "row",
631
- alignItems: "center",
632
- gap: 4,
633
- paddingHorizontal: 8,
634
- paddingVertical: 3,
635
- borderRadius: 4,
636
- backgroundColor: buoyColors.success + "26"
637
- },
638
- changesText: {
639
- fontSize: 9,
640
- fontWeight: "600",
641
- color: buoyColors.success
642
- },
643
- noChangesBadge: {
644
- paddingHorizontal: 8,
645
- paddingVertical: 3,
646
- borderRadius: 4,
647
- backgroundColor: night.color.surfaceElevated
648
- },
649
- noChangesText: {
650
- fontSize: 9,
651
- fontWeight: "600",
652
- color: night.color.textTertiary
653
- },
654
- keysContainer: {
655
- flexDirection: "row",
656
- flexWrap: "wrap",
657
- gap: 6
658
- },
659
- keyBadge: {
660
- paddingHorizontal: 8,
661
- paddingVertical: 4,
662
- borderRadius: 4,
663
- backgroundColor: night.color.accentSoft,
664
- borderWidth: 1,
665
- borderColor: night.color.accentBorder
666
- },
667
- keyText: {
668
- fontSize: 11,
669
- fontWeight: "600",
670
- color: night.color.accent,
671
- fontFamily: monoFont
672
- },
673
- countBadge: {
674
- paddingHorizontal: 8,
675
- paddingVertical: 3,
676
- borderRadius: 4,
677
- backgroundColor: night.color.accentFill
678
- },
679
- countText: {
680
- fontSize: 9,
681
- fontWeight: "700",
682
- color: night.color.accent,
683
- fontFamily: monoFont
684
- },
685
- actionsRow: {
686
- flexDirection: "row",
687
- gap: 12
688
- },
689
- actionButton: {
690
- paddingHorizontal: 14,
691
- paddingVertical: 8,
692
- borderRadius: 8,
693
- borderWidth: 1
694
- },
695
- actionButtonCopy: {
696
- borderColor: night.color.accentBorder,
697
- backgroundColor: night.color.accentSoft
698
- },
699
- actionButtonText: {
700
- fontSize: 10,
701
- fontWeight: "700",
702
- fontFamily: monoFont,
703
- letterSpacing: 0.3
704
- },
705
- dataViewerContainer: {
706
- marginTop: -12,
707
- marginHorizontal: -12,
708
- marginBottom: -12
709
- }
710
- });
1
+ "use strict";import xe,{memo as Z,useState as A,useCallback as x,useMemo as H}from"react";import{View as o,Text as c,StyleSheet as R,ScrollView as W}from"react-native";import{buoyColors as T,night as n,EventStepperFooter as $,formatRelativeTime as I,parseValue as v,Info as q,Zap as M,FileText as Q,Database as X,GitBranch as ee,copyToClipboard as te,ProUpgradeModal as oe,DiffModeTabs as re,CompareBar as ne,DetailViewToggle as ae,monoFont as u}from"@buoy-gg/shared-ui";import{DataViewer as ie,SplitDiffViewer as le,TreeDiffViewer as ce,diffThemes as de}from"@buoy-gg/shared-ui/dataViewer";import{useIsPro as se}from"@buoy-gg/license";import{jotaiStateStore as B}from"../utils/jotaiStateStore";import{JOTAI_TEST_ID as d,slugForTestID as fe}from"../utils/testIDs";import{jsx as e,jsxs as s}from"react/jsx-runtime";const ge=[{key:"change",label:"CHANGE",icon:Q},{key:"value",label:"VALUE",icon:X},{key:"diff",label:"DIFF",icon:ee}];const ue=["diff"];function me(r){switch(r){case"initial":return{label:"INITIAL",color:n.color.textTertiary,bgColor:n.color.textTertiary+"26"};default:return{label:"WRITE",color:T.success,bgColor:T.success+"26"}}}function he(r){const a=new Date(r);const i=a.getHours().toString().padStart(2,"0");const f=a.getMinutes().toString().padStart(2,"0");const g=a.getSeconds().toString().padStart(2,"0");const m=a.getMilliseconds().toString().padStart(3,"0");return`${i}:${f}:${g}.${m}`}function N(r){return new Date(r).toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function ye(r){if(r===null)return"null";if(r===void 0)return"undefined";if(Array.isArray(r))return"array";return typeof r}function P(r){return r==="initial"?"INIT":"WRITE"}const pe=[{key:"tree",label:"TREE VIEW"},{key:"split",label:"SPLIT VIEW"}];const be=Z(function r({change:a}){const i=se();const[f,g]=A(false);const m=me(a.category);const p=B.getAtomColor(a.atomLabel);const F=x(async()=>{if(!i){g(true);return}await te(a.nextValue)},[a.nextValue,i]);return s(W,{style:t.container,contentContainerStyle:t.content,showsVerticalScrollIndicator:false,testID:`${d}-detail-scroll`,children:[s(o,{style:t.card,testID:`${d}-info-card`,children:[e(o,{style:t.sectionHeader,children:s(o,{style:t.sectionHeaderLeft,children:[e(q,{size:14,color:n.color.accent}),e(c,{style:t.sectionTitle,children:"CHANGE INFO"})]})}),s(o,{style:t.cardContent,children:[s(o,{style:t.infoRow,children:[e(c,{style:t.infoLabel,children:"Atom"}),e(c,{style:[t.infoValue,t.infoValueMono,{color:p}],numberOfLines:1,ellipsizeMode:"middle",testID:`${d}-info-atom`,children:a.atomLabel})]}),s(o,{style:t.infoRow,children:[e(c,{style:t.infoLabel,children:"Time"}),e(c,{style:[t.infoValue,t.infoValueMono],testID:`${d}-info-time`,children:he(a.timestamp)})]}),s(o,{style:t.badgeRow,children:[e(o,{style:[t.categoryBadge,{backgroundColor:m.bgColor}],children:e(c,{style:[t.categoryText,{color:m.color}],testID:`${d}-info-category`,children:m.label})}),a.hasValueChange?s(o,{style:t.changesBadge,children:[e(M,{size:10,color:T.success}),e(c,{style:t.changesText,testID:`${d}-info-changes`,children:a.diffSummary||"changed"})]}):e(o,{style:t.noChangesBadge,children:e(c,{style:t.noChangesText,testID:`${d}-info-no-changes`,children:"no change"})})]})]})]}),a.changedKeys.length>0&&s(o,{style:t.card,testID:`${d}-keys-card`,children:[s(o,{style:t.sectionHeader,children:[s(o,{style:t.sectionHeaderLeft,children:[e(M,{size:14,color:n.color.accent}),e(c,{style:t.sectionTitle,children:"CHANGED KEYS"})]}),e(o,{style:t.countBadge,children:e(c,{style:t.countText,testID:`${d}-keys-count`,children:a.changedKeys.length})})]}),e(o,{style:t.cardContent,children:e(o,{style:t.keysContainer,children:a.changedKeys.map(y=>e(o,{style:t.keyBadge,children:e(c,{style:t.keyText,testID:`${d}-key-${fe(y)}`,children:y})},y))})})]}),e(o,{style:t.actionsRow,children:e(o,{style:[t.actionButton,t.actionButtonCopy],onTouchEnd:F,testID:`${d}-copy-value`,accessibilityRole:"button",accessibilityLabel:"Copy value",children:e(c,{style:[t.actionButtonText,{color:n.color.accent}],children:"COPY VALUE"})})}),e(oe,{visible:f,onClose:()=>g(false),featureName:"Copy"})]})});export function JotaiAtomDetailContent({change:r,changes:a,selectedIndex:i,onIndexChange:f,disableInternalFooter:g=false,initialView:m="change"}){const[p,F]=A(m);const[y,O]=A("tree");const b=a.length;const C=B.getAtomColor(r.atomLabel);const w=P(r.category);const L=ye(v(r.nextValue));const S=b-i;const V=i<b-1?i+1:null;const h=V!==null?a[V]:null;const z=S-1;const j=H(()=>({index:V??0,label:z>0?`#${z}`:"Initial",timestamp:h?N(h.timestamp):"",relativeTime:h?I(new Date(h.timestamp)):"value",badge:h?e(o,{style:[l.changeBadgeSmall,{backgroundColor:`${B.getAtomColor(h.atomLabel)}20`}],children:e(c,{style:[l.changeTextSmall,{color:B.getAtomColor(h.atomLabel)}],children:P(h.category)})}):void 0}),[V,h,z]);const _=H(()=>({index:i,label:`#${S}`,timestamp:N(r.timestamp),relativeTime:I(new Date(r.timestamp)),badge:e(o,{style:[l.changeBadgeSmall,{backgroundColor:`${C}20`}],children:e(c,{style:[l.changeTextSmall,{color:C}],children:w})})}),[i,r.timestamp,C,w,S]);const G=x(()=>{const D=v(r.nextValue);return s(o,{style:l.contentCard,testID:`${d}-value-card`,children:[s(o,{style:l.valueHeader,children:[e(c,{style:l.valueLabel,children:"VALUE AFTER CHANGE"}),s(o,{style:l.valueHeaderBadges,children:[e(o,{style:[l.changeBadge,{backgroundColor:`${C}20`}],children:e(c,{style:[l.changeText,{color:C}],children:w})}),e(o,{style:l.typeBadge,children:e(c,{style:l.typeText,testID:`${d}-value-type`,children:L.toUpperCase()})})]})]}),e(o,{style:t.dataViewerContainer,children:e(ie,{title:"",data:D,showTypeFilter:true,rawMode:true,initialExpanded:true})})]})},[r.nextValue,C,w,L]);const U=x(()=>{const D=v(r.prevValue);const E=v(r.nextValue);if(y==="split"){return e(W,{style:{flex:1},showsVerticalScrollIndicator:true,children:e(le,{oldValue:D,newValue:E,theme:de.devToolsDefault,options:{hideLineNumbers:false,disableWordDiff:false,showDiffOnly:false,compareMethod:"words",contextLines:3},showThemeName:false})})}return e(ce,{oldValue:D,newValue:E})},[r.prevValue,r.nextValue,y]);const J=x(()=>{f(Math.min(b-1,i+1))},[i,b,f]);const K=x(()=>{f(Math.max(0,i-1))},[i,f]);const Y=r.prevValue===void 0;const k=!g&&b>1;return s(o,{style:l.container,children:[e(ae,{views:ge,activeView:p,onViewChange:F,disabledKeys:Y?ue:void 0,testIDPrefix:d}),s(o,{style:[l.contentArea,k&&l.contentAreaFooterPad],children:[p==="change"&&e(be,{change:r}),p==="value"&&e(o,{style:l.stateContainer,children:G()}),p==="diff"&&s(o,{style:l.diffContainer,testID:`${d}-diff`,children:[e(re,{tabs:pe,activeTab:y,onTabChange:O,testIDPrefix:`${d}-diff-tab`}),e(ne,{leftEvent:j,rightEvent:_}),e(o,{style:l.diffContent,testID:`${d}-diff-content`,children:U()})]})]}),k&&e($,{currentIndex:S-1,totalItems:b,onPrevious:J,onNext:K,itemLabel:"Change",subtitle:I(new Date(r.timestamp)),absolute:true})]})}export function JotaiAtomDetailFooter({change:r,changes:a,selectedIndex:i,onIndexChange:f}){const g=a.length;if(g<=1)return null;const m=g-i;return e($,{currentIndex:m-1,totalItems:g,onPrevious:()=>f(Math.min(g-1,i+1)),onNext:()=>f(Math.max(0,i-1)),itemLabel:"Change",subtitle:I(new Date(r.timestamp))})}const l=R.create({container:{flex:1,backgroundColor:"transparent"},contentArea:{flex:1},contentAreaFooterPad:{paddingBottom:96},stateContainer:{flex:1,padding:14},contentCard:{backgroundColor:n.color.surface,borderRadius:14,padding:14,borderWidth:1,borderColor:n.color.border},valueHeader:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",marginBottom:8},valueLabel:{fontSize:10,color:n.color.textSecondary,fontFamily:u,letterSpacing:.5,fontWeight:"600"},valueHeaderBadges:{flexDirection:"row",alignItems:"center",gap:6},changeBadge:{paddingHorizontal:8,paddingVertical:2,borderRadius:4},changeText:{fontSize:9,fontWeight:"700",fontFamily:u,letterSpacing:.3},typeBadge:{paddingHorizontal:8,paddingVertical:2,borderRadius:4,backgroundColor:n.color.surfaceElevated},typeText:{fontSize:9,fontWeight:"600",color:n.color.textTertiary,fontFamily:u},valueBox:{backgroundColor:n.color.bg,borderRadius:6,borderWidth:1,borderColor:n.color.border,padding:8},valueText:{fontSize:12,color:n.color.text,fontFamily:u,lineHeight:18},diffContainer:{flex:1},diffContent:{flex:1,paddingHorizontal:14},changeBadgeSmall:{paddingHorizontal:6,paddingVertical:1,borderRadius:3},changeTextSmall:{fontSize:8,fontWeight:"700",fontFamily:u}});const t=R.create({container:{flex:1},content:{padding:8,paddingBottom:100,gap:16},card:{backgroundColor:n.color.surface,borderRadius:6,borderWidth:1,borderColor:n.color.accentBorderStrong,overflow:"hidden",shadowColor:n.color.accent,shadowOffset:{width:0,height:0},shadowOpacity:.1,shadowRadius:6},sectionHeader:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingHorizontal:12,paddingVertical:10,borderBottomWidth:1,borderBottomColor:n.color.accentBorder,backgroundColor:n.color.accentSoft},sectionHeaderLeft:{flexDirection:"row",alignItems:"center",gap:6},sectionTitle:{fontSize:12,fontWeight:"600",letterSpacing:.5,color:n.color.accent,fontFamily:u},cardContent:{padding:14,gap:10},infoRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-between"},infoLabel:{fontSize:11,color:n.color.textTertiary,fontWeight:"500"},infoValue:{fontSize:11,color:n.color.text,fontWeight:"500",flex:1,textAlign:"right",marginLeft:12},infoValueMono:{fontFamily:u},badgeRow:{flexDirection:"row",flexWrap:"wrap",gap:8,marginTop:6},categoryBadge:{paddingHorizontal:8,paddingVertical:3,borderRadius:4},categoryText:{fontSize:9,fontWeight:"700",letterSpacing:.3},changesBadge:{flexDirection:"row",alignItems:"center",gap:4,paddingHorizontal:8,paddingVertical:3,borderRadius:4,backgroundColor:T.success+"26"},changesText:{fontSize:9,fontWeight:"600",color:T.success},noChangesBadge:{paddingHorizontal:8,paddingVertical:3,borderRadius:4,backgroundColor:n.color.surfaceElevated},noChangesText:{fontSize:9,fontWeight:"600",color:n.color.textTertiary},keysContainer:{flexDirection:"row",flexWrap:"wrap",gap:6},keyBadge:{paddingHorizontal:8,paddingVertical:4,borderRadius:4,backgroundColor:n.color.accentSoft,borderWidth:1,borderColor:n.color.accentBorder},keyText:{fontSize:11,fontWeight:"600",color:n.color.accent,fontFamily:u},countBadge:{paddingHorizontal:8,paddingVertical:3,borderRadius:4,backgroundColor:n.color.accentFill},countText:{fontSize:9,fontWeight:"700",color:n.color.accent,fontFamily:u},actionsRow:{flexDirection:"row",gap:12},actionButton:{paddingHorizontal:14,paddingVertical:8,borderRadius:8,borderWidth:1},actionButtonCopy:{borderColor:n.color.accentBorder,backgroundColor:n.color.accentSoft},actionButtonText:{fontSize:10,fontWeight:"700",fontFamily:u,letterSpacing:.3},dataViewerContainer:{marginTop:-12,marginHorizontal:-12,marginBottom:-12}});