react-state-inspector-devtools 1.1.22 → 1.2.0

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/dist/index.js CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  StateInspectorProvider: () => StateInspectorProvider,
24
24
  StateInspectorUI: () => StateInspectorUI,
25
25
  createInspectorStore: () => createInspectorStore,
26
+ inferMeta: () => inferMeta,
26
27
  useComponentLabel: () => useComponentLabel,
27
28
  useInspectableState: () => useInspectableState,
28
29
  useInspectorComponent: () => useInspectorComponent,
@@ -53,33 +54,37 @@ function styleInject(css, { insertAt } = {}) {
53
54
  }
54
55
 
55
56
  // src/index.css
56
- styleInject('@import "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap";\n[data-rsi-root],\n[data-rsi-root] *,\n[data-rsi-root] *::before,\n[data-rsi-root] *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0;\n font-family:\n "Inter",\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n "Helvetica Neue",\n sans-serif;\n font-size: 14px;\n font-weight: 400;\n font-style: normal;\n font-variant: normal;\n line-height: 1.5;\n letter-spacing: normal;\n text-align: left;\n text-decoration: none;\n text-indent: 0;\n text-shadow: none;\n text-transform: none;\n word-spacing: normal;\n white-space: normal;\n background: transparent;\n color: inherit;\n outline: none;\n vertical-align: baseline;\n visibility: visible;\n opacity: 1;\n transform: none;\n transform-origin: 50% 50% 0;\n animation: none;\n transition: none;\n list-style: none;\n border-collapse: collapse;\n border-spacing: 0;\n float: none;\n clear: none;\n cursor: auto;\n}\n[data-rsi-root] {\n all: initial;\n font-family:\n "Inter",\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n "Helvetica Neue",\n sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #fff;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n}\n[data-rsi-floating-button] {\n all: initial;\n font-family:\n "Inter",\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n "Helvetica Neue",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n[data-rsi-root] button {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n cursor: pointer;\n background: transparent;\n border: none;\n padding: 0;\n margin: 0;\n}\n[data-rsi-root] input,\n[data-rsi-root] textarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n background: transparent;\n border: none;\n outline: none;\n padding: 0;\n margin: 0;\n resize: none;\n}\n[data-rsi-root] input::placeholder,\n[data-rsi-root] textarea::placeholder {\n color: inherit;\n opacity: 0.5;\n}\n[data-rsi-root] a {\n color: inherit;\n text-decoration: none;\n}\n[data-rsi-root] img,\n[data-rsi-root] svg {\n display: block;\n max-width: 100%;\n}\n[data-rsi-root] h1,\n[data-rsi-root] h2,\n[data-rsi-root] h3,\n[data-rsi-root] h4,\n[data-rsi-root] h5,\n[data-rsi-root] h6 {\n font-size: inherit;\n font-weight: inherit;\n}\n[data-rsi-root] ::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n[data-rsi-root] ::-webkit-scrollbar-track {\n background: transparent;\n}\n[data-rsi-root] ::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.2);\n border-radius: 3px;\n}\n[data-rsi-root] ::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.3);\n}\n[data-rsi-root] ::selection {\n background: rgba(99, 102, 241, 0.4);\n color: #fff;\n}\n[data-rsi-root] :focus-visible {\n outline: 2px solid rgba(99, 102, 241, 0.6);\n outline-offset: 2px;\n}\n');
57
+ styleInject('@import "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap";\n[data-rsi-root],\n[data-rsi-root] *,\n[data-rsi-root] *::before,\n[data-rsi-root] *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0;\n font-family:\n "Inter",\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n "Helvetica Neue",\n sans-serif;\n font-size: 14px;\n font-weight: 400;\n font-style: normal;\n font-variant: normal;\n line-height: 1.5;\n letter-spacing: normal;\n text-align: left;\n text-decoration: none;\n text-indent: 0;\n text-shadow: none;\n text-transform: none;\n word-spacing: normal;\n white-space: normal;\n background: transparent;\n color: inherit;\n outline: none;\n vertical-align: baseline;\n visibility: visible;\n opacity: 1;\n transform: none;\n transform-origin: 50% 50% 0;\n animation: none;\n transition: none;\n list-style: none;\n border-collapse: collapse;\n border-spacing: 0;\n float: none;\n clear: none;\n cursor: auto;\n}\n[data-rsi-root] {\n all: initial;\n font-family:\n "Inter",\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n "Helvetica Neue",\n sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #fff;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n}\n[data-rsi-floating-button] {\n all: initial;\n font-family:\n "Inter",\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n "Helvetica Neue",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n[data-rsi-root] button {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n cursor: pointer;\n background: transparent;\n border: none;\n padding: 0;\n margin: 0;\n border-radius: 8px;\n}\n[data-rsi-root] input,\n[data-rsi-root] textarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n background: transparent;\n border: none;\n outline: none;\n padding: 0;\n margin: 0;\n resize: none;\n}\n[data-rsi-root] input::placeholder,\n[data-rsi-root] textarea::placeholder {\n color: inherit;\n opacity: 0.5;\n}\n[data-rsi-root] a {\n color: inherit;\n text-decoration: none;\n}\n[data-rsi-root] img,\n[data-rsi-root] svg {\n display: block;\n max-width: 100%;\n}\n[data-rsi-root] h1,\n[data-rsi-root] h2,\n[data-rsi-root] h3,\n[data-rsi-root] h4,\n[data-rsi-root] h5,\n[data-rsi-root] h6 {\n font-size: inherit;\n font-weight: inherit;\n}\n[data-rsi-root] ::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n[data-rsi-root] ::-webkit-scrollbar-track {\n background: transparent;\n}\n[data-rsi-root] ::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.2);\n border-radius: 3px;\n}\n[data-rsi-root] ::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.3);\n}\n[data-rsi-root] ::selection {\n background: rgba(99, 102, 241, 0.4);\n color: #fff;\n}\n[data-rsi-root] :focus-visible {\n outline: 2px solid rgba(99, 102, 241, 0.6);\n outline-offset: 2px;\n}\n');
57
58
 
