@jamesyong42/infinite-canvas 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +170 -9
  2. package/dist/SelectionRenderer-CR2PBQwx.d.cts +105 -0
  3. package/dist/SelectionRenderer-CR2PBQwx.d.cts.map +1 -0
  4. package/dist/SelectionRenderer-DlsBstAq.d.mts +105 -0
  5. package/dist/SelectionRenderer-DlsBstAq.d.mts.map +1 -0
  6. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs +3560 -0
  7. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs.map +1 -0
  8. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs +3375 -0
  9. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs.map +1 -0
  10. package/dist/advanced.cjs +110 -165
  11. package/dist/advanced.cjs.map +1 -1
  12. package/dist/advanced.d.cts +58 -40
  13. package/dist/advanced.d.cts.map +1 -0
  14. package/dist/advanced.d.mts +99 -0
  15. package/dist/advanced.d.mts.map +1 -0
  16. package/dist/advanced.mjs +105 -0
  17. package/dist/advanced.mjs.map +1 -0
  18. package/dist/devtools.cjs +654 -0
  19. package/dist/devtools.cjs.map +1 -0
  20. package/dist/devtools.d.cts +23 -0
  21. package/dist/devtools.d.cts.map +1 -0
  22. package/dist/devtools.d.mts +23 -0
  23. package/dist/devtools.d.mts.map +1 -0
  24. package/dist/devtools.mjs +652 -0
  25. package/dist/devtools.mjs.map +1 -0
  26. package/dist/engine-BfbvWXSk.d.mts +982 -0
  27. package/dist/engine-BfbvWXSk.d.mts.map +1 -0
  28. package/dist/engine-CCjuFMC-.d.cts +982 -0
  29. package/dist/engine-CCjuFMC-.d.cts.map +1 -0
  30. package/dist/hooks-BwY7rRHg.mjs +425 -0
  31. package/dist/hooks-BwY7rRHg.mjs.map +1 -0
  32. package/dist/hooks-DHShH86C.cjs +707 -0
  33. package/dist/hooks-DHShH86C.cjs.map +1 -0
  34. package/dist/index.cjs +909 -803
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +199 -67
  37. package/dist/index.d.cts.map +1 -0
  38. package/dist/index.d.mts +258 -0
  39. package/dist/index.d.mts.map +1 -0
  40. package/dist/index.mjs +855 -0
  41. package/dist/index.mjs.map +1 -0
  42. package/package.json +47 -15
  43. package/dist/SelectionRenderer-CeWSNZT8.d.cts +0 -891
  44. package/dist/SelectionRenderer-CeWSNZT8.d.ts +0 -891
  45. package/dist/advanced.d.ts +0 -81
  46. package/dist/advanced.js +0 -124
  47. package/dist/advanced.js.map +0 -1
  48. package/dist/chunk-VSHXWTJH.cjs +0 -3228
  49. package/dist/chunk-VSHXWTJH.cjs.map +0 -1
  50. package/dist/chunk-Z6JQQOWL.js +0 -3142
  51. package/dist/chunk-Z6JQQOWL.js.map +0 -1
  52. package/dist/index.d.ts +0 -126
  53. package/dist/index.js +0 -602
  54. package/dist/index.js.map +0 -1
