@buoy-gg/jotai 7.0.35 → 7.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +32 -5
  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 -85
  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 -67
  11. package/lib/commonjs/jotai/sync/jotaiSyncAdapter.js +1 -149
  12. package/lib/commonjs/jotai/utils/jotaiStateStore.js +1 -321
  13. package/lib/commonjs/jotai/utils/snapshotProvider.js +1 -215
  14. package/lib/commonjs/jotai/utils/testIDs.js +1 -90
  15. package/lib/commonjs/jotai/utils/watchAtoms.js +1 -162
  16. package/lib/commonjs/jotai/utils/writeSemantics.js +1 -47
  17. package/lib/commonjs/preset.js +1 -90
  18. package/lib/module/index.js +1 -85
  19. package/lib/module/jotai/components/JotaiAtomBrowser.js +1 -195
  20. package/lib/module/jotai/components/JotaiAtomChangeItem.js +1 -81
  21. package/lib/module/jotai/components/JotaiAtomDetailContent.js +1 -710
  22. package/lib/module/jotai/components/JotaiAtomValueDetailContent.js +1 -442
  23. package/lib/module/jotai/components/JotaiEventFilterView.js +1 -39
  24. package/lib/module/jotai/components/JotaiIcon.js +1 -11
  25. package/lib/module/jotai/components/JotaiModal.js +1 -738
  26. package/lib/module/jotai/hooks/useJotaiAtomChanges.js +1 -63
  27. package/lib/module/jotai/sync/jotaiSyncAdapter.js +1 -146
  28. package/lib/module/jotai/utils/jotaiStateStore.js +1 -317
  29. package/lib/module/jotai/utils/snapshotProvider.js +1 -214
  30. package/lib/module/jotai/utils/testIDs.js +1 -82
  31. package/lib/module/jotai/utils/watchAtoms.js +1 -157
  32. package/lib/module/jotai/utils/writeSemantics.js +1 -43
  33. package/lib/module/preset.js +1 -87
  34. package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts +52 -2
  35. package/package.json +4 -4
  36. package/lib/typescript/index.d.ts.map +0 -1
  37. package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts.map +0 -1
  38. package/lib/typescript/jotai/components/JotaiAtomChangeItem.d.ts.map +0 -1
  39. package/lib/typescript/jotai/components/JotaiAtomDetailContent.d.ts.map +0 -1
  40. package/lib/typescript/jotai/components/JotaiAtomValueDetailContent.d.ts.map +0 -1
  41. package/lib/typescript/jotai/components/JotaiEventFilterView.d.ts.map +0 -1
  42. package/lib/typescript/jotai/components/JotaiIcon.d.ts.map +0 -1
  43. package/lib/typescript/jotai/components/JotaiModal.d.ts.map +0 -1
  44. package/lib/typescript/jotai/hooks/useJotaiAtomChanges.d.ts.map +0 -1
  45. package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts.map +0 -1
  46. package/lib/typescript/jotai/types/index.d.ts.map +0 -1
  47. package/lib/typescript/jotai/utils/jotaiStateStore.d.ts.map +0 -1
  48. package/lib/typescript/jotai/utils/snapshotProvider.d.ts.map +0 -1
  49. package/lib/typescript/jotai/utils/testIDs.d.ts.map +0 -1
  50. package/lib/typescript/jotai/utils/watchAtoms.d.ts.map +0 -1
  51. package/lib/typescript/jotai/utils/writeSemantics.d.ts.map +0 -1
  52. 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 React,{memo,useState,useCallback,useMemo}from"react";import{View,Text,StyleSheet,ScrollView}from"react-native";import{buoyColors,night,EventStepperFooter,formatRelativeTime,parseValue,Info,Zap,FileText,Database,GitBranch,copyToClipboard,ProUpgradeModal,DiffModeTabs,CompareBar,DetailViewToggle,monoFont}from"@buoy-gg/shared-ui";import{DataViewer,SplitDiffViewer,TreeDiffViewer,diffThemes}from"@buoy-gg/shared-ui/dataViewer";import{useIsPro}from"@buoy-gg/license";import{jotaiStateStore}from"../utils/jotaiStateStore";import{JOTAI_TEST_ID,slugForTestID}from"../utils/testIDs";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";const VIEW_CONFIGS=[{key:"change",label:"CHANGE",icon:FileText},{key:"value",label:"VALUE",icon:Database},{key:"diff",label:"DIFF",icon:GitBranch}],DIFF_DISABLED=["diff"];function getCategoryInfo(e){return"initial"===e?{label:"INITIAL",color:night.color.textTertiary,bgColor:night.color.textTertiary+"26"}:{label:"WRITE",color:buoyColors.success,bgColor:buoyColors.success+"26"}}function formatTime(e){const t=new Date(e);return`${t.getHours().toString().padStart(2,"0")}:${t.getMinutes().toString().padStart(2,"0")}:${t.getSeconds().toString().padStart(2,"0")}.${t.getMilliseconds().toString().padStart(3,"0")}`}function formatTimestamp(e){return new Date(e).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}function getValueType(e){return null===e?"null":void 0===e?"undefined":Array.isArray(e)?"array":typeof e}function getChangeLabel(e){return"initial"===e?"INIT":"WRITE"}const DIFF_MODE_TABS=[{key:"tree",label:"TREE VIEW"},{key:"split",label:"SPLIT VIEW"}],JotaiAtomInfoView=memo(function({change:e}){const t=useIsPro(),[o,n]=useState(!1),i=getCategoryInfo(e.category),a=jotaiStateStore.getAtomColor(e.atomLabel),l=useCallback(async()=>{t?await copyToClipboard(e.nextValue):n(!0)},[e.nextValue,t]);return _jsxs(ScrollView,{style:infoStyles.container,contentContainerStyle:infoStyles.content,showsVerticalScrollIndicator:!1,testID:`${JOTAI_TEST_ID}-detail-scroll`,children:[_jsxs(View,{style:infoStyles.card,testID:`${JOTAI_TEST_ID}-info-card`,children:[_jsx(View,{style:infoStyles.sectionHeader,children:_jsxs(View,{style:infoStyles.sectionHeaderLeft,children:[_jsx(Info,{size:14,color:night.color.accent}),_jsx(Text,{style:infoStyles.sectionTitle,children:"CHANGE INFO"})]})}),_jsxs(View,{style:infoStyles.cardContent,children:[_jsxs(View,{style:infoStyles.infoRow,children:[_jsx(Text,{style:infoStyles.infoLabel,children:"Atom"}),_jsx(Text,{style:[infoStyles.infoValue,infoStyles.infoValueMono,{color:a}],numberOfLines:1,ellipsizeMode:"middle",testID:`${JOTAI_TEST_ID}-info-atom`,children:e.atomLabel})]}),_jsxs(View,{style:infoStyles.infoRow,children:[_jsx(Text,{style:infoStyles.infoLabel,children:"Time"}),_jsx(Text,{style:[infoStyles.infoValue,infoStyles.infoValueMono],testID:`${JOTAI_TEST_ID}-info-time`,children:formatTime(e.timestamp)})]}),_jsxs(View,{style:infoStyles.badgeRow,children:[_jsx(View,{style:[infoStyles.categoryBadge,{backgroundColor:i.bgColor}],children:_jsx(Text,{style:[infoStyles.categoryText,{color:i.color}],testID:`${JOTAI_TEST_ID}-info-category`,children:i.label})}),e.hasValueChange?_jsxs(View,{style:infoStyles.changesBadge,children:[_jsx(Zap,{size:10,color:buoyColors.success}),_jsx(Text,{style:infoStyles.changesText,testID:`${JOTAI_TEST_ID}-info-changes`,children:e.diffSummary||"changed"})]}):_jsx(View,{style:infoStyles.noChangesBadge,children:_jsx(Text,{style:infoStyles.noChangesText,testID:`${JOTAI_TEST_ID}-info-no-changes`,children:"no change"})})]})]})]}),e.changedKeys.length>0&&_jsxs(View,{style:infoStyles.card,testID:`${JOTAI_TEST_ID}-keys-card`,children:[_jsxs(View,{style:infoStyles.sectionHeader,children:[_jsxs(View,{style:infoStyles.sectionHeaderLeft,children:[_jsx(Zap,{size:14,color:night.color.accent}),_jsx(Text,{style:infoStyles.sectionTitle,children:"CHANGED KEYS"})]}),_jsx(View,{style:infoStyles.countBadge,children:_jsx(Text,{style:infoStyles.countText,testID:`${JOTAI_TEST_ID}-keys-count`,children:e.changedKeys.length})})]}),_jsx(View,{style:infoStyles.cardContent,children:_jsx(View,{style:infoStyles.keysContainer,children:e.changedKeys.map(e=>_jsx(View,{style:infoStyles.keyBadge,children:_jsx(Text,{style:infoStyles.keyText,testID:`${JOTAI_TEST_ID}-key-${slugForTestID(e)}`,children:e})},e))})})]}),_jsx(View,{style:infoStyles.actionsRow,children:_jsx(View,{style:[infoStyles.actionButton,infoStyles.actionButtonCopy],onTouchEnd:l,testID:`${JOTAI_TEST_ID}-copy-value`,accessibilityRole:"button",accessibilityLabel:"Copy value",children:_jsx(Text,{style:[infoStyles.actionButtonText,{color:night.color.accent}],children:"COPY VALUE"})})}),_jsx(ProUpgradeModal,{visible:o,onClose:()=>n(!1),featureName:"Copy"})]})});export function JotaiAtomDetailContent({change:e,changes:t,selectedIndex:o,onIndexChange:n,disableInternalFooter:i=!1,initialView:a="change"}){const[l,r]=useState(a),[s,c]=useState("tree"),d=t.length,g=jotaiStateStore.getAtomColor(e.atomLabel),f=getChangeLabel(e.category),y=getValueType(parseValue(e.nextValue)),h=d-o,u=o<d-1?o+1:null,x=null!==u?t[u]:null,m=h-1,T=useMemo(()=>({index:u??0,label:m>0?`#${m}`:"Initial",timestamp:x?formatTimestamp(x.timestamp):"",relativeTime:x?formatRelativeTime(new Date(x.timestamp)):"value",badge:x?_jsx(View,{style:[styles.changeBadgeSmall,{backgroundColor:`${jotaiStateStore.getAtomColor(x.atomLabel)}20`}],children:_jsx(Text,{style:[styles.changeTextSmall,{color:jotaiStateStore.getAtomColor(x.atomLabel)}],children:getChangeLabel(x.category)})}):void 0}),[u,x,m]),S=useMemo(()=>({index:o,label:`#${h}`,timestamp:formatTimestamp(e.timestamp),relativeTime:formatRelativeTime(new Date(e.timestamp)),badge:_jsx(View,{style:[styles.changeBadgeSmall,{backgroundColor:`${g}20`}],children:_jsx(Text,{style:[styles.changeTextSmall,{color:g}],children:f})})}),[o,e.timestamp,g,f,h]),p=useCallback(()=>{const t=parseValue(e.nextValue);return _jsxs(View,{style:styles.contentCard,testID:`${JOTAI_TEST_ID}-value-card`,children:[_jsxs(View,{style:styles.valueHeader,children:[_jsx(Text,{style:styles.valueLabel,children:"VALUE AFTER CHANGE"}),_jsxs(View,{style:styles.valueHeaderBadges,children:[_jsx(View,{style:[styles.changeBadge,{backgroundColor:`${g}20`}],children:_jsx(Text,{style:[styles.changeText,{color:g}],children:f})}),_jsx(View,{style:styles.typeBadge,children:_jsx(Text,{style:styles.typeText,testID:`${JOTAI_TEST_ID}-value-type`,children:y.toUpperCase()})})]})]}),_jsx(View,{style:infoStyles.dataViewerContainer,children:_jsx(DataViewer,{title:"",data:t,showTypeFilter:!0,rawMode:!0,initialExpanded:!0})})]})},[e.nextValue,g,f,y]),b=useCallback(()=>{const t=parseValue(e.prevValue),o=parseValue(e.nextValue);return"split"===s?_jsx(ScrollView,{style:{flex:1},showsVerticalScrollIndicator:!0,children:_jsx(SplitDiffViewer,{oldValue:t,newValue:o,theme:diffThemes.devToolsDefault,options:{hideLineNumbers:!1,disableWordDiff:!1,showDiffOnly:!1,compareMethod:"words",contextLines:3},showThemeName:!1})}):_jsx(TreeDiffViewer,{oldValue:t,newValue:o})},[e.prevValue,e.nextValue,s]),_=useCallback(()=>{n(Math.min(d-1,o+1))},[o,d,n]),I=useCallback(()=>{n(Math.max(0,o-1))},[o,n]),V=void 0===e.prevValue,w=!i&&d>1;return _jsxs(View,{style:styles.container,children:[_jsx(DetailViewToggle,{views:VIEW_CONFIGS,activeView:l,onViewChange:r,disabledKeys:V?DIFF_DISABLED:void 0,testIDPrefix:JOTAI_TEST_ID}),_jsxs(View,{style:[styles.contentArea,w&&styles.contentAreaFooterPad],children:["change"===l&&_jsx(JotaiAtomInfoView,{change:e}),"value"===l&&_jsx(View,{style:styles.stateContainer,children:p()}),"diff"===l&&_jsxs(View,{style:styles.diffContainer,testID:`${JOTAI_TEST_ID}-diff`,children:[_jsx(DiffModeTabs,{tabs:DIFF_MODE_TABS,activeTab:s,onTabChange:c,testIDPrefix:`${JOTAI_TEST_ID}-diff-tab`}),_jsx(CompareBar,{leftEvent:T,rightEvent:S}),_jsx(View,{style:styles.diffContent,testID:`${JOTAI_TEST_ID}-diff-content`,children:b()})]})]}),w&&_jsx(EventStepperFooter,{currentIndex:h-1,totalItems:d,onPrevious:_,onNext:I,itemLabel:"Change",subtitle:formatRelativeTime(new Date(e.timestamp)),absolute:!0})]})}export function JotaiAtomDetailFooter({change:e,changes:t,selectedIndex:o,onIndexChange:n}){const i=t.length;return i<=1?null:_jsx(EventStepperFooter,{currentIndex:i-o-1,totalItems:i,onPrevious:()=>n(Math.min(i-1,o+1)),onNext:()=>n(Math.max(0,o-1)),itemLabel:"Change",subtitle:formatRelativeTime(new Date(e.timestamp))})}const styles=StyleSheet.create({container:{flex:1,backgroundColor:"transparent"},contentArea:{flex:1},contentAreaFooterPad:{paddingBottom:96},stateContainer:{flex:1,padding:14},contentCard:{backgroundColor:night.color.surface,borderRadius:14,padding:14,borderWidth:1,borderColor:night.color.border},valueHeader:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",marginBottom:8},valueLabel:{fontSize:10,color:night.color.textSecondary,fontFamily:monoFont,letterSpacing:.5,fontWeight:"600"},valueHeaderBadges:{flexDirection:"row",alignItems:"center",gap:6},changeBadge:{paddingHorizontal:8,paddingVertical:2,borderRadius:4},changeText:{fontSize:9,fontWeight:"700",fontFamily:monoFont,letterSpacing:.3},typeBadge:{paddingHorizontal:8,paddingVertical:2,borderRadius:4,backgroundColor:night.color.surfaceElevated},typeText:{fontSize:9,fontWeight:"600",color:night.color.textTertiary,fontFamily:monoFont},valueBox:{backgroundColor:night.color.bg,borderRadius:6,borderWidth:1,borderColor:night.color.border,padding:8},valueText:{fontSize:12,color:night.color.text,fontFamily:monoFont,lineHeight:18},diffContainer:{flex:1},diffContent:{flex:1,paddingHorizontal:14},changeBadgeSmall:{paddingHorizontal:6,paddingVertical:1,borderRadius:3},changeTextSmall:{fontSize:8,fontWeight:"700",fontFamily:monoFont}}),infoStyles=StyleSheet.create({container:{flex:1},content:{padding:8,paddingBottom:100,gap:16},card:{backgroundColor:night.color.surface,borderRadius:6,borderWidth:1,borderColor:night.color.accentBorderStrong,overflow:"hidden",shadowColor:night.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:night.color.accentBorder,backgroundColor:night.color.accentSoft},sectionHeaderLeft:{flexDirection:"row",alignItems:"center",gap:6},sectionTitle:{fontSize:12,fontWeight:"600",letterSpacing:.5,color:night.color.accent,fontFamily:monoFont},cardContent:{padding:14,gap:10},infoRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-between"},infoLabel:{fontSize:11,color:night.color.textTertiary,fontWeight:"500"},infoValue:{fontSize:11,color:night.color.text,fontWeight:"500",flex:1,textAlign:"right",marginLeft:12},infoValueMono:{fontFamily:monoFont},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:buoyColors.success+"26"},changesText:{fontSize:9,fontWeight:"600",color:buoyColors.success},noChangesBadge:{paddingHorizontal:8,paddingVertical:3,borderRadius:4,backgroundColor:night.color.surfaceElevated},noChangesText:{fontSize:9,fontWeight:"600",color:night.color.textTertiary},keysContainer:{flexDirection:"row",flexWrap:"wrap",gap:6},keyBadge:{paddingHorizontal:8,paddingVertical:4,borderRadius:4,backgroundColor:night.color.accentSoft,borderWidth:1,borderColor:night.color.accentBorder},keyText:{fontSize:11,fontWeight:"600",color:night.color.accent,fontFamily:monoFont},countBadge:{paddingHorizontal:8,paddingVertical:3,borderRadius:4,backgroundColor:night.color.accentFill},countText:{fontSize:9,fontWeight:"700",color:night.color.accent,fontFamily:monoFont},actionsRow:{flexDirection:"row",gap:12},actionButton:{paddingHorizontal:14,paddingVertical:8,borderRadius:8,borderWidth:1},actionButtonCopy:{borderColor:night.color.accentBorder,backgroundColor:night.color.accentSoft},actionButtonText:{fontSize:10,fontWeight:"700",fontFamily:monoFont,letterSpacing:.3},dataViewerContainer:{marginTop:-12,marginHorizontal:-12,marginBottom:-12}});