58
59
  // src/provider.tsx
59
- var import_react = require("react");
60
+ var import_react2 = require("react");
60
61
 
61
62
  // src/store.ts
62
63
  function createInspectorStore() {
63
- const components = /* @__PURE__ */ new Map();
64
+ const dataSources = /* @__PURE__ */ new Map();
64
65
  const listeners = /* @__PURE__ */ new Set();
65
66
  function emit() {
66
67
  for (const l of listeners) l();
67
68
  }
68
- function getOrCreateComponent(id) {
69
- const existing = components.get(id);
69
+ function getOrCreateDataSource(id, type, slices) {
70
+ const existing = dataSources.get(id);
70
71
  if (existing) return existing;
71
72
  const created = {
72
73
  id,
73
74
  label: "Unknown",
75
+ type,
74
76
  mounted: true,
75
- states: /* @__PURE__ */ new Map()
77
+ states: /* @__PURE__ */ new Map(),
78
+ slices: new Map(
79
+ slices?.map((sliceId) => [sliceId, { id: sliceId, label: sliceId, states: /* @__PURE__ */ new Map() }]) ?? []
80
+ )
76
81
  };
77
- components.set(id, created);
82
+ dataSources.set(id, created);
78
83
  return created;
79
84
  }
80
85
  const store = {
81
86
  enabled: true,
82
- components,
87
+ dataSources,
83
88
  subscribe(listener) {
84
89
  listeners.add(listener);
85
90
  return () => {
@@ -89,104 +94,120 @@ function createInspectorStore() {
89
94
  getSnapshot() {
90
95
  return {
91
96
  enabled: store.enabled,
92
- components: Array.from(components.values()).map((c) => ({
93
- id: c.id,
94
- label: c.label,
95
- mounted: c.mounted,
96
- stateKeys: Array.from(c.states.keys())
97
+ dataSources: Array.from(dataSources.values()).map((ds) => ({
98
+ id: ds.id,
99
+ label: ds.label,
100
+ type: ds.type,
101
+ mounted: ds.mounted,
102
+ stateKeys: Array.from(ds.states.keys()),
103
+ slices: Array.from(ds.slices.values()).map((slice) => ({
104
+ id: slice.id,
105
+ label: slice.label,
106
+ stateKeys: Array.from(slice.states.keys())
107
+ }))
97
108
  }))
98
109
  };
99
110
  },
100
- registerComponent(id, label) {
101
- const c = getOrCreateComponent(id);
102
- c.mounted = true;
103
- if (label && label.trim()) c.label = label.trim();
111
+ // Data source lifecycle
112
+ registerDataSource(id, type, label, slices) {
113
+ const ds = getOrCreateDataSource(id, type, slices);
114
+ ds.mounted = true;
115
+ if (label?.trim()) ds.label = label.trim();
104
116
  emit();
105
117
  },
106
- setComponentLabel(id, label) {
107
- const c = getOrCreateComponent(id);
108
- c.label = label.trim() || "Unknown";
118
+ setDataSourceLabel(id, label) {
119
+ const ds = dataSources.get(id);
120
+ if (!ds) return;
121
+ ds.label = label.trim() || "Unknown";
109
122
  emit();
110
123
  },
111
- unregisterComponent(id) {
112
- const c = components.get(id);
113
- if (!c) return;
114
- c.mounted = false;
124
+ unregisterDataSource(id) {
125
+ const ds = dataSources.get(id);
126
+ if (!ds) return;
127
+ ds.mounted = false;
128
+ emit();
129
+ },
130
+ // State lifecycle (for component type)
131
+ upsertState(dataSourceId, entry) {
132
+ const ds = getOrCreateDataSource(dataSourceId, "component");
133
+ ds.states.set(entry.key, entry);
115
134
  emit();
116
135
  },
117
- upsertState(componentId, entry) {
118
- const c = getOrCreateComponent(componentId);
119
- c.states.set(entry.key, entry);
136
+ updateStateValue(dataSourceId, key, value) {
137
+ const ds = dataSources.get(dataSourceId);
138
+ if (!ds) return;
139
+ const s = ds.states.get(key);
140
+ if (!s) return;
141
+ s.value = value;
142
+ emit();
143
+ },
144
+ removeState(dataSourceId, key) {
145
+ const ds = dataSources.get(dataSourceId);
146
+ if (!ds) return;
147
+ if (!ds.states.has(key)) return;
148
+ ds.states.delete(key);
120
149
  emit();
121
150
  },
122
- updateStateValue(componentId, key, value) {
123
- const c = components.get(componentId);
124
- if (!c) return;
125
- const s = c.states.get(key);
151
+ // Slice state lifecycle (for context type)
152
+ upsertSliceState(dataSourceId, sliceId, entry) {
153
+ const ds = dataSources.get(dataSourceId);
154
+ if (!ds) return;
155
+ let slice = ds.slices.get(sliceId);
156
+ if (!slice) {
157
+ slice = { id: sliceId, label: sliceId, states: /* @__PURE__ */ new Map() };
158
+ ds.slices.set(sliceId, slice);
159
+ }
160
+ slice.states.set(entry.key, entry);
161
+ emit();
162
+ },
163
+ updateSliceStateValue(dataSourceId, sliceId, key, value) {
164
+ const ds = dataSources.get(dataSourceId);
165
+ if (!ds) return;
166
+ const slice = ds.slices.get(sliceId);
167
+ if (!slice) return;
168
+ const s = slice.states.get(key);
126
169
  if (!s) return;
127
170
  s.value = value;
128
171
  emit();
129
172
  },
130
- removeState(componentId, key) {
131
- const c = components.get(componentId);
132
- if (!c) return;
133
- if (!c.states.has(key)) return;
134
- c.states.delete(key);
173
+ removeSliceState(dataSourceId, sliceId, key) {
174
+ const ds = dataSources.get(dataSourceId);
175
+ if (!ds) return;
176
+ const slice = ds.slices.get(sliceId);
177
+ if (!slice) return;
178
+ slice.states.delete(key);
135
179
  emit();
180
+ },
181
+ // Get state entries
182
+ getStateEntry(dataSourceId, key) {
183
+ const ds = dataSources.get(dataSourceId);
184
+ return ds?.states.get(key);
185
+ },
186
+ getSliceStateEntry(dataSourceId, sliceId, key) {
187
+ const ds = dataSources.get(dataSourceId);
188
+ if (!ds) return void 0;
189
+ const slice = ds.slices.get(sliceId);
190
+ return slice?.states.get(key);
191
+ },
192
+ // Legacy aliases
193
+ registerComponent(id, label) {
194
+ store.registerDataSource(id, "component", label);
195
+ },
196
+ setComponentLabel(id, label) {
197
+ store.setDataSourceLabel(id, label);
198
+ },
199
+ unregisterComponent(id) {
200
+ store.unregisterDataSource(id);
201
+ },
202
+ upsertDataSourceState(dataSourceId, sliceId, entry) {
203
+ store.upsertSliceState(dataSourceId, sliceId, entry);
136
204
  }
137
205
  };
138
206
  return store;
139
207
  }
140
208
 
141
- // src/provider.tsx
142
- var import_jsx_runtime = require("react/jsx-runtime");
143
- var InspectorContext = (0, import_react.createContext)(null);
144
- var storeRef = { current: null };
145
- function getStore() {
146
- if (!storeRef.current) {
147
- storeRef.current = createInspectorStore();
148
- }
149
- return storeRef.current;
150
- }
151
- function StateInspectorProvider({
152
- enabled = true,
153
- children
154
- }) {
155
- const store = (0, import_react.useMemo)(() => getStore(), []);
156
- (0, import_react.useEffect)(() => {
157
- storeRef.current.enabled = enabled;
158
- }, [enabled]);
159
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InspectorContext.Provider, { value: store, children });
160
- }
161
- function useInspectorStore() {
162
- const ctx = (0, import_react.useContext)(InspectorContext);
163
- if (!ctx) {
164
- throw new Error("useInspectorStore must be used inside StateInspectorProvider");
165
- }
166
- return ctx;
167
- }
168
- function useInspectorComponent(label) {
169
- const store = useInspectorStore();
170
- const reactId = (0, import_react.useId)();
171
- const componentId = (0, import_react.useMemo)(() => `c_${reactId}`, [reactId]);
172
- (0, import_react.useEffect)(() => {
173
- if (!store.enabled) return;
174
- store.registerComponent(componentId, label?.trim());
175
- return () => {
176
- store.unregisterComponent(componentId);
177
- };
178
- }, [store, componentId, label]);
179
- (0, import_react.useEffect)(() => {
180
- if (!store.enabled) return;
181
- const trimmed = label?.trim();
182
- if (trimmed) store.setComponentLabel(componentId, trimmed);
183
- }, [store, componentId, label]);
184
- console.log({ id: componentId });
185
- return (0, import_react.useMemo)(() => ({ id: componentId }), [componentId]);
186
- }
187
-
188
209
  // src/hooks.ts
189
- var import_react2 = require("react");
210
+ var import_react = require("react");
190
211
  function stableKey(input) {
191
212
  return input.trim();
192
213
  }
@@ -201,14 +222,14 @@ function inferMeta(value) {
201
222
  function useInspectableState(component, key, initial, meta) {
202
223
  const store = useInspectorStore();
203
224
  const componentId = component.id;
204
- const stateKey = (0, import_react2.useMemo)(() => stableKey(key), [key]);
205
- const [value, setValue] = (0, import_react2.useState)(initial);
206
- const setValueRef = (0, import_react2.useRef)(() => {
225
+ const stateKey = (0, import_react.useMemo)(() => stableKey(key), [key]);
226
+ const [value, setValue] = (0, import_react.useState)(initial);
227
+ const setValueRef = (0, import_react.useRef)(() => {
207
228
  });
208
229
  setValueRef.current = (next) => {
209
230
  setValue(next);
210
231
  };
211
- (0, import_react2.useEffect)(() => {
232
+ (0, import_react.useEffect)(() => {
212
233
  if (!store.enabled) return;
213
234
  const resolvedMeta = meta ?? inferMeta(value);
214
235
  store.upsertState(componentId, {
@@ -221,7 +242,7 @@ function useInspectableState(component, key, initial, meta) {
221
242
  store.removeState(componentId, stateKey);
222
243
  };
223
244
  }, [store, componentId, stateKey]);
224
- (0, import_react2.useEffect)(() => {
245
+ (0, import_react.useEffect)(() => {
225
246
  if (!store.enabled) return;
226
247
  const resolvedMeta = meta ?? inferMeta(value);
227
248
  store.upsertState(componentId, {
@@ -235,9 +256,9 @@ function useInspectableState(component, key, initial, meta) {
235
256
  }
236
257
  function useComponentLabel(label) {
237
258
  const store = useInspectorStore();
238
- const reactId = (0, import_react2.useId)();
239
- const componentId = (0, import_react2.useMemo)(() => `c_${reactId}`, [reactId]);
240
- (0, import_react2.useEffect)(() => {
259
+ const reactId = (0, import_react.useId)();
260
+ const componentId = (0, import_react.useMemo)(() => `c_${reactId}`, [reactId]);
261
+ (0, import_react.useEffect)(() => {
241
262
  if (!store.enabled) return;
242
263
  const trimmed = label.trim();
243
264
  if (!trimmed) return;
@@ -245,8 +266,108 @@ function useComponentLabel(label) {
245
266
  }, [store, componentId, label]);
246
267
  }
247
268
 
269
+ // src/provider.tsx
270
+ var import_jsx_runtime = require("react/jsx-runtime");
271
+ var InspectorContext = (0, import_react2.createContext)(null);
272
+ var storeRef = { current: null };
273
+ function getStore() {
274
+ if (!storeRef.current) {
275
+ storeRef.current = createInspectorStore();
276
+ }
277
+ return storeRef.current;
278
+ }
279
+ function StateInspectorProvider({
280
+ enabled = true,
281
+ store,
282
+ children
283
+ }) {
284
+ const mainStore = (0, import_react2.useMemo)(() => getStore(), []);
285
+ (0, import_react2.useEffect)(() => {
286
+ if (!store) return;
287
+ const initialState = store.getState();
288
+ const slices = Object.keys(initialState);
289
+ mainStore.registerDataSource("redux_context", "context", "ReduxContext", slices);
290
+ syncSliceStates(initialState);
291
+ function syncSliceStates(state) {
292
+ slices.forEach((sliceName) => {
293
+ const sliceState = state[sliceName];
294
+ if (sliceState && typeof sliceState === "object" && !Array.isArray(sliceState)) {
295
+ Object.entries(sliceState).forEach(
296
+ ([stateKey, stateValue]) => {
297
+ const meta = inferMeta(stateValue);
298
+ mainStore.upsertSliceState("redux_context", sliceName, {
299
+ key: stateKey,
300
+ value: stateValue,
301
+ ...meta !== void 0 && { meta },
302
+ setValue: (newValue) => {
303
+ store.dispatch({
304
+ type: `${sliceName}/updateField`,
305
+ payload: { field: stateKey, value: newValue }
306
+ });
307
+ }
308
+ });
309
+ }
310
+ );
311
+ } else {
312
+ const meta = inferMeta(sliceState);
313
+ mainStore.upsertSliceState("redux_context", sliceName, {
314
+ key: "value",
315
+ value: sliceState,
316
+ ...meta !== void 0 && { meta },
317
+ setValue: (newValue) => {
318
+ store.dispatch({
319
+ type: `${sliceName}/setValue`,
320
+ payload: newValue
321
+ });
322
+ }
323
+ });
324
+ }
325
+ });
326
+ }
327
+ function handleChange() {
328
+ const nextState = store.getState();
329
+ syncSliceStates(nextState);
330
+ }
331
+ const unsubscribe = store.subscribe(handleChange);
332
+ return () => {
333
+ unsubscribe();
334
+ mainStore.unregisterDataSource("redux_context");
335
+ };
336
+ }, [store, mainStore]);
337
+ (0, import_react2.useEffect)(() => {
338
+ storeRef.current.enabled = enabled;
339
+ }, [enabled]);
340
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InspectorContext.Provider, { value: mainStore, children });
341
+ }
342
+ function useInspectorStore() {
343
+ const ctx = (0, import_react2.useContext)(InspectorContext);
344
+ if (!ctx) {
345
+ throw new Error("useInspectorStore must be used inside StateInspectorProvider");
346
+ }
347
+ return ctx;
348
+ }
349
+ function useInspectorComponent(label) {
350
+ const store = useInspectorStore();
351
+ const reactId = (0, import_react2.useId)();
352
+ const componentId = (0, import_react2.useMemo)(() => `c_${reactId}`, [reactId]);
353
+ (0, import_react2.useEffect)(() => {
354
+ if (!store.enabled) return;
355
+ store.registerComponent(componentId, label?.trim());
356
+ return () => {
357
+ store.unregisterComponent(componentId);
358
+ };
359
+ }, [store, componentId, label]);
360
+ (0, import_react2.useEffect)(() => {
361
+ if (!store.enabled) return;
362
+ const trimmed = label?.trim();
363
+ if (trimmed) store.setComponentLabel(componentId, trimmed);
364
+ }, [store, componentId, label]);
365
+ console.log({ id: componentId });
366
+ return (0, import_react2.useMemo)(() => ({ id: componentId }), [componentId]);
367
+ }
368
+
248
369
  // src/StateInspectorUI.tsx
249
- var import_react5 = require("react");
370
+ var import_react6 = require("react");
250
371
  var import_react_dom = require("react-dom");
251
372
 
252
373
  // src/ui/constants.ts
@@ -545,6 +666,9 @@ function DragHandle({ children, onDragStart, onDragEnd, style }) {
545
666
  );
546
667
  }
547
668
 
669
+ // src/ui/components/ComponentList.tsx
670
+ var import_react4 = require("react");
671
+
548
672
  // src/ui/styles.ts
549
673
  var inputStyle = {
550
674
  padding: "8px 10px",
@@ -580,48 +704,128 @@ function SearchInput({ value, onChange, inputRef }) {
580
704
 
581
705
  // src/ui/components/ComponentList.tsx
582
706
  var import_jsx_runtime9 = require("react/jsx-runtime");
583
- function ComponentList({
584
- components,
585
- selectedId,
586
- onSelect,
587
- hidden,
588
- query,
589
- onQueryChange,
590
- searchRef
591
- }) {
592
- if (hidden) return null;
593
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { padding: 4, boxSizing: "border-box", backgroundColor: "#09090B" }, children: [
594
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
595
- "div",
707
+ var ComponentListItem = ({ dataSource, active, onSelect }) => {
708
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
709
+ "button",
710
+ {
711
+ id: `rsi-comp-${dataSource.id}`,
712
+ onClick: () => onSelect(dataSource.id),
713
+ style: {
714
+ display: "flex",
715
+ justifyContent: "space-between",
716
+ alignItems: "center",
717
+ padding: 8,
718
+ backgroundColor: active ? "#27272A" : "transparent",
719
+ border: "none",
720
+ cursor: "pointer"
721
+ },
722
+ children: [
723
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 500, fontSize: 12, color: active ? "#fff" : "#9F9FA9" }, children: dataSource.label }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
725
+ "div",
726
+ {
727
+ style: {
728
+ fontSize: 9,
729
+ height: 16,
730
+ width: 16,
731
+ color: "#D4D4D8",
732
+ display: "flex",
733
+ alignItems: "center",
734
+ justifyContent: "center",
735
+ borderRadius: 8,
736
+ backgroundColor: "#3F3F47"
737
+ },
738
+ children: dataSource.stateKeys.length
739
+ }
740
+ )
741
+ ]
742
+ },
743
+ dataSource.id
744
+ );
745
+ };
746
+ var ContextListItem = ({ dataSource, activeSliceId, onSelectSlice }) => {
747
+ const [expanded, setExpanded] = (0, import_react4.useState)(true);
748
+ const isActive = dataSource.slices.some(
749
+ (slice) => `${dataSource.id}:${slice.id}` === activeSliceId
750
+ );
751
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [
752
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
753
+ "button",
596
754
  {
755
+ onClick: () => setExpanded(!expanded),
597
756
  style: {
598
757
  display: "flex",
599
- flexDirection: "column",
600
- paddingTop: 4,
601
- paddingBottom: 8,
602
- paddingLeft: 4,
603
- paddingRight: 4
758
+ justifyContent: "space-between",
759
+ alignItems: "center",
760
+ padding: 8,
761
+ backgroundColor: isActive ? "#27272A" : "transparent",
762
+ border: "none",
763
+ cursor: "pointer"
604
764
  },
605
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SearchInput, { value: query, onChange: onQueryChange, inputRef: searchRef })
765
+ children: [
766
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 500, fontSize: 12, color: isActive ? "#fff" : "#9F9FA9" }, children: dataSource.label }),
767
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
768
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
769
+ "div",
770
+ {
771
+ style: {
772
+ fontSize: 9,
773
+ height: 16,
774
+ width: 16,
775
+ color: "#D4D4D8",
776
+ display: "flex",
777
+ alignItems: "center",
778
+ justifyContent: "center",
779
+ borderRadius: 8,
780
+ backgroundColor: "#3F3F47"
781
+ },
782
+ children: dataSource.slices.length
783
+ }
784
+ ),
785
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
786
+ "svg",
787
+ {
788
+ width: "10",
789
+ height: "10",
790
+ viewBox: "0 0 10 10",
791
+ fill: "none",
792
+ style: {
793
+ transform: expanded ? "rotate(180deg)" : "rotate(90deg)",
794
+ transition: "transform 0.15s ease"
795
+ },
796
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
797
+ "path",
798
+ {
799
+ d: "M3 2L7 5L3 8",
800
+ stroke: "#9F9FA9",
801
+ strokeWidth: "1.5",
802
+ strokeLinecap: "round",
803
+ strokeLinejoin: "round"
804
+ }
805
+ )
806
+ }
807
+ )
808
+ ] })
809
+ ]
606
810
  }
607
811
  ),
608
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 2, overflow: "auto", flex: 1 }, children: components.map((c) => {
609
- const active = c.id === selectedId;
812
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", flexDirection: "column", paddingLeft: 16 }, children: dataSource.slices.map((slice) => {
813
+ const active = activeSliceId === `${dataSource.id}:${slice.id}`;
610
814
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
611
815
  "button",
612
816
  {
613
- id: `rsi-comp-${c.id}`,
614
- onClick: () => onSelect(c.id),
817
+ onClick: () => onSelectSlice(dataSource.id, slice.id),
615
818
  style: {
616
819
  display: "flex",
617
820
  justifyContent: "space-between",
618
821
  alignItems: "center",
619
822
  padding: 8,
620
823
  backgroundColor: active ? "#27272A" : "transparent",
621
- border: "none"
824
+ border: "none",
825
+ cursor: "pointer"
622
826
  },
623
827
  children: [
624
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 500, fontSize: 12, color: active ? "#fff" : "#9F9FA9" }, children: c.label }),
828
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 500, fontSize: 12, color: active ? "#fff" : "#9F9FA9" }, children: slice.label }),
625
829
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
626
830
  "div",
627
831
  {
@@ -636,15 +840,59 @@ function ComponentList({
636
840
  borderRadius: 8,
637
841
  backgroundColor: "#3F3F47"
638
842
  },
639
- children: c.stateKeys.length
843
+ children: slice.stateKeys.length
640
844
  }
641
845
  )
642
846
  ]
643
847
  },
644
- c.id
848
+ slice.id
645
849
  );
646
850
  }) })
647
851
  ] });
852
+ };
853
+ function DataSourceList({
854
+ dataSources,
855
+ selectedId,
856
+ onSelect,
857
+ onSelectSlice,
858
+ hidden,
859
+ query,
860
+ onQueryChange,
861
+ searchRef
862
+ }) {
863
+ if (hidden) return null;
864
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { padding: 4, boxSizing: "border-box", backgroundColor: "#09090B" }, children: [
865
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
866
+ "div",
867
+ {
868
+ style: {
869
+ display: "flex",
870
+ flexDirection: "column",
871
+ paddingTop: 4,
872
+ paddingBottom: 8,
873
+ paddingLeft: 4,
874
+ paddingRight: 4
875
+ },
876
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SearchInput, { value: query, onChange: onQueryChange, inputRef: searchRef })
877
+ }
878
+ ),
879
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 2, overflow: "auto", flex: 1 }, children: dataSources.map((ds) => {
880
+ if (ds.type === "context") {
881
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
882
+ ContextListItem,
883
+ {
884
+ dataSource: ds,
885
+ activeSliceId: selectedId,
886
+ onSelectSlice: onSelectSlice ?? (() => {
887
+ })
888
+ },
889
+ ds.id
890
+ );
891
+ }
892
+ const active = ds.id === selectedId;
893
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ComponentListItem, { dataSource: ds, active, onSelect }, ds.id);
894
+ }) })
895
+ ] });
648
896
  }
