@buoy-gg/jotai 3.0.1 → 4.0.1

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