react-state-inspector-devtools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +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":"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","names":["import_react","import_react","import_jsx_runtime"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,500 @@
1
+ // src/provider.tsx
2
+ import { createContext, useContext, useEffect, useId, useMemo, useRef } from "react";
3
+
4
+ // src/store.ts
5
+ function createInspectorStore() {
6
+ const components = /* @__PURE__ */ new Map();
7
+ const listeners = /* @__PURE__ */ new Set();
8
+ function emit() {
9
+ for (const l of listeners) l();
10
+ }
11
+ function getOrCreateComponent(id) {
12
+ const existing = components.get(id);
13
+ if (existing) return existing;
14
+ const created = {
15
+ id,
16
+ label: "Unknown",
17
+ mounted: true,
18
+ states: /* @__PURE__ */ new Map()
19
+ };
20
+ components.set(id, created);
21
+ return created;
22
+ }
23
+ const store = {
24
+ enabled: true,
25
+ components,
26
+ subscribe(listener) {
27
+ listeners.add(listener);
28
+ return () => {
29
+ listeners.delete(listener);
30
+ };
31
+ },
32
+ getSnapshot() {
33
+ return {
34
+ enabled: store.enabled,
35
+ components: Array.from(components.values()).map((c) => ({
36
+ id: c.id,
37
+ label: c.label,
38
+ mounted: c.mounted,
39
+ stateKeys: Array.from(c.states.keys())
40
+ }))
41
+ };
42
+ },
43
+ registerComponent(id, label) {
44
+ const c = getOrCreateComponent(id);
45
+ c.mounted = true;
46
+ if (label && label.trim()) c.label = label.trim();
47
+ emit();
48
+ },
49
+ setComponentLabel(id, label) {
50
+ const c = getOrCreateComponent(id);
51
+ c.label = label.trim() || "Unknown";
52
+ emit();
53
+ },
54
+ unregisterComponent(id) {
55
+ const c = components.get(id);
56
+ if (!c) return;
57
+ c.mounted = false;
58
+ emit();
59
+ },
60
+ upsertState(componentId, entry) {
61
+ const c = getOrCreateComponent(componentId);
62
+ c.states.set(entry.key, entry);
63
+ emit();
64
+ },
65
+ updateStateValue(componentId, key, value) {
66
+ const c = components.get(componentId);
67
+ if (!c) return;
68
+ const s = c.states.get(key);
69
+ if (!s) return;
70
+ s.value = value;
71
+ emit();
72
+ },
73
+ removeState(componentId, key) {
74
+ const c = components.get(componentId);
75
+ if (!c) return;
76
+ if (!c.states.has(key)) return;
77
+ c.states.delete(key);
78
+ emit();
79
+ }
80
+ };
81
+ return store;
82
+ }
83
+
84
+ // src/provider.tsx
85
+ import { jsx } from "react/jsx-runtime";
86
+ var InspectorContext = createContext(null);
87
+ function StateInspectorProvider({
88
+ enabled = true,
89
+ children
90
+ }) {
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 });
97
+ }
98
+ function useInspectorStore() {
99
+ const ctx = useContext(InspectorContext);
100
+ if (!ctx) {
101
+ throw new Error("useInspectorStore must be used inside StateInspectorProvider");
102
+ }
103
+ return ctx;
104
+ }
105
+ function useInspectorComponent(label) {
106
+ const store = useInspectorStore();
107
+ const reactId = useId();
108
+ const componentId = useMemo(() => `c_${reactId}`, [reactId]);
109
+ useEffect(() => {
110
+ if (!store.enabled) return;
111
+ store.registerComponent(componentId, label?.trim());
112
+ return () => {
113
+ store.unregisterComponent(componentId);
114
+ };
115
+ }, [store, componentId, label]);
116
+ useEffect(() => {
117
+ if (!store.enabled) return;
118
+ const trimmed = label?.trim();
119
+ if (trimmed) store.setComponentLabel(componentId, trimmed);
120
+ }, [store, componentId, label]);
121
+ return useMemo(() => ({ id: componentId }), [componentId]);
122
+ }
123
+
124
+ // src/hooks.ts
125
+ import { useEffect as useEffect2, useId as useId2, useMemo as useMemo2, useRef as useRef2, useState } from "react";
126
+ function stableKey(input) {
127
+ return input.trim();
128
+ }
129
+ function inferMeta(value) {
130
+ const t = typeof value;
131
+ if (t === "boolean") return { type: "boolean" };
132
+ if (t === "number") return { type: "number" };
133
+ if (t === "string") return { type: "text" };
134
+ if (value && t === "object") return { type: "json" };
135
+ return void 0;
136
+ }
137
+ function useInspectableState(component, key, initial, meta) {
138
+ const store = useInspectorStore();
139
+ const componentId = component.id;
140
+ const stateKey = useMemo2(() => stableKey(key), [key]);
141
+ const [value, setValue] = useState(initial);
142
+ const setValueRef = useRef2(() => {
143
+ });
144
+ setValueRef.current = (next) => {
145
+ setValue(next);
146
+ };
147
+ useEffect2(() => {
148
+ if (!store.enabled) return;
149
+ const resolvedMeta = meta ?? inferMeta(value);
150
+ store.upsertState(componentId, {
151
+ key: stateKey,
152
+ value,
153
+ setValue: (next) => setValueRef.current(next),
154
+ ...resolvedMeta !== void 0 && { meta: resolvedMeta }
155
+ });
156
+ return () => {
157
+ store.removeState(componentId, stateKey);
158
+ };
159
+ }, [store, componentId, stateKey]);
160
+ useEffect2(() => {
161
+ if (!store.enabled) return;
162
+ const resolvedMeta = meta ?? inferMeta(value);
163
+ store.upsertState(componentId, {
164
+ key: stateKey,
165
+ value,
166
+ setValue: (next) => setValueRef.current(next),
167
+ ...resolvedMeta !== void 0 && { meta: resolvedMeta }
168
+ });
169
+ }, [store, componentId, stateKey, value, meta]);
170
+ return [value, setValue];
171
+ }
172
+ function useComponentLabel(label) {
173
+ const store = useInspectorStore();
174
+ const reactId = useId2();
175
+ const componentId = useMemo2(() => `c_${reactId}`, [reactId]);
176
+ useEffect2(() => {
177
+ if (!store.enabled) return;
178
+ const trimmed = label.trim();
179
+ if (!trimmed) return;
180
+ store.setComponentLabel(componentId, trimmed);
181
+ }, [store, componentId, label]);
182
+ }
183
+
184
+ // src/StateInspectorUI.tsx
185
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState2 } from "react";
186
+ import { createPortal } from "react-dom";
187
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
188
+ function isOptionObject(opt) {
189
+ return typeof opt === "object" && opt !== null && "value" in opt;
190
+ }
191
+ function StateInspectorUI() {
192
+ const store = useInspectorStore();
193
+ const [open, setOpen] = useState2(false);
194
+ const [selectedId, setSelectedId] = useState2(null);
195
+ const [, force] = useState2(0);
196
+ 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);
200
+ useEffect3(() => {
201
+ if (!open) return;
202
+ if (components.length === 0) {
203
+ setSelectedId(null);
204
+ return;
205
+ }
206
+ if (!selectedId || !components.some((c) => c.id === selectedId)) {
207
+ setSelectedId(components[0].id);
208
+ }
209
+ }, [open, components.length]);
210
+ useEffect3(() => {
211
+ if (!open) return;
212
+ function onKey(e) {
213
+ if (e.key === "Escape") setOpen(false);
214
+ }
215
+ window.addEventListener("keydown", onKey);
216
+ return () => window.removeEventListener("keydown", onKey);
217
+ }, [open]);
218
+ const selected = useMemo3(() => {
219
+ if (!selectedId) return null;
220
+ return store.components.get(selectedId) ?? null;
221
+ }, [store, selectedId, snapshot.components.length]);
222
+ return createPortal(
223
+ /* @__PURE__ */ jsxs(Fragment, { children: [
224
+ /* @__PURE__ */ jsx2(
225
+ "button",
226
+ {
227
+ onClick: () => setOpen((o) => !o),
228
+ style: {
229
+ position: "fixed",
230
+ right: 16,
231
+ bottom: 16,
232
+ width: 44,
233
+ height: 44,
234
+ borderRadius: 22,
235
+ border: "none",
236
+ background: "#111",
237
+ color: "#fff",
238
+ cursor: "pointer",
239
+ zIndex: 999999
240
+ },
241
+ "aria-label": "Toggle State Inspector",
242
+ title: "State Inspector",
243
+ children: "\u25CE"
244
+ }
245
+ ),
246
+ open && /* @__PURE__ */ jsxs(
247
+ "div",
248
+ {
249
+ style: {
250
+ position: "fixed",
251
+ right: 16,
252
+ bottom: 72,
253
+ width: 720,
254
+ maxWidth: "calc(100vw - 32px)",
255
+ height: 420,
256
+ maxHeight: "calc(100vh - 120px)",
257
+ background: "#1c1c1c",
258
+ color: "#fff",
259
+ borderRadius: 12,
260
+ border: "1px solid #333",
261
+ zIndex: 999999,
262
+ display: "grid",
263
+ gridTemplateColumns: "280px 1fr",
264
+ overflow: "hidden"
265
+ },
266
+ 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) => {
273
+ const active = c.id === selectedId;
274
+ return /* @__PURE__ */ jsxs(
275
+ "button",
276
+ {
277
+ onClick: () => setSelectedId(c.id),
278
+ style: {
279
+ textAlign: "left",
280
+ background: active ? "#2a2a2a" : "transparent",
281
+ border: "1px solid #333",
282
+ borderRadius: 10,
283
+ padding: 10,
284
+ color: "#fff",
285
+ cursor: "pointer"
286
+ },
287
+ children: [
288
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 700 }, children: c.label }),
289
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, opacity: 0.7 }, children: [
290
+ "states: ",
291
+ c.stateKeys.length ? c.stateKeys.length : 0
292
+ ] })
293
+ ]
294
+ },
295
+ c.id
296
+ );
297
+ }) })
298
+ ] }),
299
+ /* @__PURE__ */ jsxs("div", { style: { padding: 12, overflow: "auto" }, children: [
300
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
301
+ /* @__PURE__ */ jsxs("div", { children: [
302
+ /* @__PURE__ */ jsx2("h4", { style: { margin: 0 }, children: "State" }),
303
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 12, opacity: 0.7 }, children: selected ? selected.label : "No selection" })
304
+ ] }),
305
+ /* @__PURE__ */ jsx2(
306
+ "button",
307
+ {
308
+ onClick: () => setOpen(false),
309
+ style: {
310
+ background: "transparent",
311
+ color: "#fff",
312
+ border: "1px solid #333",
313
+ borderRadius: 10,
314
+ padding: "6px 10px",
315
+ cursor: "pointer"
316
+ },
317
+ children: "Close"
318
+ }
319
+ )
320
+ ] }),
321
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: 12, display: "grid", gap: 10 }, children: [
322
+ selected && selected.states.size === 0 && /* @__PURE__ */ jsx2("div", { style: { fontSize: 13, opacity: 0.7 }, children: "This component has no inspectable state." }),
323
+ !selected && /* @__PURE__ */ jsx2("div", { style: { opacity: 0.7, fontSize: 13 }, children: "No component selected." }),
324
+ selected && Array.from(selected.states.values()).map((s) => /* @__PURE__ */ jsxs(
325
+ "div",
326
+ {
327
+ style: {
328
+ border: "1px solid #333",
329
+ borderRadius: 12,
330
+ padding: 10,
331
+ display: "grid",
332
+ gap: 8
333
+ },
334
+ children: [
335
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
336
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: 700 }, children: s.key }),
337
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 12, opacity: 0.6 }, children: s.meta?.type ?? "auto" })
338
+ ] }),
339
+ /* @__PURE__ */ jsx2(
340
+ StateEditor,
341
+ {
342
+ value: s.value,
343
+ meta: s.meta,
344
+ onChange: (next) => s.setValue(next)
345
+ }
346
+ )
347
+ ]
348
+ },
349
+ s.key
350
+ ))
351
+ ] })
352
+ ] })
353
+ ]
354
+ }
355
+ )
356
+ ] }),
357
+ document.body
358
+ );
359
+ }
360
+ function StateEditor({
361
+ value,
362
+ meta,
363
+ onChange
364
+ }) {
365
+ if (meta?.type === "boolean" || typeof value === "boolean") {
366
+ return /* @__PURE__ */ jsxs("label", { style: { display: "flex", gap: 10, alignItems: "center" }, children: [
367
+ /* @__PURE__ */ jsx2(
368
+ "input",
369
+ {
370
+ type: "checkbox",
371
+ checked: Boolean(value),
372
+ onChange: (e) => onChange(e.target.checked)
373
+ }
374
+ ),
375
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 13, opacity: 0.8 }, children: String(Boolean(value)) })
376
+ ] });
377
+ }
378
+ if (meta?.type === "select" && Array.isArray(meta.options)) {
379
+ const current = typeof value === "string" ? value : String(value ?? "");
380
+ return /* @__PURE__ */ jsx2(
381
+ "select",
382
+ {
383
+ value: current,
384
+ onChange: (e) => onChange(e.target.value),
385
+ style: {
386
+ width: "100%",
387
+ padding: "8px 10px",
388
+ borderRadius: 10,
389
+ border: "1px solid #333",
390
+ background: "#111",
391
+ color: "#fff"
392
+ },
393
+ children: meta.options.map((opt) => {
394
+ const o = isOptionObject(opt) ? opt : { label: String(opt), value: String(opt) };
395
+ return /* @__PURE__ */ jsx2("option", { value: o.value, children: o.label }, o.value);
396
+ })
397
+ }
398
+ );
399
+ }
400
+ if (meta?.type === "number" || typeof value === "number") {
401
+ return /* @__PURE__ */ jsx2(
402
+ "input",
403
+ {
404
+ type: "number",
405
+ value: typeof value === "number" ? value : Number(value ?? 0),
406
+ min: meta?.min,
407
+ max: meta?.max,
408
+ step: meta?.step,
409
+ onChange: (e) => onChange(e.target.value === "" ? 0 : Number(e.target.value)),
410
+ style: {
411
+ width: "100%",
412
+ padding: "8px 10px",
413
+ borderRadius: 10,
414
+ border: "1px solid #333",
415
+ background: "#111",
416
+ color: "#fff"
417
+ }
418
+ }
419
+ );
420
+ }
421
+ if (meta?.type === "json" || value && typeof value === "object") {
422
+ const text = safeStringify(value);
423
+ return /* @__PURE__ */ jsx2(JsonEditor, { initial: text, onValidJson: (obj) => onChange(obj) });
424
+ }
425
+ return /* @__PURE__ */ jsx2(
426
+ "input",
427
+ {
428
+ type: "text",
429
+ value: typeof value === "string" ? value : String(value ?? ""),
430
+ placeholder: meta?.placeholder,
431
+ onChange: (e) => onChange(e.target.value),
432
+ style: {
433
+ width: "100%",
434
+ padding: "8px 10px",
435
+ borderRadius: 10,
436
+ border: "1px solid #333",
437
+ background: "#111",
438
+ color: "#fff"
439
+ }
440
+ }
441
+ );
442
+ }
443
+ function safeStringify(v) {
444
+ try {
445
+ return JSON.stringify(v, null, 2);
446
+ } catch {
447
+ return String(v);
448
+ }
449
+ }
450
+ function JsonEditor({
451
+ initial,
452
+ onValidJson
453
+ }) {
454
+ const [raw, setRaw] = useState2(initial);
455
+ const [error, setError] = useState2(null);
456
+ useEffect3(() => {
457
+ setRaw(initial);
458
+ }, [initial]);
459
+ return /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 6 }, children: [
460
+ /* @__PURE__ */ jsx2(
461
+ "textarea",
462
+ {
463
+ value: raw,
464
+ onChange: (e) => {
465
+ const next = e.target.value;
466
+ setRaw(next);
467
+ try {
468
+ const parsed = JSON.parse(next);
469
+ setError(null);
470
+ onValidJson(parsed);
471
+ } catch (err) {
472
+ setError("Invalid JSON");
473
+ }
474
+ },
475
+ rows: 6,
476
+ style: {
477
+ width: "100%",
478
+ padding: "8px 10px",
479
+ borderRadius: 10,
480
+ border: "1px solid #333",
481
+ background: "#111",
482
+ color: "#fff",
483
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
484
+ fontSize: 12
485
+ }
486
+ }
487
+ ),
488
+ error && /* @__PURE__ */ jsx2("div", { style: { fontSize: 12, color: "#ff6b6b" }, children: error })
489
+ ] });
490
+ }
491
+ export {
492
+ StateInspectorProvider,
493
+ StateInspectorUI,
494
+ createInspectorStore,
495
+ useComponentLabel,
496
+ useInspectableState,
497
+ useInspectorComponent,
498
+ useInspectorStore
499
+ };
500
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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"]}