@vibecook/ice 0.1.0 → 0.3.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 (119) hide show
  1. package/README.md +37 -2
  2. package/dist/chunk-2GUYATUM.js +643 -0
  3. package/dist/chunk-2GUYATUM.js.map +1 -0
  4. package/dist/{chunk-5S7GSXJG.js → chunk-5QY7HK6G.js} +3145 -1306
  5. package/dist/chunk-5QY7HK6G.js.map +1 -0
  6. package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
  7. package/dist/chunk-OR5FNVUE.js.map +1 -0
  8. package/dist/{chunk-FIAXPNNY.js → chunk-PKK6GLSA.js} +143 -551
  9. package/dist/chunk-PKK6GLSA.js.map +1 -0
  10. package/dist/devtools.js +508 -372
  11. package/dist/devtools.js.map +1 -1
  12. package/dist/dom.js +3 -11
  13. package/dist/ground.js +715 -0
  14. package/dist/ground.js.map +1 -0
  15. package/dist/index.js +80 -2
  16. package/dist/kernel.js +31 -1
  17. package/dist/r3f.js +605 -62
  18. package/dist/r3f.js.map +1 -1
  19. package/dist/react.js +36 -418
  20. package/dist/react.js.map +1 -1
  21. package/dist/types/packages/core/src/catalog/camera-derived.d.ts +65 -3
  22. package/dist/types/packages/core/src/catalog/gesture.d.ts +40 -4
  23. package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
  24. package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
  25. package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
  26. package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
  27. package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
  28. package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
  29. package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
  30. package/dist/types/packages/core/src/doc/autosave.d.ts +49 -2
  31. package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
  32. package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
  33. package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
  34. package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
  35. package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
  36. package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
  37. package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
  38. package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
  39. package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
  40. package/dist/types/packages/core/src/engine/engine.d.ts +11 -0
  41. package/dist/types/packages/core/src/engine/frame-control.d.ts +71 -0
  42. package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
  43. package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +79 -6
  44. package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
  45. package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
  46. package/dist/types/packages/core/src/index.d.ts +15 -7
  47. package/dist/types/packages/core/src/input/queue.d.ts +11 -0
  48. package/dist/types/packages/core/src/interaction/gesture-status.d.ts +13 -0
  49. package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
  50. package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
  51. package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
  52. package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
  53. package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
  54. package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
  55. package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
  56. package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
  57. package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
  58. package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
  59. package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
  60. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
  61. package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
  62. package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
  63. package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
  64. package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
  65. package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
  66. package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
  67. package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
  68. package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
  69. package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
  70. package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
  71. package/dist/types/packages/devtools/src/attach.d.ts +77 -0
  72. package/dist/types/packages/devtools/src/dock.d.ts +38 -0
  73. package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
  74. package/dist/types/packages/devtools/src/index.d.ts +8 -6
  75. package/dist/types/packages/dom/src/index.d.ts +1 -3
  76. package/dist/types/packages/dom/src/loop.d.ts +11 -0
  77. package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
  78. package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
  79. package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
  80. package/dist/types/packages/ground/src/index.d.ts +52 -0
  81. package/dist/types/packages/ground/src/layer.d.ts +40 -0
  82. package/dist/types/packages/ground/src/pass.d.ts +41 -0
  83. package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
  84. package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
  85. package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
  86. package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
  87. package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
  88. package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
  89. package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
  90. package/dist/types/packages/ground/src/renderer.d.ts +15 -0
  91. package/dist/types/packages/ice/src/ground.d.ts +1 -0
  92. package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
  93. package/dist/types/packages/kernel/src/coords.d.ts +15 -0
  94. package/dist/types/packages/kernel/src/easing.d.ts +11 -0
  95. package/dist/types/packages/kernel/src/index.d.ts +3 -0
  96. package/dist/types/packages/kernel/src/layout.d.ts +118 -0
  97. package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
  98. package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
  99. package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
  100. package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
  101. package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
  102. package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
  103. package/dist/types/packages/r3f/src/index.d.ts +3 -2
  104. package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
  105. package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
  106. package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
  107. package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
  108. package/dist/types/packages/react/src/engine-context.d.ts +21 -0
  109. package/dist/types/packages/react/src/index.d.ts +4 -2
  110. package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
  111. package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
  112. package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
  113. package/package.json +32 -61
  114. package/dist/chunk-5S7GSXJG.js.map +0 -1
  115. package/dist/chunk-BCJ47SX7.js.map +0 -1
  116. package/dist/chunk-FIAXPNNY.js.map +0 -1
  117. package/dist/types/packages/devtools/src/panel.d.ts +0 -48
  118. package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
  119. package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
package/dist/devtools.js CHANGED
@@ -1,417 +1,553 @@
1
1
  import {
2
- ClaimedBy,
3
2
  CursorVisual,
4
3
  Drag,
5
4
  GesturePhases,
5
+ GestureSuspended,
6
6
  HandleSpec,
7
- LocalPointer,
8
7
  LongPress,
9
8
  Pinch,
10
9
  Pointer,
11
- PointerButtons,
12
- PointerScreen,
13
10
  Port,
14
- Position,
15
11
  PrefabId,
16
- RoutedConnect,
17
- RoutedMarquee,
18
- RoutedMove,
19
- RoutedPan,
20
- RoutedResize,
21
- Size,
12
+ PresencePeer,
22
13
  Tap,
23
- Watches,
24
14
  WheelPan,
25
15
  WheelZoom,
26
- Wire,
27
- defineQuery,
28
- prefabs,
29
- schemaMeta
30
- } from "./chunk-5S7GSXJG.js";
31
- import "./chunk-BCJ47SX7.js";
16
+ Wire
17
+ } from "./chunk-5QY7HK6G.js";
18
+ import "./chunk-OR5FNVUE.js";
32
19
 
