react-state-inspector-devtools 0.1.2 → 0.1.8

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
@@ -116,16 +116,22 @@ function createInspectorStore() {
116
116
  // src/provider.tsx
117
117
  var import_jsx_runtime = require("react/jsx-runtime");
118
118
  var InspectorContext = (0, import_react.createContext)(null);
119
+ var storeRef = { current: null };
120
+ function getStore() {
121
+ if (!storeRef.current) {
122
+ storeRef.current = createInspectorStore();
123
+ }
124
+ return storeRef.current;
125
+ }
119
126
  function StateInspectorProvider({
120
127
  enabled = true,
121
128
  children
122
129
  }) {
123
- const storeRef = (0, import_react.useRef)(null);
124
- if (!storeRef.current) {
125
- storeRef.current = createInspectorStore();
126
- }
127
- storeRef.current.enabled = enabled;
128
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InspectorContext.Provider, { value: storeRef.current, children });
130
+ const store = (0, import_react.useMemo)(() => getStore(), []);
131
+ (0, import_react.useEffect)(() => {
132
+ storeRef.current.enabled = enabled;
133
+ }, [enabled]);
134
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InspectorContext.Provider, { value: store, children });
129
135
  }
130
136
  function useInspectorStore() {
131
137
  const ctx = (0, import_react.useContext)(InspectorContext);
@@ -217,20 +223,68 @@ function useComponentLabel(label) {
217
223
  var import_react3 = require("react");
218
224
  var import_react_dom = require("react-dom");
219
225
  var import_jsx_runtime2 = require("react/jsx-runtime");
226
+ var isMac = navigator.platform.toUpperCase().includes("MAC");
220
227
  function isOptionObject(opt) {
221
228
  return typeof opt === "object" && opt !== null && "value" in opt;
222
229
  }
223
230
  function StateInspectorUI() {
224
231
  const store = useInspectorStore();
232
+ const [query, setQuery] = (0, import_react3.useState)("");
225
233
  const [open, setOpen] = (0, import_react3.useState)(false);
226
234
  const [selectedId, setSelectedId] = (0, import_react3.useState)(null);
227
235
  const [, force] = (0, import_react3.useState)(0);
236
+ const searchRef = (0, import_react3.useRef)(null);
237
+ const LS_KEY = "rsi:panel-pos";
238
+ const [pos, setPos] = (0, import_react3.useState)(() => {
239
+ try {
240
+ const raw = localStorage.getItem(LS_KEY);
241
+ return raw ? JSON.parse(raw) : { right: 16, bottom: 72 };
242
+ } catch {
243
+ return { right: 16, bottom: 72 };
244
+ }
245
+ });
246
+ (0, import_react3.useEffect)(() => {
247
+ try {
248
+ localStorage.setItem(LS_KEY, JSON.stringify(pos));
249
+ } catch {
250
+ }
251
+ }, [pos]);
252
+ const SIZE_KEY = "rsi:panel-size";
253
+ const [size, setSize] = (0, import_react3.useState)(() => {
254
+ try {
255
+ const raw = localStorage.getItem(SIZE_KEY);
256
+ return raw ? JSON.parse(raw) : { width: 720, height: 420 };
257
+ } catch {
258
+ return { width: 720, height: 420 };
259
+ }
260
+ });
261
+ (0, import_react3.useEffect)(() => {
262
+ try {
263
+ localStorage.setItem(SIZE_KEY, JSON.stringify(size));
264
+ } catch {
265
+ }
266
+ }, [size]);
228
267
  (0, import_react3.useEffect)(() => store.subscribe(() => force((x) => x + 1)), [store]);
229
- if (!store.enabled) return null;
230
- const snapshot = store.getSnapshot();
231
- const components = snapshot.components.filter((c) => c.mounted);
268
+ const snapshot = (0, import_react3.useMemo)(() => store.getSnapshot(), [store]);
269
+ const components = (0, import_react3.useMemo)(
270
+ () => snapshot.components.filter((c) => c.mounted),
271
+ [snapshot.components]
272
+ );
273
+ const q = query.trim().toLowerCase();
274
+ const filtered = (0, import_react3.useMemo)(
275
+ () => q ? components.filter((c) => {
276
+ const labelHit = c.label.toLowerCase().includes(q);
277
+ const keyHit = c.stateKeys.some(
278
+ (k) => k.toLowerCase().includes(q)
279
+ );
280
+ return labelHit || keyHit;
281
+ }) : components,
282
+ [q, components]
283
+ );
232
284
  (0, import_react3.useEffect)(() => {
233
- if (!open) return;
285
+ if (!open) {
286
+ return;
287
+ }
234
288
  if (components.length === 0) {
235
289
  setSelectedId(null);
236
290
  return;
@@ -240,22 +294,56 @@ function StateInspectorUI() {
240
294
  }
241
295
  }, [open, components.length]);
242
296
  (0, import_react3.useEffect)(() => {
243
- if (!open) return;
297
+ if (!open) {
298
+ return;
299
+ }
244
300
  function onKey(e) {
245
301
  if (e.key === "Escape") setOpen(false);
246
302
  }
247
303
  window.addEventListener("keydown", onKey);
248
304
  return () => window.removeEventListener("keydown", onKey);
249
305
  }, [open]);
306
+ (0, import_react3.useEffect)(() => {
307
+ function onKey(e) {
308
+ const mod = isMac ? e.metaKey : e.ctrlKey;
309
+ if (mod && e.shiftKey && e.key.toLowerCase() === "i") {
310
+ e.preventDefault();
311
+ setOpen((o) => !o);
312
+ return;
313
+ }
314
+ if (!open) return;
315
+ if (e.key === "Escape") {
316
+ setOpen(false);
317
+ return;
318
+ }
319
+ if (e.key === "/") {
320
+ e.preventDefault();
321
+ searchRef.current?.focus();
322
+ return;
323
+ }
324
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
325
+ e.preventDefault();
326
+ const list = filtered;
327
+ if (!list.length) return;
328
+ const idx = list.findIndex((c) => c.id === selectedId);
329
+ const next = e.key === "ArrowDown" ? list[(Math.max(-1, idx) + 1) % list.length].id : list[idx <= 0 ? list.length - 1 : idx - 1].id;
330
+ setSelectedId(next);
331
+ }
332
+ }
333
+ window.addEventListener("keydown", onKey);
334
+ return () => window.removeEventListener("keydown", onKey);
335
+ }, [open, filtered, selectedId]);
250
336
  const selected = (0, import_react3.useMemo)(() => {
251
337
  if (!selectedId) return null;
252
338
  return store.components.get(selectedId) ?? null;
253
- }, [store, selectedId, snapshot.components.length]);
339
+ }, [store, selectedId]);
340
+ if (!store.enabled) return null;
254
341
  return (0, import_react_dom.createPortal)(
255
342
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
256
343
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
257
344
  "button",
258
345
  {
346
+ title: "State Inspector (\u2318\u21E7I / Ctrl\u21E7I)",
259
347
  onClick: () => setOpen((o) => !o),
260
348
  style: {
261
349
  position: "fixed",
@@ -271,7 +359,6 @@ function StateInspectorUI() {
271
359
  zIndex: 999999
272
360
  },
273
361
  "aria-label": "Toggle State Inspector",
274
- title: "State Inspector",
275
362
  children: "\u25CE"
276
363
  }
277
364
  ),
@@ -280,11 +367,11 @@ function StateInspectorUI() {
280
367
  {
281
368
  style: {
282
369
  position: "fixed",
283
- right: 16,
284
- bottom: 72,
285
- width: 720,
370
+ right: pos.right,
371
+ bottom: pos.bottom,
372
+ width: size.width,
286
373
  maxWidth: "calc(100vw - 32px)",
287
- height: 420,
374
+ height: size.height,
288
375
  maxHeight: "calc(100vh - 120px)",
289
376
  background: "#1c1c1c",
290
377
  color: "#fff",
@@ -296,12 +383,74 @@ function StateInspectorUI() {
296
383
  overflow: "hidden"
297
384
  },
298
385
  children: [
299
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { borderRight: "1px solid #333", padding: 12, height: "420px", display: "flex", flexDirection: "column", boxSizing: "border-box" }, children: [
300
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
301
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { style: { margin: 0 }, children: "Components" }),
302
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 12, opacity: 0.7 }, children: components.length })
303
- ] }),
304
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: 10, display: "grid", gap: 8, overflow: "auto", flex: 1 }, children: components.map((c) => {
386
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
387
+ "div",
388
+ {
389
+ onPointerDown: (e) => {
390
+ const startX = e.clientX;
391
+ const startY = e.clientY;
392
+ const start = size;
393
+ e.currentTarget.setPointerCapture(e.pointerId);
394
+ const onMove = (ev) => {
395
+ const dx = ev.clientX - startX;
396
+ const dy = ev.clientY - startY;
397
+ setSize({
398
+ width: Math.min(window.innerWidth - 16, Math.max(480, start.width + dx)),
399
+ height: Math.min(window.innerHeight - 120, Math.max(280, start.height + dy))
400
+ });
401
+ };
402
+ const onUp = () => {
403
+ window.removeEventListener("pointermove", onMove);
404
+ window.removeEventListener("pointerup", onUp);
405
+ };
406
+ window.addEventListener("pointermove", onMove);
407
+ window.addEventListener("pointerup", onUp);
408
+ },
409
+ style: {
410
+ position: "absolute",
411
+ right: 6,
412
+ bottom: 6,
413
+ width: 14,
414
+ height: 14,
415
+ borderRadius: 6,
416
+ border: "1px solid #333",
417
+ background: "#838383",
418
+ cursor: "nwse-resize"
419
+ }
420
+ }
421
+ ),
422
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { borderRight: "1px solid #333", padding: 12, height: size.height, display: "flex", flexDirection: "column", boxSizing: "border-box" }, children: [
423
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
424
+ "div",
425
+ {
426
+ style: { display: "flex", alignItems: "center", justifyContent: "space-between", cursor: "grab", userSelect: "none" },
427
+ onPointerDown: (e) => {
428
+ const startX = e.clientX;
429
+ const startY = e.clientY;
430
+ const start = pos;
431
+ e.currentTarget.setPointerCapture(e.pointerId);
432
+ const onMove = (ev) => {
433
+ const dx = ev.clientX - startX;
434
+ const dy = ev.clientY - startY;
435
+ setPos({
436
+ right: Math.max(8, start.right - dx),
437
+ bottom: Math.max(8, start.bottom - dy)
438
+ });
439
+ };
440
+ const onUp = () => {
441
+ window.removeEventListener("pointermove", onMove);
442
+ window.removeEventListener("pointerup", onUp);
443
+ };
444
+ window.addEventListener("pointermove", onMove);
445
+ window.addEventListener("pointerup", onUp);
446
+ },
447
+ children: [
448
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { style: { margin: 0 }, children: "Components" }),
449
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 12, opacity: 0.7 }, children: filtered.length })
450
+ ]
451
+ }
452
+ ),
453
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: 10, display: "grid", gap: 8, overflow: "auto", flex: 1 }, children: filtered.map((c) => {
305
454
  const active = c.id === selectedId;
306
455
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
307
456
  "button",
@@ -328,7 +477,7 @@ function StateInspectorUI() {
328
477
  );
329
478
  }) })
330
479
  ] }),
331
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: 12, overflow: "auto" }, children: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: 12, overflow: "auto", display: "flex", flexDirection: "column", boxSizing: "border-box" }, children: [
332
481
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
333
482
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
334
483
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { style: { margin: 0 }, children: "State" }),
@@ -344,12 +493,31 @@ function StateInspectorUI() {
344
493
  border: "1px solid #333",
345
494
  borderRadius: 10,
346
495
  padding: "6px 10px",
347
- cursor: "pointer"
496
+ cursor: "pointer",
497
+ fontSize: 12
348
498
  },
349
499
  children: "Close"
350
500
  }
351
501
  )
352
502
  ] }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
504
+ "input",
505
+ {
506
+ value: query,
507
+ onChange: (e) => setQuery(e.target.value),
508
+ placeholder: "Search components / state keys\u2026",
509
+ ref: searchRef,
510
+ style: {
511
+ marginTop: 10,
512
+ padding: "8px 10px",
513
+ borderRadius: 10,
514
+ border: "1px solid #333",
515
+ background: "#111",
516
+ color: "#fff",
517
+ outline: "none"
518
+ }
519
+ }
520
+ ),
353
521
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: 12, display: "grid", gap: 10 }, children: [
354
522
  selected && selected.states.size === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: 13, opacity: 0.7 }, children: "This component has no inspectable state." }),
355
523
  !selected && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { opacity: 0.7, fontSize: 13 }, children: "No component selected." }),
@@ -360,7 +528,8 @@ function StateInspectorUI() {
360
528
  border: "1px solid #333",
361
529
  borderRadius: 12,
362
530
  padding: 10,
363
- display: "grid",
531
+ display: "flex",
532
+ flexDirection: "column",
364
533
  gap: 8
365
534
  },
