@buoy-gg/zustand 7.0.17 → 7.0.18

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 (25) hide show
  1. package/lib/commonjs/zustand/components/ZustandModal.js +35 -4
  2. package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +3 -2
  3. package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +11 -3
  4. package/lib/commonjs/zustand/sync/zustandSyncAdapter.js +178 -6
  5. package/lib/commonjs/zustand/utils/snapshotProvider.js +110 -2
  6. package/lib/commonjs/zustand/utils/zustandStateStore.js +105 -14
  7. package/lib/module/zustand/components/ZustandModal.js +36 -5
  8. package/lib/module/zustand/components/ZustandStateDetailContent.js +3 -2
  9. package/lib/module/zustand/components/ZustandStoreBrowser.js +11 -3
  10. package/lib/module/zustand/sync/zustandSyncAdapter.js +177 -5
  11. package/lib/module/zustand/utils/snapshotProvider.js +110 -2
  12. package/lib/module/zustand/utils/zustandStateStore.js +105 -14
  13. package/lib/typescript/zustand/components/ZustandModal.d.ts +1 -1
  14. package/lib/typescript/zustand/components/ZustandModal.d.ts.map +1 -1
  15. package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts +4 -1
  16. package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts.map +1 -1
  17. package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts +3 -1
  18. package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts.map +1 -1
  19. package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts +71 -3
  20. package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts.map +1 -1
  21. package/lib/typescript/zustand/types/index.d.ts +26 -0
  22. package/lib/typescript/zustand/types/index.d.ts.map +1 -1
  23. package/lib/typescript/zustand/utils/zustandStateStore.d.ts +18 -0
  24. package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +1 -1
  25. package/package.json +3 -3
