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