366
535
  children: [
@@ -415,7 +584,6 @@ function StateEditor({
415
584
  value: current,
416
585
  onChange: (e) => onChange(e.target.value),
417
586
  style: {
418
- width: "100%",
419
587
  padding: "8px 10px",
420
588
  borderRadius: 10,
421
589
  border: "1px solid #333",
@@ -429,7 +597,7 @@ function StateEditor({
429
597
  }
430
598
  );
431
599
  }
432
- if (meta?.type === "number" || typeof value === "number") {
600
+ if (meta?.type === "number") {
433
601
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
434
602
  "input",
435
603
  {
@@ -440,7 +608,6 @@ function StateEditor({
440
608
  step: meta?.step,
441
609
  onChange: (e) => onChange(e.target.value === "" ? 0 : Number(e.target.value)),
442
610
  style: {
443
- width: "100%",
444
611
  padding: "8px 10px",
445
612
  borderRadius: 10,
446
613
  border: "1px solid #333",
@@ -459,10 +626,9 @@ function StateEditor({
459
626
  {
460
627
  type: "text",
461
628
  value: typeof value === "string" ? value : String(value ?? ""),
462
- placeholder: meta?.placeholder,
629
+ placeholder: meta?.type === "text" ? meta.placeholder : void 0,
463
630
  onChange: (e) => onChange(e.target.value),
464
631
  style: {
465
- width: "100%",
466
632
  padding: "8px 10px",
467
633
  borderRadius: 10,
468
634
  border: "1px solid #333",
@@ -500,13 +666,12 @@ function JsonEditor({
500
666
  const parsed = JSON.parse(next);
501
667
  setError(null);
502
668
  onValidJson(parsed);
503
- } catch (err) {
669
+ } catch {
504
670
  setError("Invalid JSON");
505
671
  }
506
672
  },
507
673
  rows: 6,
508
674
  style: {
509
- width: "100%",
510
675
  padding: "8px 10px",
511
676
  borderRadius: 10,
512
677
  border: "1px solid #333",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/store.ts","../src/hooks.ts","../src/StateInspectorUI.tsx"],"sourcesContent":["export * from \"./provider\";\nexport * from \"./store\";\nexport * from \"./hooks\";\nexport * from \"./StateInspectorUI\";\n","import React, { createContext, useContext, useEffect, useId, useMemo, useRef } from \"react\";\nimport { createInspectorStore, InspectorComponentRef, InspectorStore } from \"./store\";\n\nconst InspectorContext = createContext<InspectorStore | null>(null);\n\nexport function StateInspectorProvider({\n enabled = true,\n children,\n}: {\n enabled?: boolean;\n children: React.ReactNode;\n}) {\n const storeRef = useRef<InspectorStore>(null);\n\n if (!storeRef.current) {\n storeRef.current = createInspectorStore();\n }\n\n storeRef.current.enabled = enabled;\n\n return (\n <InspectorContext.Provider value={storeRef.current}>\n {children}\n </InspectorContext.Provider>\n );\n}\n\nexport function useInspectorStore(): InspectorStore {\n const ctx = useContext(InspectorContext);\n if (!ctx) {\n throw new Error(\"useInspectorStore must be used inside StateInspectorProvider\");\n }\n return ctx;\n}\n\nexport function useInspectorComponent(label?: string): InspectorComponentRef {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n\n store.registerComponent(componentId, label?.trim());\n return () => {\n store.unregisterComponent(componentId);\n };\n }, [store, componentId, label]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label?.trim();\n if (trimmed) store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n\n return useMemo(() => ({ id: componentId }), [componentId]);\n}\n","export type ComponentId = string;\n\nexport type InspectableMeta =\n | { type: \"boolean\" }\n | { type: \"text\"; placeholder?: string }\n | { type: \"number\"; min?: number; max?: number; step?: number }\n | { type: \"select\"; options: Array<string | { label: string; value: string }> }\n | { type: \"json\" }\n | { type: \"custom\"; renderId: string };\n\nexport interface InspectableStateEntry {\n key: string;\n value: unknown;\n setValue: (next: unknown) => void;\n meta?: InspectableMeta;\n}\n\nexport interface ComponentEntry {\n id: ComponentId;\n label: string;\n mounted: boolean;\n states: Map<string, InspectableStateEntry>;\n}\n\nexport interface InspectorSnapshot {\n enabled: boolean;\n components: Array<{\n id: ComponentId;\n label: string;\n mounted: boolean;\n stateKeys: string[];\n }>;\n}\n\ntype Listener = () => void;\n\nexport interface InspectorComponentRef {\n id: string;\n}\n\nexport interface InspectorStore {\n enabled: boolean;\n\n // internal registry\n components: Map<ComponentId, ComponentEntry>;\n\n // subscriptions\n subscribe: (listener: Listener) => () => void;\n getSnapshot: () => InspectorSnapshot;\n\n // component lifecycle\n registerComponent: (id: ComponentId, label?: string) => void;\n setComponentLabel: (id: ComponentId, label: string) => void;\n unregisterComponent: (id: ComponentId) => void;\n\n // state lifecycle\n upsertState: (\n componentId: ComponentId,\n entry: InspectableStateEntry,\n ) => void;\n\n updateStateValue: (componentId: ComponentId, key: string, value: unknown) => void;\n removeState: (componentId: ComponentId, key: string) => void;\n}\n\nexport function createInspectorStore(): InspectorStore {\n const components = new Map<ComponentId, ComponentEntry>();\n const listeners = new Set<Listener>();\n\n function emit() {\n for (const l of listeners) l();\n }\n\n function getOrCreateComponent(id: ComponentId): ComponentEntry {\n const existing = components.get(id);\n if (existing) return existing;\n\n const created: ComponentEntry = {\n id,\n label: \"Unknown\",\n mounted: true,\n states: new Map(),\n };\n components.set(id, created);\n return created;\n }\n\n const store: InspectorStore = {\n enabled: true,\n components,\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n getSnapshot() {\n return {\n enabled: store.enabled,\n components: Array.from(components.values()).map((c) => ({\n id: c.id,\n label: c.label,\n mounted: c.mounted,\n stateKeys: Array.from(c.states.keys()),\n })),\n };\n },\n\n registerComponent(id, label) {\n const c = getOrCreateComponent(id);\n c.mounted = true;\n if (label && label.trim()) c.label = label.trim();\n emit();\n },\n\n setComponentLabel(id, label) {\n const c = getOrCreateComponent(id);\n c.label = label.trim() || \"Unknown\";\n emit();\n },\n\n unregisterComponent(id) {\n const c = components.get(id);\n if (!c) return;\n\n // StrictMode double-unmount safe:\n // we mark as unmounted; UI can choose to hide unmounted entries.\n c.mounted = false;\n\n // Optional: immediate cleanup if you prefer:\n // components.delete(id);\n\n emit();\n },\n\n upsertState(componentId, entry) {\n const c = getOrCreateComponent(componentId);\n c.states.set(entry.key, entry);\n emit();\n },\n\n updateStateValue(componentId, key, value) {\n const c = components.get(componentId);\n if (!c) return;\n const s = c.states.get(key);\n if (!s) return;\n s.value = value;\n emit();\n },\n\n removeState(componentId, key) {\n const c = components.get(componentId);\n if (!c) return;\n if (!c.states.has(key)) return;\n c.states.delete(key);\n emit();\n },\n };\n\n return store;\n}\n","import React, { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { useInspectorStore } from \"./provider\";\nimport type { InspectableMeta, InspectorComponentRef } from \"./store\";\n\nfunction stableKey(input: string): string {\n return input.trim();\n}\n\nfunction inferMeta(value: unknown): InspectableMeta | undefined {\n const t = typeof value;\n if (t === \"boolean\") return { type: \"boolean\" };\n if (t === \"number\") return { type: \"number\" };\n if (t === \"string\") return { type: \"text\" };\n if (value && t === \"object\") return { type: \"json\" };\n return undefined;\n}\n\n/**\n * Registers a piece of state to the inspector registry.\n * Opt-in by replacing useState with useInspectableState.\n */\nexport function useInspectableState<T>(\n component: InspectorComponentRef,\n key: string,\n initial: T | (() => T),\n meta?: InspectableMeta,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const store = useInspectorStore();\n const componentId = component.id;\n\n const stateKey = useMemo(() => stableKey(key), [key]);\n\n const [value, setValue] = useState<T>(initial);\n\n // Keep latest setter stable for registry consumers\n const setValueRef = useRef<(next: unknown) => void>(() => {});\n setValueRef.current = (next: unknown) => {\n setValue(next as T);\n };\n\n // Register component + state entry\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n\n return () => {\n store.removeState(componentId, stateKey);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store, componentId, stateKey]);\n\n // Sync updates on each render when value/meta changes\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n }, [store, componentId, stateKey, value, meta]);\n\n return [value, setValue];\n}\n\n/**\n * Optional helper to give the current component instance a human label.\n */\nexport function useComponentLabel(label: string) {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label.trim();\n if (!trimmed) return;\n store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useInspectorStore } from \"./provider\";\nuseInspectorStore\n\nfunction isOptionObject(\n opt: string | { label: string; value: string },\n): opt is { label: string; value: string } {\n return typeof opt === \"object\" && opt !== null && \"value\" in opt;\n}\n\nexport function StateInspectorUI() {\n const store = useInspectorStore();\n\n const [open, setOpen] = useState(false);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [, force] = useState(0);\n\n useEffect(() => store.subscribe(() => force((x) => x + 1)), [store]);\n\n if (!store.enabled) return null;\n\n const snapshot = store.getSnapshot();\n const components = snapshot.components.filter((c) => c.mounted);\n\n // Keep a valid selection\n useEffect(() => {\n if (!open) return;\n\n if (components.length === 0) {\n setSelectedId(null);\n return;\n }\n\n if (!selectedId || !components.some((c) => c.id === selectedId)) {\n setSelectedId(components[0]!.id);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, components.length]);\n\n useEffect(() => {\n if (!open) return;\n\n function onKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") setOpen(false);\n }\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open]);\n\n const selected = useMemo(() => {\n if (!selectedId) return null;\n return store.components.get(selectedId) ?? null;\n }, [store, selectedId, snapshot.components.length]);\n\n return createPortal(\n <>\n {/* Floating Button */}\n <button\n onClick={() => setOpen((o) => !o)}\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 16,\n width: 44,\n height: 44,\n borderRadius: 22,\n border: \"none\",\n background: \"#111\",\n color: \"#fff\",\n cursor: \"pointer\",\n zIndex: 999999,\n }}\n aria-label=\"Toggle State Inspector\"\n title=\"State Inspector\"\n >\n ◎\n </button>\n\n {/* Floating Card */}\n {open && (\n <div\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 72,\n width: 720,\n maxWidth: \"calc(100vw - 32px)\",\n height: 420,\n maxHeight: \"calc(100vh - 120px)\",\n background: \"#1c1c1c\",\n color: \"#fff\",\n borderRadius: 12,\n border: \"1px solid #333\",\n zIndex: 999999,\n display: \"grid\",\n gridTemplateColumns: \"280px 1fr\",\n overflow: \"hidden\",\n }}\n >\n {/* Left: component list */}\n <div style={{ borderRight: \"1px solid #333\", padding: 12, height: \"420px\", display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <h4 style={{ margin: 0 }}>Components</h4>\n <span style={{ fontSize: 12, opacity: 0.7 }}>{components.length}</span>\n </div>\n\n <div style={{ marginTop: 10, display: \"grid\", gap: 8, overflow: \"auto\", flex: 1 }}>\n {components.map((c) => {\n const active = c.id === selectedId;\n return (\n <button\n key={c.id}\n onClick={() => setSelectedId(c.id)}\n style={{\n textAlign: \"left\",\n background: active ? \"#2a2a2a\" : \"transparent\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: 10,\n color: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n <div style={{ fontWeight: 700 }}>{c.label}</div>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n states: {c.stateKeys.length ? c.stateKeys.length : 0}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Right: state editors */}\n <div style={{ padding: 12, overflow: \"auto\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <div>\n <h4 style={{ margin: 0 }}>State</h4>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n {selected ? selected.label : \"No selection\"}\n </div>\n </div>\n\n <button\n onClick={() => setOpen(false)}\n style={{\n background: \"transparent\",\n color: \"#fff\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: \"6px 10px\",\n cursor: \"pointer\",\n }}\n >\n Close\n </button>\n </div>\n\n <div style={{ marginTop: 12, display: \"grid\", gap: 10 }}>\n {selected && selected.states.size === 0 && (\n <div style={{ fontSize: 13, opacity: 0.7 }}>\n This component has no inspectable state.\n </div>\n )}\n\n {!selected && (\n <div style={{ opacity: 0.7, fontSize: 13 }}>\n No component selected.\n </div>\n )}\n\n {selected &&\n Array.from(selected.states.values()).map((s) => (\n <div\n key={s.key}\n style={{\n border: \"1px solid #333\",\n borderRadius: 12,\n padding: 10,\n display: \"grid\",\n gap: 8,\n }}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\", gap: 12 }}>\n <div style={{ fontWeight: 700 }}>{s.key}</div>\n <div style={{ fontSize: 12, opacity: 0.6 }}>{s.meta?.type ?? \"auto\"}</div>\n </div>\n\n <StateEditor\n value={s.value}\n meta={s.meta}\n onChange={(next) => s.setValue(next)}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>,\n document.body,\n );\n}\n\nfunction StateEditor({\n value,\n meta,\n onChange,\n}: {\n value: unknown;\n meta: any;\n onChange: (next: unknown) => void;\n}) {\n // boolean\n if (meta?.type === \"boolean\" || typeof value === \"boolean\") {\n return (\n <label style={{ display: \"flex\", gap: 10, alignItems: \"center\" }}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n />\n <span style={{ fontSize: 13, opacity: 0.8 }}>{String(Boolean(value))}</span>\n </label>\n );\n }\n\n // select\n if (meta?.type === \"select\" && Array.isArray(meta.options)) {\n const current = typeof value === \"string\" ? value : String(value ?? \"\");\n return (\n <select\n value={current}\n onChange={(e) => onChange(e.target.value)}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n >\n {meta.options.map((opt: any) => {\n const o = isOptionObject(opt) ? opt : { label: String(opt), value: String(opt) };\n return (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n );\n })}\n </select>\n );\n }\n\n // number\n if (meta?.type === \"number\" || typeof value === \"number\") {\n return (\n <input\n type=\"number\"\n value={typeof value === \"number\" ? value : Number(value ?? 0)}\n min={meta?.min}\n max={meta?.max}\n step={meta?.step}\n onChange={(e) => onChange(e.target.value === \"\" ? 0 : Number(e.target.value))}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n }\n\n // json\n if (meta?.type === \"json\" || (value && typeof value === \"object\")) {\n const text = safeStringify(value);\n return (\n <JsonEditor initial={text} onValidJson={(obj) => onChange(obj)} />\n );\n }\n\n // text (default)\n return (\n <input\n type=\"text\"\n value={typeof value === \"string\" ? value : String(value ?? \"\")}\n placeholder={meta?.placeholder}\n onChange={(e) => onChange(e.target.value)}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n}\n\nfunction safeStringify(v: unknown) {\n try {\n return JSON.stringify(v, null, 2);\n } catch {\n return String(v);\n }\n}\n\nfunction JsonEditor({\n initial,\n onValidJson,\n}: {\n initial: string;\n onValidJson: (obj: unknown) => void;\n}) {\n const [raw, setRaw] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setRaw(initial);\n }, [initial]);\n\n return (\n <div style={{ display: \"grid\", gap: 6 }}>\n <textarea\n value={raw}\n onChange={(e) => {\n const next = e.target.value;\n setRaw(next);\n\n try {\n const parsed = JSON.parse(next);\n setError(null);\n onValidJson(parsed);\n } catch (err: any) {\n setError(\"Invalid JSON\");\n }\n }}\n rows={6}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n fontSize: 12,\n }}\n />\n {error && <div style={{ fontSize: 12, color: \"#ff6b6b\" }}>{error}</div>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAoF;;;ACiE7E,SAAS,uBAAuC;AACrD,QAAM,aAAa,oBAAI,IAAiC;AACxD,QAAM,YAAY,oBAAI,IAAc;AAEpC,WAAS,OAAO;AACd,eAAW,KAAK,UAAW,GAAE;AAAA,EAC/B;AAEA,WAAS,qBAAqB,IAAiC;AAC7D,UAAM,WAAW,WAAW,IAAI,EAAE;AAClC,QAAI,SAAU,QAAO;AAErB,UAAM,UAA0B;AAAA,MAC9B;AAAA,MACA,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ,oBAAI,IAAI;AAAA,IAClB;AACA,eAAW,IAAI,IAAI,OAAO;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,QAAwB;AAAA,IAC5B,SAAS;AAAA,IACT;AAAA,IAEA,UAAU,UAAU;AAClB,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM;AACX,kBAAU,OAAO,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,IAEA,cAAc;AACZ,aAAO;AAAA,QACL,SAAS,MAAM;AAAA,QACf,YAAY,MAAM,KAAK,WAAW,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,UACtD,IAAI,EAAE;AAAA,UACN,OAAO,EAAE;AAAA,UACT,SAAS,EAAE;AAAA,UACX,WAAW,MAAM,KAAK,EAAE,OAAO,KAAK,CAAC;AAAA,QACvC,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,kBAAkB,IAAI,OAAO;AAC3B,YAAM,IAAI,qBAAqB,EAAE;AACjC,QAAE,UAAU;AACZ,UAAI,SAAS,MAAM,KAAK,EAAG,GAAE,QAAQ,MAAM,KAAK;AAChD,WAAK;AAAA,IACP;AAAA,IAEA,kBAAkB,IAAI,OAAO;AAC3B,YAAM,IAAI,qBAAqB,EAAE;AACjC,QAAE,QAAQ,MAAM,KAAK,KAAK;AAC1B,WAAK;AAAA,IACP;AAAA,IAEA,oBAAoB,IAAI;AACtB,YAAM,IAAI,WAAW,IAAI,EAAE;AAC3B,UAAI,CAAC,EAAG;AAIR,QAAE,UAAU;AAKZ,WAAK;AAAA,IACP;AAAA,IAEA,YAAY,aAAa,OAAO;AAC9B,YAAM,IAAI,qBAAqB,WAAW;AAC1C,QAAE,OAAO,IAAI,MAAM,KAAK,KAAK;AAC7B,WAAK;AAAA,IACP;AAAA,IAEA,iBAAiB,aAAa,KAAK,OAAO;AACxC,YAAM,IAAI,WAAW,IAAI,WAAW;AACpC,UAAI,CAAC,EAAG;AACR,YAAM,IAAI,EAAE,OAAO,IAAI,GAAG;AAC1B,UAAI,CAAC,EAAG;AACR,QAAE,QAAQ;AACV,WAAK;AAAA,IACP;AAAA,IAEA,YAAY,aAAa,KAAK;AAC5B,YAAM,IAAI,WAAW,IAAI,WAAW;AACpC,UAAI,CAAC,EAAG;AACR,UAAI,CAAC,EAAE,OAAO,IAAI,GAAG,EAAG;AACxB,QAAE,OAAO,OAAO,GAAG;AACnB,WAAK;AAAA,IACP;AAAA,EACF;AAEA,SAAO;AACT;;;AD7II;AAlBJ,IAAM,uBAAmB,4BAAqC,IAAI;AAE3D,SAAS,uBAAuB;AAAA,EACrC,UAAU;AAAA,EACV;AACF,GAGG;AACD,QAAM,eAAW,qBAAuB,IAAI;AAE5C,MAAI,CAAC,SAAS,SAAS;AACrB,aAAS,UAAU,qBAAqB;AAAA,EAC1C;AAEA,WAAS,QAAQ,UAAU;AAE3B,SACE,4CAAC,iBAAiB,UAAjB,EAA0B,OAAO,SAAS,SACxC,UACH;AAEJ;AAEO,SAAS,oBAAoC;AAClD,QAAM,UAAM,yBAAW,gBAAgB;AACvC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAChF;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,OAAuC;AAC3E,QAAM,QAAQ,kBAAkB;AAChC,QAAM,cAAU,oBAAM;AACtB,QAAM,kBAAc,sBAAQ,MAAM,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC;AAE3D,8BAAU,MAAM;AACd,QAAI,CAAC,MAAM,QAAS;AAEpB,UAAM,kBAAkB,aAAa,OAAO,KAAK,CAAC;AAClD,WAAO,MAAM;AACX,YAAM,oBAAoB,WAAW;AAAA,IACvC;AAAA,EACF,GAAG,CAAC,OAAO,aAAa,KAAK,CAAC;AAE9B,8BAAU,MAAM;AACd,QAAI,CAAC,MAAM,QAAS;AACpB,UAAM,UAAU,OAAO,KAAK;AAC5B,QAAI,QAAS,OAAM,kBAAkB,aAAa,OAAO;AAAA,EAC3D,GAAG,CAAC,OAAO,aAAa,KAAK,CAAC;AAE9B,aAAO,sBAAQ,OAAO,EAAE,IAAI,YAAY,IAAI,CAAC,WAAW,CAAC;AAC3D;;;AExDA,IAAAA,gBAAmE;AAInE,SAAS,UAAU,OAAuB;AACxC,SAAO,MAAM,KAAK;AACpB;AAEA,SAAS,UAAU,OAA6C;AAC9D,QAAM,IAAI,OAAO;AACjB,MAAI,MAAM,UAAW,QAAO,EAAE,MAAM,UAAU;AAC9C,MAAI,MAAM,SAAU,QAAO,EAAE,MAAM,SAAS;AAC5C,MAAI,MAAM,SAAU,QAAO,EAAE,MAAM,OAAO;AAC1C,MAAI,SAAS,MAAM,SAAU,QAAO,EAAE,MAAM,OAAO;AACnD,SAAO;AACT;AAMO,SAAS,oBACd,WACA,KACA,SACA,MAC8C;AAC9C,QAAM,QAAQ,kBAAkB;AAChC,QAAM,cAAc,UAAU;AAE9B,QAAM,eAAW,uBAAQ,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC;AAEpD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAY,OAAO;AAG7C,QAAM,kBAAc,sBAAgC,MAAM;AAAA,EAAC,CAAC;AAC5D,cAAY,UAAU,CAAC,SAAkB;AACvC,aAAS,IAAS;AAAA,EACpB;AAGA,+BAAU,MAAM;AACd,QAAI,CAAC,MAAM,QAAS;AAEpB,UAAM,eAAe,QAAQ,UAAU,KAAK;AAC5C,UAAM,YAAY,aAAa;AAAA,MAC7B,KAAK;AAAA,MACL;AAAA,MACA,UAAU,CAAC,SAAS,YAAY,QAAQ,IAAI;AAAA,MAC5C,GAAI,iBAAiB,UAAa,EAAE,MAAM,aAAa;AAAA,IACzD,CAAC;AAED,WAAO,MAAM;AACX,YAAM,YAAY,aAAa,QAAQ;AAAA,IACzC;AAAA,EAEF,GAAG,CAAC,OAAO,aAAa,QAAQ,CAAC;AAGjC,+BAAU,MAAM;AACd,QAAI,CAAC,MAAM,QAAS;AAEpB,UAAM,eAAe,QAAQ,UAAU,KAAK;AAC5C,UAAM,YAAY,aAAa;AAAA,MAC7B,KAAK;AAAA,MACL;AAAA,MACA,UAAU,CAAC,SAAS,YAAY,QAAQ,IAAI;AAAA,MAC5C,GAAI,iBAAiB,UAAa,EAAE,MAAM,aAAa;AAAA,IACzD,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,aAAa,UAAU,OAAO,IAAI,CAAC;AAE9C,SAAO,CAAC,OAAO,QAAQ;AACzB;AAKO,SAAS,kBAAkB,OAAe;AAC/C,QAAM,QAAQ,kBAAkB;AAChC,QAAM,cAAU,qBAAM;AACtB,QAAM,kBAAc,uBAAQ,MAAM,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC;AAE3D,+BAAU,MAAM;AACd,QAAI,CAAC,MAAM,QAAS;AACpB,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,CAAC,QAAS;AACd,UAAM,kBAAkB,aAAa,OAAO;AAAA,EAC9C,GAAG,CAAC,OAAO,aAAa,KAAK,CAAC;AAChC;;;ACxFA,IAAAC,gBAA6C;AAC7C,uBAA6B;AAwDzB,IAAAC,sBAAA;AApDJ,SAAS,eACP,KACyC;AACzC,SAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,WAAW;AAC/D;AAEO,SAAS,mBAAmB;AACjC,QAAM,QAAQ,kBAAkB;AAEhC,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,KAAK;AACtC,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAwB,IAAI;AAChE,QAAM,CAAC,EAAE,KAAK,QAAI,wBAAS,CAAC;AAE5B,+BAAU,MAAM,MAAM,UAAU,MAAM,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;AAEnE,MAAI,CAAC,MAAM,QAAS,QAAO;AAE3B,QAAM,WAAW,MAAM,YAAY;AACnC,QAAM,aAAa,SAAS,WAAW,OAAO,CAAC,MAAM,EAAE,OAAO;AAG9D,+BAAU,MAAM;AACd,QAAI,CAAC,KAAM;AAEX,QAAI,WAAW,WAAW,GAAG;AAC3B,oBAAc,IAAI;AAClB;AAAA,IACF;AAEA,QAAI,CAAC,cAAc,CAAC,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU,GAAG;AAC/D,oBAAc,WAAW,CAAC,EAAG,EAAE;AAAA,IACjC;AAAA,EAEF,GAAG,CAAC,MAAM,WAAW,MAAM,CAAC;AAE5B,+BAAU,MAAM;AACd,QAAI,CAAC,KAAM;AAEX,aAAS,MAAM,GAAkB;AAC/B,UAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,IACvC;AAEA,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM,OAAO,oBAAoB,WAAW,KAAK;AAAA,EAC1D,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,eAAW,uBAAQ,MAAM;AAC7B,QAAI,CAAC,WAAY,QAAO;AACxB,WAAO,MAAM,WAAW,IAAI,UAAU,KAAK;AAAA,EAC7C,GAAG,CAAC,OAAO,YAAY,SAAS,WAAW,MAAM,CAAC;AAElD,aAAO;AAAA,IACL,8EAEE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,UAChC,OAAO;AAAA,YACL,UAAU;AAAA,YACV,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,cAAc;AAAA,YACd,QAAQ;AAAA,YACR,YAAY;AAAA,YACZ,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV;AAAA,UACA,cAAW;AAAA,UACX,OAAM;AAAA,UACP;AAAA;AAAA,MAED;AAAA,MAGC,QACC;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,UAAU;AAAA,YACV,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,YAAY;AAAA,YACZ,OAAO;AAAA,YACP,cAAc;AAAA,YACd,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,qBAAqB;AAAA,YACrB,UAAU;AAAA,UACZ;AAAA,UAGA;AAAA,0DAAC,SAAI,OAAO,EAAE,aAAa,kBAAkB,SAAS,IAAI,QAAQ,SAAS,SAAS,QAAQ,eAAe,UAAU,WAAW,aAAa,GAC3I;AAAA,4DAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,gBAAgB,GACnF;AAAA,6DAAC,QAAG,OAAO,EAAE,QAAQ,EAAE,GAAG,wBAAU;AAAA,gBACpC,6CAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,qBAAW,QAAO;AAAA,iBAClE;AAAA,cAEA,6CAAC,SAAI,OAAO,EAAE,WAAW,IAAI,SAAS,QAAQ,KAAK,GAAG,UAAU,QAAQ,MAAM,EAAE,GAC7E,qBAAW,IAAI,CAAC,MAAM;AACrB,sBAAM,SAAS,EAAE,OAAO;AACxB,uBACE;AAAA,kBAAC;AAAA;AAAA,oBAEC,SAAS,MAAM,cAAc,EAAE,EAAE;AAAA,oBACjC,OAAO;AAAA,sBACL,WAAW;AAAA,sBACX,YAAY,SAAS,YAAY;AAAA,sBACjC,QAAQ;AAAA,sBACR,cAAc;AAAA,sBACd,SAAS;AAAA,sBACT,OAAO;AAAA,sBACP,QAAQ;AAAA,oBACV;AAAA,oBAEA;AAAA,mEAAC,SAAI,OAAO,EAAE,YAAY,IAAI,GAAI,YAAE,OAAM;AAAA,sBAC1C,8CAAC,SAAI,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAG;AAAA;AAAA,wBACjC,EAAE,UAAU,SAAS,EAAE,UAAU,SAAS;AAAA,yBACrD;AAAA;AAAA;AAAA,kBAfK,EAAE;AAAA,gBAgBT;AAAA,cAEJ,CAAC,GACH;AAAA,eACF;AAAA,YAGA,8CAAC,SAAI,OAAO,EAAE,SAAS,IAAI,UAAU,OAAO,GAC1C;AAAA,4DAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,gBAAgB,GACnF;AAAA,8DAAC,SACC;AAAA,+DAAC,QAAG,OAAO,EAAE,QAAQ,EAAE,GAAG,mBAAK;AAAA,kBAC/B,6CAAC,SAAI,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GACtC,qBAAW,SAAS,QAAQ,gBAC/B;AAAA,mBACF;AAAA,gBAEA;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAS,MAAM,QAAQ,KAAK;AAAA,oBAC5B,OAAO;AAAA,sBACL,YAAY;AAAA,sBACZ,OAAO;AAAA,sBACP,QAAQ;AAAA,sBACR,cAAc;AAAA,sBACd,SAAS;AAAA,sBACT,QAAQ;AAAA,oBACV;AAAA,oBACD;AAAA;AAAA,gBAED;AAAA,iBACF;AAAA,cAEA,8CAAC,SAAI,OAAO,EAAE,WAAW,IAAI,SAAS,QAAQ,KAAK,GAAG,GACnD;AAAA,4BAAY,SAAS,OAAO,SAAS,KACpC,6CAAC,SAAI,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAG,sDAE5C;AAAA,gBAGD,CAAC,YACA,6CAAC,SAAI,OAAO,EAAE,SAAS,KAAK,UAAU,GAAG,GAAG,oCAE5C;AAAA,gBAGD,YACC,MAAM,KAAK,SAAS,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,MACxC;AAAA,kBAAC;AAAA;AAAA,oBAEC,OAAO;AAAA,sBACL,QAAQ;AAAA,sBACR,cAAc;AAAA,sBACd,SAAS;AAAA,sBACT,SAAS;AAAA,sBACT,KAAK;AAAA,oBACP;AAAA,oBAEA;AAAA,oEAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,KAAK,GAAG,GACtE;AAAA,qEAAC,SAAI,OAAO,EAAE,YAAY,IAAI,GAAI,YAAE,KAAI;AAAA,wBACxC,6CAAC,SAAI,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,MAAM,QAAQ,QAAO;AAAA,yBACtE;AAAA,sBAEA;AAAA,wBAAC;AAAA;AAAA,0BACC,OAAO,EAAE;AAAA,0BACT,MAAM,EAAE;AAAA,0BACR,UAAU,CAAC,SAAS,EAAE,SAAS,IAAI;AAAA;AAAA,sBACrC;AAAA;AAAA;AAAA,kBAlBK,EAAE;AAAA,gBAmBT,CACD;AAAA,iBACL;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA,OAEJ;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AAED,MAAI,MAAM,SAAS,aAAa,OAAO,UAAU,WAAW;AAC1D,WACE,8CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,KAAK,IAAI,YAAY,SAAS,GAC7D;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,QAAQ,KAAK;AAAA,UACtB,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,OAAO;AAAA;AAAA,MAC5C;AAAA,MACA,6CAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,iBAAO,QAAQ,KAAK,CAAC,GAAE;AAAA,OACvE;AAAA,EAEJ;AAGA,MAAI,MAAM,SAAS,YAAY,MAAM,QAAQ,KAAK,OAAO,GAAG;AAC1D,UAAM,UAAU,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,EAAE;AACtE,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,QACxC,OAAO;AAAA,UACL,OAAO;AAAA,UACP,SAAS;AAAA,UACT,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,OAAO;AAAA,QACT;AAAA,QAEC,eAAK,QAAQ,IAAI,CAAC,QAAa;AAC9B,gBAAM,IAAI,eAAe,GAAG,IAAI,MAAM,EAAE,OAAO,OAAO,GAAG,GAAG,OAAO,OAAO,GAAG,EAAE;AAC/E,iBACE,6CAAC,YAAqB,OAAO,EAAE,OAC5B,YAAE,SADQ,EAAE,KAEf;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH;AAAA,EAEJ;AAGA,MAAI,MAAM,SAAS,YAAY,OAAO,UAAU,UAAU;AACxD,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,CAAC;AAAA,QAC5D,KAAK,MAAM;AAAA,QACX,KAAK,MAAM;AAAA,QACX,MAAM,MAAM;AAAA,QACZ,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,UAAU,KAAK,IAAI,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,QAC5E,OAAO;AAAA,UACL,OAAO;AAAA,UACP,SAAS;AAAA,UACT,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,OAAO;AAAA,QACT;AAAA;AAAA,IACF;AAAA,EAEJ;AAGA,MAAI,MAAM,SAAS,UAAW,SAAS,OAAO,UAAU,UAAW;AACjE,UAAM,OAAO,cAAc,KAAK;AAChC,WACE,6CAAC,cAAW,SAAS,MAAM,aAAa,CAAC,QAAQ,SAAS,GAAG,GAAG;AAAA,EAEpE;AAGA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,OAAO,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,EAAE;AAAA,MAC7D,aAAa,MAAM;AAAA,MACnB,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,OAAO;AAAA,QACL,OAAO;AAAA,QACP,SAAS;AAAA,QACT,cAAc;AAAA,QACd,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,cAAc,GAAY;AACjC,MAAI;AACF,WAAO,KAAK,UAAU,GAAG,MAAM,CAAC;AAAA,EAClC,QAAQ;AACN,WAAO,OAAO,CAAC;AAAA,EACjB;AACF;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,KAAK,MAAM,QAAI,wBAAS,OAAO;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAwB,IAAI;AAEtD,+BAAU,MAAM;AACd,WAAO,OAAO;AAAA,EAChB,GAAG,CAAC,OAAO,CAAC;AAEZ,SACE,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,EAAE,GACpC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,MAAM;AACf,gBAAM,OAAO,EAAE,OAAO;AACtB,iBAAO,IAAI;AAEX,cAAI;AACF,kBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,qBAAS,IAAI;AACb,wBAAY,MAAM;AAAA,UACpB,SAAS,KAAU;AACjB,qBAAS,cAAc;AAAA,UACzB;AAAA,QACF;AAAA,QACA,MAAM;AAAA,QACN,OAAO;AAAA,UACL,OAAO;AAAA,UACP,SAAS;AAAA,UACT,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AAAA;AAAA,IACF;AAAA,IACC,SAAS,6CAAC,SAAI,OAAO,EAAE,UAAU,IAAI,OAAO,UAAU,GAAI,iBAAM;AAAA,KACnE;AAEJ;","names":["import_react","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/store.ts","../src/hooks.ts","../src/StateInspectorUI.tsx"],"sourcesContent":["export * from \"./provider\";\nexport * from \"./store\";\nexport * from \"./hooks\";\nexport * from \"./StateInspectorUI\";\n","import React, { createContext, useContext, useEffect, useId, useMemo } from \"react\";\nimport { createInspectorStore, InspectorComponentRef, InspectorStore } from \"./store\";\n\nconst InspectorContext = createContext<InspectorStore | null>(null);\n\nconst storeRef = { current: null as InspectorStore | null };\n\nfunction getStore(): InspectorStore {\n if (!storeRef.current) {\n storeRef.current = createInspectorStore();\n }\n return storeRef.current;\n}\n\nexport function StateInspectorProvider({\n enabled = true,\n children,\n}: {\n enabled?: boolean;\n children: React.ReactNode;\n}) {\n const store = useMemo(() => getStore(), []);\n\n useEffect(() => {\n storeRef.current!.enabled = enabled;\n }, [enabled]);\n\n return (\n <InspectorContext.Provider value={store}>\n {children}\n </InspectorContext.Provider>\n );\n}\n\nexport function useInspectorStore(): InspectorStore {\n const ctx = useContext(InspectorContext);\n if (!ctx) {\n throw new Error(\"useInspectorStore must be used inside StateInspectorProvider\");\n }\n return ctx;\n}\n\nexport function useInspectorComponent(label?: string): InspectorComponentRef {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n\n store.registerComponent(componentId, label?.trim());\n return () => {\n store.unregisterComponent(componentId);\n };\n }, [store, componentId, label]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label?.trim();\n if (trimmed) store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n\n return useMemo(() => ({ id: componentId }), [componentId]);\n}\n","export type ComponentId = string;\n\nexport type InspectableMeta =\n | { type: \"boolean\" }\n | { type: \"text\"; placeholder?: string }\n | { type: \"number\"; min?: number; max?: number; step?: number }\n | { type: \"select\"; options: Array<string | { label: string; value: string }> }\n | { type: \"json\" }\n | { type: \"custom\"; renderId: string };\n\nexport interface InspectableStateEntry {\n key: string;\n value: unknown;\n setValue: (next: unknown) => void;\n meta?: InspectableMeta;\n}\n\nexport interface ComponentEntry {\n id: ComponentId;\n label: string;\n mounted: boolean;\n states: Map<string, InspectableStateEntry>;\n}\n\nexport interface InspectorSnapshot {\n enabled: boolean;\n components: Array<{\n id: ComponentId;\n label: string;\n mounted: boolean;\n stateKeys: string[];\n }>;\n}\n\ntype Listener = () => void;\n\nexport interface InspectorComponentRef {\n id: string;\n}\n\nexport interface InspectorStore {\n enabled: boolean;\n\n // internal registry\n components: Map<ComponentId, ComponentEntry>;\n\n // subscriptions\n subscribe: (listener: Listener) => () => void;\n getSnapshot: () => InspectorSnapshot;\n\n // component lifecycle\n registerComponent: (id: ComponentId, label?: string) => void;\n setComponentLabel: (id: ComponentId, label: string) => void;\n unregisterComponent: (id: ComponentId) => void;\n\n // state lifecycle\n upsertState: (\n componentId: ComponentId,\n entry: InspectableStateEntry,\n ) => void;\n\n updateStateValue: (componentId: ComponentId, key: string, value: unknown) => void;\n removeState: (componentId: ComponentId, key: string) => void;\n}\n\nexport function createInspectorStore(): InspectorStore {\n const components = new Map<ComponentId, ComponentEntry>();\n const listeners = new Set<Listener>();\n\n function emit() {\n for (const l of listeners) l();\n }\n\n function getOrCreateComponent(id: ComponentId): ComponentEntry {\n const existing = components.get(id);\n if (existing) return existing;\n\n const created: ComponentEntry = {\n id,\n label: \"Unknown\",\n mounted: true,\n states: new Map(),\n };\n components.set(id, created);\n return created;\n }\n\n const store: InspectorStore = {\n enabled: true,\n components,\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n getSnapshot() {\n return {\n enabled: store.enabled,\n components: Array.from(components.values()).map((c) => ({\n id: c.id,\n label: c.label,\n mounted: c.mounted,\n stateKeys: Array.from(c.states.keys()),\n })),\n };\n },\n\n registerComponent(id, label) {\n const c = getOrCreateComponent(id);\n c.mounted = true;\n if (label && label.trim()) c.label = label.trim();\n emit();\n },\n\n setComponentLabel(id, label) {\n const c = getOrCreateComponent(id);\n c.label = label.trim() || \"Unknown\";\n emit();\n },\n\n unregisterComponent(id) {\n const c = components.get(id);\n if (!c) return;\n\n // StrictMode double-unmount safe:\n // we mark as unmounted; UI can choose to hide unmounted entries.\n c.mounted = false;\n\n // Optional: immediate cleanup if you prefer:\n // components.delete(id);\n\n emit();\n },\n\n upsertState(componentId, entry) {\n const c = getOrCreateComponent(componentId);\n c.states.set(entry.key, entry);\n emit();\n },\n\n updateStateValue(componentId, key, value) {\n const c = components.get(componentId);\n if (!c) return;\n const s = c.states.get(key);\n if (!s) return;\n s.value = value;\n emit();\n },\n\n removeState(componentId, key) {\n const c = components.get(componentId);\n if (!c) return;\n if (!c.states.has(key)) return;\n c.states.delete(key);\n emit();\n },\n };\n\n return store;\n}\n","import React, { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { useInspectorStore } from \"./provider\";\nimport type { InspectableMeta, InspectorComponentRef } from \"./store\";\n\nfunction stableKey(input: string): string {\n return input.trim();\n}\n\nfunction inferMeta(value: unknown): InspectableMeta | undefined {\n const t = typeof value;\n if (t === \"boolean\") return { type: \"boolean\" };\n if (t === \"number\") return { type: \"number\" };\n if (t === \"string\") return { type: \"text\" };\n if (value && t === \"object\") return { type: \"json\" };\n return undefined;\n}\n\n/**\n * Registers a piece of state to the inspector registry.\n * Opt-in by replacing useState with useInspectableState.\n */\nexport function useInspectableState<T>(\n component: InspectorComponentRef,\n key: string,\n initial: T | (() => T),\n meta?: InspectableMeta,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const store = useInspectorStore();\n const componentId = component.id;\n\n const stateKey = useMemo(() => stableKey(key), [key]);\n\n const [value, setValue] = useState<T>(initial);\n\n // Keep latest setter stable for registry consumers\n const setValueRef = useRef<(next: unknown) => void>(() => {});\n setValueRef.current = (next: unknown) => {\n setValue(next as T);\n };\n\n // Register component + state entry\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n\n return () => {\n store.removeState(componentId, stateKey);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store, componentId, stateKey]);\n\n // Sync updates on each render when value/meta changes\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n }, [store, componentId, stateKey, value, meta]);\n\n return [value, setValue];\n}\n\n/**\n * Optional helper to give the current component instance a human label.\n */\nexport function useComponentLabel(label: string) {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label.trim();\n if (!trimmed) return;\n store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n}\n","import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useInspectorStore } from \"./provider\";\nimport { InspectableMeta } from \"./store\";\nconst isMac = navigator.platform.toUpperCase().includes(\"MAC\");\n\nfunction isOptionObject(\n opt: string | { label: string; value: string },\n): opt is { label: string; value: string } {\n return typeof opt === \"object\" && opt !== null && \"value\" in opt;\n}\n\nexport function StateInspectorUI() {\n const store = useInspectorStore();\n const [query, setQuery] = useState<string>(\"\");\n\n const [open, setOpen] = useState(false);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [, force] = useState(0);\n\n const searchRef = useRef<HTMLInputElement | null>(null);\n\n const LS_KEY = \"rsi:panel-pos\";\n const [pos, setPos] = useState<{ right: number; bottom: number }>(() => {\n try {\n const raw = localStorage.getItem(LS_KEY);\n return raw ? JSON.parse(raw) : { right: 16, bottom: 72 };\n } catch {\n // Fallback to default position if localStorage is unavailable\n return { right: 16, bottom: 72 };\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(LS_KEY, JSON.stringify(pos));\n } catch {\n // Silently fail if localStorage is unavailable\n }\n }, [pos]);\n\n const SIZE_KEY = \"rsi:panel-size\";\n const [size, setSize] = useState<{ width: number; height: number }>(() => {\n try {\n const raw = localStorage.getItem(SIZE_KEY);\n return raw ? JSON.parse(raw) : { width: 720, height: 420 };\n } catch {\n // Fallback to default size if localStorage is unavailable\n return { width: 720, height: 420 };\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(SIZE_KEY, JSON.stringify(size));\n } catch {\n // Silently fail if localStorage is unavailable\n }\n }, [size]);\n\n useEffect(() => store.subscribe(() => force((x) => x + 1)), [store]);\n\n const snapshot = useMemo(() => store.getSnapshot(), [store]);\n const components = useMemo(\n () => snapshot.components.filter((c) => c.mounted),\n [snapshot.components]\n );\n\n const q = query.trim().toLowerCase();\n\n const filtered = useMemo(\n () =>\n q\n ? components.filter((c) => {\n const labelHit = c.label.toLowerCase().includes(q);\n const keyHit = c.stateKeys.some((k) =>\n k.toLowerCase().includes(q)\n );\n return labelHit || keyHit;\n })\n : components,\n [q, components]\n );\n\n // Keep a valid selection\n useEffect(() => {\n if (!open) {\n return;\n }\n\n if (components.length === 0) {\n setSelectedId(null);\n return;\n }\n\n if (!selectedId || !components.some((c) => c.id === selectedId)) {\n setSelectedId(components[0]!.id);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, components.length]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n function onKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") setOpen(false);\n }\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open]);\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n const mod = isMac ? e.metaKey : e.ctrlKey;\n\n // Cmd/Ctrl + Shift + I → toggle inspector\n if (mod && e.shiftKey && e.key.toLowerCase() === \"i\") {\n e.preventDefault();\n setOpen((o) => !o);\n return;\n }\n\n if (!open) return;\n\n // Esc → close\n if (e.key === \"Escape\") {\n setOpen(false);\n return;\n }\n\n // \"/\" → focus search\n if (e.key === \"/\") {\n e.preventDefault();\n searchRef.current?.focus();\n return;\n }\n\n // Arrow navigation\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n const list = filtered;\n if (!list.length) return;\n\n const idx = list.findIndex((c) => c.id === selectedId);\n const next =\n e.key === \"ArrowDown\"\n ? list[(Math.max(-1, idx) + 1) % list.length]!.id\n : list[(idx <= 0 ? list.length - 1 : idx - 1)]!.id;\n\n setSelectedId(next);\n }\n }\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open, filtered, selectedId]);\n\n const selected = useMemo(() => {\n if (!selectedId) return null;\n return store.components.get(selectedId) ?? null;\n }, [store, selectedId]);\n\n if (!store.enabled) return null;\n\n return createPortal(\n <>\n {/* Floating Button */}\n <button\n title=\"State Inspector (⌘⇧I / Ctrl⇧I)\"\n onClick={() => setOpen((o) => !o)}\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 16,\n width: 44,\n height: 44,\n borderRadius: 22,\n border: \"none\",\n background: \"#111\",\n color: \"#fff\",\n cursor: \"pointer\",\n zIndex: 999999,\n }}\n aria-label=\"Toggle State Inspector\"\n >\n ◎\n </button>\n\n {/* Floating Card */}\n {open && (\n <div\n style={{\n position: \"fixed\",\n right: pos.right,\n bottom: pos.bottom,\n width: size.width,\n maxWidth: \"calc(100vw - 32px)\",\n height: size.height,\n maxHeight: \"calc(100vh - 120px)\",\n background: \"#1c1c1c\",\n color: \"#fff\",\n borderRadius: 12,\n border: \"1px solid #333\",\n zIndex: 999999,\n display: \"grid\",\n gridTemplateColumns: \"280px 1fr\",\n overflow: \"hidden\",\n }}\n >\n {/* Resize Handle */}\n <div\n onPointerDown={(e) => {\n const startX = e.clientX;\n const startY = e.clientY;\n const start = size;\n\n (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);\n\n const onMove = (ev: PointerEvent) => {\n const dx = ev.clientX - startX;\n const dy = ev.clientY - startY;\n setSize({\n width: Math.min(window.innerWidth - 16, Math.max(480, start.width + dx)),\n height: Math.min(window.innerHeight - 120, Math.max(280, start.height + dy)),\n });\n };\n\n const onUp = () => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n };\n\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n }}\n style={{\n position: \"absolute\",\n right: 6,\n bottom: 6,\n width: 14,\n height: 14,\n borderRadius: 6,\n border: \"1px solid #333\",\n background: \"#838383\",\n cursor: \"nwse-resize\",\n }}\n />\n\n {/* Left: component list */}\n <div style={{ borderRight: \"1px solid #333\", padding: 12, height: size.height, display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\" }}>\n <div \n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", cursor: \"grab\", userSelect: \"none\" }}\n onPointerDown={(e) => {\n const startX = e.clientX;\n const startY = e.clientY;\n const start = pos;\n\n (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);\n\n const onMove = (ev: PointerEvent) => {\n const dx = ev.clientX - startX;\n const dy = ev.clientY - startY;\n setPos({\n right: Math.max(8, start.right - dx),\n bottom: Math.max(8, start.bottom - dy),\n });\n };\n\n const onUp = () => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n };\n\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n }}\n >\n <h4 style={{ margin: 0 }}>Components</h4>\n <span style={{ fontSize: 12, opacity: 0.7 }}>{filtered.length}</span>\n </div>\n\n <div style={{ marginTop: 10, display: \"grid\", gap: 8, overflow: \"auto\", flex: 1 }}>\n {filtered.map((c) => {\n const active = c.id === selectedId;\n return (\n <button\n key={c.id}\n onClick={() => setSelectedId(c.id)}\n style={{\n textAlign: \"left\",\n background: active ? \"#2a2a2a\" : \"transparent\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: 10,\n color: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n <div style={{ fontWeight: 700 }}>{c.label}</div>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n states: {c.stateKeys.length ? c.stateKeys.length : 0}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Right: state editors */}\n <div style={{ padding: 12, overflow: \"auto\", display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <div>\n <h4 style={{ margin: 0 }}>State</h4>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n {selected ? selected.label : \"No selection\"}\n </div>\n </div>\n\n <button\n onClick={() => setOpen(false)}\n style={{\n background: \"transparent\",\n color: \"#fff\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: \"6px 10px\",\n cursor: \"pointer\",\n fontSize: 12,\n }}\n >\n Close\n </button>\n </div>\n\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search components / state keys…\"\n ref={searchRef}\n style={{\n marginTop: 10,\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n outline: \"none\",\n }}\n />\n\n <div style={{ marginTop: 12, display: \"grid\", gap: 10 }}>\n {selected && selected.states.size === 0 && (\n <div style={{ fontSize: 13, opacity: 0.7 }}>\n This component has no inspectable state.\n </div>\n )}\n\n {!selected && (\n <div style={{ opacity: 0.7, fontSize: 13 }}>\n No component selected.\n </div>\n )}\n\n {selected &&\n Array.from(selected.states.values()).map((s) => (\n <div\n key={s.key}\n style={{\n border: \"1px solid #333\",\n borderRadius: 12,\n padding: 10,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 8\n }}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\", gap: 12 }}>\n <div style={{ fontWeight: 700 }}>{s.key}</div>\n <div style={{ fontSize: 12, opacity: 0.6 }}>{s.meta?.type ?? \"auto\"}</div>\n </div>\n\n <StateEditor\n value={s.value}\n meta={s.meta}\n onChange={(next) => s.setValue(next)}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>,\n document.body,\n );\n}\n\nfunction StateEditor({\n value,\n meta,\n onChange,\n}: {\n value: unknown;\n meta: InspectableMeta | undefined;\n onChange: (next: unknown) => void;\n}) {\n // boolean\n if (meta?.type === \"boolean\" || typeof value === \"boolean\") {\n return (\n <label style={{ display: \"flex\", gap: 10, alignItems: \"center\" }}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n />\n <span style={{ fontSize: 13, opacity: 0.8 }}>{String(Boolean(value))}</span>\n </label>\n );\n }\n\n // select\n if (meta?.type === \"select\" && Array.isArray(meta.options)) {\n const current = typeof value === \"string\" ? value : String(value ?? \"\");\n return (\n <select\n value={current}\n onChange={(e) => onChange(e.target.value)}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n >\n {meta.options.map((opt: string | { label: string; value: string }) => {\n const o = isOptionObject(opt) ? opt : { label: String(opt), value: String(opt) };\n return (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n );\n })}\n </select>\n );\n }\n\n // number\n if (meta?.type === \"number\") {\n return (\n <input\n type=\"number\"\n value={typeof value === \"number\" ? value : Number(value ?? 0)}\n min={meta?.min}\n max={meta?.max}\n step={meta?.step}\n onChange={(e) => onChange(e.target.value === \"\" ? 0 : Number(e.target.value))}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n }\n\n // json\n if (meta?.type === \"json\" || (value && typeof value === \"object\")) {\n const text = safeStringify(value);\n return (\n <JsonEditor initial={text} onValidJson={(obj) => onChange(obj)} />\n );\n }\n\n // text (default)\n return (\n <input\n type=\"text\"\n value={typeof value === \"string\" ? value : String(value ?? \"\")}\n placeholder={meta?.type === \"text\" ? meta.placeholder : undefined}\n onChange={(e) => onChange(e.target.value)}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n}\n\nfunction safeStringify(v: unknown) {\n try {\n return JSON.stringify(v, null, 2);\n } catch {\n return String(v);\n }\n}\n\nfunction JsonEditor({\n initial,\n onValidJson,\n}: {\n initial: string;\n onValidJson: (obj: unknown) => void;\n}) {\n const [raw, setRaw] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setRaw(initial);\n }, [initial]);\n\n return (\n <div style={{ display: \"grid\", gap: 6 }}>\n <textarea\n value={raw}\n onChange={(e) => {\n const next = e.target.value;\n setRaw(next);\n\n try {\n const parsed = JSON.parse(next);\n setError(null);\n onValidJson(parsed);\n } catch {\n setError(\"Invalid JSON\");\n }\n }}\n rows={6}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n fontSize: 12,\n }}\n />\n {error && <div style={{ fontSize: 12, color: \"#ff6b6b\" }}>{error}</div>}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/provider.tsx
2
- import { createContext, useContext, useEffect, useId, useMemo, useRef } from "react";
2
+ import { createContext, useContext, useEffect, useId, useMemo } from "react";
3
3
 
4
4
  // src/store.ts
5
5
  function createInspectorStore() {
@@ -84,16 +84,22 @@ function createInspectorStore() {
84
84
  // src/provider.tsx
85
85
  import { jsx } from "react/jsx-runtime";
86
86
  var InspectorContext = createContext(null);
87
+ var storeRef = { current: null };
88
+ function getStore() {
89
+ if (!storeRef.current) {
90
+ storeRef.current = createInspectorStore();
91
+ }
92
+ return storeRef.current;
93
+ }
87
94
  function StateInspectorProvider({
88
95
  enabled = true,
89
96
  children
90
97
  }) {
91
- const storeRef = useRef(null);
92
- if (!storeRef.current) {
93
- storeRef.current = createInspectorStore();
94
- }
95
- storeRef.current.enabled = enabled;
96
- return /* @__PURE__ */ jsx(InspectorContext.Provider, { value: storeRef.current, children });
98
+ const store = useMemo(() => getStore(), []);
99
+ useEffect(() => {
100
+ storeRef.current.enabled = enabled;
101
+ }, [enabled]);
102
+ return /* @__PURE__ */ jsx(InspectorContext.Provider, { value: store, children });
97
103
  }
98
104
  function useInspectorStore() {
99
105
  const ctx = useContext(InspectorContext);
@@ -122,7 +128,7 @@ function useInspectorComponent(label) {
122
128
  }
123
129
 
124
130
  // src/hooks.ts
125
- import { useEffect as useEffect2, useId as useId2, useMemo as useMemo2, useRef as useRef2, useState } from "react";
131
+ import { useEffect as useEffect2, useId as useId2, useMemo as useMemo2, useRef, useState } from "react";
126
132
  function stableKey(input) {
127
133
  return input.trim();
128
134
  }
@@ -139,7 +145,7 @@ function useInspectableState(component, key, initial, meta) {
139
145
  const componentId = component.id;
140
146
  const stateKey = useMemo2(() => stableKey(key), [key]);
141
147
  const [value, setValue] = useState(initial);
142
- const setValueRef = useRef2(() => {
148
+ const setValueRef = useRef(() => {
143
149
  });
144
150
  setValueRef.current = (next) => {
145
151
  setValue(next);
@@ -182,23 +188,71 @@ function useComponentLabel(label) {
182
188
  }
183
189
 
184
190
  // src/StateInspectorUI.tsx
185
- import { useEffect as useEffect3, useMemo as useMemo3, useState as useState2 } from "react";
191
+ import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
186
192
  import { createPortal } from "react-dom";
187
193
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
194
+ var isMac = navigator.platform.toUpperCase().includes("MAC");
188
195
  function isOptionObject(opt) {
189
196
  return typeof opt === "object" && opt !== null && "value" in opt;
190
197
  }
191
198
  function StateInspectorUI() {
192
199
  const store = useInspectorStore();
200
+ const [query, setQuery] = useState2("");
193
201
  const [open, setOpen] = useState2(false);
194
202
  const [selectedId, setSelectedId] = useState2(null);
195
203
  const [, force] = useState2(0);
204
+ const searchRef = useRef2(null);
205
+ const LS_KEY = "rsi:panel-pos";
206
+ const [pos, setPos] = useState2(() => {
207
+ try {
208
+ const raw = localStorage.getItem(LS_KEY);
209
+ return raw ? JSON.parse(raw) : { right: 16, bottom: 72 };
210
+ } catch {
211
+ return { right: 16, bottom: 72 };
212
+ }
213
+ });
214
+ useEffect3(() => {
215
+ try {
216
+ localStorage.setItem(LS_KEY, JSON.stringify(pos));
217
+ } catch {
218
+ }
219
+ }, [pos]);
220
+ const SIZE_KEY = "rsi:panel-size";
221
+ const [size, setSize] = useState2(() => {
222
+ try {
223
+ const raw = localStorage.getItem(SIZE_KEY);
224
+ return raw ? JSON.parse(raw) : { width: 720, height: 420 };
225
+ } catch {
226
+ return { width: 720, height: 420 };
227
+ }
228
+ });
229
+ useEffect3(() => {
230
+ try {
231
+ localStorage.setItem(SIZE_KEY, JSON.stringify(size));
232
+ } catch {
233
+ }
234
+ }, [size]);
196
235
  useEffect3(() => store.subscribe(() => force((x) => x + 1)), [store]);
197
- if (!store.enabled) return null;
198
- const snapshot = store.getSnapshot();
199
- const components = snapshot.components.filter((c) => c.mounted);
236
+ const snapshot = useMemo3(() => store.getSnapshot(), [store]);
237
+ const components = useMemo3(
238
+ () => snapshot.components.filter((c) => c.mounted),
239
+ [snapshot.components]
240
+ );
241
+ const q = query.trim().toLowerCase();
242
+ const filtered = useMemo3(
243
+ () => q ? components.filter((c) => {
244
+ const labelHit = c.label.toLowerCase().includes(q);
245
+ const keyHit = c.stateKeys.some(
246
+ (k) => k.toLowerCase().includes(q)
247
+ );
248
+ return labelHit || keyHit;
249
+ }) : components,
250
+ [q, components]
251
+ );
200
252
  useEffect3(() => {
201
- if (!open) return;
253
+ if (!open) {
254
+ return;
255
+ }
202
256
  if (components.length === 0) {
203
257
  setSelectedId(null);
204
258
  return;
@@ -208,22 +262,56 @@ function StateInspectorUI() {
208
262
  }
209
263
  }, [open, components.length]);
210
264
  useEffect3(() => {
211
- if (!open) return;
265
+ if (!open) {
266
+ return;
267
+ }
212
268
  function onKey(e) {
213
269
  if (e.key === "Escape") setOpen(false);
214
270
  }
215
271
  window.addEventListener("keydown", onKey);
216
272
  return () => window.removeEventListener("keydown", onKey);
217
273
  }, [open]);
274
+ useEffect3(() => {
275
+ function onKey(e) {
276
+ const mod = isMac ? e.metaKey : e.ctrlKey;
277
+ if (mod && e.shiftKey && e.key.toLowerCase() === "i") {
278
+ e.preventDefault();
279
+ setOpen((o) => !o);
280
+ return;
281
+ }
282
+ if (!open) return;
283
+ if (e.key === "Escape") {
284
+ setOpen(false);
285
+ return;
286
+ }
287
+ if (e.key === "/") {
288
+ e.preventDefault();
289
+ searchRef.current?.focus();
290
+ return;
291
+ }
292
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
293
+ e.preventDefault();
294
+ const list = filtered;
295
+ if (!list.length) return;
296
+ const idx = list.findIndex((c) => c.id === selectedId);
297
+ const next = e.key === "ArrowDown" ? list[(Math.max(-1, idx) + 1) % list.length].id : list[idx <= 0 ? list.length - 1 : idx - 1].id;
298
+ setSelectedId(next);
299
+ }
300
+ }
301
+ window.addEventListener("keydown", onKey);
302
+ return () => window.removeEventListener("keydown", onKey);
303
+ }, [open, filtered, selectedId]);
218
304
  const selected = useMemo3(() => {
219
305
  if (!selectedId) return null;
220
306
  return store.components.get(selectedId) ?? null;
221
- }, [store, selectedId, snapshot.components.length]);
307
+ }, [store, selectedId]);
308
+ if (!store.enabled) return null;
222
309
  return createPortal(
223
310
  /* @__PURE__ */ jsxs(Fragment, { children: [
224
311
  /* @__PURE__ */ jsx2(
225
312
  "button",
226
313
  {
314
+ title: "State Inspector (\u2318\u21E7I / Ctrl\u21E7I)",
227
315
  onClick: () => setOpen((o) => !o),
228
316
  style: {
229
317
  position: "fixed",
@@ -239,7 +327,6 @@ function StateInspectorUI() {
239
327
  zIndex: 999999
240
328
  },
241
329
  "aria-label": "Toggle State Inspector",
242
- title: "State Inspector",
243
330
  children: "\u25CE"
244
331
  }
245
332
  ),
@@ -248,11 +335,11 @@ function StateInspectorUI() {
248
335
  {
249
336
  style: {
250
337
  position: "fixed",
251
- right: 16,
252
- bottom: 72,
253
- width: 720,
338
+ right: pos.right,
339
+ bottom: pos.bottom,
340
+ width: size.width,
254
341
  maxWidth: "calc(100vw - 32px)",
255
- height: 420,
342
+ height: size.height,
256
343
  maxHeight: "calc(100vh - 120px)",
257
344
  background: "#1c1c1c",
258
345
  color: "#fff",
@@ -264,12 +351,74 @@ function StateInspectorUI() {
264
351
  overflow: "hidden"
265
352
  },
266
353
  children: [
267
- /* @__PURE__ */ jsxs("div", { style: { borderRight: "1px solid #333", padding: 12, height: "420px", display: "flex", flexDirection: "column", boxSizing: "border-box" }, children: [
268
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
269
- /* @__PURE__ */ jsx2("h4", { style: { margin: 0 }, children: "Components" }),
270
- /* @__PURE__ */ jsx2("span", { style: { fontSize: 12, opacity: 0.7 }, children: components.length })
271
- ] }),
272
- /* @__PURE__ */ jsx2("div", { style: { marginTop: 10, display: "grid", gap: 8, overflow: "auto", flex: 1 }, children: components.map((c) => {
354
+ /* @__PURE__ */ jsx2(
355
+ "div",
356
+ {
357
+ onPointerDown: (e) => {
358
+ const startX = e.clientX;
359
+ const startY = e.clientY;
360
+ const start = size;
361
+ e.currentTarget.setPointerCapture(e.pointerId);
362
+ const onMove = (ev) => {
363
+ const dx = ev.clientX - startX;
364
+ const dy = ev.clientY - startY;
365
+ setSize({
366
+ width: Math.min(window.innerWidth - 16, Math.max(480, start.width + dx)),
367
+ height: Math.min(window.innerHeight - 120, Math.max(280, start.height + dy))
368
+ });
369
+ };
370
+ const onUp = () => {
371
+ window.removeEventListener("pointermove", onMove);
372
+ window.removeEventListener("pointerup", onUp);
373
+ };
374
+ window.addEventListener("pointermove", onMove);
375
+ window.addEventListener("pointerup", onUp);
376
+ },
377
+ style: {
378
+ position: "absolute",
379
+ right: 6,
380
+ bottom: 6,
381
+ width: 14,
382
+ height: 14,
383
+ borderRadius: 6,
384
+ border: "1px solid #333",
385
+ background: "#838383",
386
+ cursor: "nwse-resize"
387
+ }
388
+ }
389
+ ),
390
+ /* @__PURE__ */ jsxs("div", { style: { borderRight: "1px solid #333", padding: 12, height: size.height, display: "flex", flexDirection: "column", boxSizing: "border-box" }, children: [
391
+ /* @__PURE__ */ jsxs(
392
+ "div",
393
+ {
394
+ style: { display: "flex", alignItems: "center", justifyContent: "space-between", cursor: "grab", userSelect: "none" },
395
+ onPointerDown: (e) => {
396
+ const startX = e.clientX;
397
+ const startY = e.clientY;
398
+ const start = pos;
399
+ e.currentTarget.setPointerCapture(e.pointerId);
400
+ const onMove = (ev) => {
401
+ const dx = ev.clientX - startX;
402
+ const dy = ev.clientY - startY;
403
+ setPos({
404
+ right: Math.max(8, start.right - dx),
405
+ bottom: Math.max(8, start.bottom - dy)
406
+ });
407
+ };
408
+ const onUp = () => {
409
+ window.removeEventListener("pointermove", onMove);
410
+ window.removeEventListener("pointerup", onUp);
411
+ };
412
+ window.addEventListener("pointermove", onMove);
413
+ window.addEventListener("pointerup", onUp);
414
+ },
415
+ children: [
416
+ /* @__PURE__ */ jsx2("h4", { style: { margin: 0 }, children: "Components" }),
417
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 12, opacity: 0.7 }, children: filtered.length })
418
+ ]
419
+ }
420
+ ),
421
+ /* @__PURE__ */ jsx2("div", { style: { marginTop: 10, display: "grid", gap: 8, overflow: "auto", flex: 1 }, children: filtered.map((c) => {
273
422
  const active = c.id === selectedId;
274
423
  return /* @__PURE__ */ jsxs(
275
424
  "button",
@@ -296,7 +445,7 @@ function StateInspectorUI() {
296
445
  );
297
446
  }) })
298
447
  ] }),
299
- /* @__PURE__ */ jsxs("div", { style: { padding: 12, overflow: "auto" }, children: [
448
+ /* @__PURE__ */ jsxs("div", { style: { padding: 12, overflow: "auto", display: "flex", flexDirection: "column", boxSizing: "border-box" }, children: [
300
449
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
301
450
  /* @__PURE__ */ jsxs("div", { children: [
302
451
  /* @__PURE__ */ jsx2("h4", { style: { margin: 0 }, children: "State" }),
@@ -312,12 +461,31 @@ function StateInspectorUI() {
312
461
  border: "1px solid #333",
313
462
  borderRadius: 10,
314
463
  padding: "6px 10px",
315
- cursor: "pointer"
464
+ cursor: "pointer",
465
+ fontSize: 12
316
466
  },
317
467
  children: "Close"
318
468
  }
319
469
  )
320
470
  ] }),
471
+ /* @__PURE__ */ jsx2(
472
+ "input",
473
+ {
474
+ value: query,
475
+ onChange: (e) => setQuery(e.target.value),
476
+ placeholder: "Search components / state keys\u2026",
477
+ ref: searchRef,
478
+ style: {
479
+ marginTop: 10,
480
+ padding: "8px 10px",
481
+ borderRadius: 10,
482
+ border: "1px solid #333",
483
+ background: "#111",
484
+ color: "#fff",
485
+ outline: "none"
486
+ }
487
+ }
488
+ ),
321
489
  /* @__PURE__ */ jsxs("div", { style: { marginTop: 12, display: "grid", gap: 10 }, children: [
322
490
  selected && selected.states.size === 0 && /* @__PURE__ */ jsx2("div", { style: { fontSize: 13, opacity: 0.7 }, children: "This component has no inspectable state." }),
323
491
  !selected && /* @__PURE__ */ jsx2("div", { style: { opacity: 0.7, fontSize: 13 }, children: "No component selected." }),
@@ -328,7 +496,8 @@ function StateInspectorUI() {
328
496
  border: "1px solid #333",
329
497
  borderRadius: 12,
330
498
  padding: 10,
331
- display: "grid",
499
+ display: "flex",
500
+ flexDirection: "column",
332
501
  gap: 8
333
502
  },
334
503
  children: [
@@ -383,7 +552,6 @@ function StateEditor({
383
552
  value: current,
384
553
  onChange: (e) => onChange(e.target.value),
385
554
  style: {
386
- width: "100%",
387
555
  padding: "8px 10px",
388
556
  borderRadius: 10,
389
557
  border: "1px solid #333",
@@ -397,7 +565,7 @@ function StateEditor({
397
565
  }
398
566
  );
399
567
  }
400
- if (meta?.type === "number" || typeof value === "number") {
568
+ if (meta?.type === "number") {
401
569
  return /* @__PURE__ */ jsx2(
402
570
  "input",
403
571
  {
@@ -408,7 +576,6 @@ function StateEditor({
408
576
  step: meta?.step,
409
577
  onChange: (e) => onChange(e.target.value === "" ? 0 : Number(e.target.value)),
410
578
  style: {
411
- width: "100%",
412
579
  padding: "8px 10px",
413
580
  borderRadius: 10,
414
581
  border: "1px solid #333",
@@ -427,10 +594,9 @@ function StateEditor({
427
594
  {
428
595
  type: "text",
429
596
  value: typeof value === "string" ? value : String(value ?? ""),
430
- placeholder: meta?.placeholder,
597
+ placeholder: meta?.type === "text" ? meta.placeholder : void 0,
431
598
  onChange: (e) => onChange(e.target.value),
432
599
  style: {
433
- width: "100%",
434
600
  padding: "8px 10px",
435
601
  borderRadius: 10,
436
602
  border: "1px solid #333",
@@ -468,13 +634,12 @@ function JsonEditor({
468
634
  const parsed = JSON.parse(next);
469
635
  setError(null);
470
636
  onValidJson(parsed);
471
- } catch (err) {
637
+ } catch {
472
638
  setError("Invalid JSON");
473
639
  }
474
640
  },
475
641
  rows: 6,
476
642
  style: {
477
- width: "100%",
478
643
  padding: "8px 10px",
479
644
  borderRadius: 10,
480
645
  border: "1px solid #333",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.tsx","../src/store.ts","../src/hooks.ts","../src/StateInspectorUI.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useId, useMemo, useRef } from \"react\";\nimport { createInspectorStore, InspectorComponentRef, InspectorStore } from \"./store\";\n\nconst InspectorContext = createContext<InspectorStore | null>(null);\n\nexport function StateInspectorProvider({\n enabled = true,\n children,\n}: {\n enabled?: boolean;\n children: React.ReactNode;\n}) {\n const storeRef = useRef<InspectorStore>(null);\n\n if (!storeRef.current) {\n storeRef.current = createInspectorStore();\n }\n\n storeRef.current.enabled = enabled;\n\n return (\n <InspectorContext.Provider value={storeRef.current}>\n {children}\n </InspectorContext.Provider>\n );\n}\n\nexport function useInspectorStore(): InspectorStore {\n const ctx = useContext(InspectorContext);\n if (!ctx) {\n throw new Error(\"useInspectorStore must be used inside StateInspectorProvider\");\n }\n return ctx;\n}\n\nexport function useInspectorComponent(label?: string): InspectorComponentRef {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n\n store.registerComponent(componentId, label?.trim());\n return () => {\n store.unregisterComponent(componentId);\n };\n }, [store, componentId, label]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label?.trim();\n if (trimmed) store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n\n return useMemo(() => ({ id: componentId }), [componentId]);\n}\n","export type ComponentId = string;\n\nexport type InspectableMeta =\n | { type: \"boolean\" }\n | { type: \"text\"; placeholder?: string }\n | { type: \"number\"; min?: number; max?: number; step?: number }\n | { type: \"select\"; options: Array<string | { label: string; value: string }> }\n | { type: \"json\" }\n | { type: \"custom\"; renderId: string };\n\nexport interface InspectableStateEntry {\n key: string;\n value: unknown;\n setValue: (next: unknown) => void;\n meta?: InspectableMeta;\n}\n\nexport interface ComponentEntry {\n id: ComponentId;\n label: string;\n mounted: boolean;\n states: Map<string, InspectableStateEntry>;\n}\n\nexport interface InspectorSnapshot {\n enabled: boolean;\n components: Array<{\n id: ComponentId;\n label: string;\n mounted: boolean;\n stateKeys: string[];\n }>;\n}\n\ntype Listener = () => void;\n\nexport interface InspectorComponentRef {\n id: string;\n}\n\nexport interface InspectorStore {\n enabled: boolean;\n\n // internal registry\n components: Map<ComponentId, ComponentEntry>;\n\n // subscriptions\n subscribe: (listener: Listener) => () => void;\n getSnapshot: () => InspectorSnapshot;\n\n // component lifecycle\n registerComponent: (id: ComponentId, label?: string) => void;\n setComponentLabel: (id: ComponentId, label: string) => void;\n unregisterComponent: (id: ComponentId) => void;\n\n // state lifecycle\n upsertState: (\n componentId: ComponentId,\n entry: InspectableStateEntry,\n ) => void;\n\n updateStateValue: (componentId: ComponentId, key: string, value: unknown) => void;\n removeState: (componentId: ComponentId, key: string) => void;\n}\n\nexport function createInspectorStore(): InspectorStore {\n const components = new Map<ComponentId, ComponentEntry>();\n const listeners = new Set<Listener>();\n\n function emit() {\n for (const l of listeners) l();\n }\n\n function getOrCreateComponent(id: ComponentId): ComponentEntry {\n const existing = components.get(id);\n if (existing) return existing;\n\n const created: ComponentEntry = {\n id,\n label: \"Unknown\",\n mounted: true,\n states: new Map(),\n };\n components.set(id, created);\n return created;\n }\n\n const store: InspectorStore = {\n enabled: true,\n components,\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n getSnapshot() {\n return {\n enabled: store.enabled,\n components: Array.from(components.values()).map((c) => ({\n id: c.id,\n label: c.label,\n mounted: c.mounted,\n stateKeys: Array.from(c.states.keys()),\n })),\n };\n },\n\n registerComponent(id, label) {\n const c = getOrCreateComponent(id);\n c.mounted = true;\n if (label && label.trim()) c.label = label.trim();\n emit();\n },\n\n setComponentLabel(id, label) {\n const c = getOrCreateComponent(id);\n c.label = label.trim() || \"Unknown\";\n emit();\n },\n\n unregisterComponent(id) {\n const c = components.get(id);\n if (!c) return;\n\n // StrictMode double-unmount safe:\n // we mark as unmounted; UI can choose to hide unmounted entries.\n c.mounted = false;\n\n // Optional: immediate cleanup if you prefer:\n // components.delete(id);\n\n emit();\n },\n\n upsertState(componentId, entry) {\n const c = getOrCreateComponent(componentId);\n c.states.set(entry.key, entry);\n emit();\n },\n\n updateStateValue(componentId, key, value) {\n const c = components.get(componentId);\n if (!c) return;\n const s = c.states.get(key);\n if (!s) return;\n s.value = value;\n emit();\n },\n\n removeState(componentId, key) {\n const c = components.get(componentId);\n if (!c) return;\n if (!c.states.has(key)) return;\n c.states.delete(key);\n emit();\n },\n };\n\n return store;\n}\n","import React, { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { useInspectorStore } from \"./provider\";\nimport type { InspectableMeta, InspectorComponentRef } from \"./store\";\n\nfunction stableKey(input: string): string {\n return input.trim();\n}\n\nfunction inferMeta(value: unknown): InspectableMeta | undefined {\n const t = typeof value;\n if (t === \"boolean\") return { type: \"boolean\" };\n if (t === \"number\") return { type: \"number\" };\n if (t === \"string\") return { type: \"text\" };\n if (value && t === \"object\") return { type: \"json\" };\n return undefined;\n}\n\n/**\n * Registers a piece of state to the inspector registry.\n * Opt-in by replacing useState with useInspectableState.\n */\nexport function useInspectableState<T>(\n component: InspectorComponentRef,\n key: string,\n initial: T | (() => T),\n meta?: InspectableMeta,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const store = useInspectorStore();\n const componentId = component.id;\n\n const stateKey = useMemo(() => stableKey(key), [key]);\n\n const [value, setValue] = useState<T>(initial);\n\n // Keep latest setter stable for registry consumers\n const setValueRef = useRef<(next: unknown) => void>(() => {});\n setValueRef.current = (next: unknown) => {\n setValue(next as T);\n };\n\n // Register component + state entry\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n\n return () => {\n store.removeState(componentId, stateKey);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store, componentId, stateKey]);\n\n // Sync updates on each render when value/meta changes\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n }, [store, componentId, stateKey, value, meta]);\n\n return [value, setValue];\n}\n\n/**\n * Optional helper to give the current component instance a human label.\n */\nexport function useComponentLabel(label: string) {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label.trim();\n if (!trimmed) return;\n store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n}\n","import { useEffect, useMemo, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useInspectorStore } from \"./provider\";\nuseInspectorStore\n\nfunction isOptionObject(\n opt: string | { label: string; value: string },\n): opt is { label: string; value: string } {\n return typeof opt === \"object\" && opt !== null && \"value\" in opt;\n}\n\nexport function StateInspectorUI() {\n const store = useInspectorStore();\n\n const [open, setOpen] = useState(false);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [, force] = useState(0);\n\n useEffect(() => store.subscribe(() => force((x) => x + 1)), [store]);\n\n if (!store.enabled) return null;\n\n const snapshot = store.getSnapshot();\n const components = snapshot.components.filter((c) => c.mounted);\n\n // Keep a valid selection\n useEffect(() => {\n if (!open) return;\n\n if (components.length === 0) {\n setSelectedId(null);\n return;\n }\n\n if (!selectedId || !components.some((c) => c.id === selectedId)) {\n setSelectedId(components[0]!.id);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, components.length]);\n\n useEffect(() => {\n if (!open) return;\n\n function onKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") setOpen(false);\n }\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open]);\n\n const selected = useMemo(() => {\n if (!selectedId) return null;\n return store.components.get(selectedId) ?? null;\n }, [store, selectedId, snapshot.components.length]);\n\n return createPortal(\n <>\n {/* Floating Button */}\n <button\n onClick={() => setOpen((o) => !o)}\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 16,\n width: 44,\n height: 44,\n borderRadius: 22,\n border: \"none\",\n background: \"#111\",\n color: \"#fff\",\n cursor: \"pointer\",\n zIndex: 999999,\n }}\n aria-label=\"Toggle State Inspector\"\n title=\"State Inspector\"\n >\n ◎\n </button>\n\n {/* Floating Card */}\n {open && (\n <div\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 72,\n width: 720,\n maxWidth: \"calc(100vw - 32px)\",\n height: 420,\n maxHeight: \"calc(100vh - 120px)\",\n background: \"#1c1c1c\",\n color: \"#fff\",\n borderRadius: 12,\n border: \"1px solid #333\",\n zIndex: 999999,\n display: \"grid\",\n gridTemplateColumns: \"280px 1fr\",\n overflow: \"hidden\",\n }}\n >\n {/* Left: component list */}\n <div style={{ borderRight: \"1px solid #333\", padding: 12, height: \"420px\", display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <h4 style={{ margin: 0 }}>Components</h4>\n <span style={{ fontSize: 12, opacity: 0.7 }}>{components.length}</span>\n </div>\n\n <div style={{ marginTop: 10, display: \"grid\", gap: 8, overflow: \"auto\", flex: 1 }}>\n {components.map((c) => {\n const active = c.id === selectedId;\n return (\n <button\n key={c.id}\n onClick={() => setSelectedId(c.id)}\n style={{\n textAlign: \"left\",\n background: active ? \"#2a2a2a\" : \"transparent\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: 10,\n color: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n <div style={{ fontWeight: 700 }}>{c.label}</div>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n states: {c.stateKeys.length ? c.stateKeys.length : 0}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Right: state editors */}\n <div style={{ padding: 12, overflow: \"auto\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <div>\n <h4 style={{ margin: 0 }}>State</h4>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n {selected ? selected.label : \"No selection\"}\n </div>\n </div>\n\n <button\n onClick={() => setOpen(false)}\n style={{\n background: \"transparent\",\n color: \"#fff\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: \"6px 10px\",\n cursor: \"pointer\",\n }}\n >\n Close\n </button>\n </div>\n\n <div style={{ marginTop: 12, display: \"grid\", gap: 10 }}>\n {selected && selected.states.size === 0 && (\n <div style={{ fontSize: 13, opacity: 0.7 }}>\n This component has no inspectable state.\n </div>\n )}\n\n {!selected && (\n <div style={{ opacity: 0.7, fontSize: 13 }}>\n No component selected.\n </div>\n )}\n\n {selected &&\n Array.from(selected.states.values()).map((s) => (\n <div\n key={s.key}\n style={{\n border: \"1px solid #333\",\n borderRadius: 12,\n padding: 10,\n display: \"grid\",\n gap: 8,\n }}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\", gap: 12 }}>\n <div style={{ fontWeight: 700 }}>{s.key}</div>\n <div style={{ fontSize: 12, opacity: 0.6 }}>{s.meta?.type ?? \"auto\"}</div>\n </div>\n\n <StateEditor\n value={s.value}\n meta={s.meta}\n onChange={(next) => s.setValue(next)}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>,\n document.body,\n );\n}\n\nfunction StateEditor({\n value,\n meta,\n onChange,\n}: {\n value: unknown;\n meta: any;\n onChange: (next: unknown) => void;\n}) {\n // boolean\n if (meta?.type === \"boolean\" || typeof value === \"boolean\") {\n return (\n <label style={{ display: \"flex\", gap: 10, alignItems: \"center\" }}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n />\n <span style={{ fontSize: 13, opacity: 0.8 }}>{String(Boolean(value))}</span>\n </label>\n );\n }\n\n // select\n if (meta?.type === \"select\" && Array.isArray(meta.options)) {\n const current = typeof value === \"string\" ? value : String(value ?? \"\");\n return (\n <select\n value={current}\n onChange={(e) => onChange(e.target.value)}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n >\n {meta.options.map((opt: any) => {\n const o = isOptionObject(opt) ? opt : { label: String(opt), value: String(opt) };\n return (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n );\n })}\n </select>\n );\n }\n\n // number\n if (meta?.type === \"number\" || typeof value === \"number\") {\n return (\n <input\n type=\"number\"\n value={typeof value === \"number\" ? value : Number(value ?? 0)}\n min={meta?.min}\n max={meta?.max}\n step={meta?.step}\n onChange={(e) => onChange(e.target.value === \"\" ? 0 : Number(e.target.value))}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n }\n\n // json\n if (meta?.type === \"json\" || (value && typeof value === \"object\")) {\n const text = safeStringify(value);\n return (\n <JsonEditor initial={text} onValidJson={(obj) => onChange(obj)} />\n );\n }\n\n // text (default)\n return (\n <input\n type=\"text\"\n value={typeof value === \"string\" ? value : String(value ?? \"\")}\n placeholder={meta?.placeholder}\n onChange={(e) => onChange(e.target.value)}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n}\n\nfunction safeStringify(v: unknown) {\n try {\n return JSON.stringify(v, null, 2);\n } catch {\n return String(v);\n }\n}\n\nfunction JsonEditor({\n initial,\n onValidJson,\n}: {\n initial: string;\n onValidJson: (obj: unknown) => void;\n}) {\n const [raw, setRaw] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setRaw(initial);\n }, [initial]);\n\n return (\n <div style={{ display: \"grid\", gap: 6 }}>\n <textarea\n value={raw}\n onChange={(e) => {\n const next = e.target.value;\n setRaw(next);\n\n try {\n const parsed = JSON.parse(next);\n setError(null);\n onValidJson(parsed);\n } catch (err: any) {\n setError(\"Invalid JSON\");\n }\n }}\n rows={6}\n style={{\n width: \"100%\",\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n fontSize: 12,\n }}\n />\n {error && <div style={{ fontSize: 12, color: \"#ff6b6b\" }}>{error}</div>}\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useId","useMemo","useRef","useMemo","useRef","useEffect","useId","useEffect","useMemo","useState","jsx","useState","useEffect","useMemo","jsx"]}
1
+ {"version":3,"sources":["../src/provider.tsx","../src/store.ts","../src/hooks.ts","../src/StateInspectorUI.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useId, useMemo } from \"react\";\nimport { createInspectorStore, InspectorComponentRef, InspectorStore } from \"./store\";\n\nconst InspectorContext = createContext<InspectorStore | null>(null);\n\nconst storeRef = { current: null as InspectorStore | null };\n\nfunction getStore(): InspectorStore {\n if (!storeRef.current) {\n storeRef.current = createInspectorStore();\n }\n return storeRef.current;\n}\n\nexport function StateInspectorProvider({\n enabled = true,\n children,\n}: {\n enabled?: boolean;\n children: React.ReactNode;\n}) {\n const store = useMemo(() => getStore(), []);\n\n useEffect(() => {\n storeRef.current!.enabled = enabled;\n }, [enabled]);\n\n return (\n <InspectorContext.Provider value={store}>\n {children}\n </InspectorContext.Provider>\n );\n}\n\nexport function useInspectorStore(): InspectorStore {\n const ctx = useContext(InspectorContext);\n if (!ctx) {\n throw new Error(\"useInspectorStore must be used inside StateInspectorProvider\");\n }\n return ctx;\n}\n\nexport function useInspectorComponent(label?: string): InspectorComponentRef {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n\n store.registerComponent(componentId, label?.trim());\n return () => {\n store.unregisterComponent(componentId);\n };\n }, [store, componentId, label]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label?.trim();\n if (trimmed) store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n\n return useMemo(() => ({ id: componentId }), [componentId]);\n}\n","export type ComponentId = string;\n\nexport type InspectableMeta =\n | { type: \"boolean\" }\n | { type: \"text\"; placeholder?: string }\n | { type: \"number\"; min?: number; max?: number; step?: number }\n | { type: \"select\"; options: Array<string | { label: string; value: string }> }\n | { type: \"json\" }\n | { type: \"custom\"; renderId: string };\n\nexport interface InspectableStateEntry {\n key: string;\n value: unknown;\n setValue: (next: unknown) => void;\n meta?: InspectableMeta;\n}\n\nexport interface ComponentEntry {\n id: ComponentId;\n label: string;\n mounted: boolean;\n states: Map<string, InspectableStateEntry>;\n}\n\nexport interface InspectorSnapshot {\n enabled: boolean;\n components: Array<{\n id: ComponentId;\n label: string;\n mounted: boolean;\n stateKeys: string[];\n }>;\n}\n\ntype Listener = () => void;\n\nexport interface InspectorComponentRef {\n id: string;\n}\n\nexport interface InspectorStore {\n enabled: boolean;\n\n // internal registry\n components: Map<ComponentId, ComponentEntry>;\n\n // subscriptions\n subscribe: (listener: Listener) => () => void;\n getSnapshot: () => InspectorSnapshot;\n\n // component lifecycle\n registerComponent: (id: ComponentId, label?: string) => void;\n setComponentLabel: (id: ComponentId, label: string) => void;\n unregisterComponent: (id: ComponentId) => void;\n\n // state lifecycle\n upsertState: (\n componentId: ComponentId,\n entry: InspectableStateEntry,\n ) => void;\n\n updateStateValue: (componentId: ComponentId, key: string, value: unknown) => void;\n removeState: (componentId: ComponentId, key: string) => void;\n}\n\nexport function createInspectorStore(): InspectorStore {\n const components = new Map<ComponentId, ComponentEntry>();\n const listeners = new Set<Listener>();\n\n function emit() {\n for (const l of listeners) l();\n }\n\n function getOrCreateComponent(id: ComponentId): ComponentEntry {\n const existing = components.get(id);\n if (existing) return existing;\n\n const created: ComponentEntry = {\n id,\n label: \"Unknown\",\n mounted: true,\n states: new Map(),\n };\n components.set(id, created);\n return created;\n }\n\n const store: InspectorStore = {\n enabled: true,\n components,\n\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n getSnapshot() {\n return {\n enabled: store.enabled,\n components: Array.from(components.values()).map((c) => ({\n id: c.id,\n label: c.label,\n mounted: c.mounted,\n stateKeys: Array.from(c.states.keys()),\n })),\n };\n },\n\n registerComponent(id, label) {\n const c = getOrCreateComponent(id);\n c.mounted = true;\n if (label && label.trim()) c.label = label.trim();\n emit();\n },\n\n setComponentLabel(id, label) {\n const c = getOrCreateComponent(id);\n c.label = label.trim() || \"Unknown\";\n emit();\n },\n\n unregisterComponent(id) {\n const c = components.get(id);\n if (!c) return;\n\n // StrictMode double-unmount safe:\n // we mark as unmounted; UI can choose to hide unmounted entries.\n c.mounted = false;\n\n // Optional: immediate cleanup if you prefer:\n // components.delete(id);\n\n emit();\n },\n\n upsertState(componentId, entry) {\n const c = getOrCreateComponent(componentId);\n c.states.set(entry.key, entry);\n emit();\n },\n\n updateStateValue(componentId, key, value) {\n const c = components.get(componentId);\n if (!c) return;\n const s = c.states.get(key);\n if (!s) return;\n s.value = value;\n emit();\n },\n\n removeState(componentId, key) {\n const c = components.get(componentId);\n if (!c) return;\n if (!c.states.has(key)) return;\n c.states.delete(key);\n emit();\n },\n };\n\n return store;\n}\n","import React, { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { useInspectorStore } from \"./provider\";\nimport type { InspectableMeta, InspectorComponentRef } from \"./store\";\n\nfunction stableKey(input: string): string {\n return input.trim();\n}\n\nfunction inferMeta(value: unknown): InspectableMeta | undefined {\n const t = typeof value;\n if (t === \"boolean\") return { type: \"boolean\" };\n if (t === \"number\") return { type: \"number\" };\n if (t === \"string\") return { type: \"text\" };\n if (value && t === \"object\") return { type: \"json\" };\n return undefined;\n}\n\n/**\n * Registers a piece of state to the inspector registry.\n * Opt-in by replacing useState with useInspectableState.\n */\nexport function useInspectableState<T>(\n component: InspectorComponentRef,\n key: string,\n initial: T | (() => T),\n meta?: InspectableMeta,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n const store = useInspectorStore();\n const componentId = component.id;\n\n const stateKey = useMemo(() => stableKey(key), [key]);\n\n const [value, setValue] = useState<T>(initial);\n\n // Keep latest setter stable for registry consumers\n const setValueRef = useRef<(next: unknown) => void>(() => {});\n setValueRef.current = (next: unknown) => {\n setValue(next as T);\n };\n\n // Register component + state entry\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n\n return () => {\n store.removeState(componentId, stateKey);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store, componentId, stateKey]);\n\n // Sync updates on each render when value/meta changes\n useEffect(() => {\n if (!store.enabled) return;\n\n const resolvedMeta = meta ?? inferMeta(value);\n store.upsertState(componentId, {\n key: stateKey,\n value,\n setValue: (next) => setValueRef.current(next),\n ...(resolvedMeta !== undefined && { meta: resolvedMeta }),\n });\n }, [store, componentId, stateKey, value, meta]);\n\n return [value, setValue];\n}\n\n/**\n * Optional helper to give the current component instance a human label.\n */\nexport function useComponentLabel(label: string) {\n const store = useInspectorStore();\n const reactId = useId();\n const componentId = useMemo(() => `c_${reactId}`, [reactId]);\n\n useEffect(() => {\n if (!store.enabled) return;\n const trimmed = label.trim();\n if (!trimmed) return;\n store.setComponentLabel(componentId, trimmed);\n }, [store, componentId, label]);\n}\n","import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useInspectorStore } from \"./provider\";\nimport { InspectableMeta } from \"./store\";\nconst isMac = navigator.platform.toUpperCase().includes(\"MAC\");\n\nfunction isOptionObject(\n opt: string | { label: string; value: string },\n): opt is { label: string; value: string } {\n return typeof opt === \"object\" && opt !== null && \"value\" in opt;\n}\n\nexport function StateInspectorUI() {\n const store = useInspectorStore();\n const [query, setQuery] = useState<string>(\"\");\n\n const [open, setOpen] = useState(false);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [, force] = useState(0);\n\n const searchRef = useRef<HTMLInputElement | null>(null);\n\n const LS_KEY = \"rsi:panel-pos\";\n const [pos, setPos] = useState<{ right: number; bottom: number }>(() => {\n try {\n const raw = localStorage.getItem(LS_KEY);\n return raw ? JSON.parse(raw) : { right: 16, bottom: 72 };\n } catch {\n // Fallback to default position if localStorage is unavailable\n return { right: 16, bottom: 72 };\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(LS_KEY, JSON.stringify(pos));\n } catch {\n // Silently fail if localStorage is unavailable\n }\n }, [pos]);\n\n const SIZE_KEY = \"rsi:panel-size\";\n const [size, setSize] = useState<{ width: number; height: number }>(() => {\n try {\n const raw = localStorage.getItem(SIZE_KEY);\n return raw ? JSON.parse(raw) : { width: 720, height: 420 };\n } catch {\n // Fallback to default size if localStorage is unavailable\n return { width: 720, height: 420 };\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(SIZE_KEY, JSON.stringify(size));\n } catch {\n // Silently fail if localStorage is unavailable\n }\n }, [size]);\n\n useEffect(() => store.subscribe(() => force((x) => x + 1)), [store]);\n\n const snapshot = useMemo(() => store.getSnapshot(), [store]);\n const components = useMemo(\n () => snapshot.components.filter((c) => c.mounted),\n [snapshot.components]\n );\n\n const q = query.trim().toLowerCase();\n\n const filtered = useMemo(\n () =>\n q\n ? components.filter((c) => {\n const labelHit = c.label.toLowerCase().includes(q);\n const keyHit = c.stateKeys.some((k) =>\n k.toLowerCase().includes(q)\n );\n return labelHit || keyHit;\n })\n : components,\n [q, components]\n );\n\n // Keep a valid selection\n useEffect(() => {\n if (!open) {\n return;\n }\n\n if (components.length === 0) {\n setSelectedId(null);\n return;\n }\n\n if (!selectedId || !components.some((c) => c.id === selectedId)) {\n setSelectedId(components[0]!.id);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, components.length]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n function onKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") setOpen(false);\n }\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open]);\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n const mod = isMac ? e.metaKey : e.ctrlKey;\n\n // Cmd/Ctrl + Shift + I → toggle inspector\n if (mod && e.shiftKey && e.key.toLowerCase() === \"i\") {\n e.preventDefault();\n setOpen((o) => !o);\n return;\n }\n\n if (!open) return;\n\n // Esc → close\n if (e.key === \"Escape\") {\n setOpen(false);\n return;\n }\n\n // \"/\" → focus search\n if (e.key === \"/\") {\n e.preventDefault();\n searchRef.current?.focus();\n return;\n }\n\n // Arrow navigation\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n const list = filtered;\n if (!list.length) return;\n\n const idx = list.findIndex((c) => c.id === selectedId);\n const next =\n e.key === \"ArrowDown\"\n ? list[(Math.max(-1, idx) + 1) % list.length]!.id\n : list[(idx <= 0 ? list.length - 1 : idx - 1)]!.id;\n\n setSelectedId(next);\n }\n }\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open, filtered, selectedId]);\n\n const selected = useMemo(() => {\n if (!selectedId) return null;\n return store.components.get(selectedId) ?? null;\n }, [store, selectedId]);\n\n if (!store.enabled) return null;\n\n return createPortal(\n <>\n {/* Floating Button */}\n <button\n title=\"State Inspector (⌘⇧I / Ctrl⇧I)\"\n onClick={() => setOpen((o) => !o)}\n style={{\n position: \"fixed\",\n right: 16,\n bottom: 16,\n width: 44,\n height: 44,\n borderRadius: 22,\n border: \"none\",\n background: \"#111\",\n color: \"#fff\",\n cursor: \"pointer\",\n zIndex: 999999,\n }}\n aria-label=\"Toggle State Inspector\"\n >\n ◎\n </button>\n\n {/* Floating Card */}\n {open && (\n <div\n style={{\n position: \"fixed\",\n right: pos.right,\n bottom: pos.bottom,\n width: size.width,\n maxWidth: \"calc(100vw - 32px)\",\n height: size.height,\n maxHeight: \"calc(100vh - 120px)\",\n background: \"#1c1c1c\",\n color: \"#fff\",\n borderRadius: 12,\n border: \"1px solid #333\",\n zIndex: 999999,\n display: \"grid\",\n gridTemplateColumns: \"280px 1fr\",\n overflow: \"hidden\",\n }}\n >\n {/* Resize Handle */}\n <div\n onPointerDown={(e) => {\n const startX = e.clientX;\n const startY = e.clientY;\n const start = size;\n\n (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);\n\n const onMove = (ev: PointerEvent) => {\n const dx = ev.clientX - startX;\n const dy = ev.clientY - startY;\n setSize({\n width: Math.min(window.innerWidth - 16, Math.max(480, start.width + dx)),\n height: Math.min(window.innerHeight - 120, Math.max(280, start.height + dy)),\n });\n };\n\n const onUp = () => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n };\n\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n }}\n style={{\n position: \"absolute\",\n right: 6,\n bottom: 6,\n width: 14,\n height: 14,\n borderRadius: 6,\n border: \"1px solid #333\",\n background: \"#838383\",\n cursor: \"nwse-resize\",\n }}\n />\n\n {/* Left: component list */}\n <div style={{ borderRight: \"1px solid #333\", padding: 12, height: size.height, display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\" }}>\n <div \n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", cursor: \"grab\", userSelect: \"none\" }}\n onPointerDown={(e) => {\n const startX = e.clientX;\n const startY = e.clientY;\n const start = pos;\n\n (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);\n\n const onMove = (ev: PointerEvent) => {\n const dx = ev.clientX - startX;\n const dy = ev.clientY - startY;\n setPos({\n right: Math.max(8, start.right - dx),\n bottom: Math.max(8, start.bottom - dy),\n });\n };\n\n const onUp = () => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n };\n\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n }}\n >\n <h4 style={{ margin: 0 }}>Components</h4>\n <span style={{ fontSize: 12, opacity: 0.7 }}>{filtered.length}</span>\n </div>\n\n <div style={{ marginTop: 10, display: \"grid\", gap: 8, overflow: \"auto\", flex: 1 }}>\n {filtered.map((c) => {\n const active = c.id === selectedId;\n return (\n <button\n key={c.id}\n onClick={() => setSelectedId(c.id)}\n style={{\n textAlign: \"left\",\n background: active ? \"#2a2a2a\" : \"transparent\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: 10,\n color: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n <div style={{ fontWeight: 700 }}>{c.label}</div>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n states: {c.stateKeys.length ? c.stateKeys.length : 0}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Right: state editors */}\n <div style={{ padding: 12, overflow: \"auto\", display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\" }}>\n <div>\n <h4 style={{ margin: 0 }}>State</h4>\n <div style={{ fontSize: 12, opacity: 0.7 }}>\n {selected ? selected.label : \"No selection\"}\n </div>\n </div>\n\n <button\n onClick={() => setOpen(false)}\n style={{\n background: \"transparent\",\n color: \"#fff\",\n border: \"1px solid #333\",\n borderRadius: 10,\n padding: \"6px 10px\",\n cursor: \"pointer\",\n fontSize: 12,\n }}\n >\n Close\n </button>\n </div>\n\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search components / state keys…\"\n ref={searchRef}\n style={{\n marginTop: 10,\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n outline: \"none\",\n }}\n />\n\n <div style={{ marginTop: 12, display: \"grid\", gap: 10 }}>\n {selected && selected.states.size === 0 && (\n <div style={{ fontSize: 13, opacity: 0.7 }}>\n This component has no inspectable state.\n </div>\n )}\n\n {!selected && (\n <div style={{ opacity: 0.7, fontSize: 13 }}>\n No component selected.\n </div>\n )}\n\n {selected &&\n Array.from(selected.states.values()).map((s) => (\n <div\n key={s.key}\n style={{\n border: \"1px solid #333\",\n borderRadius: 12,\n padding: 10,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 8\n }}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\", gap: 12 }}>\n <div style={{ fontWeight: 700 }}>{s.key}</div>\n <div style={{ fontSize: 12, opacity: 0.6 }}>{s.meta?.type ?? \"auto\"}</div>\n </div>\n\n <StateEditor\n value={s.value}\n meta={s.meta}\n onChange={(next) => s.setValue(next)}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>,\n document.body,\n );\n}\n\nfunction StateEditor({\n value,\n meta,\n onChange,\n}: {\n value: unknown;\n meta: InspectableMeta | undefined;\n onChange: (next: unknown) => void;\n}) {\n // boolean\n if (meta?.type === \"boolean\" || typeof value === \"boolean\") {\n return (\n <label style={{ display: \"flex\", gap: 10, alignItems: \"center\" }}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n />\n <span style={{ fontSize: 13, opacity: 0.8 }}>{String(Boolean(value))}</span>\n </label>\n );\n }\n\n // select\n if (meta?.type === \"select\" && Array.isArray(meta.options)) {\n const current = typeof value === \"string\" ? value : String(value ?? \"\");\n return (\n <select\n value={current}\n onChange={(e) => onChange(e.target.value)}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n >\n {meta.options.map((opt: string | { label: string; value: string }) => {\n const o = isOptionObject(opt) ? opt : { label: String(opt), value: String(opt) };\n return (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n );\n })}\n </select>\n );\n }\n\n // number\n if (meta?.type === \"number\") {\n return (\n <input\n type=\"number\"\n value={typeof value === \"number\" ? value : Number(value ?? 0)}\n min={meta?.min}\n max={meta?.max}\n step={meta?.step}\n onChange={(e) => onChange(e.target.value === \"\" ? 0 : Number(e.target.value))}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n }\n\n // json\n if (meta?.type === \"json\" || (value && typeof value === \"object\")) {\n const text = safeStringify(value);\n return (\n <JsonEditor initial={text} onValidJson={(obj) => onChange(obj)} />\n );\n }\n\n // text (default)\n return (\n <input\n type=\"text\"\n value={typeof value === \"string\" ? value : String(value ?? \"\")}\n placeholder={meta?.type === \"text\" ? meta.placeholder : undefined}\n onChange={(e) => onChange(e.target.value)}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n }}\n />\n );\n}\n\nfunction safeStringify(v: unknown) {\n try {\n return JSON.stringify(v, null, 2);\n } catch {\n return String(v);\n }\n}\n\nfunction JsonEditor({\n initial,\n onValidJson,\n}: {\n initial: string;\n onValidJson: (obj: unknown) => void;\n}) {\n const [raw, setRaw] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setRaw(initial);\n }, [initial]);\n\n return (\n <div style={{ display: \"grid\", gap: 6 }}>\n <textarea\n value={raw}\n onChange={(e) => {\n const next = e.target.value;\n setRaw(next);\n\n try {\n const parsed = JSON.parse(next);\n setError(null);\n onValidJson(parsed);\n } catch {\n setError(\"Invalid JSON\");\n }\n }}\n rows={6}\n style={{\n padding: \"8px 10px\",\n borderRadius: 10,\n border: \"1px solid #333\",\n background: \"#111\",\n color: \"#fff\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n fontSize: 12,\n }}\n />\n {error && <div style={{ fontSize: 12, color: \"#ff6b6b\" }}>{error}</div>}\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useId","useMemo","useMemo","useEffect","useId","useEffect","useMemo","useRef","useState","jsx","useState","useRef","useEffect","useMemo"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-state-inspector-devtools",
3
- "version": "0.1.2",
3
+ "version": "0.1.8",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,8 +26,9 @@
26
26
  },
27
27
  "scripts": {
28
28
  "build": "tsup",
29
- "typecheck": "tsc --noEmit",
29
+ "typecheck": "pnpm -r typecheck",
30
30
  "lint": "eslint .",
31
- "format": "prettier . --check"
31
+ "format": "prettier . --check",
32
+ "format:write": "prettier . --write"
32
33
  }
33
34
  }