649
897
 
650
898
  // src/ui/components/StatePanelHeader.tsx
@@ -682,12 +930,12 @@ function StatePanelHeader({
682
930
  }
683
931
 
684
932
  // src/ui/components/JsonEditor.tsx
685
- var import_react4 = require("react");
933
+ var import_react5 = require("react");
686
934
  var import_jsx_runtime11 = require("react/jsx-runtime");
687
935
  function JsonEditor({ initial, onValidJson }) {
688
- const [raw, setRaw] = (0, import_react4.useState)(initial);
689
- const [error, setError] = (0, import_react4.useState)(null);
690
- (0, import_react4.useEffect)(() => {
936
+ const [raw, setRaw] = (0, import_react5.useState)(initial);
937
+ const [error, setError] = (0, import_react5.useState)(null);
938
+ (0, import_react5.useEffect)(() => {
691
939
  setRaw(initial);
692
940
  }, [initial]);
693
941
  const handleChange = (e) => {
@@ -817,7 +1065,7 @@ function StateCard({ state }) {
817
1065
  {
818
1066
  value: state.value,
819
1067
  meta: state.meta,
820
- onChange: (next) => state.setValue(next)
1068
+ onChange: (next) => state.setValue?.(next)
821
1069
  }
822
1070
  )
823
1071
  ]
@@ -1068,16 +1316,18 @@ var DockHeader_default = DockHeader;
1068
1316
  var import_jsx_runtime18 = require("react/jsx-runtime");
1069
1317
  function StateInspectorUI() {
1070
1318
  const store = useInspectorStore();
1071
- const [query, setQuery] = (0, import_react5.useState)("");
1072
- const [leftCollapsed, setLeftCollapsed] = (0, import_react5.useState)(false);
1073
- const [layout, setLayout] = (0, import_react5.useState)("floating");
1074
- const [preview, setPreview] = (0, import_react5.useState)(null);
1075
- const [isTransitioning, setIsTransitioning] = (0, import_react5.useState)(false);
1076
- const [open, setOpen] = (0, import_react5.useState)(false);
1077
- const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
1078
- const [tick, force] = (0, import_react5.useState)(0);
1079
- const searchRef = (0, import_react5.useRef)(null);
1080
- const [pos, setPos] = (0, import_react5.useState)(() => {
1319
+ const [query, setQuery] = (0, import_react6.useState)("");
1320
+ const [leftCollapsed, setLeftCollapsed] = (0, import_react6.useState)(false);
1321
+ const [layout, setLayout] = (0, import_react6.useState)("floating");
1322
+ const [preview, setPreview] = (0, import_react6.useState)(null);
1323
+ const [isTransitioning, setIsTransitioning] = (0, import_react6.useState)(false);
1324
+ const [open, setOpen] = (0, import_react6.useState)(false);
1325
+ const [selectedId, setSelectedId] = (0, import_react6.useState)(null);
1326
+ const [selectedSlice, setSelectedSlice] = (0, import_react6.useState)(null);
1327
+ const [tick, force] = (0, import_react6.useState)(0);
1328
+ console.log(store);
1329
+ const searchRef = (0, import_react6.useRef)(null);
1330
+ const [pos, setPos] = (0, import_react6.useState)(() => {
1081
1331
  try {
1082
1332
  const raw = localStorage.getItem(LS_KEY);
1083
1333
  return raw ? JSON.parse(raw) : { right: 16, bottom: 72 };
@@ -1085,7 +1335,7 @@ function StateInspectorUI() {
1085
1335
  return { right: 16, bottom: 72 };
1086
1336
  }
1087
1337
  });
1088
- const [leftWidth, setLeftWidth] = (0, import_react5.useState)(() => {
1338
+ const [leftWidth, setLeftWidth] = (0, import_react6.useState)(() => {
1089
1339
  try {
1090
1340
  const raw = localStorage.getItem(LS_LEFT_W);
1091
1341
  return raw ? Number(raw) : 280;
@@ -1093,7 +1343,7 @@ function StateInspectorUI() {
1093
1343
  return 280;
1094
1344
  }
1095
1345
  });
1096
- const [size, setSize] = (0, import_react5.useState)(() => {
1346
+ const [size, setSize] = (0, import_react6.useState)(() => {
1097
1347
  try {
1098
1348
  const raw = localStorage.getItem(LS_SIZE);
1099
1349
  return raw ? JSON.parse(raw) : { width: 720, height: 420 };
@@ -1101,64 +1351,83 @@ function StateInspectorUI() {
1101
1351
  return { width: 720, height: 420 };
1102
1352
  }
1103
1353
  });
1104
- (0, import_react5.useEffect)(() => {
1354
+ (0, import_react6.useEffect)(() => {
1105
1355
  try {
1106
1356
  localStorage.setItem(LS_LAYOUT, layout);
1107
1357
  } catch (e) {
1108
1358
  console.error(e);
1109
1359
  }
1110
1360
  }, [layout]);
1111
- (0, import_react5.useEffect)(() => {
1361
+ (0, import_react6.useEffect)(() => {
1112
1362
  try {
1113
1363
  localStorage.setItem(LS_KEY, JSON.stringify(pos));
1114
1364
  } catch (e) {
1115
1365
  console.error(e);
1116
1366
  }
1117
1367
  }, [pos]);
1118
- (0, import_react5.useEffect)(() => {
1368
+ (0, import_react6.useEffect)(() => {
1119
1369
  try {
1120
1370
  localStorage.setItem(LS_LEFT_W, String(leftWidth));
1121
1371
  } catch (e) {
1122
1372
  console.error(e);
1123
1373
  }
1124
1374
  }, [leftWidth]);
1125
- (0, import_react5.useEffect)(() => {
1375
+ (0, import_react6.useEffect)(() => {
1126
1376
  try {
1127
1377
  localStorage.setItem(LS_SIZE, JSON.stringify(size));
1128
1378
  } catch (e) {
1129
1379
  console.error(e);
1130
1380
  }
1131
1381
  }, [size]);
1132
- (0, import_react5.useEffect)(() => {
1382
+ (0, import_react6.useEffect)(() => {
1133
1383
  const shouldCollapse = leftWidth <= 200 || (size.width ?? 720) - leftWidth < 320;
1134
1384
  setLeftCollapsed(shouldCollapse);
1135
1385
  }, [leftWidth, size.width]);
1136
- (0, import_react5.useEffect)(() => store.subscribe(() => force((x) => x + 1)), [store]);
1137
- const snapshot = (0, import_react5.useMemo)(() => store.getSnapshot(), [store, tick]);
1138
- const components = (0, import_react5.useMemo)(
1139
- () => snapshot.components.filter((c) => c.mounted),
1140
- [snapshot.components]
1386
+ (0, import_react6.useEffect)(() => store.subscribe(() => force((x) => x + 1)), [store]);
1387
+ const snapshot = (0, import_react6.useMemo)(() => store.getSnapshot(), [store, tick]);
1388
+ const dataSources = (0, import_react6.useMemo)(
1389
+ () => snapshot.dataSources.filter((ds) => ds.mounted),
1390
+ [snapshot.dataSources]
1141
1391
  );
1142
1392
  const q = query.trim().toLowerCase();
1143
- const filtered = (0, import_react5.useMemo)(
1144
- () => q ? components.filter((c) => {
1145
- const labelHit = c.label.toLowerCase().includes(q);
1146
- const keyHit = c.stateKeys.some((k) => k.toLowerCase().includes(q));
1147
- return labelHit || keyHit;
1148
- }) : components,
1149
- [q, components]
1393
+ const filtered = (0, import_react6.useMemo)(
1394
+ () => q ? dataSources.filter((ds) => {
1395
+ const labelHit = ds.label.toLowerCase().includes(q);
1396
+ const stateKeyHit = ds.stateKeys.some((k) => k.toLowerCase().includes(q));
1397
+ const sliceKeyHit = ds.slices.some(
1398
+ (slice) => slice.stateKeys.some((k) => k.toLowerCase().includes(q))
1399
+ );
1400
+ return labelHit || stateKeyHit || sliceKeyHit;
1401
+ }) : dataSources,
1402
+ [q, dataSources]
1150
1403
  );
1151
- (0, import_react5.useEffect)(() => {
1404
+ (0, import_react6.useEffect)(() => {
1152
1405
  if (!open) return;
1153
- if (components.length === 0) {
1406
+ if (dataSources.length === 0) {
1154
1407
  setSelectedId(null);
1155
1408
  return;
1156
1409
  }
1157
- if (!selectedId || !components.some((c) => c.id === selectedId)) {
1158
- setSelectedId(components[0].id);
1410
+ const isValidSelection = selectedId && dataSources.some((ds) => {
1411
+ if (ds.id === selectedId) return true;
1412
+ if (selectedId.includes(":")) {
1413
+ const [dsId, sliceId] = selectedId.split(":");
1414
+ return ds.id === dsId && ds.slices.some((s) => s.id === sliceId);
1415
+ }
1416
+ return false;
1417
+ });
1418
+ if (!isValidSelection) {
1419
+ const firstDs = dataSources[0];
1420
+ if (firstDs?.type === "context" && firstDs.slices.length > 0) {
1421
+ const firstSlice = firstDs.slices[0];
1422
+ setSelectedId(`${firstDs.id}:${firstSlice.id}`);
1423
+ setSelectedSlice({ dataSourceId: firstDs.id, sliceId: firstSlice.id });
1424
+ } else if (firstDs) {
1425
+ setSelectedId(firstDs.id);
1426
+ setSelectedSlice(null);
1427
+ }
1159
1428
  }
1160
- }, [open, components.length]);
1161
- (0, import_react5.useEffect)(() => {
1429
+ }, [open, dataSources.length]);
1430
+ (0, import_react6.useEffect)(() => {
1162
1431
  function onKey(e) {
1163
1432
  const mod = isMac ? e.metaKey : e.ctrlKey;
1164
1433
  if (mod && e.shiftKey && e.key.toLowerCase() === "i") {
@@ -1191,10 +1460,40 @@ function StateInspectorUI() {
1191
1460
  window.addEventListener("keydown", onKey);
1192
1461
  return () => window.removeEventListener("keydown", onKey);
1193
1462
  }, [open, filtered, selectedId]);
1194
- const selected = (0, import_react5.useMemo)(() => {
1463
+ const selected = (0, import_react6.useMemo)(() => {
1464
+ if (selectedSlice) {
1465
+ const ds2 = store.dataSources.get(selectedSlice.dataSourceId);
1466
+ if (ds2 && ds2.type === "context") {
1467
+ const slice = ds2.slices.get(selectedSlice.sliceId);
1468
+ if (slice) {
1469
+ return {
1470
+ id: `${selectedSlice.dataSourceId}:${selectedSlice.sliceId}`,
1471
+ label: slice.label,
1472
+ mounted: ds2.mounted,
1473
+ states: slice.states
1474
+ };
1475
+ }
1476
+ }
1477
+ return null;
1478
+ }
1195
1479
  if (!selectedId) return null;
1196
- return store.components.get(selectedId) ?? null;
1197
- }, [store, selectedId]);
1480
+ const ds = store.dataSources.get(selectedId);
1481
+ if (!ds) return null;
1482
+ return {
1483
+ id: ds.id,
1484
+ label: ds.label,
1485
+ mounted: ds.mounted,
1486
+ states: ds.states
1487
+ };
1488
+ }, [store, selectedId, selectedSlice, tick]);
1489
+ const handleSelectSlice = (dataSourceId, sliceId) => {
1490
+ setSelectedId(`${dataSourceId}:${sliceId}`);
1491
+ setSelectedSlice({ dataSourceId, sliceId });
1492
+ };
1493
+ const handleSelectComponent = (id) => {
1494
+ setSelectedId(id);
1495
+ setSelectedSlice(null);
1496
+ };
1198
1497
  const handleDragEnd = () => {
1199
1498
  if (preview) {
1200
1499
  setLayout(preview);
@@ -1337,14 +1636,15 @@ function StateInspectorUI() {
1337
1636
  children: [
1338
1637
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResizeHandle, { onResize: handlePanelResize(size, pos) }),
1339
1638
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1340
- ComponentList,
1639
+ DataSourceList,
1341
1640
  {
1342
1641
  query,
1343
1642
  onQueryChange: setQuery,
1344
1643
  searchRef,
1345
- components: filtered,
1644
+ dataSources: filtered,
1346
1645
  selectedId,
1347
- onSelect: setSelectedId,
1646
+ onSelect: handleSelectComponent,
1647
+ onSelectSlice: handleSelectSlice,
1348
1648
  hidden: leftCollapsed
1349
1649
  }
1350
1650
  ),
@@ -1379,6 +1679,7 @@ function StateInspectorUI() {
1379
1679
  StateInspectorProvider,
1380
1680
  StateInspectorUI,
1381
1681
  createInspectorStore,
1682
+ inferMeta,
1382
1683
  useComponentLabel,
1383
1684
  useInspectableState,
1384
1685
  useInspectorComponent,