@@ -0,0 +1,652 @@
1
+ import { G as WidgetData, U as Widget, a as useEntityTags, c as useRegisteredTags, d as useTaggedEntities, g as useLayoutEngine, i as useEntityComponents, p as EngineProvider, r as useComponent, s as useRegisteredComponents, t as useAllEntities, z as Selected } from "./hooks-BwY7rRHg.mjs";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/react/devtools/EcsDevtools.tsx
5
+ /**
6
+ * Live ECS editor: spawn widgets, browse entities, edit components, toggle tags.
7
+ * Ship in a dev mode or behind a feature flag — not intended for production users.
8
+ */
9
+ function EcsDevtools({ engine, onClose }) {
10
+ if (engine) return /* @__PURE__ */ jsx(EngineProvider, {
11
+ value: engine,
12
+ children: /* @__PURE__ */ jsx(EcsDevtoolsInner, { onClose })
13
+ });
14
+ return /* @__PURE__ */ jsx(EcsDevtoolsInner, { onClose });
15
+ }
16
+ function EcsDevtoolsInner({ onClose }) {
17
+ const engine = useLayoutEngine();
18
+ const allEntities = useAllEntities();
19
+ const selectedIds = useTaggedEntities(Selected);
20
+ const widgets = useMemo(() => engine.getWidgets(), [engine]);
21
+ const [showAll, setShowAll] = useState(false);
22
+ const [spawnType, setSpawnType] = useState(widgets[0]?.type ?? "");
23
+ const [manualFocusId, setManualFocusId] = useState(null);
24
+ const focusId = selectedIds[0] ?? manualFocusId;
25
+ const entityList = useMemo(() => {
26
+ if (showAll) return allEntities;
27
+ return allEntities.filter((id) => engine.has(id, Widget));
28
+ }, [
29
+ engine,
30
+ allEntities,
31
+ showAll
32
+ ]);
33
+ const handleSpawn = () => {
34
+ if (!spawnType) return;
35
+ const id = engine.spawnAtCameraCenter(spawnType);
36
+ engine.markDirty();
37
+ setManualFocusId(id);
38
+ };
39
+ return /* @__PURE__ */ jsxs("div", {
40
+ className: "ic-ecs-root",
41
+ children: [
42
+ /* @__PURE__ */ jsx(StyleTag, {}),
43
+ /* @__PURE__ */ jsxs("div", {
44
+ className: "ic-ecs-header",
45
+ children: [/* @__PURE__ */ jsx("span", {
46
+ className: "ic-ecs-title",
47
+ children: "ECS Editor"
48
+ }), onClose && /* @__PURE__ */ jsx("button", {
49
+ type: "button",
50
+ className: "ic-ecs-close",
51
+ onClick: onClose,
52
+ title: "Close",
53
+ children: "×"
54
+ })]
55
+ }),
56
+ /* @__PURE__ */ jsxs("div", {
57
+ className: "ic-ecs-section",
58
+ children: [/* @__PURE__ */ jsx("div", {
59
+ className: "ic-ecs-label",
60
+ children: "Spawn widget"
61
+ }), /* @__PURE__ */ jsxs("div", {
62
+ className: "ic-ecs-row",
63
+ children: [/* @__PURE__ */ jsx("select", {
64
+ className: "ic-ecs-select",
65
+ value: spawnType,
66
+ onChange: (e) => setSpawnType(e.target.value),
67
+ children: widgets.map((w) => /* @__PURE__ */ jsx("option", {
68
+ value: w.type,
69
+ children: w.type
70
+ }, w.type))
71
+ }), /* @__PURE__ */ jsx("button", {
72
+ type: "button",
73
+ className: "ic-ecs-btn ic-ecs-btn-primary",
74
+ onClick: handleSpawn,
75
+ disabled: !spawnType,
76
+ children: "+ Spawn"
77
+ })]
78
+ })]
79
+ }),
80
+ /* @__PURE__ */ jsxs("div", {
81
+ className: "ic-ecs-section",
82
+ children: [/* @__PURE__ */ jsxs("div", {
83
+ className: "ic-ecs-label-row",
84
+ children: [/* @__PURE__ */ jsxs("span", {
85
+ className: "ic-ecs-label",
86
+ children: [
87
+ "Entities (",
88
+ entityList.length,
89
+ showAll ? "" : " widgets",
90
+ ")"
91
+ ]
92
+ }), /* @__PURE__ */ jsxs("label", {
93
+ className: "ic-ecs-check",
94
+ children: [/* @__PURE__ */ jsx("input", {
95
+ type: "checkbox",
96
+ checked: showAll,
97
+ onChange: (e) => setShowAll(e.target.checked)
98
+ }), "show all"]
99
+ })]
100
+ }), /* @__PURE__ */ jsxs("div", {
101
+ className: "ic-ecs-list",
102
+ children: [entityList.map((id) => /* @__PURE__ */ jsx(EntityRow, {
103
+ entity: id,
104
+ focused: id === focusId,
105
+ onClick: () => setManualFocusId(id)
106
+ }, id)), entityList.length === 0 && /* @__PURE__ */ jsx("div", {
107
+ className: "ic-ecs-empty",
108
+ children: "no entities"
109
+ })]
110
+ })]
111
+ }),
112
+ focusId !== null && focusId !== void 0 && engine.world.entityExists(focusId) && /* @__PURE__ */ jsx(EntityInspector, { entity: focusId })
113
+ ]
114
+ });
115
+ }
116
+ function EntityRow({ entity, focused, onClick }) {
117
+ const engine = useLayoutEngine();
118
+ const label = useComponent(entity, Widget)?.type ?? "entity";
119
+ return /* @__PURE__ */ jsxs("div", {
120
+ className: `ic-ecs-entity-row ${focused ? "is-focused" : ""}`,
121
+ children: [/* @__PURE__ */ jsxs("button", {
122
+ type: "button",
123
+ className: "ic-ecs-entity-btn",
124
+ onClick,
125
+ children: [/* @__PURE__ */ jsxs("span", {
126
+ className: "ic-ecs-entity-id",
127
+ children: ["e", entity]
128
+ }), /* @__PURE__ */ jsx("span", {
129
+ className: "ic-ecs-entity-label",
130
+ children: label
131
+ })]
132
+ }), /* @__PURE__ */ jsx("button", {
133
+ type: "button",
134
+ className: "ic-ecs-btn ic-ecs-btn-danger ic-ecs-btn-sm",
135
+ onClick: () => {
136
+ engine.destroyEntity(entity);
137
+ engine.markDirty();
138
+ },
139
+ title: "Destroy entity",
140
+ children: "×"
141
+ })]
142
+ });
143
+ }
144
+ function EntityInspector({ entity }) {
145
+ const engine = useLayoutEngine();
146
+ const components = useEntityComponents(entity);
147
+ const tags = useEntityTags(entity);
148
+ const registeredComponents = useRegisteredComponents();
149
+ const registeredTags = useRegisteredTags();
150
+ const widget = useComponent(entity, Widget);
151
+ const absentComponents = useMemo(() => {
152
+ const present = new Set(components.map((c) => c.name));
153
+ return registeredComponents.filter((c) => !present.has(c.name));
154
+ }, [components, registeredComponents]);
155
+ const [componentToAdd, setComponentToAdd] = useState("");
156
+ useEffect(() => {
157
+ setComponentToAdd(absentComponents[0]?.name ?? "");
158
+ }, [absentComponents]);
159
+ const handleAddComponent = () => {
160
+ const type = absentComponents.find((c) => c.name === componentToAdd);
161
+ if (!type) return;
162
+ engine.addComponent(entity, type);
163
+ };
164
+ return /* @__PURE__ */ jsxs("div", {
165
+ className: "ic-ecs-section",
166
+ children: [
167
+ /* @__PURE__ */ jsx("div", {
168
+ className: "ic-ecs-inspect-head",
169
+ children: /* @__PURE__ */ jsxs("span", {
170
+ className: "ic-ecs-label",
171
+ children: [
172
+ "Entity ",
173
+ /* @__PURE__ */ jsxs("span", {
174
+ className: "ic-ecs-entity-id",
175
+ children: ["e", entity]
176
+ }),
177
+ widget?.type && /* @__PURE__ */ jsxs("span", {
178
+ className: "ic-ecs-entity-label",
179
+ children: [" · ", widget.type]
180
+ })
181
+ ]
182
+ })
183
+ }),
184
+ /* @__PURE__ */ jsx("div", {
185
+ className: "ic-ecs-sub-label",
186
+ children: "Components"
187
+ }),
188
+ /* @__PURE__ */ jsx("div", {
189
+ className: "ic-ecs-components",
190
+ children: components.map((type) => /* @__PURE__ */ jsx(ComponentEditor, {
191
+ entity,
192
+ type
193
+ }, type.name))
194
+ }),
195
+ absentComponents.length > 0 && /* @__PURE__ */ jsxs("div", {
196
+ className: "ic-ecs-row",
197
+ style: { marginTop: 6 },
198
+ children: [/* @__PURE__ */ jsx("select", {
199
+ className: "ic-ecs-select",
200
+ value: componentToAdd,
201
+ onChange: (e) => setComponentToAdd(e.target.value),
202
+ children: absentComponents.map((c) => /* @__PURE__ */ jsx("option", {
203
+ value: c.name,
204
+ children: c.name
205
+ }, c.name))
206
+ }), /* @__PURE__ */ jsx("button", {
207
+ type: "button",
208
+ className: "ic-ecs-btn",
209
+ onClick: handleAddComponent,
210
+ children: "+ Add component"
211
+ })]
212
+ }),
213
+ /* @__PURE__ */ jsx("div", {
214
+ className: "ic-ecs-sub-label",
215
+ children: "Tags"
216
+ }),
217
+ /* @__PURE__ */ jsx("div", {
218
+ className: "ic-ecs-tags",
219
+ children: registeredTags.map((type) => /* @__PURE__ */ jsx(TagPill, {
220
+ entity,
221
+ type,
222
+ active: tags.some((t) => t.name === type.name)
223
+ }, type.name))
224
+ })
225
+ ]
226
+ });
227
+ }
228
+ function ComponentEditor({ entity, type }) {
229
+ const engine = useLayoutEngine();
230
+ const value = useComponent(entity, type);
231
+ const [collapsed, setCollapsed] = useState(false);
232
+ if (!value) return null;
233
+ const isWidgetData = type.name === "WidgetData";
234
+ return /* @__PURE__ */ jsxs("div", {
235
+ className: "ic-ecs-component",
236
+ children: [/* @__PURE__ */ jsxs("div", {
237
+ className: "ic-ecs-component-head",
238
+ children: [/* @__PURE__ */ jsxs("button", {
239
+ type: "button",
240
+ className: "ic-ecs-toggle",
241
+ onClick: () => setCollapsed((c) => !c),
242
+ children: [
243
+ collapsed ? "▶" : "▼",
244
+ " ",
245
+ type.name
246
+ ]
247
+ }), /* @__PURE__ */ jsx("button", {
248
+ type: "button",
249
+ className: "ic-ecs-btn ic-ecs-btn-danger ic-ecs-btn-sm",
250
+ onClick: () => engine.removeComponent(entity, type),
251
+ title: "Remove component",
252
+ children: "×"
253
+ })]
254
+ }), !collapsed && /* @__PURE__ */ jsx("div", {
255
+ className: "ic-ecs-fields",
256
+ children: isWidgetData ? /* @__PURE__ */ jsx(WidgetDataEditor, {
257
+ entity,
258
+ value
259
+ }) : /* @__PURE__ */ jsx(GenericFieldsEditor, {
260
+ entity,
261
+ type,
262
+ value
263
+ })
264
+ })]
265
+ });
266
+ }
267
+ function GenericFieldsEditor({ entity, type, value }) {
268
+ const engine = useLayoutEngine();
269
+ const defaults = type.defaults;
270
+ const val = value;
271
+ return /* @__PURE__ */ jsx(Fragment, { children: Object.keys(defaults).map((key) => /* @__PURE__ */ jsx(FieldRow, {
272
+ label: key,
273
+ value: val[key],
274
+ onChange: (next) => {
275
+ engine.set(entity, type, { [key]: next });
276
+ }
277
+ }, key)) });
278
+ }
279
+ function WidgetDataEditor({ entity, value }) {
280
+ const engine = useLayoutEngine();
281
+ const data = value.data ?? {};
282
+ const keys = Object.keys(data);
283
+ if (keys.length === 0) return /* @__PURE__ */ jsx("div", {
284
+ className: "ic-ecs-empty",
285
+ children: "(no fields)"
286
+ });
287
+ return /* @__PURE__ */ jsx(Fragment, { children: keys.map((key) => /* @__PURE__ */ jsx(FieldRow, {
288
+ label: key,
289
+ value: data[key],
290
+ onChange: (next) => {
291
+ engine.set(entity, WidgetData, { data: {
292
+ ...data,
293
+ [key]: next
294
+ } });
295
+ }
296
+ }, key)) });
297
+ }
298
+ function FieldRow({ label, value, onChange }) {
299
+ return /* @__PURE__ */ jsxs("div", {
300
+ className: "ic-ecs-field",
301
+ children: [/* @__PURE__ */ jsx("span", {
302
+ className: "ic-ecs-field-label",
303
+ children: label
304
+ }), /* @__PURE__ */ jsx(FieldInput, {
305
+ value,
306
+ onChange
307
+ })]
308
+ });
309
+ }
310
+ function FieldInput({ value, onChange }) {
311
+ if (typeof value === "number") return /* @__PURE__ */ jsx("input", {
312
+ type: "number",
313
+ className: "ic-ecs-input",
314
+ value,
315
+ onChange: (e) => {
316
+ const n = Number.parseFloat(e.target.value);
317
+ if (!Number.isNaN(n)) onChange(n);
318
+ }
319
+ });
320
+ if (typeof value === "boolean") return /* @__PURE__ */ jsx("input", {
321
+ type: "checkbox",
322
+ className: "ic-ecs-checkbox",
323
+ checked: value,
324
+ onChange: (e) => onChange(e.target.checked)
325
+ });
326
+ if (typeof value === "string") return /* @__PURE__ */ jsx("input", {
327
+ type: "text",
328
+ className: "ic-ecs-input",
329
+ value,
330
+ onChange: (e) => onChange(e.target.value)
331
+ });
332
+ return /* @__PURE__ */ jsx(JsonInput, {
333
+ value,
334
+ onChange
335
+ });
336
+ }
337
+ function JsonInput({ value, onChange }) {
338
+ const [text, setText] = useState(JSON.stringify(value));
339
+ const [error, setError] = useState(false);
340
+ useEffect(() => {
341
+ setText(JSON.stringify(value));
342
+ setError(false);
343
+ }, [value]);
344
+ const commit = () => {
345
+ try {
346
+ const parsed = JSON.parse(text);
347
+ setError(false);
348
+ onChange(parsed);
349
+ } catch {
350
+ setError(true);
351
+ }
352
+ };
353
+ return /* @__PURE__ */ jsx("input", {
354
+ type: "text",
355
+ className: `ic-ecs-input ic-ecs-input-json ${error ? "is-error" : ""}`,
356
+ value: text,
357
+ onChange: (e) => setText(e.target.value),
358
+ onBlur: commit,
359
+ onKeyDown: (e) => {
360
+ if (e.key === "Enter") commit();
361
+ }
362
+ });
363
+ }
364
+ function TagPill({ entity, type, active }) {
365
+ const engine = useLayoutEngine();
366
+ return /* @__PURE__ */ jsx("button", {
367
+ type: "button",
368
+ className: `ic-ecs-tag ${active ? "is-active" : ""}`,
369
+ onClick: () => {
370
+ if (active) engine.removeTag(entity, type);
371
+ else engine.addTag(entity, type);
372
+ },
373
+ children: type.name
374
+ });
375
+ }
376
+ const STYLE_ID = "ic-ecs-devtools-style";
377
+ const CSS = `
378
+ .ic-ecs-root {
379
+ --ic-ecs-bg: rgba(255, 255, 255, 0.96);
380
+ --ic-ecs-bg-elev: #f7f7f7;
381
+ --ic-ecs-fg: #1a1a1a;
382
+ --ic-ecs-fg-muted: #6b7280;
383
+ --ic-ecs-fg-faint: #9ca3af;
384
+ --ic-ecs-border: #e5e7eb;
385
+ --ic-ecs-accent: #0d99ff;
386
+ --ic-ecs-accent-fg: #ffffff;
387
+ --ic-ecs-danger: #dc2626;
388
+ --ic-ecs-focus-bg: rgba(13, 153, 255, 0.12);
389
+ position: absolute;
390
+ top: 4rem;
391
+ right: 1rem;
392
+ z-index: 50;
393
+ width: 360px;
394
+ max-height: calc(100vh - 6rem);
395
+ overflow-y: auto;
396
+ background: var(--ic-ecs-bg);
397
+ color: var(--ic-ecs-fg);
398
+ border: 1px solid var(--ic-ecs-border);
399
+ border-radius: 8px;
400
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
401
+ backdrop-filter: blur(6px);
402
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
403
+ font-size: 11px;
404
+ line-height: 1.4;
405
+ }
406
+ .dark .ic-ecs-root,
407
+ :root[data-theme="dark"] .ic-ecs-root {
408
+ --ic-ecs-bg: rgba(23, 23, 23, 0.96);
409
+ --ic-ecs-bg-elev: #1f1f1f;
410
+ --ic-ecs-fg: #e5e5e5;
411
+ --ic-ecs-fg-muted: #a1a1aa;
412
+ --ic-ecs-fg-faint: #525252;
413
+ --ic-ecs-border: #2a2a2a;
414
+ --ic-ecs-focus-bg: rgba(13, 153, 255, 0.18);
415
+ }
416
+ .ic-ecs-header {
417
+ display: flex;
418
+ align-items: center;
419
+ justify-content: space-between;
420
+ padding: 8px 12px;
421
+ border-bottom: 1px solid var(--ic-ecs-border);
422
+ }
423
+ .ic-ecs-title {
424
+ font-weight: 600;
425
+ font-size: 12px;
426
+ }
427
+ .ic-ecs-close {
428
+ background: transparent;
429
+ border: 0;
430
+ color: var(--ic-ecs-fg-muted);
431
+ cursor: pointer;
432
+ font-size: 16px;
433
+ line-height: 1;
434
+ padding: 0 4px;
435
+ }
436
+ .ic-ecs-close:hover { color: var(--ic-ecs-fg); }
437
+ .ic-ecs-section {
438
+ padding: 8px 12px;
439
+ border-bottom: 1px solid var(--ic-ecs-border);
440
+ }
441
+ .ic-ecs-section:last-child { border-bottom: 0; }
442
+ .ic-ecs-label {
443
+ font-size: 10px;
444
+ font-weight: 600;
445
+ letter-spacing: 0.05em;
446
+ text-transform: uppercase;
447
+ color: var(--ic-ecs-fg-muted);
448
+ }
449
+ .ic-ecs-label-row {
450
+ display: flex;
451
+ align-items: center;
452
+ justify-content: space-between;
453
+ margin-bottom: 4px;
454
+ }
455
+ .ic-ecs-sub-label {
456
+ font-size: 9px;
457
+ font-weight: 600;
458
+ letter-spacing: 0.05em;
459
+ text-transform: uppercase;
460
+ color: var(--ic-ecs-fg-faint);
461
+ margin: 8px 0 4px;
462
+ }
463
+ .ic-ecs-row {
464
+ display: flex;
465
+ gap: 6px;
466
+ align-items: center;
467
+ margin-top: 4px;
468
+ }
469
+ .ic-ecs-select {
470
+ flex: 1 1 auto;
471
+ min-width: 0;
472
+ background: var(--ic-ecs-bg-elev);
473
+ color: var(--ic-ecs-fg);
474
+ border: 1px solid var(--ic-ecs-border);
475
+ border-radius: 4px;
476
+ padding: 4px 6px;
477
+ font: inherit;
478
+ }
479
+ .ic-ecs-input {
480
+ flex: 1 1 auto;
481
+ min-width: 0;
482
+ background: var(--ic-ecs-bg-elev);
483
+ color: var(--ic-ecs-fg);
484
+ border: 1px solid var(--ic-ecs-border);
485
+ border-radius: 4px;
486
+ padding: 2px 6px;
487
+ font: inherit;
488
+ }
489
+ .ic-ecs-input:focus { outline: 1px solid var(--ic-ecs-accent); outline-offset: -1px; }
490
+ .ic-ecs-input-json.is-error { border-color: var(--ic-ecs-danger); }
491
+ .ic-ecs-checkbox {
492
+ accent-color: var(--ic-ecs-accent);
493
+ }
494
+ .ic-ecs-btn {
495
+ background: var(--ic-ecs-bg-elev);
496
+ color: var(--ic-ecs-fg);
497
+ border: 1px solid var(--ic-ecs-border);
498
+ border-radius: 4px;
499
+ padding: 3px 8px;
500
+ font: inherit;
501
+ cursor: pointer;
502
+ white-space: nowrap;
503
+ }
504
+ .ic-ecs-btn:hover { background: var(--ic-ecs-border); }
505
+ .ic-ecs-btn:disabled { opacity: 0.5; cursor: not-allowed; }
506
+ .ic-ecs-btn-primary {
507
+ background: var(--ic-ecs-accent);
508
+ color: var(--ic-ecs-accent-fg);
509
+ border-color: var(--ic-ecs-accent);
510
+ }
511
+ .ic-ecs-btn-primary:hover { filter: brightness(1.08); background: var(--ic-ecs-accent); }
512
+ .ic-ecs-btn-danger { color: var(--ic-ecs-danger); }
513
+ .ic-ecs-btn-sm { padding: 0 6px; font-size: 11px; }
514
+ .ic-ecs-check {
515
+ display: inline-flex;
516
+ align-items: center;
517
+ gap: 4px;
518
+ font-size: 10px;
519
+ color: var(--ic-ecs-fg-muted);
520
+ cursor: pointer;
521
+ }
522
+ .ic-ecs-list {
523
+ display: flex;
524
+ flex-direction: column;
525
+ gap: 2px;
526
+ max-height: 200px;
527
+ overflow-y: auto;
528
+ margin-top: 4px;
529
+ }
530
+ .ic-ecs-empty {
531
+ color: var(--ic-ecs-fg-faint);
532
+ font-style: italic;
533
+ padding: 4px 0;
534
+ }
535
+ .ic-ecs-entity-row {
536
+ display: flex;
537
+ align-items: center;
538
+ gap: 4px;
539
+ padding: 2px 4px;
540
+ border-radius: 4px;
541
+ }
542
+ .ic-ecs-entity-row.is-focused { background: var(--ic-ecs-focus-bg); }
543
+ .ic-ecs-entity-btn {
544
+ flex: 1 1 auto;
545
+ display: flex;
546
+ gap: 8px;
547
+ align-items: center;
548
+ background: transparent;
549
+ border: 0;
550
+ color: inherit;
551
+ cursor: pointer;
552
+ text-align: left;
553
+ padding: 2px 0;
554
+ font: inherit;
555
+ min-width: 0;
556
+ }
557
+ .ic-ecs-entity-id {
558
+ color: var(--ic-ecs-fg-muted);
559
+ font-variant-numeric: tabular-nums;
560
+ }
561
+ .ic-ecs-entity-label {
562
+ color: var(--ic-ecs-fg);
563
+ overflow: hidden;
564
+ text-overflow: ellipsis;
565
+ white-space: nowrap;
566
+ }
567
+ .ic-ecs-inspect-head {
568
+ display: flex;
569
+ align-items: center;
570
+ justify-content: space-between;
571
+ margin-bottom: 4px;
572
+ }
573
+ .ic-ecs-components {
574
+ display: flex;
575
+ flex-direction: column;
576
+ gap: 2px;
577
+ }
578
+ .ic-ecs-component {
579
+ border: 1px solid var(--ic-ecs-border);
580
+ border-radius: 4px;
581
+ background: var(--ic-ecs-bg-elev);
582
+ }
583
+ .ic-ecs-component-head {
584
+ display: flex;
585
+ align-items: center;
586
+ justify-content: space-between;
587
+ padding: 2px 6px;
588
+ }
589
+ .ic-ecs-toggle {
590
+ background: transparent;
591
+ border: 0;
592
+ color: inherit;
593
+ cursor: pointer;
594
+ font: inherit;
595
+ font-weight: 600;
596
+ padding: 2px 0;
597
+ }
598
+ .ic-ecs-fields {
599
+ display: flex;
600
+ flex-direction: column;
601
+ gap: 3px;
602
+ padding: 4px 6px 6px;
603
+ border-top: 1px dashed var(--ic-ecs-border);
604
+ }
605
+ .ic-ecs-field {
606
+ display: grid;
607
+ grid-template-columns: 70px 1fr;
608
+ align-items: center;
609
+ gap: 6px;
610
+ }
611
+ .ic-ecs-field-label {
612
+ color: var(--ic-ecs-fg-muted);
613
+ overflow: hidden;
614
+ text-overflow: ellipsis;
615
+ white-space: nowrap;
616
+ }
617
+ .ic-ecs-tags {
618
+ display: flex;
619
+ flex-wrap: wrap;
620
+ gap: 4px;
621
+ }
622
+ .ic-ecs-tag {
623
+ background: transparent;
624
+ color: var(--ic-ecs-fg-faint);
625
+ border: 1px solid var(--ic-ecs-border);
626
+ border-radius: 999px;
627
+ padding: 2px 8px;
628
+ font: inherit;
629
+ cursor: pointer;
630
+ }
631
+ .ic-ecs-tag:hover { color: var(--ic-ecs-fg); }
632
+ .ic-ecs-tag.is-active {
633
+ background: var(--ic-ecs-accent);
634
+ color: var(--ic-ecs-accent-fg);
635
+ border-color: var(--ic-ecs-accent);
636
+ }
637
+ `;
638
+ function StyleTag() {
639
+ useEffect(() => {
640
+ if (typeof document === "undefined") return;
641
+ if (document.getElementById(STYLE_ID)) return;
642
+ const el = document.createElement("style");
643
+ el.id = STYLE_ID;
644
+ el.textContent = CSS;
645
+ document.head.appendChild(el);
646
+ }, []);
647
+ return null;
648
+ }
649
+ //#endregion
650
+ export { EcsDevtools };
651
+
652
+ //# sourceMappingURL=devtools.mjs.map