@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
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@buoy-gg/jotai?style=flat-square&labelColor=1c1c1c&color=10B981)](https://www.npmjs.com/package/@buoy-gg/jotai) [![npm downloads](https://img.shields.io/npm/dm/@buoy-gg/jotai?style=flat-square&labelColor=1c1c1c&color=10B981)](https://www.npmjs.com/package/@buoy-gg/jotai)
4
4
 
5
- **Jotai devtools on the device — every atom write shows `prev → next`, with per-atom history and live values.**
5
+ Inspect registered Jotai atoms and their captured changes, with live values and per-atom history.
6
6
 
7
- Part of [Buoy](https://github.com/Buoy-gg/buoy) — devtools that live inside your React Native app. Install it and it auto-appears in the floating menu from [`@buoy-gg/core`](https://www.npmjs.com/package/@buoy-gg/core).
7
+ Part of [Buoy](https://github.com/Buoy-gg/buoy). Install the package, complete account setup, and follow the integration steps below.
8
8
 
9
9
  ## Install
10
10
 
@@ -12,6 +12,24 @@ Part of [Buoy](https://github.com/Buoy-gg/buoy) — devtools that live inside yo
12
12
  npm install @buoy-gg/core @buoy-gg/jotai
13
13
  ```
14
14
 
15
+ ## Before you start
16
+
17
+ Use a development build with `@buoy-gg/core` and a Free or Pro Buoy account key. From your app’s directory, sign in:
18
+
19
+ ```bash
20
+ npx --package=@buoy-gg/core buoy login
21
+ ```
22
+
23
+ For Expo, initialize Buoy before rendering the menu:
24
+
25
+ ```tsx
26
+ import { Buoy } from "@buoy-gg/core";
27
+
28
+ Buoy.init({ licenseKey: process.env.EXPO_PUBLIC_BUOY_KEY });
29
+ ```
30
+
31
+ The login command writes the Expo key to `.env.local`. For React Native CLI, pass the key from your app’s environment configuration; React Native does not load `.env.local` automatically. Mount `FloatingDevTools` inside the same providers as your screens and restart the development server after installation. The [Quick Start](https://buoy.gg/buoy/latest/docs/quick-start) shows the complete root component setup.
32
+
15
33
  ## Quick start
16
34
 
17
35
  One `watchAtoms(store, atoms)` call at module scope — pass your Jotai store and a named map of atoms. No wrappers, no `atomWithDevTools`, no changes to existing atoms; observation is subscribe-only.
@@ -33,12 +51,21 @@ watchAtoms(getDefaultStore(), {
33
51
  Using a `<Provider>` with a custom store? Pass it instead:
34
52
 
35
53
  ```tsx
54
+ import { createStore, Provider } from "jotai";
55
+
36
56
  const myStore = createStore();
37
57
  watchAtoms(myStore, { countAtom, authAtom });
58
+
59
+ // Use this same store in the provider around your app and Buoy menu.
60
+ <Provider store={myStore}>{/* Existing app and FloatingDevTools */}</Provider>;
38
61
  ```
39
62
 
40
63
  Registered atoms appear in the Jotai tool inside the floating menu.
41
64
 
65
+ ## Check the integration
66
+
67
+ Change a registered atom through your app and inspect its history. Use the same store as your Jotai provider. Whether an atom can be edited depends on whether it is writable; a derived atom can also be writable.
68
+
42
69
  ## What you get
43
70
 
44
71
  - **`prev → next` on every event** — see `0 → 5` or `null → {name, email}` at a glance, no tapping in for simple updates
@@ -52,11 +79,11 @@ Registered atoms appear in the Jotai tool inside the floating menu.
52
79
 
53
80
  ## Desktop & AI
54
81
 
55
- The same live session streams to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) (free, macOS/Windows/Linux) and to Claude Code or Cursor via the [Buoy MCP server](https://buoy.gg/buoy/latest/docs/mcp).
82
+ To connect a React Native app to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) or MCP, install `@buoy-gg/external-sync` and follow the [Desktop connection guide](https://buoy.gg/buoy/latest/docs/desktop). Sign in to Desktop separately. [MCP setup](https://buoy.gg/buoy/latest/docs/mcp) also requires a process account and Pro access. Available remote actions depend on the tool and app integration.
56
83
 
57
- ## Free vs Pro
84
+ ## Account and plan requirements
58
85
 
59
- Every tool is free. [Pro](https://buoy.gg/pricing) unlocks production builds, the MCP server, and unlimited capture. Every weekend, Pro features unlock free for anyone holding a key — including a free one (`npx buoy login`).
86
+ Use a Free or Pro Buoy account. History limits and paid features vary by tool; see [pricing](https://buoy.gg/pricing). Production access requires Pro where supported. Development-only hooks and actions remain unavailable in release builds.
60
87
 
61
88
  ---
62
89
 
@@ -1,112 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "JOTAI_ICON_COLOR", {
7
- enumerable: true,
8
- get: function () {
9
- return _JotaiIcon.JOTAI_ICON_COLOR;
10
- }
11
- });
12
- Object.defineProperty(exports, "JotaiAtomChangeItem", {
13
- enumerable: true,
14
- get: function () {
15
- return _JotaiAtomChangeItem.JotaiAtomChangeItem;
16
- }
17
- });
18
- Object.defineProperty(exports, "JotaiAtomDetailContent", {
19
- enumerable: true,
20
- get: function () {
21
- return _JotaiAtomDetailContent.JotaiAtomDetailContent;
22
- }
23
- });
24
- Object.defineProperty(exports, "JotaiAtomDetailFooter", {
25
- enumerable: true,
26
- get: function () {
27
- return _JotaiAtomDetailContent.JotaiAtomDetailFooter;
28
- }
29
- });
30
- Object.defineProperty(exports, "JotaiAtomValueDetailContent", {
31
- enumerable: true,
32
- get: function () {
33
- return _JotaiAtomValueDetailContent.JotaiAtomValueDetailContent;
34
- }
35
- });
36
- Object.defineProperty(exports, "JotaiIcon", {
37
- enumerable: true,
38
- get: function () {
39
- return _JotaiIcon.JotaiIcon;
40
- }
41
- });
42
- Object.defineProperty(exports, "JotaiModal", {
43
- enumerable: true,
44
- get: function () {
45
- return _JotaiModal.JotaiModal;
46
- }
47
- });
48
- Object.defineProperty(exports, "JotaiToolView", {
49
- enumerable: true,
50
- get: function () {
51
- return _JotaiModal.JotaiToolView;
52
- }
53
- });
54
- Object.defineProperty(exports, "createJotaiTool", {
55
- enumerable: true,
56
- get: function () {
57
- return _preset.createJotaiTool;
58
- }
59
- });
60
- Object.defineProperty(exports, "isAtomWatched", {
61
- enumerable: true,
62
- get: function () {
63
- return _watchAtoms.isAtomWatched;
64
- }
65
- });
66
- Object.defineProperty(exports, "jotaiStateStore", {
67
- enumerable: true,
68
- get: function () {
69
- return _jotaiStateStore.jotaiStateStore;
70
- }
71
- });
72
- Object.defineProperty(exports, "jotaiSyncAdapter", {
73
- enumerable: true,
74
- get: function () {
75
- return _jotaiSyncAdapter.jotaiSyncAdapter;
76
- }
77
- });
78
- Object.defineProperty(exports, "jotaiToolPreset", {
79
- enumerable: true,
80
- get: function () {
81
- return _preset.jotaiToolPreset;
82
- }
83
- });
84
- Object.defineProperty(exports, "useJotaiAtomChanges", {
85
- enumerable: true,
86
- get: function () {
87
- return _useJotaiAtomChanges.useJotaiAtomChanges;
88
- }
89
- });
90
- Object.defineProperty(exports, "watchAtoms", {
91
- enumerable: true,
92
- get: function () {
93
- return _watchAtoms.watchAtoms;
94
- }
95
- });
96
- Object.defineProperty(exports, "watchDefaultStoreAtoms", {
97
- enumerable: true,
98
- get: function () {
99
- return _watchAtoms.watchDefaultStoreAtoms;
100
- }
101
- });
102
- require("./jotai/utils/snapshotProvider");
103
- var _preset = require("./preset");
104
- var _watchAtoms = require("./jotai/utils/watchAtoms");
105
- var _useJotaiAtomChanges = require("./jotai/hooks/useJotaiAtomChanges");
106
- var _JotaiModal = require("./jotai/components/JotaiModal");
107
- var _JotaiAtomChangeItem = require("./jotai/components/JotaiAtomChangeItem");
108
- var _JotaiAtomDetailContent = require("./jotai/components/JotaiAtomDetailContent");
109
- var _JotaiAtomValueDetailContent = require("./jotai/components/JotaiAtomValueDetailContent");
110
- var _JotaiIcon = require("./jotai/components/JotaiIcon");
111
- var _jotaiSyncAdapter = require("./jotai/sync/jotaiSyncAdapter");
112
- var _jotaiStateStore = require("./jotai/utils/jotaiStateStore");
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"JOTAI_ICON_COLOR",{enumerable:!0,get:function(){return _JotaiIcon.JOTAI_ICON_COLOR}}),Object.defineProperty(exports,"JotaiAtomChangeItem",{enumerable:!0,get:function(){return _JotaiAtomChangeItem.JotaiAtomChangeItem}}),Object.defineProperty(exports,"JotaiAtomDetailContent",{enumerable:!0,get:function(){return _JotaiAtomDetailContent.JotaiAtomDetailContent}}),Object.defineProperty(exports,"JotaiAtomDetailFooter",{enumerable:!0,get:function(){return _JotaiAtomDetailContent.JotaiAtomDetailFooter}}),Object.defineProperty(exports,"JotaiAtomValueDetailContent",{enumerable:!0,get:function(){return _JotaiAtomValueDetailContent.JotaiAtomValueDetailContent}}),Object.defineProperty(exports,"JotaiIcon",{enumerable:!0,get:function(){return _JotaiIcon.JotaiIcon}}),Object.defineProperty(exports,"JotaiModal",{enumerable:!0,get:function(){return _JotaiModal.JotaiModal}}),Object.defineProperty(exports,"JotaiToolView",{enumerable:!0,get:function(){return _JotaiModal.JotaiToolView}}),Object.defineProperty(exports,"createJotaiTool",{enumerable:!0,get:function(){return _preset.createJotaiTool}}),Object.defineProperty(exports,"isAtomWatched",{enumerable:!0,get:function(){return _watchAtoms.isAtomWatched}}),Object.defineProperty(exports,"jotaiStateStore",{enumerable:!0,get:function(){return _jotaiStateStore.jotaiStateStore}}),Object.defineProperty(exports,"jotaiSyncAdapter",{enumerable:!0,get:function(){return _jotaiSyncAdapter.jotaiSyncAdapter}}),Object.defineProperty(exports,"jotaiToolPreset",{enumerable:!0,get:function(){return _preset.jotaiToolPreset}}),Object.defineProperty(exports,"useJotaiAtomChanges",{enumerable:!0,get:function(){return _useJotaiAtomChanges.useJotaiAtomChanges}}),Object.defineProperty(exports,"watchAtoms",{enumerable:!0,get:function(){return _watchAtoms.watchAtoms}}),Object.defineProperty(exports,"watchDefaultStoreAtoms",{enumerable:!0,get:function(){return _watchAtoms.watchDefaultStoreAtoms}}),require("./jotai/utils/snapshotProvider");var _preset=require("./preset"),_watchAtoms=require("./jotai/utils/watchAtoms"),_useJotaiAtomChanges=require("./jotai/hooks/useJotaiAtomChanges"),_JotaiModal=require("./jotai/components/JotaiModal"),_JotaiAtomChangeItem=require("./jotai/components/JotaiAtomChangeItem"),_JotaiAtomDetailContent=require("./jotai/components/JotaiAtomDetailContent"),_JotaiAtomValueDetailContent=require("./jotai/components/JotaiAtomValueDetailContent"),_JotaiIcon=require("./jotai/components/JotaiIcon"),_jotaiSyncAdapter=require("./jotai/sync/jotaiSyncAdapter"),_jotaiStateStore=require("./jotai/utils/jotaiStateStore");
@@ -1,199 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.JotaiAtomBrowser = JotaiAtomBrowser;
7
- var _react = require("react");
8
- var _reactNative = require("react-native");
9
- var _sharedUi = require("@buoy-gg/shared-ui");
10
- var _jotaiStateStore = require("../utils/jotaiStateStore");
11
- var _testIDs = require("../utils/testIDs");
12
- var _jsxRuntime = require("react/jsx-runtime");
13
- /**
14
- * JotaiAtomBrowser
15
- *
16
- * Atoms tab — shows all registered Jotai atoms and their current value.
17
- * Mirrors ZustandStoreBrowser.tsx from @buoy-gg/zustand: tapping a card
18
- * NAVIGATES to the atom's detail screen (live editor + details + read-only
19
- * tree) rather than expanding in place — the modal owns that selection.
20
- */
21
-
22
- function getValuePreview(atom) {
23
- try {
24
- const value = atom.getValue();
25
- if (value && typeof value === "object" && value.__buoyValueOnDevice === true) {
26
- return "on device";
27
- }
28
- if (value === undefined) return "undefined";
29
- if (value === null) return "null";
30
- if (Array.isArray(value)) {
31
- if (value.length === 0) return "[]";
32
- return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
33
- }
34
- if (typeof value === "object") {
35
- const keys = Object.keys(value);
36
- if (keys.length === 0) return "{}";
37
- if (keys.length <= 3) return keys.join(", ");
38
- return `${keys.slice(0, 2).join(", ")} +${keys.length - 2}`;
39
- }
40
- return String(value).slice(0, 40);
41
- } catch {
42
- return "";
43
- }
44
- }
45
-
46
- /**
47
- * The card's categorical slot — the atom's write story, mirroring the
48
- * zustand card's "persisted"/"in-memory" backing label.
49
- */
50
- function getWriteKindLabel(atom) {
51
- if (atom.writeKind === "value") return "primitive";
52
- if (atom.writeKind === "action") return "custom write";
53
- return atom.setValue ? "writable" : "derived";
54
- }
55
- function EmptyBrowserState() {
56
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolEmptyState, {
57
- testID: `${_testIDs.JOTAI_TEST_ID}-atoms-empty`
58
- // scanning = "scanning for targets… and finding none". The registry is
59
- // discovered, not configured, so an empty one is a scan result.
60
- ,
61
- otto: "scanning",
62
- title: "No atoms registered",
63
- message: "Use watchAtoms(store, { atomName }) to register your Jotai atoms.\nThey will appear here with their current value."
64
- });
65
- }
66
- function JotaiAtomBrowser({
67
- atoms,
68
- searchQuery,
69
- onOpenAtom
70
- }) {
71
- const filteredAtoms = (0, _react.useMemo)(() => {
72
- if (!searchQuery) return atoms;
73
- const search = searchQuery.toLowerCase();
74
- return atoms.filter(a => a.label.toLowerCase().includes(search));
75
- }, [atoms, searchQuery]);
76
- if (filteredAtoms.length === 0 && !searchQuery) {
77
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(EmptyBrowserState, {});
78
- }
79
- if (filteredAtoms.length === 0 && searchQuery) {
80
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolEmptyState, {
81
- testID: `${_testIDs.JOTAI_TEST_ID}-atoms-no-match`,
82
- ...(0, _sharedUi.resolveListEmptyState)({
83
- subject: "atoms",
84
- verb: "registered",
85
- search: searchQuery,
86
- total: atoms.length,
87
- waiting: {
88
- otto: "scanning",
89
- title: "No atoms registered",
90
- message: "Use watchAtoms() to register your Jotai atoms."
91
- }
92
- })
93
- });
94
- }
95
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
96
- style: styles.container,
97
- contentContainerStyle: styles.scrollContent,
98
- showsVerticalScrollIndicator: true,
99
- testID: `${_testIDs.JOTAI_TEST_ID}-atoms-scroll`,
100
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
101
- style: styles.sectionHeader,
102
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
103
- style: styles.sectionTitle,
104
- children: "ATOMS"
105
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
106
- style: styles.sectionCountBadge,
107
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
108
- style: styles.sectionCountText,
109
- testID: `${_testIDs.JOTAI_TEST_ID}-atoms-count`,
110
- children: filteredAtoms.length
111
- })
112
- })]
113
- }), filteredAtoms.map(atom => {
114
- const valuePreview = getValuePreview(atom);
115
- const atomColor = _jotaiStateStore.jotaiStateStore.getAtomColor(atom.label);
116
- const rowID = (0, _testIDs.atomRowTestID)(atom.label);
117
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
118
- style: styles.atomRowWrapper,
119
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CompactRow, {
120
- statusDotColor: atomColor,
121
- statusLabel: getWriteKindLabel(atom),
122
- primaryText: atom.label,
123
- secondaryText: valuePreview || undefined,
124
- showChevron: true,
125
- onPress: () => onOpenAtom(atom.label),
126
- testID: rowID,
127
- statusTestID: `${rowID}-kind`,
128
- primaryTestID: `${rowID}-name`,
129
- secondaryTestID: `${rowID}-preview`
130
- }), atom.changeCount > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
131
- style: [styles.absCountBadge, {
132
- backgroundColor: atomColor + "22",
133
- borderColor: atomColor + "55"
134
- }],
135
- pointerEvents: "none",
136
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
137
- style: [styles.absCountText, {
138
- color: atomColor
139
- }],
140
- testID: `${rowID}-count`,
141
- children: String(atom.changeCount)
142
- })
143
- })]
144
- }, atom.label);
145
- })]
146
- });
147
- }
148
- const styles = _reactNative.StyleSheet.create({
149
- container: {
150
- flex: 1
151
- },
152
- atomRowWrapper: {
153
- position: "relative"
154
- },
155
- absCountBadge: {
156
- position: "absolute",
157
- top: 4,
158
- right: 10,
159
- paddingHorizontal: 5,
160
- paddingVertical: 1,
161
- borderRadius: 4,
162
- borderWidth: 1,
163
- zIndex: 1
164
- },
165
- absCountText: {
166
- fontSize: 9,
167
- fontWeight: "700",
168
- fontFamily: _sharedUi.monoFont
169
- },
170
- scrollContent: {
171
- paddingTop: 8,
172
- paddingBottom: 20
173
- },
174
- sectionHeader: {
175
- flexDirection: "row",
176
- alignItems: "center",
177
- paddingHorizontal: 16,
178
- paddingVertical: 8,
179
- gap: 8
180
- },
181
- sectionTitle: {
182
- fontSize: 11,
183
- fontWeight: "700",
184
- letterSpacing: 0.5,
185
- color: _sharedUi.night.color.textTertiary
186
- },
187
- sectionCountBadge: {
188
- backgroundColor: _sharedUi.night.color.accentFill,
189
- paddingHorizontal: 8,
190
- paddingVertical: 2,
191
- borderRadius: 4
192
- },
193
- sectionCountText: {
194
- fontSize: 10,
195
- fontWeight: "700",
196
- color: _sharedUi.night.color.accent,
197
- fontFamily: _sharedUi.monoFont
198
- }
199
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.JotaiAtomBrowser=JotaiAtomBrowser;var _react=require("react"),_reactNative=require("react-native"),_sharedUi=require("@buoy-gg/shared-ui"),_jotaiStateStore=require("../utils/jotaiStateStore"),_testIDs=require("../utils/testIDs"),_jsxRuntime=require("react/jsx-runtime");function getValuePreview(e){try{const t=e.getValue();if(t&&"object"==typeof t&&!0===t.__buoyValueOnDevice)return"on device";if(void 0===t)return"undefined";if(null===t)return"null";if(Array.isArray(t))return 0===t.length?"[]":`[${t.length} item${1===t.length?"":"s"}]`;if("object"==typeof t){const e=Object.keys(t);return 0===e.length?"{}":e.length<=3?e.join(", "):`${e.slice(0,2).join(", ")} +${e.length-2}`}return String(t).slice(0,40)}catch{return""}}function getWriteKindLabel(e){return"value"===e.writeKind?"primitive":"action"===e.writeKind?"custom write":e.setValue?"writable":"derived"}function EmptyBrowserState(){return(0,_jsxRuntime.jsx)(_sharedUi.ToolEmptyState,{testID:`${_testIDs.JOTAI_TEST_ID}-atoms-empty`,otto:"scanning",title:"No atoms registered",message:"Use watchAtoms(store, { atomName }) to register your Jotai atoms.\nThey will appear here with their current value."})}function JotaiAtomBrowser({atoms:e,searchQuery:t,onOpenAtom:r}){const o=(0,_react.useMemo)(()=>{if(!t)return e;const r=t.toLowerCase();return e.filter(e=>e.label.toLowerCase().includes(r))},[e,t]);return 0!==o.length||t?0===o.length&&t?(0,_jsxRuntime.jsx)(_sharedUi.ToolEmptyState,{testID:`${_testIDs.JOTAI_TEST_ID}-atoms-no-match`,...(0,_sharedUi.resolveListEmptyState)({subject:"atoms",verb:"registered",search:t,total:e.length,waiting:{otto:"scanning",title:"No atoms registered",message:"Use watchAtoms() to register your Jotai atoms."}})}):(0,_jsxRuntime.jsxs)(_reactNative.ScrollView,{style:styles.container,contentContainerStyle:styles.scrollContent,showsVerticalScrollIndicator:!0,testID:`${_testIDs.JOTAI_TEST_ID}-atoms-scroll`,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.sectionHeader,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.sectionTitle,children:"ATOMS"}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.sectionCountBadge,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.sectionCountText,testID:`${_testIDs.JOTAI_TEST_ID}-atoms-count`,children:o.length})})]}),o.map(e=>{const t=getValuePreview(e),o=_jotaiStateStore.jotaiStateStore.getAtomColor(e.label),s=(0,_testIDs.atomRowTestID)(e.label);return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.atomRowWrapper,children:[(0,_jsxRuntime.jsx)(_sharedUi.CompactRow,{statusDotColor:o,statusLabel:getWriteKindLabel(e),primaryText:e.label,secondaryText:t||void 0,showChevron:!0,onPress:()=>r(e.label),testID:s,statusTestID:`${s}-kind`,primaryTestID:`${s}-name`,secondaryTestID:`${s}-preview`}),e.changeCount>0&&(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.absCountBadge,{backgroundColor:o+"22",borderColor:o+"55"}],pointerEvents:"none",children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.absCountText,{color:o}],testID:`${s}-count`,children:String(e.changeCount)})})]},e.label)})]}):(0,_jsxRuntime.jsx)(EmptyBrowserState,{})}const styles=_reactNative.StyleSheet.create({container:{flex:1},atomRowWrapper:{position:"relative"},absCountBadge:{position:"absolute",top:4,right:10,paddingHorizontal:5,paddingVertical:1,borderRadius:4,borderWidth:1,zIndex:1},absCountText:{fontSize:9,fontWeight:"700",fontFamily:_sharedUi.monoFont},scrollContent:{paddingTop:8,paddingBottom:20},sectionHeader:{flexDirection:"row",alignItems:"center",paddingHorizontal:16,paddingVertical:8,gap:8},sectionTitle:{fontSize:11,fontWeight:"700",letterSpacing:.5,color:_sharedUi.night.color.textTertiary},sectionCountBadge:{backgroundColor:_sharedUi.night.color.accentFill,paddingHorizontal:8,paddingVertical:2,borderRadius:4},sectionCountText:{fontSize:10,fontWeight:"700",color:_sharedUi.night.color.accent,fontFamily:_sharedUi.monoFont}});
@@ -1,85 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.JotaiAtomChangeItem = void 0;
7
- var _react = require("react");
8
- var _sharedUi = require("@buoy-gg/shared-ui");
9
- var _jotaiStateStore = require("../utils/jotaiStateStore");
10
- var _jsxRuntime = require("react/jsx-runtime");
11
- /**
12
- * Compact list item for displaying a Jotai atom change
13
- *
14
- * Mirrors ZustandStateChangeItem.tsx — uses shared CompactRow for consistent styling
15
- */
16
-
17
- function getStatusLabel(category, atomLabel) {
18
- if (category === "initial") return "Initial";
19
- return atomLabel.charAt(0).toUpperCase() + atomLabel.slice(1);
20
- }
21
- function getCategoryColor(category, atomLabel) {
22
- if (category === "initial") return _sharedUi.night.color.textTertiary;
23
- return _jotaiStateStore.jotaiStateStore.getAtomColor(atomLabel);
24
- }
25
- function formatCompact(value, maxLen = 20) {
26
- if (value === undefined) return "undefined";
27
- if (value === null) return "null";
28
- if (typeof value === "boolean") return String(value);
29
- if (typeof value === "number") return String(value);
30
- if (typeof value === "string") {
31
- const truncated = value.length > maxLen ? value.slice(0, maxLen - 1) + "…" : value;
32
- return `"${truncated}"`;
33
- }
34
- if (Array.isArray(value)) {
35
- return value.length === 0 ? "[]" : `[${value.length} item${value.length === 1 ? "" : "s"}]`;
36
- }
37
- if (typeof value === "object") {
38
- const keys = Object.keys(value);
39
- if (keys.length === 0) return "{}";
40
- return `{${keys.slice(0, 2).join(", ")}${keys.length > 2 ? "…" : ""}}`;
41
- }
42
- return String(value).slice(0, maxLen);
43
- }
44
- function getSublabel(change) {
45
- if (!change.hasValueChange) return "no change";
46
- return `${formatCompact(change.prevValue)} → ${formatCompact(change.nextValue)}`;
47
- }
48
- function getBadgeText(change) {
49
- if (change.category === "initial") return "INIT";
50
- return "WRITE";
51
- }
52
- function getPrimaryText(change) {
53
- if (change.changedKeys.length > 0 && change.changedKeys.length <= 3) {
54
- return change.changedKeys.join(", ");
55
- }
56
- if (change.changedKeys.length > 3) {
57
- return `${change.changedKeys.slice(0, 2).join(", ")} +${change.changedKeys.length - 2}`;
58
- }
59
- return change.valuePreview || "atom()";
60
- }
61
-
62
- // Memo justified by profiling (tool-bench 2026-07-06) — see
63
- // ZustandStateChangeItem (same pattern).
64
- const JotaiAtomChangeItem = exports.JotaiAtomChangeItem = /*#__PURE__*/(0, _react.memo)(function JotaiAtomChangeItem({
65
- change,
66
- onPress,
67
- testID
68
- }) {
69
- const statusColor = getCategoryColor(change.category, change.atomLabel);
70
- const statusLabel = getStatusLabel(change.category, change.atomLabel);
71
- const sublabel = getSublabel(change);
72
- const primaryText = getPrimaryText(change);
73
- const badgeText = getBadgeText(change);
74
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChangeRow, {
75
- statusColor: statusColor,
76
- statusLabel: statusLabel,
77
- sublabel: sublabel,
78
- primaryText: primaryText,
79
- timestamp: change.timestamp,
80
- badgeText: badgeText,
81
- changed: change.hasValueChange,
82
- onPress: () => onPress(change),
83
- testID: testID
84
- });
85
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.JotaiAtomChangeItem=void 0;var _react=require("react"),_sharedUi=require("@buoy-gg/shared-ui"),_jotaiStateStore=require("../utils/jotaiStateStore"),_jsxRuntime=require("react/jsx-runtime");function getStatusLabel(e,t){return"initial"===e?"Initial":t.charAt(0).toUpperCase()+t.slice(1)}function getCategoryColor(e,t){return"initial"===e?_sharedUi.night.color.textTertiary:_jotaiStateStore.jotaiStateStore.getAtomColor(t)}function formatCompact(e,t=20){if(void 0===e)return"undefined";if(null===e)return"null";if("boolean"==typeof e)return String(e);if("number"==typeof e)return String(e);if("string"==typeof e)return`"${e.length>t?e.slice(0,t-1)+"…":e}"`;if(Array.isArray(e))return 0===e.length?"[]":`[${e.length} item${1===e.length?"":"s"}]`;if("object"==typeof e){const t=Object.keys(e);return 0===t.length?"{}":`{${t.slice(0,2).join(", ")}${t.length>2?"…":""}}`}return String(e).slice(0,t)}function getSublabel(e){return e.hasValueChange?`${formatCompact(e.prevValue)} → ${formatCompact(e.nextValue)}`:"no change"}function getBadgeText(e){return"initial"===e.category?"INIT":"WRITE"}function getPrimaryText(e){return e.changedKeys.length>0&&e.changedKeys.length<=3?e.changedKeys.join(", "):e.changedKeys.length>3?`${e.changedKeys.slice(0,2).join(", ")} +${e.changedKeys.length-2}`:e.valuePreview||"atom()"}const JotaiAtomChangeItem=exports.JotaiAtomChangeItem=(0,_react.memo)(function({change:e,onPress:t,testID:r}){const a=getCategoryColor(e.category,e.atomLabel),n=getStatusLabel(e.category,e.atomLabel),o=getSublabel(e),i=getPrimaryText(e),g=getBadgeText(e);return(0,_jsxRuntime.jsx)(_sharedUi.ChangeRow,{statusColor:a,statusLabel:n,sublabel:o,primaryText:i,timestamp:e.timestamp,badgeText:g,changed:e.hasValueChange,onPress:()=>t(e),testID:r})});