@@ -62,9 +62,10 @@ export function ZustandStateDetailContent({
62
62
  changes,
63
63
  selectedIndex,
64
64
  onIndexChange,
65
- disableInternalFooter = false
65
+ disableInternalFooter = false,
66
+ initialView = "change"
66
67
  }) {
67
- const [activeView, setActiveView] = useState("change");
68
+ const [activeView, setActiveView] = useState(initialView);
68
69
  const [diffMode, setDiffMode] = useState("tree");
69
70
  const totalChanges = changes.length;
70
71
  const changeColor = getChangeColor(change);
@@ -22,6 +22,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
22
  function getKeysPreview(store) {
23
23
  try {
24
24
  const state = store.api.getState();
25
+ if (state && typeof state === "object" && state.__buoyStateOnDevice === true) {
26
+ return "on device";
27
+ }
25
28
  if (state && typeof state === "object") {
26
29
  const keys = Object.keys(state);
27
30
  const dataKeys = keys.filter(k => {
@@ -142,7 +145,8 @@ export function ZustandStoreBrowser({
142
145
  stores,
143
146
  searchQuery,
144
147
  onViewHistory,
145
- isLoading = false
148
+ isLoading = false,
149
+ onStoreExpand
146
150
  }) {
147
151
  const [expandedStore, setExpandedStore] = useState(null);
148
152
 
@@ -153,8 +157,12 @@ export function ZustandStoreBrowser({
153
157
  return stores.filter(s => s.name.toLowerCase().includes(search) || s.persistName && s.persistName.toLowerCase().includes(search));
154
158
  }, [stores, searchQuery]);
155
159
  const handleStorePress = useCallback(store => {
156
- setExpandedStore(prev => prev === store.name ? null : store.name);
157
- }, []);
160
+ setExpandedStore(prev => {
161
+ const next = prev === store.name ? null : store.name;
162
+ if (next) onStoreExpand?.(store.name);
163
+ return next;
164
+ });
165
+ }, [onStoreExpand]);
158
166
  if (filteredStores.length === 0 && !searchQuery) {
159
167
  return isLoading ? /*#__PURE__*/_jsx(LoadingBrowserState, {}) : /*#__PURE__*/_jsx(EmptyBrowserState, {});
160
168
  }
@@ -1,10 +1,82 @@
1
1
  "use strict";
2
2
 
3
3
  import { zustandStateStore } from "../utils/zustandStateStore";
4
+ /**
5
+ * Sentinel used in WIRE-FORM snapshots in place of raw prevState/nextState.
6
+ * Same hazard as redux: up to 200 changes × 2 full trees on every snapshot
7
+ * freeze/OOM-killed large apps. Dashboards fetch trees via `getChangeDetail`.
8
+ */
9
+ export const STATE_ON_DEVICE = Object.freeze({
10
+ __buoyStateOnDevice: true,
11
+ note: "State trees stay on the device — fetched on demand via getChangeDetail / getStoreState."
12
+ });
13
+
14
+ /** Wire-form sentinel for an oversized `partial` (replace can carry a whole tree). */
15
+ export const PAYLOAD_ON_DEVICE = Object.freeze({
16
+ __buoyPayloadOnDevice: true,
17
+ note: "Partial stays on the device — fetched on demand via getChangeDetail."
18
+ });
19
+ const MAX_CHANGE_DETAIL_BYTES = 8 * 1024 * 1024;
20
+ const MAX_WIRE_PAYLOAD_BYTES = 16 * 1024;
21
+ function jsonLongerThan(value, limit) {
22
+ let bytes = 0;
23
+ const stack = [value];
24
+ const seen = new Set();
25
+ while (stack.length > 0) {
26
+ if (bytes > limit) return true;
27
+ const v = stack.pop();
28
+ if (v === null || v === undefined) bytes += 4;else if (typeof v === "string") bytes += v.length + 2;else if (typeof v === "number") bytes += 8;else if (typeof v === "boolean") bytes += 5;else if (typeof v === "object") {
29
+ if (seen.has(v)) continue;
30
+ seen.add(v);
31
+ if (Array.isArray(v)) {
32
+ bytes += 2 + v.length;
33
+ for (let i = 0; i < v.length; i++) stack.push(v[i]);
34
+ } else {
35
+ bytes += 2;
36
+ for (const k in v) {
37
+ bytes += k.length + 4;
38
+ stack.push(v[k]);
39
+ }
40
+ }
41
+ } else bytes += 4;
42
+ }
43
+ return bytes > limit;
44
+ }
4
45
  /** Top-level keys of an object state, for the compact (no-values) view. */
5
46
  function topLevelKeys(state) {
6
47
  return state && typeof state === "object" && !Array.isArray(state) ? Object.keys(state) : undefined;
7
48
  }
49
+ function toWireChange(change) {
50
+ return {
51
+ ...change,
52
+ prevState: STATE_ON_DEVICE,
53
+ nextState: STATE_ON_DEVICE,
54
+ partial: toWirePartial(change.partial)
55
+ };
56
+ }
57
+ function toWirePartial(value) {
58
+ if (value === undefined) return value;
59
+ try {
60
+ JSON.stringify(value);
61
+ } catch {
62
+ return PAYLOAD_ON_DEVICE;
63
+ }
64
+ if (jsonLongerThan(value, MAX_WIRE_PAYLOAD_BYTES)) {
65
+ return PAYLOAD_ON_DEVICE;
66
+ }
67
+ return value;
68
+ }
69
+ function toWireState(value) {
70
+ try {
71
+ JSON.stringify(value);
72
+ } catch {
73
+ return STATE_ON_DEVICE;
74
+ }
75
+ if (jsonLongerThan(value, MAX_WIRE_PAYLOAD_BYTES)) {
76
+ return STATE_ON_DEVICE;
77
+ }
78
+ return value;
79
+ }
8
80
 
9
81
  /**
10
82
  * Sync adapter for the zustand tool, consumed by @buoy-gg/external-sync's
@@ -17,13 +89,21 @@ function topLevelKeys(state) {
17
89
  * change timeline and the store registry (with each store's current state)
18
90
  * so the dashboard can render the store browser.
19
91
  *
92
+ * v2: change-log prevState/nextState are replaced with on-device markers
93
+ * (see STATE_ON_DEVICE). Dashboards fetch trees via `getChangeDetail`.
94
+ * Oversized / unserializable `stores[].currentState` uses the same marker
95
+ * and is fetched via `getStoreState`.
96
+ *
20
97
  * The `setState` action powers remote time-travel/reset from the dashboard.
21
98
  */
22
99
  export const zustandSyncAdapter = {
23
- version: 1,
100
+ version: 2,
24
101
  getSnapshot: () => ({
25
- changes: zustandStateStore.getStateChanges(),
26
- stores: zustandStateStore.getStoreSnapshots()
102
+ changes: zustandStateStore.getStateChanges().map(toWireChange),
103
+ stores: zustandStateStore.getStoreSnapshots().map(store => ({
104
+ ...store,
105
+ currentState: toWireState(store.currentState)
106
+ }))
27
107
  }),
28
108
  subscribe: onChange => {
29
109
  const unsubscribeChanges = zustandStateStore.subscribe(onChange);
@@ -37,6 +117,86 @@ export const zustandSyncAdapter = {
37
117
  clearEvents: () => {
38
118
  zustandStateStore.clearStateChanges();
39
119
  },
120
+ /**
121
+ * On-demand detail: one change's real prevState/nextState/partial.
122
+ * The per-snapshot stream stays tree-free; this is the explicit,
123
+ * size-guarded channel for the detail pane.
124
+ */
125
+ getChangeDetail: params => {
126
+ const {
127
+ id
128
+ } = params ?? {};
129
+ if (!id) return {
130
+ found: false,
131
+ reason: "missing id"
132
+ };
133
+ const change = zustandStateStore.getStateChangeById(id);
134
+ if (!change) return {
135
+ found: false,
136
+ reason: "unknown id"
137
+ };
138
+ const cappedNote = {
139
+ __buoyTruncated: true,
140
+ note: `Exceeds the ${MAX_CHANGE_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
141
+ };
142
+ const cap = value => {
143
+ try {
144
+ JSON.stringify(value);
145
+ } catch {
146
+ return cappedNote;
147
+ }
148
+ return jsonLongerThan(value, MAX_CHANGE_DETAIL_BYTES) ? cappedNote : value;
149
+ };
150
+ return {
151
+ found: true,
152
+ id,
153
+ prevState: cap(change.prevState),
154
+ nextState: cap(change.nextState),
155
+ partial: cap(change.partial)
156
+ };
157
+ },
158
+ /**
159
+ * On-demand current state for one store. Wire-form snapshots replace
160
+ * oversized / unserializable `currentState` with STATE_ON_DEVICE.
161
+ */
162
+ getStoreState: params => {
163
+ const {
164
+ storeName
165
+ } = params ?? {};
166
+ if (!storeName) return {
167
+ found: false,
168
+ reason: "missing storeName"
169
+ };
170
+ const store = zustandStateStore.getStore(storeName);
171
+ if (!store) return {
172
+ found: false,
173
+ reason: "unknown storeName"
174
+ };
175
+ let currentState;
176
+ try {
177
+ currentState = store.api.getState();
178
+ } catch {
179
+ currentState = undefined;
180
+ }
181
+ const cappedNote = {
182
+ __buoyTruncated: true,
183
+ note: `Exceeds the ${MAX_CHANGE_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
184
+ };
185
+ try {
186
+ JSON.stringify(currentState);
187
+ } catch {
188
+ return {
189
+ found: true,
190
+ storeName,
191
+ currentState: STATE_ON_DEVICE
192
+ };
193
+ }
194
+ return {
195
+ found: true,
196
+ storeName,
197
+ currentState: jsonLongerThan(currentState, MAX_CHANGE_DETAIL_BYTES) ? cappedNote : currentState
198
+ };
199
+ },
40
200
  /**
41
201
  * Compact current-state reader for a remote driver (MCP/LLM): the registered
42
202
  * stores with light metadata; each store's full `currentState` object stays
@@ -73,8 +233,20 @@ export const zustandSyncAdapter = {
73
233
  storeName,
74
234
  state,
75
235
  replace
76
- } = params;
77
- zustandStateStore.getStore(storeName)?.api.setState(state, replace ?? true);
236
+ } = params ?? {};
237
+ if (!storeName) return {
238
+ ok: false,
239
+ error: "Missing storeName."
240
+ };
241
+ const store = zustandStateStore.getStore(storeName);
242
+ if (!store) return {
243
+ ok: false,
244
+ error: `No store named "${storeName}".`
245
+ };
246
+ store.api.setState(state, replace ?? true);
247
+ return {
248
+ ok: true
249
+ };
78
250
  }
79
251
  }
80
252
  };
@@ -14,6 +14,7 @@
14
14
  * LIVE store's functions onto the snapshot before replacing.
15
15
  */
16
16
  import { registerStateSnapshotProvider } from "@buoy-gg/shared-ui";
17
+ import { boundedDeepEqual, jsonLikelyLargerThan } from "@buoy-gg/shared-ui/dataViewer";
17
18
  import { zustandStateStore } from "./zustandStateStore";
18
19
  const MAX_DEPTH = 8;
19
20
  function isPlainObjectLike(v) {
@@ -116,8 +117,12 @@ async function capture() {
116
117
  warnings
117
118
  };
118
119
  }
119
- async function restore(data) {
120
+ async function restore(data, options) {
120
121
  const snapshot = data;
122
+ const excludedSet = new Set(options?.excludeKeys ?? []);
123
+ const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
124
+ const touch = key => !excludedSet.has(key) && (!includedSet || includedSet.has(key));
125
+ let excludedHits = 0;
121
126
  const result = {
122
127
  ok: true,
123
128
  applied: 0,
@@ -133,6 +138,10 @@ async function restore(data) {
133
138
  };
134
139
  }
135
140
  for (const [name, envelope] of Object.entries(snapshot.stores)) {
141
+ if (!touch(name)) {
142
+ excludedHits += 1;
143
+ continue;
144
+ }
136
145
  const info = zustandStateStore.getStore(name);
137
146
  if (!info) {
138
147
  result.skipped.push({
@@ -159,12 +168,111 @@ async function restore(data) {
159
168
  result.ok = false;
160
169
  }
161
170
  }
171
+ if (excludedHits > 0) {
172
+ result.warnings.push(`${excludedHits} store(s) excluded by you`);
173
+ }
162
174
  return result;
163
175
  }
176
+ const ITEM_RENDER_BYTE_LIMIT = 256 * 1024;
177
+
178
+ /**
179
+ * Per-store restore preview. Both sides are function-stripped through the
180
+ * SAME depth-8 normalizer as capture — diffing snapshot-vs-RAW-live would
181
+ * report every stripped function and every beyond-depth subtree as removed.
182
+ */
183
+ async function previewItems(target, base) {
184
+ const targetStores = target?.stores ?? {};
185
+ const liveMode = base === undefined;
186
+ const baseStores = {};
187
+ if (liveMode) {
188
+ for (const info of zustandStateStore.getStores()) {
189
+ const fnPaths = [];
190
+ try {
191
+ baseStores[info.name] = {
192
+ state: stripFunctions(info.api.getState(), "", fnPaths, new WeakSet(), 0),
193
+ fnPaths
194
+ };
195
+ } catch {
196
+ // unreadable live store — treated as absent
197
+ }
198
+ }
199
+ } else {
200
+ Object.assign(baseStores, base?.stores ?? {});
201
+ }
202
+ const items = [];
203
+ const notes = [];
204
+ let preservedFns = 0;
205
+ const names = new Set([...Object.keys(baseStores), ...Object.keys(targetStores)]);
206
+ for (const name of names) {
207
+ const inTarget = name in targetStores;
208
+ const inBase = name in baseStores;
209
+ if (!inTarget) {
210
+ // Restore never touches stores the snapshot doesn't know about.
211
+ if (liveMode) notes.push(`store "${name}" not in snapshot — untouched`);else items.push({
212
+ key: name,
213
+ label: name,
214
+ verdict: "removed",
215
+ oldValue: baseStores[name].state
216
+ });
217
+ continue;
218
+ }
219
+ preservedFns += targetStores[name].fnPaths?.length ?? 0;
220
+ if (liveMode && !zustandStateStore.getStore(name)) {
221
+ items.push({
222
+ key: name,
223
+ label: name,
224
+ verdict: "wont-apply",
225
+ reason: "store not registered (renamed, or created after snapshot?)"
226
+ });
227
+ continue;
228
+ }
229
+ if (!inBase) {
230
+ items.push({
231
+ key: name,
232
+ label: name,
233
+ verdict: "added",
234
+ newValue: targetStores[name].state
235
+ });
236
+ continue;
237
+ }
238
+ const oldState = baseStores[name].state;
239
+ const newState = targetStores[name].state;
240
+ if (boundedDeepEqual(oldState, newState)) {
241
+ items.push({
242
+ key: name,
243
+ label: name,
244
+ verdict: "unchanged"
245
+ });
246
+ } else if (jsonLikelyLargerThan(oldState, ITEM_RENDER_BYTE_LIMIT) || jsonLikelyLargerThan(newState, ITEM_RENDER_BYTE_LIMIT)) {
247
+ items.push({
248
+ key: name,
249
+ label: name,
250
+ verdict: "too-large",
251
+ reason: "store too large to render a diff — restore still applies it"
252
+ });
253
+ } else {
254
+ items.push({
255
+ key: name,
256
+ label: name,
257
+ verdict: "changed",
258
+ oldValue: oldState,
259
+ newValue: newState
260
+ });
261
+ }
262
+ }
263
+ if (preservedFns > 0) {
264
+ notes.push(`${preservedFns} store action function${preservedFns === 1 ? "" : "s"} preserved on restore (not diffed)`);
265
+ }
266
+ return {
267
+ items,
268
+ notes
269
+ };
270
+ }
164
271
  registerStateSnapshotProvider({
165
272
  id: "zustand",
166
273
  label: "Zustand",
167
274
  status,
168
275
  capture,
169
- restore
276
+ restore,
277
+ previewItems
170
278
  });
@@ -272,6 +272,27 @@ class ZustandStateStore {
272
272
 
273
273
  // ---- Remote Mirror Mode ----
274
274
 
275
+ /**
276
+ * Replace the change log without touching the store registry. Used when a
277
+ * host keeps real registered stores (JUMP / RESET stay wired) but needs to
278
+ * reseed the event list — the docs live tour, or any mirror that already
279
+ * called `registerStore`. Does not fire `onClear`. Respects the enabled
280
+ * flag so the UI's pause button freezes the reseed.
281
+ */
282
+ replaceStateChanges(changes) {
283
+ if (!this.isEnabled) return;
284
+ this.stateChanges = changes.slice(0, this.maxChanges);
285
+ for (const store of this.stores.values()) {
286
+ store.stateChangeCount = 0;
287
+ }
288
+ for (const change of this.stateChanges) {
289
+ const store = this.stores.get(change.storeName);
290
+ if (store) store.stateChangeCount++;
291
+ }
292
+ this.notifyListeners();
293
+ this.notifyStoreListeners();
294
+ }
295
+
275
296
  /**
276
297
  * Permanently suppress local capture. Use when this store acts as a mirror
277
298
  * of a remote device's state changes (e.g. the desktop dashboard): data
@@ -326,22 +347,92 @@ class ZustandStateStore {
326
347
  */
327
348
  replaceFromSnapshot(changes, storeSnapshots) {
328
349
  if (!this.isEnabled) return;
329
- this.stateChanges = changes.slice(0, this.maxChanges);
330
- this.stores = new Map(storeSnapshots.map(snapshot => [snapshot.name, {
331
- name: snapshot.name,
332
- api: {
333
- getState: () => snapshot.currentState,
334
- setState: (partial, replace) => {
335
- this.remoteSetStateHandler?.(snapshot.name, partial, replace);
350
+
351
+ // Preserve trees a remote mirror already hydrated — wire-form snapshots
352
+ // always carry markers, and a later snapshot must not blank the detail pane.
353
+ const hydrated = new Map();
354
+ for (const existing of this.stateChanges) {
355
+ const prev = existing.prevState;
356
+ if (!prev || prev.__buoyStateOnDevice !== true) {
357
+ hydrated.set(existing.id, existing);
358
+ }
359
+ }
360
+ this.stateChanges = changes.slice(0, this.maxChanges).map(change => {
361
+ const kept = hydrated.get(change.id);
362
+ const incoming = change.prevState;
363
+ return kept && incoming?.__buoyStateOnDevice === true ? {
364
+ ...change,
365
+ prevState: kept.prevState,
366
+ nextState: kept.nextState,
367
+ partial: kept.partial
368
+ } : change;
369
+ });
370
+
371
+ // Preserve currentState a remote mirror already hydrated — oversized
372
+ // stores arrive as STATE_ON_DEVICE markers on every snapshot.
373
+ const hydratedStores = new Map();
374
+ for (const [name, store] of this.stores) {
375
+ try {
376
+ const state = store.api.getState();
377
+ const marker = state;
378
+ if (!marker || marker.__buoyStateOnDevice !== true) {
379
+ hydratedStores.set(name, state);
380
+ }
381
+ } catch {
382
+ // ignore
383
+ }
384
+ }
385
+ this.stores = new Map(storeSnapshots.map(snapshot => {
386
+ const incoming = snapshot.currentState;
387
+ const kept = hydratedStores.get(snapshot.name);
388
+ const currentState = kept !== undefined && incoming?.__buoyStateOnDevice === true ? kept : snapshot.currentState;
389
+ return [snapshot.name, {
390
+ name: snapshot.name,
391
+ api: {
392
+ getState: () => currentState,
393
+ setState: (partial, replace) => {
394
+ this.remoteSetStateHandler?.(snapshot.name, partial, replace);
395
+ },
396
+ subscribe: () => () => {}
336
397
  },
337
- subscribe: () => () => {}
338
- },
339
- stateChangeCount: snapshot.stateChangeCount,
340
- isPersisted: snapshot.isPersisted,
341
- persistName: snapshot.persistName,
342
- color: snapshot.color
343
- }]));
398
+ stateChangeCount: snapshot.stateChangeCount,
399
+ isPersisted: snapshot.isPersisted,
400
+ persistName: snapshot.persistName,
401
+ color: snapshot.color
402
+ }];
403
+ }));
404
+ this.notifyListeners();
405
+ this.notifyStoreListeners();
406
+ }
407
+
408
+ /**
409
+ * Hydrate one mirrored change with real trees fetched on demand from the
410
+ * device via the adapter's `getChangeDetail`. Remote-mirror (desktop) only.
411
+ */
412
+ hydrateChange(id, patch) {
413
+ const index = this.stateChanges.findIndex(c => c.id === id);
414
+ if (index === -1) return;
415
+ this.stateChanges = [...this.stateChanges.slice(0, index), {
416
+ ...this.stateChanges[index],
417
+ ...patch
418
+ }, ...this.stateChanges.slice(index + 1)];
344
419
  this.notifyListeners();
420
+ }
421
+
422
+ /**
423
+ * Hydrate one mirrored store's currentState fetched on demand from the
424
+ * device via the adapter's `getStoreState`. Remote-mirror (desktop) only.
425
+ */
426
+ hydrateStoreState(storeName, currentState) {
427
+ const store = this.stores.get(storeName);
428
+ if (!store) return;
429
+ this.stores.set(storeName, {
430
+ ...store,
431
+ api: {
432
+ ...store.api,
433
+ getState: () => currentState
434
+ }
435
+ });
345
436
  this.notifyStoreListeners();
346
437
  }
347
438
 
@@ -6,7 +6,7 @@
6
6
  * - Events tab: Live state change monitoring with diffs
7
7
  */
8
8
  import type { ZustandModalProps } from "../types";
9
- export declare function ZustandModal({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, inline, storesLoading, }: ZustandModalProps): import("react").JSX.Element | null;
9
+ export declare function ZustandModal({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, inline, storesLoading, initialTab, initialSelectedChangeId, initialDetailView, onChangeSelect, onStoreExpand, }: ZustandModalProps): import("react").JSX.Element | null;
10
10
  /**
11
11
  * The same Zustand tool as `ZustandModal`, rendered INLINE for hosts that
12
12
  * place tools in a panel rather than a modal. Exists so there is exactly one
@@ -1 +1 @@
1
- {"version":3,"file":"ZustandModal.d.ts","sourceRoot":"","sources":["../../../../src/zustand/components/ZustandModal.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH,OAAO,KAAK,EAAE,iBAAiB,EAAsB,MAAM,UAAU,CAAC;AAqBtE,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,MAAc,EACd,aAAqB,GACtB,EAAE,iBAAiB,sCAwjBnB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,GAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAM,+BAGtE"}
1
+ {"version":3,"file":"ZustandModal.d.ts","sourceRoot":"","sources":["../../../../src/zustand/components/ZustandModal.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH,OAAO,KAAK,EAAE,iBAAiB,EAAsB,MAAM,UAAU,CAAC;AAqBtE,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,MAAc,EACd,aAAqB,EACrB,UAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,EAAE,iBAAiB,sCAklBnB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,GAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAM,+BAGtE"}
@@ -9,14 +9,17 @@
9
9
  * Mirrors ReduxActionDetailContent.tsx
10
10
  */
11
11
  import type { ZustandStateChange } from "../types";
12
+ import { type ZustandDetailViewMode } from "./ZustandDetailViewToggle";
12
13
  interface ZustandStateDetailContentProps {
13
14
  change: ZustandStateChange;
14
15
  changes: ZustandStateChange[];
15
16
  selectedIndex: number;
16
17
  onIndexChange: (index: number) => void;
17
18
  disableInternalFooter?: boolean;
19
+ /** Seed the CHANGE / STATE / DIFF toggle once; taps own it afterwards. */
20
+ initialView?: ZustandDetailViewMode;
18
21
  }
19
- export declare function ZustandStateDetailContent({ change, changes, selectedIndex, onIndexChange, disableInternalFooter, }: ZustandStateDetailContentProps): import("react").JSX.Element;
22
+ export declare function ZustandStateDetailContent({ change, changes, selectedIndex, onIndexChange, disableInternalFooter, initialView, }: ZustandStateDetailContentProps): import("react").JSX.Element;
20
23
  /**
21
24
  * External footer component for modal use
22
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ZustandStateDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/zustand/components/ZustandStateDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAiBH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,UAAU,8BAA8B;IACtC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAyCD,wBAAgB,yBAAyB,CAAC,EACxC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,qBAA6B,GAC9B,EAAE,8BAA8B,+BA6LhC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,GACd,EAAE;IACD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,sCAmBA"}
1
+ {"version":3,"file":"ZustandStateDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/zustand/components/ZustandStateDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAiBH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,2BAA2B,CAAC;AAKnC,UAAU,8BAA8B;IACtC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,0EAA0E;IAC1E,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACrC;AAyCD,wBAAgB,yBAAyB,CAAC,EACxC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,qBAA6B,EAC7B,WAAsB,GACvB,EAAE,8BAA8B,+BA6LhC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,GACd,EAAE;IACD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,sCAmBA"}
@@ -19,7 +19,9 @@ interface ZustandStoreBrowserProps {
19
19
  * watch handshake).
20
20
  */
21
21
  isLoading?: boolean;
22
+ /** Fired when a store row is expanded (not when collapsed). */
23
+ onStoreExpand?: (storeName: string) => void;
22
24
  }
23
- export declare function ZustandStoreBrowser({ stores, searchQuery, onViewHistory, isLoading, }: ZustandStoreBrowserProps): import("react").JSX.Element;
25
+ export declare function ZustandStoreBrowser({ stores, searchQuery, onViewHistory, isLoading, onStoreExpand, }: ZustandStoreBrowserProps): import("react").JSX.Element;
24
26
  export {};
25
27
  //# sourceMappingURL=ZustandStoreBrowser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ZustandStoreBrowser.d.ts","sourceRoot":"","sources":["../../../../src/zustand/components/ZustandStoreBrowser.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsBH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjD,UAAU,wBAAwB;IAChC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAkHD,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,WAAW,EACX,aAAa,EACb,SAAiB,GAClB,EAAE,wBAAwB,+BA4F1B"}
1
+ {"version":3,"file":"ZustandStoreBrowser.d.ts","sourceRoot":"","sources":["../../../../src/zustand/components/ZustandStoreBrowser.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsBH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjD,UAAU,wBAAwB;IAChC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAyHD,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,WAAW,EACX,aAAa,EACb,SAAiB,EACjB,aAAa,GACd,EAAE,wBAAwB,+BA8F1B"}