33
- // ../devtools/src/panel.ts
34
- var TABS = ["pointers", "planes", "sovereignty", "loop"];
35
- var TAB_LABELS = {
36
- pointers: "POINTERS",
37
- planes: "PLANES",
38
- sovereignty: "SOVEREIGNTY",
39
- loop: "LOOP"
40
- };
41
- var pointerQ = defineQuery([Pointer]);
42
- var prefabIdQ = defineQuery([PrefabId]);
43
- var widgetQ = defineQuery([PrefabId, Position, Size]);
44
- var portQ = defineQuery([Port]);
45
- var wireQ = defineQuery([Wire]);
46
- var handleQ = defineQuery([HandleSpec]);
47
- var cursorQ = defineQuery([CursorVisual]);
48
- var RECOGNIZER_KINDS = [
49
- { label: "Drag", q: defineQuery([Drag]) },
50
- { label: "Tap", q: defineQuery([Tap]) },
51
- { label: "LongPress", q: defineQuery([LongPress]) },
52
- { label: "Pinch", q: defineQuery([Pinch]) },
53
- { label: "WheelPan", q: defineQuery([WheelPan]) },
54
- { label: "WheelZoom", q: defineQuery([WheelZoom]) }
55
- ];
56
- var ROUTE_TAGS = [
57
- { label: "move", tag: RoutedMove },
58
- { label: "resize", tag: RoutedResize },
59
- { label: "connect", tag: RoutedConnect },
60
- { label: "marquee", tag: RoutedMarquee },
61
- { label: "pan", tag: RoutedPan }
62
- ];
63
- var C = {
64
- bg: "#16181d",
65
- panel: "#1c1f26",
66
- border: "#2a2e37",
67
- text: "#cdd3de",
68
- dim: "#8b93a1",
69
- accent: "#5aa9e6",
70
- head: "#0f1115"
71
- };
72
- var BADGE = { doc: "#2f6d43", eligible: "#2a5f87", runtime: "#3a3f49", tag: "#4a3a68" };
73
- function el(doc, tag, style, text) {
74
- const node = doc.createElement(tag);
75
- if (style) Object.assign(node.style, style);
76
- if (text !== void 0) node.textContent = text;
77
- return node;
78
- }
79
- function row(doc, text) {
80
- return el(doc, "div", {
81
- padding: "2px 6px",
82
- borderBottom: `1px solid ${C.border}`,
83
- whiteSpace: "pre-wrap",
84
- wordBreak: "break-word"
85
- }, text);
86
- }
87
- function badge(doc, label, color) {
88
- return el(doc, "span", {
89
- display: "inline-block",
90
- padding: "0 4px",
91
- marginLeft: "4px",
92
- borderRadius: "3px",
93
- background: color,
94
- color: C.text,
95
- fontSize: "9px",
96
- verticalAlign: "middle"
97
- }, label);
98
- }
99
- function heading(doc, text) {
100
- return el(doc, "div", {
101
- padding: "4px 6px",
102
- color: C.dim,
103
- textTransform: "uppercase",
104
- fontSize: "9px",
105
- letterSpacing: "0.06em",
106
- position: "sticky",
107
- top: "0",
108
- background: C.panel
109
- }, text);
110
- }
111
- function empty(doc, text) {
112
- return el(doc, "div", { padding: "8px 6px", color: C.dim, fontStyle: "italic" }, text);
113
- }
114
- function renderPointers({ doc, world }) {
115
- const out = [heading(doc, "pointers")];
116
- const pointers = world.entities(pointerQ);
117
- if (pointers.length === 0) out.push(empty(doc, "no live pointers"));
118
- for (const e of pointers) {
119
- const id = world.readField(e, Pointer, "id") ?? "?";
120
- const device = world.readField(e, Pointer, "device") ?? "?";
121
- const local = world.hasTag(e, LocalPointer) ? "\xB7local" : "";
122
- const sx = world.has(e, PointerScreen) ? world.readField(e, PointerScreen, "x") : void 0;
123
- const sy = world.has(e, PointerScreen) ? world.readField(e, PointerScreen, "y") : void 0;
124
- const btns = world.has(e, PointerButtons) ? world.readField(e, PointerButtons, "buttons") : void 0;
125
- const claim = world.getRelation(e, ClaimedBy);
126
- const at = sx === void 0 ? "" : ` (${fmt(sx)},${fmt(sy ?? 0)})`;
127
- const b = btns ? ` btn=${btns}` : "";
128
- const c = claim === void 0 ? "" : ` \u2192claim #${claim}`;
129
- const r = row(doc, `#${e} ${device}${local} "${id}"${at}${b}${c}`);
130
- r.dataset.row = "pointer";
131
- out.push(r);
132
- }
133
- out.push(heading(doc, "recognizers"));
134
- let anyRec = false;
135
- for (const kind of RECOGNIZER_KINDS) {
136
- for (const e of world.entities(kind.q)) {
137
- anyRec = true;
138
- const phase = GesturePhases.current(world, e) ?? "\u2014";
139
- const watches = world.getRelations(e, Watches);
140
- const claims = world.getReverse(e, ClaimedBy);
141
- const routes = ROUTE_TAGS.filter((rt) => world.hasTag(e, rt.tag)).map((rt) => rt.label);
142
- const w = watches.length ? ` watches:[${watches.map((p) => `#${p}`).join(",")}]` : "";
143
- const cl = claims.length ? ` claims:[${claims.map((p) => `#${p}`).join(",")}]` : "";
144
- const ro = routes.length ? ` routes:${routes.join("+")}` : "";
145
- const r = row(doc, `#${e} ${kind.label} [${phase}]${w}${cl}${ro}`);
146
- r.dataset.row = "recognizer";
147
- out.push(r);
148
- }
20
+ // ../devtools/src/attach.ts
21
+ import {
22
+ attachObserver,
23
+ attachProfiler,
24
+ defaultDescribe
25
+ } from "@vibecook/strata-ecs/tools";
26
+
27
+ // ../devtools/src/dock.ts
28
+ var LS_KEY = "ice-dock:layout";
29
+ var STYLE_ID = "ice-dock-style";
30
+ var MARGIN = 8;
31
+ var KEEP = 90;
32
+ var SLOT_ORDER = ["profiler", "gl", "observer"];
33
+ function loadLayout() {
34
+ try {
35
+ return JSON.parse(localStorage.getItem(LS_KEY) ?? "{}");
36
+ } catch {
37
+ return {};
149
38
  }
150
- if (!anyRec) out.push(empty(doc, "no live recognizers"));
151
- return out;
152
39
  }
153
- function renderPlanes({ doc, world, engine, state }) {
154
- const out = [heading(doc, "reflectors (flushed last frame)")];
155
- const frame = engine.lastFrame();
156
- const flushedNow = new Set(frame?.reflectorsFlushed ?? []);
157
- for (const name of engine.reflectorNames()) state.seenReflectors.add(name);
158
- for (const name of flushedNow) state.seenReflectors.add(name);
159
- if (state.seenReflectors.size === 0) {
160
- out.push(empty(doc, "no reflectors registered"));
161
- }
162
- for (const name of [...state.seenReflectors].sort()) {
163
- const r = row(doc, name);
164
- r.appendChild(
165
- flushedNow.has(name) ? badge(doc, "flushed", BADGE.eligible) : badge(doc, "idle", BADGE.runtime)
166
- );
167
- r.dataset.row = "reflector";
168
- out.push(r);
40
+ function saveLayout(patch) {
41
+ try {
42
+ localStorage.setItem(LS_KEY, JSON.stringify({ ...loadLayout(), ...patch }));
43
+ } catch {
169
44
  }
170
- out.push(heading(doc, "entity counts"));
171
- const counts = [
172
- ["widgets", world.count(widgetQ)],
173
- ["ports", world.count(portQ)],
174
- ["wires", world.count(wireQ)],
175
- ["chrome handles", world.count(handleQ)],
176
- ["cursors", world.count(cursorQ)]
177
- ];
178
- for (const [label, n] of counts) {
179
- const r = row(doc, `${label}: ${n}`);
180
- r.dataset.row = "count";
181
- out.push(r);
182
- }
183
- return out;
184
45
  }
185
- function renderSovereignty(ctx) {
186
- const { doc, world, state } = ctx;
187
- const out = [heading(doc, "prefabs (click a row to inspect)")];
188
- const buckets = /* @__PURE__ */ new Map();
189
- for (const e of world.entities(prefabIdQ)) {
190
- const id = world.readField(e, PrefabId, "id");
191
- if (id === void 0 || id === null) continue;
192
- const b = buckets.get(id);
193
- if (b) b.count++;
194
- else buckets.set(id, { count: 1, first: e });
195
- }
196
- const all = prefabs.all();
197
- if (all.length === 0) out.push(empty(doc, "no prefabs registered"));
198
- for (const p of all) {
199
- const bucket = buckets.get(p.id);
200
- const n = bucket?.count ?? 0;
201
- const r = el(doc, "div", {
202
- padding: "2px 6px",
203
- borderBottom: `1px solid ${C.border}`,
204
- cursor: "pointer",
205
- background: state.selectedPrefab === p.id ? C.head : "transparent"
206
- });
207
- r.textContent = `${p.id} \xD7${n}`;
208
- r.appendChild(badge(doc, p.store, storeColor(p.store)));
209
- r.dataset.row = "prefab";
210
- r.dataset.prefab = p.id;
211
- out.push(r);
212
- }
213
- const sel = state.selectedPrefab;
214
- if (sel !== null) {
215
- const p = prefabs.get(sel);
216
- const bucket = buckets.get(sel);
217
- out.push(heading(doc, `components \u2014 ${sel}`));
218
- if (p === void 0 || bucket === void 0) {
219
- out.push(empty(doc, "no live entity of this type"));
220
- } else {
221
- out.push(...renderEntityCells(ctx, p, bucket.first));
222
- }
223
- }
224
- return out;
46
+ var CSS = `
47
+ .ice-dock { position: fixed; z-index: 99999; width: 420px; min-width: 340px; max-width: 92vw;
48
+ background: rgba(13,17,23,.96); border: 1px solid #30363d; border-radius: 10px;
49
+ color: #e6edf3; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
50
+ box-shadow: 0 8px 28px rgba(0,0,0,.45); overflow: hidden; resize: horizontal; }
51
+ .ice-dock * { box-sizing: border-box; }
52
+ .ice-dock.collapsed { resize: none; }
53
+ .ice-dock.dragging { opacity: .92; cursor: grabbing; }
54
+ .ice-dock-head { display: flex; align-items: center; gap: 7px; padding: 7px 10px; cursor: grab;
55
+ background: rgba(22,27,34,.96); user-select: none; }
56
+ .ice-dock-grip { color: #484f58; }
57
+ .ice-dock-name { font-weight: 700; color: #56d4dd; }
58
+ .ice-dock-btn { background: none; border: none; color: #8b949e; cursor: pointer; font: inherit;
59
+ padding: 2px 6px; margin-left: auto; }
60
+ .ice-dock-btn:hover { color: #e6edf3; }
61
+ .ice-dock-body { max-height: calc(100vh - 96px); overflow-y: auto; }
62
+ .ice-dock.collapsed .ice-dock-body { display: none; }
63
+ .ice-dock-slot { border-top: 1px solid #30363d; }
64
+ .ice-dock-slot:empty { display: none; }
65
+
66
+ /* De-window the hosted tools \u2014 the dock is the window now. */
67
+ .ice-dock .strata-obs, .ice-dock .strata-prof, .ice-dock .ice-gl {
68
+ position: static !important; width: auto !important; min-width: 0 !important;
69
+ border: none !important; border-radius: 0 !important; box-shadow: none !important; }
70
+ .ice-dock .strata-obs { resize: vertical !important; }
71
+ .ice-dock .strata-obs-head { cursor: default; }
72
+ .ice-dock .strata-prof-body, .ice-dock .ice-gl-body { min-width: 0; }
73
+ `;
74
+ function injectStyle(doc) {
75
+ if (doc.getElementById(STYLE_ID) !== null) return;
76
+ const el = doc.createElement("style");
77
+ el.id = STYLE_ID;
78
+ el.textContent = CSS;
79
+ doc.head.appendChild(el);
225
80
  }
226
- function renderEntityCells(ctx, p, e) {
227
- const { doc, world, opts } = ctx;
228
- const out = [];
229
- out.push(row(doc, opts.keyOf ? `entity #${e} key: ${String(opts.keyOf(e))}` : `entity #${e}`));
230
- const durable = p.store === "durable";
231
- for (const c of p.eligible) {
232
- if (!world.has(e, c)) continue;
233
- const name = schemaMeta.component(c)?.name ?? "(component)";
234
- const r = row(doc, name);
235
- if (durable && opts.cellInDoc?.(e, c) === true) r.appendChild(badge(doc, "doc", BADGE.doc));
236
- else if (durable) r.appendChild(badge(doc, "doc?", BADGE.eligible));
237
- else r.appendChild(badge(doc, "rt", BADGE.runtime));
238
- r.dataset.row = "component";
239
- out.push(r);
81
+ function createDock(opts = {}) {
82
+ const container = opts.container ?? document.body;
83
+ const doc = container.ownerDocument;
84
+ const win = doc.defaultView ?? window;
85
+ injectStyle(doc);
86
+ const clampPos = (x, y, w2) => {
87
+ const maxX = win.innerWidth - KEEP;
88
+ const minX = Math.min(MARGIN, MARGIN - (w2 - KEEP));
89
+ const maxY = win.innerHeight - 34;
90
+ return { x: Math.min(Math.max(x, minX), maxX), y: Math.min(Math.max(y, MARGIN), maxY) };
91
+ };
92
+ const root = doc.createElement("div");
93
+ root.className = "ice-dock";
94
+ const head = doc.createElement("div");
95
+ head.className = "ice-dock-head";
96
+ const grip = doc.createElement("span");
97
+ grip.className = "ice-dock-grip";
98
+ grip.setAttribute("aria-hidden", "true");
99
+ grip.textContent = "\u283F";
100
+ const name = doc.createElement("span");
101
+ name.className = "ice-dock-name";
102
+ name.textContent = opts.title ?? "ice devtools";
103
+ const collapse = doc.createElement("button");
104
+ collapse.type = "button";
105
+ collapse.className = "ice-dock-btn";
106
+ collapse.title = "Collapse";
107
+ head.append(grip, name, collapse);
108
+ const body = doc.createElement("div");
109
+ body.className = "ice-dock-body";
110
+ const slots = /* @__PURE__ */ new Map();
111
+ for (const id of SLOT_ORDER) {
112
+ const s = doc.createElement("div");
113
+ s.className = "ice-dock-slot";
114
+ s.dataset.slot = id;
115
+ body.appendChild(s);
116
+ slots.set(id, s);
240
117
  }
241
- return out;
118
+ root.append(head, body);
119
+ container.appendChild(root);
120
+ const l = loadLayout();
121
+ const w = typeof l.w === "number" ? l.w : opts.width ?? 420;
122
+ root.style.width = `${w}px`;
123
+ const corner = opts.corner ?? "tr";
124
+ const fx = corner === "tl" || corner === "bl" ? 16 : win.innerWidth - w - 16;
125
+ const fy = corner === "tl" || corner === "tr" ? 16 : win.innerHeight - 220;
126
+ const p = clampPos(l.x ?? fx, l.y ?? fy, w);
127
+ root.style.left = `${p.x}px`;
128
+ root.style.top = `${p.y}px`;
129
+ let open = l.open ?? true;
130
+ const applyOpen = () => {
131
+ root.classList.toggle("collapsed", !open);
132
+ collapse.textContent = open ? "\u25BE" : "\u25B8";
133
+ };
134
+ applyOpen();
135
+ collapse.addEventListener("click", () => {
136
+ open = !open;
137
+ saveLayout({ open });
138
+ applyOpen();
139
+ });
140
+ let drag = null;
141
+ head.addEventListener("pointerdown", (e) => {
142
+ if (e.target.closest("button") !== null) return;
143
+ const r = root.getBoundingClientRect();
144
+ drag = { id: e.pointerId, sx: e.clientX, sy: e.clientY, ox: r.left, oy: r.top };
145
+ if (typeof head.setPointerCapture === "function") {
146
+ try {
147
+ head.setPointerCapture(e.pointerId);
148
+ } catch {
149
+ }
150
+ }
151
+ root.classList.add("dragging");
152
+ e.preventDefault();
153
+ });
154
+ head.addEventListener("pointermove", (e) => {
155
+ if (drag?.id !== e.pointerId) return;
156
+ const q = clampPos(drag.ox + (e.clientX - drag.sx), drag.oy + (e.clientY - drag.sy), root.offsetWidth);
157
+ root.style.left = `${q.x}px`;
158
+ root.style.top = `${q.y}px`;
159
+ });
160
+ const endDrag = (e) => {
161
+ if (drag?.id !== e.pointerId) return;
162
+ drag = null;
163
+ root.classList.remove("dragging");
164
+ const r = root.getBoundingClientRect();
165
+ saveLayout({ x: r.left, y: r.top });
166
+ };
167
+ head.addEventListener("pointerup", endDrag);
168
+ head.addEventListener("pointercancel", endDrag);
169
+ const onWinResize = () => {
170
+ const r = root.getBoundingClientRect();
171
+ const q = clampPos(r.left, r.top, root.offsetWidth);
172
+ root.style.left = `${q.x}px`;
173
+ root.style.top = `${q.y}px`;
174
+ };
175
+ win.addEventListener("resize", onWinResize);
176
+ const ro = new ResizeObserver(() => {
177
+ saveLayout({ w: root.offsetWidth });
178
+ });
179
+ ro.observe(root);
180
+ let disposed = false;
181
+ return {
182
+ el: root,
183
+ slot(id) {
184
+ return slots.get(id);
185
+ },
186
+ dispose() {
187
+ if (disposed) return;
188
+ disposed = true;
189
+ win.removeEventListener("resize", onWinResize);
190
+ ro.disconnect();
191
+ root.remove();
192
+ }
193
+ };
242
194
  }
243
- function storeColor(store) {
244
- if (store === "durable") return BADGE.doc;
245
- if (store === "runtime") return BADGE.eligible;
246
- return BADGE.tag;
195
+
196
+ // ../devtools/src/gl-panel.ts
197
+ var STYLE_ID2 = "ice-gl-style";
198
+ var CSS2 = `
199
+ .ice-gl { position: fixed; z-index: 99998; background: rgba(13,17,23,.96);
200
+ border: 1px solid #30363d; border-radius: 10px; color: #e6edf3;
201
+ font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
202
+ box-shadow: 0 6px 22px rgba(0,0,0,.45); user-select: none; }
203
+ .ice-gl * { box-sizing: border-box; }
204
+ .ice-gl.tl { top: 10px; left: 10px; } .ice-gl.tr { top: 10px; right: 10px; }
205
+ .ice-gl.bl { bottom: 10px; left: 10px; } .ice-gl.br { bottom: 10px; right: 10px; }
206
+ .ice-gl-head { display: flex; align-items: center; gap: 8px; padding: 6px 9px; cursor: pointer; }
207
+ .ice-gl-name { font-weight: 700; color: #56d4dd; }
208
+ .ice-gl-fps { font-weight: 700; color: #a371f7; min-width: 46px; }
209
+ .ice-gl-gpu { color: #8b949e; min-width: 74px; }
210
+ .ice-gl-caret { color: #8b949e; margin-left: auto; }
211
+ .ice-gl-body { display: none; border-top: 1px solid #21262d; padding: 7px 9px 8px; min-width: 300px; }
212
+ .ice-gl.open .ice-gl-body { display: block; }
213
+ .ice-gl-sect { color: #8b949e; font-weight: 700; padding: 5px 0 2px; }
214
+ .ice-gl-row { display: flex; align-items: center; gap: 7px; padding: 1px 0; white-space: nowrap; }
215
+ .ice-gl-name2 { min-width: 64px; color: #8b949e; }
216
+ .ice-gl-bar { flex: 1; height: 5px; border-radius: 3px; background: #21262d; overflow: hidden; }
217
+ .ice-gl-bar > i { display: block; height: 100%; background: #58a6ff; border-radius: 3px; }
218
+ .ice-gl-stat { color: #e6edf3; margin-left: auto; }
219
+ .ice-gl-dim { color: #8b949e; }
220
+ .ice-gl-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px 10px; padding: 2px 0; }
221
+ .ice-gl-cell b { display: block; color: #e6edf3; font-weight: 700; }
222
+ .ice-gl-cell span { color: #8b949e; }
223
+ .ice-gl-seg { display: flex; height: 7px; border-radius: 3px; overflow: hidden; background: #21262d; margin: 3px 0 2px; }
224
+ .ice-gl-seg > i { display: block; height: 100%; }
225
+ .ice-gl-legend { color: #8b949e; padding-bottom: 2px; }
226
+ `;
227
+ var PHASE_COLORS = {
228
+ hot: "#ff7b72",
229
+ waking: "#58a6ff",
230
+ warm: "#3fb950",
231
+ cold: "#8b949e",
232
+ dormant: "#484f58"
233
+ };
234
+ var SPARK_W = 132;
235
+ var SPARK_H = 26;
236
+ var SPARK_SAMPLES = SPARK_W / 2;
237
+ function injectStyle2(doc) {
238
+ if (doc.getElementById(STYLE_ID2) !== null) return;
239
+ const el = doc.createElement("style");
240
+ el.id = STYLE_ID2;
241
+ el.textContent = CSS2;
242
+ doc.head.appendChild(el);
247
243
  }
248
- function renderLoop({ doc, engine }) {
249
- const frame = engine.lastFrame();
250
- if (frame === void 0) {
251
- return [heading(doc, "loop"), empty(doc, "no telemetry yet \u2014 arm it and step the engine")];
252
- }
253
- const out = [heading(doc, "loop")];
254
- const ran = frame.systems.filter((s) => s.ran).length;
255
- const total = frame.systems.length;
256
- out.push(row(doc, `tick #${frame.tick} ${fmt(frame.totalMicros)}\xB5s ran ${ran}/${total}`));
257
- out.push(heading(doc, "systems (by cost)"));
258
- const sorted = [...frame.systems].sort((a, b) => b.micros - a.micros);
259
- if (sorted.length === 0) out.push(empty(doc, "no systems registered"));
260
- for (const s of sorted) {
261
- const mark = s.ran ? "\u25CF" : "\u25CB";
262
- const r = row(doc, `${mark} ${s.phase}/${s.system} ${fmt(s.micros)}\xB5s`);
263
- r.style.color = s.ran ? C.text : C.dim;
264
- r.dataset.row = "system";
265
- out.push(r);
266
- }
267
- if (frame.phaseFlushMicros.size > 0) {
268
- out.push(heading(doc, "phase flush"));
269
- for (const [phase, micros] of frame.phaseFlushMicros) {
270
- const r = row(doc, `${phase} ${fmt(micros)}\xB5s`);
271
- r.dataset.row = "phase";
272
- out.push(r);
244
+ var fmtInt = (n) => n.toLocaleString();
245
+ var fmtK = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
246
+ var fmtMs = (ms) => `${ms.toFixed(2)}ms`;
247
+ var fmtMB = (b) => `${(b / 1048576).toFixed(0)}MB`;
248
+ function createGlPanel(opts = {}) {
249
+ const container = opts.container ?? document.body;
250
+ const doc = container.ownerDocument;
251
+ const budgetMs = opts.budgetMs ?? 16.7;
252
+ injectStyle2(doc);
253
+ const root = doc.createElement("div");
254
+ root.className = `ice-gl ${opts.corner ?? "bl"}${opts.expanded === true ? " open" : ""}`;
255
+ const head = doc.createElement("div");
256
+ head.className = "ice-gl-head";
257
+ const name = doc.createElement("span");
258
+ name.className = "ice-gl-name";
259
+ name.textContent = "ice gl";
260
+ const fpsEl = doc.createElement("span");
261
+ fpsEl.className = "ice-gl-fps";
262
+ const gpuEl = doc.createElement("span");
263
+ gpuEl.className = "ice-gl-gpu";
264
+ const spark = doc.createElement("canvas");
265
+ spark.width = SPARK_W;
266
+ spark.height = SPARK_H;
267
+ spark.style.display = "block";
268
+ const caret = doc.createElement("span");
269
+ caret.className = "ice-gl-caret";
270
+ caret.textContent = "\u25BE";
271
+ head.append(name, fpsEl, gpuEl, spark, caret);
272
+ head.addEventListener("click", () => root.classList.toggle("open"));
273
+ const body = doc.createElement("div");
274
+ body.className = "ice-gl-body";
275
+ root.append(head, body);
276
+ container.appendChild(root);
277
+ const sect = (label) => {
278
+ const s = doc.createElement("div");
279
+ s.className = "ice-gl-sect";
280
+ s.textContent = label;
281
+ body.appendChild(s);
282
+ };
283
+ const bar = (label) => {
284
+ const row = doc.createElement("div");
285
+ row.className = "ice-gl-row";
286
+ const n = doc.createElement("span");
287
+ n.className = "ice-gl-name2";
288
+ n.textContent = label;
289
+ const b = doc.createElement("div");
290
+ b.className = "ice-gl-bar";
291
+ const fill = doc.createElement("i");
292
+ b.appendChild(fill);
293
+ const stat = doc.createElement("span");
294
+ stat.className = "ice-gl-stat";
295
+ row.append(n, b, stat);
296
+ body.appendChild(row);
297
+ return { fill, stat };
298
+ };
299
+ const textRow = () => {
300
+ const row = doc.createElement("div");
301
+ row.className = "ice-gl-row";
302
+ body.appendChild(row);
303
+ return row;
304
+ };
305
+ sect("frame");
306
+ const cpuBar = bar("cpu");
307
+ const gpuBar = bar("gpu");
308
+ sect("render");
309
+ const grid = doc.createElement("div");
310
+ grid.className = "ice-gl-grid";
311
+ body.appendChild(grid);
312
+ const cell = (label) => {
313
+ const c = doc.createElement("div");
314
+ c.className = "ice-gl-cell";
315
+ const v = doc.createElement("b");
316
+ const l = doc.createElement("span");
317
+ l.textContent = label;
318
+ c.append(v, l);
319
+ grid.appendChild(c);
320
+ return v;
321
+ };
322
+ const callsEl = cell("calls");
323
+ const trisEl = cell("triangles");
324
+ const ptsEl = cell("pts+lines");
325
+ const progsEl = cell("programs");
326
+ const geosEl = cell("geometries");
327
+ const texsEl = cell("textures");
328
+ sect("virtual textures");
329
+ const rtRow = textRow();
330
+ const fboBar = bar("fbo mem");
331
+ const seg = doc.createElement("div");
332
+ seg.className = "ice-gl-seg";
333
+ body.appendChild(seg);
334
+ const legend = doc.createElement("div");
335
+ legend.className = "ice-gl-legend";
336
+ body.appendChild(legend);
337
+ const paintRow = textRow();
338
+ sect("lod");
339
+ const lodRow = textRow();
340
+ const bandsRow = textRow();
341
+ sect("widgets");
342
+ const cullBar = bar("visible");
343
+ const samples = [];
344
+ let latest;
345
+ let maxCpu = 0;
346
+ let maxGpu = 0;
347
+ let evictedTotal = 0;
348
+ let lastRender = 0;
349
+ let disposed = false;
350
+ const drawSpark = () => {
351
+ const ctx = spark.getContext("2d");
352
+ if (ctx === null) return;
353
+ ctx.clearRect(0, 0, SPARK_W, SPARK_H);
354
+ const top = budgetMs * 2;
355
+ for (let i = 0; i < samples.length; i++) {
356
+ const v = samples[i] ?? 0;
357
+ const h = Math.min(SPARK_H, v / top * SPARK_H);
358
+ ctx.fillStyle = v > budgetMs ? "#ff7b72" : "#3fb950";
359
+ ctx.fillRect(i * 2, SPARK_H - h, 1.6, h);
273
360
  }
274
- }
275
- return out;
361
+ ctx.fillStyle = "rgba(139,148,158,.55)";
362
+ ctx.fillRect(0, SPARK_H / 2, SPARK_W, 1);
363
+ };
364
+ const render = () => {
365
+ const s = latest;
366
+ if (s === void 0) return;
367
+ fpsEl.textContent = s.fps > 0 ? `${Math.round(s.fps)} fps` : "\u2013 fps";
368
+ gpuEl.textContent = s.gpuMs > 0 ? `gpu ${fmtMs(s.gpuMs)}` : "gpu \u2013";
369
+ drawSpark();
370
+ if (!root.classList.contains("open")) return;
371
+ cpuBar.fill.style.width = `${Math.min(100, s.cpuMs / budgetMs * 100)}%`;
372
+ cpuBar.stat.textContent = `${fmtMs(s.cpuMs)} \xB7 max ${fmtMs(maxCpu)}`;
373
+ gpuBar.fill.style.width = `${Math.min(100, s.gpuMs / budgetMs * 100)}%`;
374
+ gpuBar.fill.style.background = s.gpuMs > budgetMs ? "#ff7b72" : "#58a6ff";
375
+ gpuBar.stat.textContent = `${fmtMs(s.gpuMs)} \xB7 max ${fmtMs(maxGpu)}`;
376
+ callsEl.textContent = fmtInt(s.drawCalls);
377
+ trisEl.textContent = fmtK(s.triangles);
378
+ ptsEl.textContent = fmtK(s.points + s.lines);
379
+ progsEl.textContent = fmtInt(s.programs);
380
+ geosEl.textContent = fmtInt(s.geometries);
381
+ texsEl.textContent = fmtInt(s.textures);
382
+ rtRow.textContent = `${s.renderTargets} targets \xB7 ${s.renderMegaPixels.toFixed(1)} MP total`;
383
+ fboBar.fill.style.width = `${Math.min(100, s.fboBytes / s.fboBudgetBytes * 100)}%`;
384
+ fboBar.fill.style.background = s.fboBytes > s.fboBudgetBytes * 0.85 ? "#ffa657" : "#58a6ff";
385
+ fboBar.stat.textContent = `${fmtMB(s.fboBytes)} / ${fmtMB(s.fboBudgetBytes)}`;
386
+ seg.textContent = "";
387
+ const phases = [
388
+ ["hot", s.islands.hot],
389
+ ["waking", s.islands.waking],
390
+ ["warm", s.islands.warm],
391
+ ["cold", s.islands.cold],
392
+ ["dormant", s.islands.dormant]
393
+ ];
394
+ const denom = Math.max(1, s.islands.total);
395
+ const legendParts = [];
396
+ for (const [phase, count] of phases) {
397
+ if (count === 0) continue;
398
+ const i = doc.createElement("i");
399
+ i.style.width = `${count / denom * 100}%`;
400
+ i.style.background = PHASE_COLORS[phase] ?? "#8b949e";
401
+ seg.appendChild(i);
402
+ legendParts.push(`${count} ${phase}`);
403
+ }
404
+ legend.textContent = legendParts.length > 0 ? legendParts.join(" \xB7 ") : "no islands";
405
+ paintRow.textContent = `repainted ${s.repainted}/frame \xB7 pending ${s.pendingPaints} \xB7 evicted \u03A3${evictedTotal}`;
406
+ lodRow.textContent = `zoom ${(s.zoom * 100).toFixed(0)}% \u2192 band \xD7${s.band} \u2192 paint dpr ${s.effectiveDpr.toFixed(2)}`;
407
+ const bands = Object.entries(s.bandHistogram).sort((a, b) => a[0].localeCompare(b[0]));
408
+ bandsRow.textContent = bands.length > 0 ? `targets by band: ${bands.map(([k, v]) => `${k}: ${v}`).join(" \xB7 ")}` : "no live targets";
409
+ const pop = Math.max(1, s.visibleWidgets + s.culledWidgets);
410
+ cullBar.fill.style.width = `${s.visibleWidgets / pop * 100}%`;
411
+ cullBar.fill.style.background = "#3fb950";
412
+ cullBar.stat.textContent = `${s.visibleWidgets} visible \xB7 ${s.culledWidgets} culled`;
413
+ };
414
+ return {
415
+ push(s) {
416
+ if (disposed) return;
417
+ latest = s;
418
+ maxCpu = Math.max(maxCpu, s.cpuMs);
419
+ maxGpu = Math.max(maxGpu, s.gpuMs);
420
+ evictedTotal += s.evicted;
421
+ samples.push(s.gpuMs > 0 ? s.gpuMs : s.cpuMs);
422
+ if (samples.length > SPARK_SAMPLES) samples.splice(0, samples.length - SPARK_SAMPLES);
423
+ const now = performance.now();
424
+ if (now - lastRender >= 120) {
425
+ lastRender = now;
426
+ render();
427
+ }
428
+ },
429
+ dispose() {
430
+ if (disposed) return;
431
+ disposed = true;
432
+ root.remove();
433
+ }
434
+ };
276
435
  }
277
- var RENDERERS = {
278
- pointers: renderPointers,
279
- planes: renderPlanes,
280
- sovereignty: renderSovereignty,
281
- loop: renderLoop
436
+
437
+ // ../devtools/src/attach.ts
438
+ var COLOR = {
439
+ widget: "#3fb950",
440
+ gesture: "#a371f7",
441
+ pointer: "#58a6ff",
442
+ graph: "#ffa657",
443
+ presence: "#f778ba",
444
+ chrome: "#8b949e"
282
445
  };
283
- function fmt(n) {
284
- return Number.isInteger(n) ? String(n) : n.toFixed(1);
446
+ var PHASE_ORDER = ["Active", "Possible", "Recognized", "Pending", "Ended", "Cancelled", "Failed"];
447
+ function phaseOf(world, e) {
448
+ if (world.hasTag(e, GestureSuspended)) return "suspended";
449
+ for (const name of PHASE_ORDER) {
450
+ if (world.hasTag(e, GesturePhases.tags[name])) return name.toLowerCase();
451
+ }
452
+ return null;
453
+ }
454
+ function engineDescribe(world, e) {
455
+ const prefab = world.get(e, PrefabId)?.id;
456
+ if (typeof prefab === "string") return { label: prefab, color: COLOR.widget };
457
+ const kind = world.has(e, Tap) ? "tap" : world.has(e, LongPress) ? "longpress" : world.has(e, Pinch) ? "pinch" : world.has(e, WheelZoom) ? "wheelzoom" : world.has(e, WheelPan) ? "wheelpan" : world.has(e, Drag) ? "drag" : void 0;
458
+ if (kind !== void 0) return { label: kind, color: COLOR.gesture, phase: phaseOf(world, e) };
459
+ if (world.has(e, Pointer)) return { label: `pointer:${world.read(e, Pointer).device}`, color: COLOR.pointer };
460
+ if (world.has(e, Port)) return { label: "port", color: COLOR.graph };
461
+ if (world.hasTag(e, Wire)) return { label: "wire", color: COLOR.graph };
462
+ if (world.hasTag(e, PresencePeer)) return { label: "peer", color: COLOR.presence };
463
+ if (world.has(e, CursorVisual)) return { label: "cursor", color: COLOR.presence };
464
+ if (world.has(e, HandleSpec)) return { label: "handle", color: COLOR.chrome };
465
+ return defaultDescribe(world, e);
285
466
  }
286
467
  function attachDevtools(engine, opts = {}) {
287
- const world = engine.world;
288
- const container = opts.container ?? document.body;
289
- const doc = container.ownerDocument ?? document;
290
- if (opts.telemetry !== false) engine.enableTelemetry();
291
- const state = {
292
- active: "pointers",
293
- collapsed: false,
294
- selectedPrefab: null,
295
- seenReflectors: /* @__PURE__ */ new Set()
468
+ const isFacade = "docs" in engine;
469
+ const core = isFacade ? engine.engine : engine;
470
+ const world = core.world;
471
+ const docs = isFacade ? engine.docs : void 0;
472
+ const durable = docs === void 0 ? void 0 : () => {
473
+ const s = docs.current();
474
+ if (s === void 0) return null;
475
+ return {
476
+ store: s.store,
477
+ attachment: s.attachment
478
+ };
296
479
  };
297
- let detached = false;
298
- const root = el(doc, "div", {
299
- position: "fixed",
300
- right: "12px",
301
- bottom: "12px",
302
- width: "360px",
303
- maxHeight: "70vh",
304
- display: "flex",
305
- flexDirection: "column",
306
- overflow: "hidden",
307
- background: C.bg,
308
- color: C.text,
309
- font: "11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace",
310
- border: `1px solid ${C.border}`,
311
- borderRadius: "6px",
312
- boxShadow: "0 6px 24px rgba(0,0,0,0.4)",
313
- zIndex: "2147483000"
314
- });
315
- root.dataset.iceDevtools = "";
316
- const header = el(doc, "div", {
317
- display: "flex",
318
- alignItems: "center",
319
- justifyContent: "space-between",
320
- padding: "4px 8px",
321
- background: C.head,
322
- borderBottom: `1px solid ${C.border}`,
323
- cursor: "default",
324
- userSelect: "none"
325
- });
326
- header.appendChild(el(doc, "span", { color: C.accent, fontWeight: "600" }, "ice devtools"));
327
- const collapseBtn = el(doc, "button", {
328
- background: "transparent",
329
- border: "none",
330
- color: C.text,
331
- cursor: "pointer",
332
- font: "inherit",
333
- padding: "0 4px"
334
- }, "\u25BE");
335
- collapseBtn.dataset.collapse = "";
336
- header.appendChild(collapseBtn);
337
- root.appendChild(header);
338
- const tabBar = el(doc, "div", {
339
- display: "flex",
340
- borderBottom: `1px solid ${C.border}`,
341
- background: C.panel
342
- });
343
- const tabButtons = /* @__PURE__ */ new Map();
344
- for (const id of TABS) {
345
- const btn = el(doc, "button", {
346
- flex: "1",
347
- padding: "4px 2px",
348
- background: "transparent",
349
- border: "none",
350
- borderRight: `1px solid ${C.border}`,
351
- color: C.dim,
352
- cursor: "pointer",
353
- font: "inherit",
354
- fontSize: "9px",
355
- letterSpacing: "0.04em"
356
- }, TAB_LABELS[id]);
357
- btn.dataset.tab = id;
358
- btn.addEventListener("click", () => {
359
- state.active = id;
360
- render();
361
- });
362
- tabButtons.set(id, btn);
363
- tabBar.appendChild(btn);
364
- }
365
- root.appendChild(tabBar);
366
- const content = el(doc, "div", { flex: "1", overflowY: "auto", overflowX: "hidden" });
367
- root.appendChild(content);
368
- content.addEventListener("click", (ev) => {
369
- const target = ev.target;
370
- const prefabRow = target?.closest("[data-prefab]");
371
- if (prefabRow?.dataset.prefab) {
372
- state.selectedPrefab = prefabRow.dataset.prefab;
373
- render();
480
+ const presence = opts.presence;
481
+ const ephemeral = presence === void 0 ? void 0 : () => presence()?.eph ?? null;
482
+ let dock = null;
483
+ const mountIn = (id) => {
484
+ if (opts.dock === false) return opts.container !== void 0 ? { container: opts.container } : {};
485
+ if (dock === null) {
486
+ dock = createDock({
487
+ ...typeof opts.dock === "object" ? opts.dock : {},
488
+ ...opts.container !== void 0 ? { container: opts.container } : {}
489
+ });
374
490
  }
491
+ return { container: dock.slot(id) };
492
+ };
493
+ const observer = opts.observer === false ? null : attachObserver(world, {
494
+ ...typeof opts.observer === "object" ? opts.observer : {},
495
+ ...mountIn("observer"),
496
+ describe: opts.describe ?? engineDescribe,
497
+ ...durable !== void 0 ? { durable } : {},
498
+ ...ephemeral !== void 0 ? { ephemeral } : {}
375
499
  });
376
- collapseBtn.addEventListener("click", () => {
377
- state.collapsed = !state.collapsed;
378
- render();
500
+ const profiler = opts.profiler === false ? null : attachProfiler(world, {
501
+ ...typeof opts.profiler === "object" ? opts.profiler : {},
502
+ ...mountIn("profiler")
379
503
  });
380
- const ctx = { doc, world, engine, opts, state };
381
- function render() {
382
- if (detached) return;
383
- for (const [id, btn] of tabButtons) {
384
- const on = id === state.active && !state.collapsed;
385
- btn.style.color = on ? C.text : C.dim;
386
- btn.style.background = on ? C.bg : "transparent";
387
- }
388
- collapseBtn.textContent = state.collapsed ? "\u25B8" : "\u25BE";
389
- if (state.collapsed) {
390
- tabBar.style.display = "none";
391
- content.style.display = "none";
392
- return;
393
- }
394
- tabBar.style.display = "flex";
395
- content.style.display = "block";
396
- content.replaceChildren(...RENDERERS[state.active](ctx));
504
+ let removePublish;
505
+ if (profiler !== null) {
506
+ core.enableTelemetry();
507
+ removePublish = core.onPublish(() => {
508
+ const us = core.lastFrame()?.phaseFlushMicros.get("reflect");
509
+ if (us !== void 0 && us > 0) profiler.lane("reflect", us / 1e3);
510
+ });
397
511
  }
398
- container.appendChild(root);
399
- render();
400
- const timer = setInterval(render, opts.intervalMs ?? 250);
512
+ let glPanel = null;
513
+ let detached = false;
401
514
  return {
515
+ observer,
516
+ profiler,
517
+ lane(name, ms) {
518
+ profiler?.lane(name, ms);
519
+ },
520
+ glStats(stats) {
521
+ if (detached || opts.glPanel === false) return;
522
+ if (glPanel === null) {
523
+ glPanel = createGlPanel({
524
+ ...typeof opts.glPanel === "object" ? opts.glPanel : {},
525
+ ...mountIn("gl")
526
+ });
527
+ }
528
+ glPanel.push(stats);
529
+ },
402
530
  detach() {
403
531
  if (detached) return;
404
532
  detached = true;
405
- clearInterval(timer);
406
- root.remove();
533
+ removePublish?.();
534
+ observer?.dispose();
535
+ profiler?.dispose();
536
+ glPanel?.dispose();
537
+ glPanel = null;
538
+ dock?.dispose();
539
+ dock = null;
407
540
  }
408
541
  };
409
542
  }
410
543
 
411
544
  // ../devtools/src/index.ts
412
- var DEVTOOLS_VERSION = "0.0.0";
545
+ var DEVTOOLS_VERSION = "0.1.0";
413
546
  export {
414
547
  DEVTOOLS_VERSION,
415
- attachDevtools
548
+ attachDevtools,
549
+ createDock,
550
+ createGlPanel,
551
+ engineDescribe
416
552
  };
417
553
  //# sourceMappingURL=devtools.js.map