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.
- package/dist/index.d.mts +86 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +533 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +500 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +32 -0
|
@@ -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"]}
|