@buoy-gg/jotai 7.0.32 → 7.0.34
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.
- package/lib/commonjs/jotai/components/JotaiAtomBrowser.js +20 -41
- package/lib/commonjs/jotai/components/JotaiAtomChangeItem.js +7 -52
- package/lib/commonjs/jotai/components/JotaiAtomDetailContent.js +8 -87
- package/lib/commonjs/jotai/components/JotaiEventFilterView.js +21 -299
- package/lib/commonjs/jotai/components/JotaiModal.js +82 -124
- package/lib/commonjs/jotai/hooks/useJotaiAtomChanges.js +33 -49
- package/lib/commonjs/jotai/sync/jotaiSyncAdapter.js +19 -66
- package/lib/commonjs/jotai/utils/jotaiStateStore.js +23 -188
- package/lib/commonjs/jotai/utils/snapshotProvider.js +7 -25
- package/lib/commonjs/preset.js +14 -22
- package/lib/module/jotai/components/JotaiAtomBrowser.js +21 -42
- package/lib/module/jotai/components/JotaiAtomChangeItem.js +9 -54
- package/lib/module/jotai/components/JotaiAtomDetailContent.js +9 -88
- package/lib/module/jotai/components/JotaiEventFilterView.js +24 -302
- package/lib/module/jotai/components/JotaiModal.js +84 -126
- package/lib/module/jotai/hooks/useJotaiAtomChanges.js +34 -50
- package/lib/module/jotai/sync/jotaiSyncAdapter.js +20 -67
- package/lib/module/jotai/utils/jotaiStateStore.js +23 -188
- package/lib/module/jotai/utils/snapshotProvider.js +8 -26
- package/lib/module/preset.js +13 -20
- package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomChangeItem.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomDetailContent.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiEventFilterView.d.ts +2 -2
- package/lib/typescript/jotai/components/JotaiEventFilterView.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiModal.d.ts.map +1 -1
- package/lib/typescript/jotai/hooks/useJotaiAtomChanges.d.ts +2 -1
- package/lib/typescript/jotai/hooks/useJotaiAtomChanges.d.ts.map +1 -1
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts +54 -68
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/jotai/utils/jotaiStateStore.d.ts +2 -25
- package/lib/typescript/jotai/utils/jotaiStateStore.d.ts.map +1 -1
- package/lib/typescript/preset.d.ts +2 -8
- package/lib/typescript/preset.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.jotaiStateStore = void 0;
|
|
7
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
7
8
|
/**
|
|
8
9
|
* Jotai state store — captures and stores Jotai atom changes
|
|
9
10
|
*
|
|
@@ -40,44 +41,7 @@ const ATOM_COLORS = {
|
|
|
40
41
|
filter: "#84CC16" // lime
|
|
41
42
|
};
|
|
42
43
|
function getAtomColor(label) {
|
|
43
|
-
|
|
44
|
-
if (ATOM_COLORS[lower]) return ATOM_COLORS[lower];
|
|
45
|
-
for (const [key, color] of Object.entries(ATOM_COLORS)) {
|
|
46
|
-
if (lower.includes(key)) return color;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Generate consistent hex from name hash
|
|
50
|
-
const hash = label.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
51
|
-
const hue = hash * 137 % 360;
|
|
52
|
-
const s = 0.7;
|
|
53
|
-
const l = 0.6;
|
|
54
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
55
|
-
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
56
|
-
const m = l - c / 2;
|
|
57
|
-
let r = 0,
|
|
58
|
-
g = 0,
|
|
59
|
-
b = 0;
|
|
60
|
-
if (hue < 60) {
|
|
61
|
-
r = c;
|
|
62
|
-
g = x;
|
|
63
|
-
} else if (hue < 120) {
|
|
64
|
-
r = x;
|
|
65
|
-
g = c;
|
|
66
|
-
} else if (hue < 180) {
|
|
67
|
-
g = c;
|
|
68
|
-
b = x;
|
|
69
|
-
} else if (hue < 240) {
|
|
70
|
-
g = x;
|
|
71
|
-
b = c;
|
|
72
|
-
} else if (hue < 300) {
|
|
73
|
-
r = x;
|
|
74
|
-
b = c;
|
|
75
|
-
} else {
|
|
76
|
-
r = c;
|
|
77
|
-
b = x;
|
|
78
|
-
}
|
|
79
|
-
const toHex = v => Math.round((v + m) * 255).toString(16).padStart(2, "0");
|
|
80
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
44
|
+
return (0, _sharedUi.entityColor)(label, ATOM_COLORS);
|
|
81
45
|
}
|
|
82
46
|
|
|
83
47
|
// ============================================
|
|
@@ -85,97 +49,25 @@ function getAtomColor(label) {
|
|
|
85
49
|
// ============================================
|
|
86
50
|
|
|
87
51
|
function formatValuePreview(value, maxLength = 40) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (typeof value === "string") {
|
|
93
|
-
return value.length > maxLength ? `"${value.slice(0, maxLength - 3)}..."` : `"${value}"`;
|
|
94
|
-
}
|
|
95
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
96
|
-
return String(value);
|
|
97
|
-
}
|
|
98
|
-
if (Array.isArray(value)) {
|
|
99
|
-
if (value.length === 0) return "[]";
|
|
100
|
-
const preview = JSON.stringify(value);
|
|
101
|
-
return preview.length > maxLength ? `[${value.length} items]` : preview;
|
|
102
|
-
}
|
|
103
|
-
if (typeof value === "object") {
|
|
104
|
-
const keys = Object.keys(value);
|
|
105
|
-
if (keys.length === 0) return "{}";
|
|
106
|
-
const preview = JSON.stringify(value);
|
|
107
|
-
if (preview.length <= maxLength) return preview;
|
|
108
|
-
return `{ ${keys.length} keys }`;
|
|
109
|
-
}
|
|
110
|
-
return String(value).slice(0, maxLength);
|
|
111
|
-
} catch {
|
|
112
|
-
return "[complex]";
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function getValueDiffSummary(prevValue, nextValue) {
|
|
116
|
-
if (prevValue === nextValue) {
|
|
117
|
-
return {
|
|
118
|
-
summary: "no change",
|
|
119
|
-
changedKeys: [],
|
|
120
|
-
changedCount: 0
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
if (typeof prevValue !== "object" || typeof nextValue !== "object" || prevValue === null || nextValue === null) {
|
|
124
|
-
return {
|
|
125
|
-
summary: "changed",
|
|
126
|
-
changedKeys: [],
|
|
127
|
-
changedCount: 0
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
const prevKeys = Object.keys(prevValue);
|
|
131
|
-
const nextKeys = Object.keys(nextValue);
|
|
132
|
-
const added = nextKeys.filter(k => !prevKeys.includes(k));
|
|
133
|
-
const removed = prevKeys.filter(k => !nextKeys.includes(k));
|
|
134
|
-
const changed = [];
|
|
135
|
-
for (const key of prevKeys) {
|
|
136
|
-
if (nextKeys.includes(key) && prevValue[key] !== nextValue[key]) {
|
|
137
|
-
changed.push(key);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const allChangedKeys = [...added, ...removed, ...changed];
|
|
141
|
-
const parts = [];
|
|
142
|
-
if (added.length > 0) parts.push(`+${added.length}`);
|
|
143
|
-
if (removed.length > 0) parts.push(`-${removed.length}`);
|
|
144
|
-
if (changed.length > 0) parts.push(`~${changed.length}`);
|
|
145
|
-
const total = allChangedKeys.length;
|
|
146
|
-
if (parts.length === 0) {
|
|
147
|
-
return {
|
|
148
|
-
summary: "nested change",
|
|
149
|
-
changedKeys: [],
|
|
150
|
-
changedCount: 0
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
return {
|
|
154
|
-
summary: `${parts.join(" ")} ${total === 1 ? "key" : "keys"}`,
|
|
155
|
-
changedKeys: allChangedKeys,
|
|
156
|
-
changedCount: total
|
|
157
|
-
};
|
|
52
|
+
return (0, _sharedUi.formatValuePreview)(value, {
|
|
53
|
+
maxLength,
|
|
54
|
+
functionLabel: "(fn)"
|
|
55
|
+
});
|
|
158
56
|
}
|
|
57
|
+
const getValueDiffSummary = _sharedUi.shallowDiffSummary;
|
|
159
58
|
|
|
160
59
|
// ============================================
|
|
161
60
|
// Jotai State Store
|
|
162
61
|
// ============================================
|
|
163
62
|
|
|
164
|
-
class JotaiStateStore {
|
|
165
|
-
atomChanges = [];
|
|
63
|
+
class JotaiStateStore extends _sharedUi.ChangeLog {
|
|
166
64
|
atoms = new Map();
|
|
167
|
-
listeners = new Set();
|
|
168
65
|
atomListeners = new Set();
|
|
169
|
-
clearListeners = new Set();
|
|
170
|
-
maxChanges = 200;
|
|
171
|
-
idCounter = 0;
|
|
172
|
-
isEnabled = true;
|
|
173
|
-
captureSuppressed = false;
|
|
174
66
|
|
|
175
67
|
// ---- Change Tracking ----
|
|
176
68
|
|
|
177
69
|
addAtomChange(params) {
|
|
178
|
-
if (!this.
|
|
70
|
+
if (!this.canRecord()) return;
|
|
179
71
|
const {
|
|
180
72
|
atomLabel,
|
|
181
73
|
prevValue,
|
|
@@ -190,7 +82,7 @@ class JotaiStateStore {
|
|
|
190
82
|
} = getValueDiffSummary(prevValue, nextValue);
|
|
191
83
|
const valuePreview = formatValuePreview(nextValue);
|
|
192
84
|
const change = {
|
|
193
|
-
id:
|
|
85
|
+
id: this.nextId(),
|
|
194
86
|
atomLabel,
|
|
195
87
|
timestamp: Date.now(),
|
|
196
88
|
prevValue,
|
|
@@ -203,7 +95,7 @@ class JotaiStateStore {
|
|
|
203
95
|
valuePreview,
|
|
204
96
|
isSlowUpdate: false
|
|
205
97
|
};
|
|
206
|
-
this.
|
|
98
|
+
this.push(change);
|
|
207
99
|
const atomInfo = this.atoms.get(atomLabel);
|
|
208
100
|
if (atomInfo) {
|
|
209
101
|
atomInfo.changeCount++;
|
|
@@ -212,54 +104,21 @@ class JotaiStateStore {
|
|
|
212
104
|
this.notifyAtomListeners();
|
|
213
105
|
}
|
|
214
106
|
getAtomChanges() {
|
|
215
|
-
return
|
|
107
|
+
return this.getAll();
|
|
216
108
|
}
|
|
217
109
|
getAtomChangeById(id) {
|
|
218
|
-
return this.
|
|
110
|
+
return this.getById(id);
|
|
219
111
|
}
|
|
220
112
|
clearAtomChanges() {
|
|
221
|
-
this.atomChanges = [];
|
|
222
113
|
for (const atom of this.atoms.values()) {
|
|
223
114
|
atom.changeCount = 0;
|
|
224
115
|
}
|
|
225
|
-
this.
|
|
116
|
+
this.clear();
|
|
226
117
|
this.notifyAtomListeners();
|
|
227
|
-
this.clearListeners.forEach(listener => {
|
|
228
|
-
try {
|
|
229
|
-
listener();
|
|
230
|
-
} catch {
|
|
231
|
-
// Ignore listener errors
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Listen for clearAtomChanges() calls. Used in remote mirror mode to
|
|
238
|
-
* forward a clear performed in the dashboard UI to the synced device.
|
|
239
|
-
*/
|
|
240
|
-
onClear(listener) {
|
|
241
|
-
this.clearListeners.add(listener);
|
|
242
|
-
return () => {
|
|
243
|
-
this.clearListeners.delete(listener);
|
|
244
|
-
};
|
|
245
118
|
}
|
|
246
119
|
|
|
247
120
|
// ---- Remote Mirror Mode ----
|
|
248
121
|
|
|
249
|
-
/**
|
|
250
|
-
* Permanently suppress local capture. Use when this store acts as a mirror
|
|
251
|
-
* of a remote device's atom changes (e.g. the desktop dashboard): data
|
|
252
|
-
* arrives via replaceFromSnapshot() and local watchers must never record.
|
|
253
|
-
*/
|
|
254
|
-
disableCapture() {
|
|
255
|
-
this.captureSuppressed = true;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/** Whether the store is in remote mirror mode (capture suppressed). */
|
|
259
|
-
isCaptureSuppressed() {
|
|
260
|
-
return this.captureSuppressed;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
122
|
/**
|
|
264
123
|
* Serializable snapshot of the tracked atoms (registry metadata plus each
|
|
265
124
|
* atom's current value). Used by the sync adapter on the device side —
|
|
@@ -290,18 +149,18 @@ class JotaiStateStore {
|
|
|
290
149
|
* freezes the mirror.
|
|
291
150
|
*/
|
|
292
151
|
replaceFromSnapshot(changes, atomSnapshots) {
|
|
293
|
-
if (!this.
|
|
152
|
+
if (!this.getEnabled()) return;
|
|
294
153
|
|
|
295
154
|
// Preserve values a remote mirror already hydrated — wire-form snapshots
|
|
296
155
|
// always carry markers, and a later snapshot must not blank the detail pane.
|
|
297
156
|
const hydrated = new Map();
|
|
298
|
-
for (const existing of this.
|
|
157
|
+
for (const existing of this.items) {
|
|
299
158
|
const prev = existing.prevValue;
|
|
300
159
|
if (!prev || prev.__buoyValueOnDevice !== true) {
|
|
301
160
|
hydrated.set(existing.id, existing);
|
|
302
161
|
}
|
|
303
162
|
}
|
|
304
|
-
|
|
163
|
+
const merged = changes.slice(0, this.max).map(change => {
|
|
305
164
|
const kept = hydrated.get(change.id);
|
|
306
165
|
const incoming = change.prevValue;
|
|
307
166
|
return kept && incoming?.__buoyValueOnDevice === true ? {
|
|
@@ -339,7 +198,7 @@ class JotaiStateStore {
|
|
|
339
198
|
writeKind: snapshot.writeKind
|
|
340
199
|
}];
|
|
341
200
|
}));
|
|
342
|
-
this.
|
|
201
|
+
this.setItems(merged);
|
|
343
202
|
this.notifyAtomListeners();
|
|
344
203
|
}
|
|
345
204
|
|
|
@@ -348,13 +207,7 @@ class JotaiStateStore {
|
|
|
348
207
|
* device via the adapter's `getChangeDetail`. Remote-mirror (desktop) only.
|
|
349
208
|
*/
|
|
350
209
|
hydrateChange(id, patch) {
|
|
351
|
-
|
|
352
|
-
if (index === -1) return;
|
|
353
|
-
this.atomChanges = [...this.atomChanges.slice(0, index), {
|
|
354
|
-
...this.atomChanges[index],
|
|
355
|
-
...patch
|
|
356
|
-
}, ...this.atomChanges.slice(index + 1)];
|
|
357
|
-
this.notifyListeners();
|
|
210
|
+
this.hydrateById(id, patch);
|
|
358
211
|
}
|
|
359
212
|
|
|
360
213
|
/**
|
|
@@ -418,7 +271,7 @@ class JotaiStateStore {
|
|
|
418
271
|
// ---- Filtering ----
|
|
419
272
|
|
|
420
273
|
filterAtomChanges(filter) {
|
|
421
|
-
let filtered =
|
|
274
|
+
let filtered = this.getAll();
|
|
422
275
|
if (filter.searchText) {
|
|
423
276
|
const search = filter.searchText.toLowerCase();
|
|
424
277
|
filtered = filtered.filter(c => c.atomLabel.toLowerCase().includes(search) || c.valuePreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
|
|
@@ -435,8 +288,8 @@ class JotaiStateStore {
|
|
|
435
288
|
// ---- Stats ----
|
|
436
289
|
|
|
437
290
|
getStats() {
|
|
438
|
-
const total = this.
|
|
439
|
-
const withChanges = this.
|
|
291
|
+
const total = this.items.length;
|
|
292
|
+
const withChanges = this.items.filter(c => c.hasValueChange).length;
|
|
440
293
|
return {
|
|
441
294
|
totalChanges: total,
|
|
442
295
|
changesWithValueChange: withChanges,
|
|
@@ -450,34 +303,16 @@ class JotaiStateStore {
|
|
|
450
303
|
|
|
451
304
|
// ---- Enable / Disable ----
|
|
452
305
|
|
|
453
|
-
setEnabled(enabled) {
|
|
454
|
-
this.isEnabled = enabled;
|
|
455
|
-
}
|
|
456
|
-
getEnabled() {
|
|
457
|
-
return this.isEnabled;
|
|
458
|
-
}
|
|
459
306
|
setMaxChanges(max) {
|
|
460
|
-
this.
|
|
461
|
-
if (this.atomChanges.length > max) {
|
|
462
|
-
this.atomChanges = this.atomChanges.slice(0, max);
|
|
463
|
-
this.notifyListeners();
|
|
464
|
-
}
|
|
307
|
+
this.setMax(max);
|
|
465
308
|
}
|
|
466
309
|
|
|
467
310
|
// ---- Subscriptions ----
|
|
468
311
|
|
|
469
|
-
subscribe(listener) {
|
|
470
|
-
this.listeners.add(listener);
|
|
471
|
-
return () => this.listeners.delete(listener);
|
|
472
|
-
}
|
|
473
312
|
subscribeToAtoms(listener) {
|
|
474
313
|
this.atomListeners.add(listener);
|
|
475
314
|
return () => this.atomListeners.delete(listener);
|
|
476
315
|
}
|
|
477
|
-
notifyListeners() {
|
|
478
|
-
const changes = this.getAtomChanges();
|
|
479
|
-
this.listeners.forEach(listener => listener(changes));
|
|
480
|
-
}
|
|
481
316
|
notifyAtomListeners() {
|
|
482
317
|
const atoms = this.getAtoms();
|
|
483
318
|
this.atomListeners.forEach(listener => listener(atoms));
|
|
@@ -65,30 +65,14 @@ async function capture() {
|
|
|
65
65
|
}
|
|
66
66
|
async function restore(data, options) {
|
|
67
67
|
const snapshot = data;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const touch = key => !excludedSet.has(key) && (!includedSet || includedSet.has(key));
|
|
71
|
-
let excludedHits = 0;
|
|
72
|
-
const result = {
|
|
73
|
-
ok: true,
|
|
74
|
-
applied: 0,
|
|
75
|
-
skipped: [],
|
|
76
|
-
warnings: []
|
|
77
|
-
};
|
|
68
|
+
const scope = (0, _sharedUi.createRestoreScope)(options);
|
|
69
|
+
const result = (0, _sharedUi.emptyRestoreResult)();
|
|
78
70
|
if (!snapshot?.atoms) {
|
|
79
|
-
return
|
|
80
|
-
ok: false,
|
|
81
|
-
applied: 0,
|
|
82
|
-
skipped: [],
|
|
83
|
-
warnings: ["empty jotai snapshot"]
|
|
84
|
-
};
|
|
71
|
+
return (0, _sharedUi.failedRestore)("empty jotai snapshot");
|
|
85
72
|
}
|
|
86
73
|
let derived = 0;
|
|
87
74
|
for (const [label, entry] of Object.entries(snapshot.atoms)) {
|
|
88
|
-
if (!
|
|
89
|
-
excludedHits += 1;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
75
|
+
if (!scope.allows(label)) continue;
|
|
92
76
|
if (!entry.writable) {
|
|
93
77
|
derived += 1;
|
|
94
78
|
continue;
|
|
@@ -122,9 +106,8 @@ async function restore(data, options) {
|
|
|
122
106
|
if (derived > 0) {
|
|
123
107
|
result.warnings.push(`${derived} derived atom(s) left to recompute`);
|
|
124
108
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
109
|
+
const excludedWarning = scope.excludedWarning("atom(s)");
|
|
110
|
+
if (excludedWarning) result.warnings.push(excludedWarning);
|
|
128
111
|
return result;
|
|
129
112
|
}
|
|
130
113
|
|
|
@@ -149,8 +132,7 @@ async function previewItems(target, base) {
|
|
|
149
132
|
}
|
|
150
133
|
const items = [];
|
|
151
134
|
const notes = [];
|
|
152
|
-
const
|
|
153
|
-
for (const label of labels) {
|
|
135
|
+
for (const label of (0, _sharedUi.unionKeys)(baseAtoms, targetAtoms)) {
|
|
154
136
|
const inTarget = label in targetAtoms;
|
|
155
137
|
if (!inTarget) {
|
|
156
138
|
if (liveMode) notes.push(`atom "${label}" not in snapshot — untouched`);else items.push({
|
package/lib/commonjs/preset.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.createJotaiTool =
|
|
7
|
-
exports.jotaiToolPreset = void 0;
|
|
6
|
+
exports.jotaiToolPreset = exports.createJotaiTool = void 0;
|
|
8
7
|
var _JotaiModal = require("./jotai/components/JotaiModal");
|
|
9
8
|
var _JotaiIcon = require("./jotai/components/JotaiIcon");
|
|
9
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
10
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
11
|
/**
|
|
12
12
|
* Pre-configured Jotai DevTools preset for FloatingDevTools
|
|
@@ -48,7 +48,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
48
48
|
* - Filter by atom label
|
|
49
49
|
* - Changed keys tracking for object atoms
|
|
50
50
|
*/
|
|
51
|
-
const jotaiToolPreset = exports.jotaiToolPreset = {
|
|
51
|
+
const jotaiToolPreset = exports.jotaiToolPreset = (0, _sharedUi.defineToolPreset)({
|
|
52
52
|
id: "jotai",
|
|
53
53
|
name: "JOTAI",
|
|
54
54
|
description: "Jotai atom & state inspector",
|
|
@@ -62,7 +62,7 @@ const jotaiToolPreset = exports.jotaiToolPreset = {
|
|
|
62
62
|
props: {
|
|
63
63
|
enableSharedModalDimensions: false
|
|
64
64
|
}
|
|
65
|
-
};
|
|
65
|
+
});
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Create a custom Jotai DevTools configuration.
|
|
@@ -78,21 +78,13 @@ const jotaiToolPreset = exports.jotaiToolPreset = {
|
|
|
78
78
|
* });
|
|
79
79
|
* ```
|
|
80
80
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
color: options?.iconColor
|
|
92
|
-
}),
|
|
93
|
-
component: _JotaiModal.JotaiModal,
|
|
94
|
-
props: {
|
|
95
|
-
enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
81
|
+
const createJotaiTool = exports.createJotaiTool = (0, _sharedUi.createToolFactory)(jotaiToolPreset, options => ({
|
|
82
|
+
enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
|
|
83
|
+
}), options => ({
|
|
84
|
+
icon: ({
|
|
85
|
+
size
|
|
86
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_JotaiIcon.JotaiIcon, {
|
|
87
|
+
size: size,
|
|
88
|
+
color: options.iconColor
|
|
89
|
+
})
|
|
90
|
+
}));
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { useMemo } from "react";
|
|
13
13
|
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
|
14
|
-
import { CompactRow, night,
|
|
14
|
+
import { CompactRow, night, ToolEmptyState, resolveListEmptyState } from "@buoy-gg/shared-ui";
|
|
15
15
|
import { jotaiStateStore } from "../utils/jotaiStateStore";
|
|
16
16
|
import { JOTAI_TEST_ID, atomRowTestID } from "../utils/testIDs";
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -49,20 +49,14 @@ function getWriteKindLabel(atom) {
|
|
|
49
49
|
return atom.setValue ? "writable" : "derived";
|
|
50
50
|
}
|
|
51
51
|
function EmptyBrowserState() {
|
|
52
|
-
return /*#__PURE__*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
testID: `${JOTAI_TEST_ID}-atoms-empty-title`,
|
|
61
|
-
children: "No atoms registered"
|
|
62
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
63
|
-
style: styles.emptyText,
|
|
64
|
-
children: "Use watchAtoms(store, { atomName }) to register your Jotai atoms.\nThey will appear here with their current value."
|
|
65
|
-
})]
|
|
52
|
+
return /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
53
|
+
testID: `${JOTAI_TEST_ID}-atoms-empty`
|
|
54
|
+
// scanning = "scanning for targets… and finding none". The registry is
|
|
55
|
+
// discovered, not configured, so an empty one is a scan result.
|
|
56
|
+
,
|
|
57
|
+
otto: "scanning",
|
|
58
|
+
title: "No atoms registered",
|
|
59
|
+
message: "Use watchAtoms(store, { atomName }) to register your Jotai atoms.\nThey will appear here with their current value."
|
|
66
60
|
});
|
|
67
61
|
}
|
|
68
62
|
export function JotaiAtomBrowser({
|
|
@@ -79,17 +73,19 @@ export function JotaiAtomBrowser({
|
|
|
79
73
|
return /*#__PURE__*/_jsx(EmptyBrowserState, {});
|
|
80
74
|
}
|
|
81
75
|
if (filteredAtoms.length === 0 && searchQuery) {
|
|
82
|
-
return /*#__PURE__*/
|
|
83
|
-
style: styles.emptyState,
|
|
76
|
+
return /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
84
77
|
testID: `${JOTAI_TEST_ID}-atoms-no-match`,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
...resolveListEmptyState({
|
|
79
|
+
subject: "atoms",
|
|
80
|
+
verb: "registered",
|
|
81
|
+
search: searchQuery,
|
|
82
|
+
total: atoms.length,
|
|
83
|
+
waiting: {
|
|
84
|
+
otto: "scanning",
|
|
85
|
+
title: "No atoms registered",
|
|
86
|
+
message: "Use watchAtoms() to register your Jotai atoms."
|
|
87
|
+
}
|
|
88
|
+
})
|
|
93
89
|
});
|
|
94
90
|
}
|
|
95
91
|
return /*#__PURE__*/_jsxs(ScrollView, {
|
|
@@ -195,22 +191,5 @@ const styles = StyleSheet.create({
|
|
|
195
191
|
fontWeight: "700",
|
|
196
192
|
color: night.color.accent,
|
|
197
193
|
fontFamily: "monospace"
|
|
198
|
-
},
|
|
199
|
-
emptyState: {
|
|
200
|
-
alignItems: "center",
|
|
201
|
-
paddingVertical: 40
|
|
202
|
-
},
|
|
203
|
-
emptyTitle: {
|
|
204
|
-
color: night.color.text,
|
|
205
|
-
fontSize: 14,
|
|
206
|
-
fontWeight: "600",
|
|
207
|
-
marginTop: 12,
|
|
208
|
-
marginBottom: 6
|
|
209
|
-
},
|
|
210
|
-
emptyText: {
|
|
211
|
-
color: night.color.textTertiary,
|
|
212
|
-
fontSize: 12,
|
|
213
|
-
textAlign: "center",
|
|
214
|
-
lineHeight: 18
|
|
215
194
|
}
|
|
216
195
|
});
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { memo } from "react";
|
|
10
|
-
import {
|
|
11
|
-
import { CompactRow, buoyColors, night, Zap, RelativeTime } from "@buoy-gg/shared-ui";
|
|
10
|
+
import { ChangeRow, night } from "@buoy-gg/shared-ui";
|
|
12
11
|
import { jotaiStateStore } from "../utils/jotaiStateStore";
|
|
13
|
-
import { jsx as _jsx
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
13
|
function getStatusLabel(category, atomLabel) {
|
|
15
14
|
if (category === "initial") return "Initial";
|
|
16
15
|
return atomLabel.charAt(0).toUpperCase() + atomLabel.slice(1);
|
|
@@ -68,59 +67,15 @@ export const JotaiAtomChangeItem = /*#__PURE__*/memo(function JotaiAtomChangeIte
|
|
|
68
67
|
const sublabel = getSublabel(change);
|
|
69
68
|
const primaryText = getPrimaryText(change);
|
|
70
69
|
const badgeText = getBadgeText(change);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// alternative — letting CompactRow draw `badgeText` itself — leaves the one
|
|
74
|
-
// row whose badge matters most (a write that moved the atom's identity but
|
|
75
|
-
// none of its contents) as the only row in the list with no handle on its
|
|
76
|
-
// badge. Same call, and the same reason, as ZustandStateChangeItem.
|
|
77
|
-
const customBadge = /*#__PURE__*/_jsxs(View, {
|
|
78
|
-
style: styles.badgeContainer,
|
|
79
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
80
|
-
style: [styles.badgeText, {
|
|
81
|
-
color: statusColor
|
|
82
|
-
}],
|
|
83
|
-
testID: testID ? `${testID}-badge` : undefined,
|
|
84
|
-
children: badgeText
|
|
85
|
-
}), change.hasValueChange ? /*#__PURE__*/_jsx(View, {
|
|
86
|
-
style: styles.changeBadge,
|
|
87
|
-
testID: testID ? `${testID}-change` : undefined,
|
|
88
|
-
children: /*#__PURE__*/_jsx(Zap, {
|
|
89
|
-
size: 12,
|
|
90
|
-
color: buoyColors.warning
|
|
91
|
-
})
|
|
92
|
-
}) : null]
|
|
93
|
-
});
|
|
94
|
-
return /*#__PURE__*/_jsx(CompactRow, {
|
|
95
|
-
statusDotColor: statusColor,
|
|
70
|
+
return /*#__PURE__*/_jsx(ChangeRow, {
|
|
71
|
+
statusColor: statusColor,
|
|
96
72
|
statusLabel: statusLabel,
|
|
97
|
-
|
|
73
|
+
sublabel: sublabel,
|
|
98
74
|
primaryText: primaryText,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
customBadge: customBadge,
|
|
103
|
-
badgeColor: statusColor,
|
|
104
|
-
showChevron: true,
|
|
75
|
+
timestamp: change.timestamp,
|
|
76
|
+
badgeText: badgeText,
|
|
77
|
+
changed: change.hasValueChange,
|
|
105
78
|
onPress: () => onPress(change),
|
|
106
|
-
testID: testID
|
|
107
|
-
statusTestID: testID ? `${testID}-status` : undefined,
|
|
108
|
-
statusSublabelTestID: testID ? `${testID}-sublabel` : undefined,
|
|
109
|
-
primaryTestID: testID ? `${testID}-keys` : undefined
|
|
79
|
+
testID: testID
|
|
110
80
|
});
|
|
111
|
-
});
|
|
112
|
-
const styles = StyleSheet.create({
|
|
113
|
-
badgeContainer: {
|
|
114
|
-
flexDirection: "row",
|
|
115
|
-
alignItems: "center",
|
|
116
|
-
gap: 4
|
|
117
|
-
},
|
|
118
|
-
badgeText: {
|
|
119
|
-
fontSize: 11,
|
|
120
|
-
fontWeight: "600",
|
|
121
|
-
fontFamily: "monospace"
|
|
122
|
-
},
|
|
123
|
-
changeBadge: {
|
|
124
|
-
paddingHorizontal: 4
|
|
125
|
-
}
|
|
126
81
|
});
|