@fluixi/devtools 0.2.0-alpha.2 → 0.2.0-alpha.4

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 (158) hide show
  1. package/README.md +80 -2
  2. package/dist/callsite.cjs +72 -0
  3. package/dist/callsite.d.ts +22 -0
  4. package/dist/callsite.d.ts.map +1 -0
  5. package/dist/callsite.js +82 -0
  6. package/dist/callsite.mjs +51 -0
  7. package/dist/change-view.cjs +134 -0
  8. package/dist/change-view.d.ts +10 -0
  9. package/dist/change-view.d.ts.map +1 -0
  10. package/dist/change-view.js +102 -0
  11. package/dist/change-view.mjs +111 -0
  12. package/dist/copy.cjs +63 -0
  13. package/dist/copy.d.ts +9 -0
  14. package/dist/copy.d.ts.map +1 -0
  15. package/dist/copy.js +52 -0
  16. package/dist/copy.mjs +42 -0
  17. package/dist/describe.cjs +55 -0
  18. package/dist/describe.d.ts +3 -0
  19. package/dist/describe.d.ts.map +1 -0
  20. package/dist/describe.js +47 -0
  21. package/dist/describe.mjs +34 -0
  22. package/dist/detail-panel.cjs +307 -0
  23. package/dist/detail-panel.d.ts +17 -0
  24. package/dist/detail-panel.d.ts.map +1 -0
  25. package/dist/detail-panel.js +217 -0
  26. package/dist/detail-panel.mjs +284 -0
  27. package/dist/diff.cjs +225 -0
  28. package/dist/diff.d.ts +31 -0
  29. package/dist/diff.d.ts.map +1 -0
  30. package/dist/diff.js +132 -0
  31. package/dist/diff.mjs +202 -0
  32. package/dist/dom.cjs +204 -0
  33. package/dist/dom.d.ts +81 -0
  34. package/dist/dom.d.ts.map +1 -0
  35. package/dist/dom.js +269 -0
  36. package/dist/dom.mjs +183 -0
  37. package/dist/export.cjs +221 -0
  38. package/dist/export.d.ts +55 -0
  39. package/dist/export.d.ts.map +1 -0
  40. package/dist/export.js +90 -0
  41. package/dist/export.mjs +209 -0
  42. package/dist/float.cjs +232 -0
  43. package/dist/float.d.ts +54 -0
  44. package/dist/float.d.ts.map +1 -0
  45. package/dist/float.js +256 -0
  46. package/dist/float.mjs +211 -0
  47. package/dist/format.cjs +175 -0
  48. package/dist/format.d.ts +16 -0
  49. package/dist/format.d.ts.map +1 -0
  50. package/dist/format.js +188 -0
  51. package/dist/format.mjs +154 -0
  52. package/dist/graph-view.cjs +893 -0
  53. package/dist/graph-view.d.ts +79 -2
  54. package/dist/graph-view.d.ts.map +1 -1
  55. package/dist/graph-view.js +866 -50
  56. package/dist/graph-view.mjs +870 -0
  57. package/dist/highlight.cjs +140 -0
  58. package/dist/highlight.d.ts +56 -0
  59. package/dist/highlight.d.ts.map +1 -0
  60. package/dist/highlight.js +154 -0
  61. package/dist/highlight.mjs +119 -0
  62. package/dist/history.cjs +95 -0
  63. package/dist/history.d.ts +59 -0
  64. package/dist/history.d.ts.map +1 -0
  65. package/dist/history.js +85 -0
  66. package/dist/history.mjs +74 -0
  67. package/dist/hook.cjs +1562 -0
  68. package/dist/hook.d.ts +60 -0
  69. package/dist/hook.d.ts.map +1 -0
  70. package/dist/hook.js +142 -0
  71. package/dist/hook.mjs +1553 -0
  72. package/dist/index.cjs +4552 -0
  73. package/dist/index.d.ts +44 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +25 -1
  76. package/dist/index.mjs +4544 -0
  77. package/dist/inspector.cjs +688 -0
  78. package/dist/inspector.d.ts +60 -0
  79. package/dist/inspector.d.ts.map +1 -0
  80. package/dist/inspector.js +415 -0
  81. package/dist/inspector.mjs +676 -0
  82. package/dist/instrument.cjs +497 -0
  83. package/dist/instrument.d.ts +147 -5
  84. package/dist/instrument.d.ts.map +1 -1
  85. package/dist/instrument.js +164 -44
  86. package/dist/instrument.mjs +487 -0
  87. package/dist/kind-filter.cjs +172 -0
  88. package/dist/kind-filter.d.ts +39 -0
  89. package/dist/kind-filter.d.ts.map +1 -0
  90. package/dist/kind-filter.js +151 -0
  91. package/dist/kind-filter.mjs +151 -0
  92. package/dist/menu.cjs +141 -0
  93. package/dist/menu.d.ts +37 -0
  94. package/dist/menu.d.ts.map +1 -0
  95. package/dist/menu.js +125 -0
  96. package/dist/menu.mjs +120 -0
  97. package/dist/observe.cjs +1352 -0
  98. package/dist/observe.d.ts +80 -0
  99. package/dist/observe.d.ts.map +1 -0
  100. package/dist/observe.js +893 -0
  101. package/dist/observe.mjs +1345 -0
  102. package/dist/palette.cjs +52 -0
  103. package/dist/palette.d.ts +11 -0
  104. package/dist/palette.d.ts.map +1 -0
  105. package/dist/palette.js +27 -0
  106. package/dist/palette.mjs +31 -0
  107. package/dist/path.cjs +45 -0
  108. package/dist/path.d.ts +19 -0
  109. package/dist/path.d.ts.map +1 -0
  110. package/dist/path.js +49 -0
  111. package/dist/path.mjs +24 -0
  112. package/dist/plugin.cjs +87 -0
  113. package/dist/plugin.d.ts +121 -0
  114. package/dist/plugin.d.ts.map +1 -0
  115. package/dist/plugin.js +74 -0
  116. package/dist/plugin.mjs +66 -0
  117. package/dist/plugins/context.cjs +98 -0
  118. package/dist/plugins/context.d.ts +10 -0
  119. package/dist/plugins/context.d.ts.map +1 -0
  120. package/dist/plugins/context.js +80 -0
  121. package/dist/plugins/context.mjs +75 -0
  122. package/dist/plugins/directives.cjs +98 -0
  123. package/dist/plugins/directives.d.ts +25 -0
  124. package/dist/plugins/directives.d.ts.map +1 -0
  125. package/dist/plugins/directives.js +78 -0
  126. package/dist/plugins/directives.mjs +75 -0
  127. package/dist/plugins/index.cjs +162 -0
  128. package/dist/plugins/index.d.ts +7 -0
  129. package/dist/plugins/index.d.ts.map +1 -0
  130. package/dist/plugins/index.js +9 -0
  131. package/dist/plugins/index.mjs +140 -0
  132. package/dist/source.cjs +111 -0
  133. package/dist/source.d.ts +72 -0
  134. package/dist/source.d.ts.map +1 -0
  135. package/dist/source.js +153 -0
  136. package/dist/source.mjs +90 -0
  137. package/dist/timeline.cjs +350 -0
  138. package/dist/timeline.d.ts +30 -0
  139. package/dist/timeline.d.ts.map +1 -0
  140. package/dist/timeline.js +271 -0
  141. package/dist/timeline.mjs +327 -0
  142. package/dist/tree.cjs +574 -0
  143. package/dist/tree.d.ts +128 -0
  144. package/dist/tree.d.ts.map +1 -0
  145. package/dist/tree.js +645 -0
  146. package/dist/tree.mjs +551 -0
  147. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  148. package/dist/value-pane.cjs +223 -0
  149. package/dist/value-pane.d.ts +10 -0
  150. package/dist/value-pane.d.ts.map +1 -0
  151. package/dist/value-pane.js +61 -0
  152. package/dist/value-pane.mjs +200 -0
  153. package/dist/wire.cjs +109 -0
  154. package/dist/wire.d.ts +100 -0
  155. package/dist/wire.d.ts.map +1 -0
  156. package/dist/wire.js +93 -0
  157. package/dist/wire.mjs +89 -0
  158. package/package.json +17 -5
package/dist/tree.cjs ADDED
@@ -0,0 +1,574 @@
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/tree.ts
21
+ var tree_exports = {};
22
+ __export(tree_exports, {
23
+ TREE_CSS: () => TREE_CSS,
24
+ TREE_KINDS: () => TREE_KINDS,
25
+ componentTree: () => componentTree,
26
+ createComponentTree: () => createComponentTree,
27
+ idsIn: () => idsIn,
28
+ inPageOrder: () => inPageOrder
29
+ });
30
+ module.exports = __toCommonJS(tree_exports);
31
+
32
+ // src/plugin.ts
33
+ var plugins = [];
34
+ function pluginElementBadge(extra) {
35
+ if (!extra) return void 0;
36
+ for (const plugin of plugins) {
37
+ try {
38
+ const badge = plugin.elementBadge?.(extra);
39
+ if (badge) return badge;
40
+ } catch {
41
+ }
42
+ }
43
+ return void 0;
44
+ }
45
+ function pluginBadge(node) {
46
+ for (const plugin of plugins) {
47
+ try {
48
+ const badge = plugin.badge?.(node);
49
+ if (badge) return badge;
50
+ } catch {
51
+ }
52
+ }
53
+ return void 0;
54
+ }
55
+
56
+ // src/palette.ts
57
+ var KIND_COLOR = {
58
+ state: "#34d399",
59
+ memo: "#a78bfa",
60
+ effect: "#fbbf24",
61
+ store: "#38bdf8",
62
+ resource: "#fb7185",
63
+ component: "#f0abfc",
64
+ provider: "#fb923c",
65
+ control: "#22d3ee",
66
+ router: "#818cf8",
67
+ handler: "#f472b6",
68
+ context: "#a3e635",
69
+ directive: "#5eead4",
70
+ binding: "#5eead4",
71
+ setter: "#fb7185",
72
+ group: "#8a90a2",
73
+ element: "#8a90a2"
74
+ };
75
+ var kindVar = (kind) => `--fx-kind-${kind}`;
76
+ var kindColor = (kind) => `var(${kindVar(kind)}, ${KIND_COLOR[kind]})`;
77
+ var KINDS = Object.keys(KIND_COLOR);
78
+ function kindRules(selector, property) {
79
+ return KINDS.map((kind) => `${selector(kind)} { ${property}: ${kindColor(kind)}; }`).join("\n");
80
+ }
81
+
82
+ // src/format.ts
83
+ function shorten(text, limit = 18) {
84
+ return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;
85
+ }
86
+
87
+ // src/tree.ts
88
+ var TREE_KINDS = /* @__PURE__ */ new Set([
89
+ "component",
90
+ "control",
91
+ "router",
92
+ "handler",
93
+ // A component a plugin recognised as providing a context. Still a tag the author wrote,
94
+ // so leaving it out took it and everything it rendered out of the tree.
95
+ "provider"
96
+ ]);
97
+ function inPageOrder(nodes) {
98
+ const slots = [];
99
+ const placed = [];
100
+ nodes.forEach((node, at) => {
101
+ if (node.order !== void 0) {
102
+ slots.push(at);
103
+ placed.push(node);
104
+ }
105
+ });
106
+ if (placed.length < 2) return nodes;
107
+ placed.sort((a, b) => a.order - b.order);
108
+ const out = nodes.slice();
109
+ slots.forEach((slot, i) => out[slot] = placed[i]);
110
+ return out;
111
+ }
112
+ var badges = /* @__PURE__ */ new WeakMap();
113
+ function badgeOf(node) {
114
+ if (!badges.has(node)) badges.set(node, pluginBadge(node));
115
+ return badges.get(node);
116
+ }
117
+ function componentTree(graph, options = {}) {
118
+ const wanted = (node) => TREE_KINDS.has(node.kind) || options.values === true && node.authored !== false;
119
+ const tagged = inPageOrder([...graph.values()].filter(wanted).sort((a, b) => a.id - b.id));
120
+ const made = /* @__PURE__ */ new Map();
121
+ for (const node of tagged) {
122
+ made.set(node.id, {
123
+ id: node.id,
124
+ label: node.label,
125
+ kind: node.kind,
126
+ children: [],
127
+ ...TREE_KINDS.has(node.kind) ? {} : { value: true },
128
+ ...node.lazy ? { lazy: true } : {},
129
+ ...badgeOf(node) ? { badge: badgeOf(node) } : {}
130
+ });
131
+ }
132
+ const owner = (node) => {
133
+ const seen = /* @__PURE__ */ new Set([node.id]);
134
+ let at = node.createdIn;
135
+ while (at !== void 0 && !seen.has(at)) {
136
+ seen.add(at);
137
+ const found = made.get(at);
138
+ if (found) return found;
139
+ at = graph.get(at)?.createdIn;
140
+ }
141
+ return void 0;
142
+ };
143
+ const roots = [];
144
+ for (const node of tagged) {
145
+ const parent = owner(node);
146
+ if (parent) parent.children.push(made.get(node.id));
147
+ else roots.push(made.get(node.id));
148
+ }
149
+ const term = options.filter?.trim().toLowerCase();
150
+ return term ? keep(roots, term) : roots;
151
+ }
152
+ function keep(nodes, term) {
153
+ const out = [];
154
+ for (const node of nodes) {
155
+ const children = keep(node.children, term);
156
+ const hit = node.label.toLowerCase().includes(term);
157
+ if (hit || children.length) out.push({ ...node, children: hit ? node.children : children });
158
+ }
159
+ return out;
160
+ }
161
+ function idsIn(nodes) {
162
+ const out = [];
163
+ const walk = (list) => {
164
+ for (const node of list) {
165
+ out.push(node.id);
166
+ walk(node.children);
167
+ }
168
+ };
169
+ walk(nodes);
170
+ return out;
171
+ }
172
+ var escape = (text) => text.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
173
+ function createComponentTree(host, options = {}) {
174
+ const empty = options.empty ?? "nothing rendered yet";
175
+ const shut = /* @__PURE__ */ new Set();
176
+ let signature = "";
177
+ const defaulted = /* @__PURE__ */ new Set();
178
+ let marked = null;
179
+ let markedKey = null;
180
+ let values = options.values === true;
181
+ let filter = "";
182
+ const doc = host.ownerDocument;
183
+ const bar = doc.createElement("label");
184
+ bar.className = "fx-tree-bar";
185
+ const box = doc.createElement("input");
186
+ box.type = "checkbox";
187
+ box.checked = values;
188
+ bar.append(box, doc.createTextNode("values"));
189
+ bar.title = "List the signals and memos each tag declares";
190
+ const search = doc.createElement("input");
191
+ search.type = "search";
192
+ search.className = "fx-tree-search";
193
+ search.placeholder = "filter by name";
194
+ search.addEventListener("input", () => {
195
+ filter = search.value;
196
+ signature = "";
197
+ });
198
+ const list = doc.createElement("div");
199
+ list.className = "fx-tree-list";
200
+ host.replaceChildren(bar, search, list);
201
+ let last = null;
202
+ box.addEventListener("change", () => {
203
+ values = box.checked;
204
+ signature = "";
205
+ if (last) api.render(last.graph, last.selected);
206
+ });
207
+ const row = (node, depth) => {
208
+ const has = node.children.length > 0;
209
+ const open = has && !shut.has(node.key);
210
+ const twist = has ? open ? "▾" : "▸" : node.prop ? "" : "·";
211
+ const tag = node.props ? `<span class="fx-tree-props-label">props</span><span class="fx-tree-props-count">${node.children.length}</span>` : node.prop ? `<span class="fx-tree-prop-name">${escape(node.prop.name)}</span>${node.prop.value === void 0 ? "" : `<span class="fx-tree-prop-value">${escape(shorten(node.prop.value, 44))}</span>`}` : node.value ? escape(node.label) : has ? `&lt;${escape(node.label)}&gt;` : `&lt;${escape(node.label)} /&gt;`;
212
+ const count = has && !open && !node.props ? `<span class="fx-tree-count">${node.children.length}</span>` : "";
213
+ const chunk = node.lazy ? '<span class="fx-tree-lazy" title="loaded as its own chunk">lazy</span>' : "";
214
+ const mark = node.badge ? `<span class="fx-tree-plugin" title="${escape(node.badge)}">${escape(node.badge)}</span>` : "";
215
+ return `<div class="fx-tree-row n-${node.kind}${node.value ? " is-value" : ""}${node.prop ? " is-prop" : ""}${node.props ? " is-props" : ""}" data-key="${escape(node.key)}"${node.id !== void 0 ? ` data-id="${node.id}"` : ""} style="--depth:${depth}"><button type="button" class="fx-tree-twist" data-twist="${escape(node.key)}"${has ? "" : ' tabindex="-1"'}>${twist}</button><span class="fx-tree-tag"${node.prop?.value ? ` title="${escape(node.prop.name)}=${escape(node.prop.value)}"` : ""}>${tag}</span>${mark}${chunk}${count}</div>` + (open ? node.children.map((child) => row(child, depth + 1)).join("") : "");
216
+ };
217
+ const pickOf = (rowEl) => {
218
+ const found = shown.get(rowEl.dataset.key ?? "");
219
+ if (!found || found.kind !== "element") return void 0;
220
+ return {
221
+ key: found.key,
222
+ label: found.label,
223
+ ...found.owner !== void 0 ? { owner: found.owner } : {},
224
+ ...found.writes?.length ? { writes: found.writes } : {},
225
+ ...found.at ? { at: found.at } : {},
226
+ ...found.extra ? { extra: found.extra } : {},
227
+ ...found.element ? { element: found.element } : {}
228
+ };
229
+ };
230
+ const onClick = (event) => {
231
+ const target = event.target;
232
+ const twist = target?.closest?.("[data-twist]");
233
+ if (twist) {
234
+ const key = twist.dataset.twist ?? "";
235
+ if (shut.has(key)) shut.delete(key);
236
+ else shut.add(key);
237
+ signature = "";
238
+ event.stopPropagation();
239
+ return;
240
+ }
241
+ const rowEl = target?.closest?.(".fx-tree-row");
242
+ if (!rowEl) return;
243
+ if (rowEl.dataset.id) {
244
+ markedKey = null;
245
+ options.onSelect?.(Number(rowEl.dataset.id));
246
+ return;
247
+ }
248
+ const held = shown.get(rowEl.dataset.key ?? "");
249
+ if (held?.props) {
250
+ if (shut.has(held.key)) shut.delete(held.key);
251
+ else shut.add(held.key);
252
+ signature = "";
253
+ if (last) api.render(last.graph, last.selected);
254
+ return;
255
+ }
256
+ if (held?.prop) {
257
+ if (held.owner === void 0) return;
258
+ markedKey = held.key;
259
+ paint();
260
+ options.onSelect?.(held.owner);
261
+ return;
262
+ }
263
+ const pick = pickOf(rowEl);
264
+ if (!pick) return;
265
+ markedKey = pick.key;
266
+ paint();
267
+ options.onPick?.(pick);
268
+ };
269
+ const onDouble = (event) => {
270
+ const rowEl = event.target?.closest?.(".fx-tree-row");
271
+ if (!rowEl) return;
272
+ if (rowEl.dataset.id) {
273
+ options.onReveal?.(Number(rowEl.dataset.id));
274
+ return;
275
+ }
276
+ const pick = pickOf(rowEl);
277
+ if (pick) options.onRevealPick?.(pick);
278
+ };
279
+ let shown = /* @__PURE__ */ new Map();
280
+ const onOver = (event) => {
281
+ const rowEl = event.target?.closest?.(".fx-tree-row");
282
+ const found = rowEl ? shown.get(rowEl.dataset.key ?? "") : void 0;
283
+ if (!found) {
284
+ options.onHover?.(null);
285
+ return;
286
+ }
287
+ options.onHover?.(found.id ?? null, found.element, found.id === void 0 ? found.key : void 0);
288
+ };
289
+ const onOut = () => options.onHover?.(null);
290
+ host.addEventListener("click", onClick);
291
+ host.addEventListener("dblclick", onDouble);
292
+ if (options.onHover) {
293
+ host.addEventListener("pointermove", onOver);
294
+ host.addEventListener("pointerleave", onOut);
295
+ }
296
+ const paint = () => {
297
+ for (const el of list.querySelectorAll(".fx-tree-row")) {
298
+ const row2 = el;
299
+ const id = row2.dataset.id;
300
+ const on = id !== void 0 ? Number(id) === marked : row2.dataset.key === markedKey;
301
+ el.classList.toggle("on", on);
302
+ }
303
+ };
304
+ function rowsFor(nodes, graph) {
305
+ const byId = /* @__PURE__ */ new Map();
306
+ const build = (node) => {
307
+ const written = values ? graph.get(node.id)?.props ?? [] : [];
308
+ const listed = written.map((p, i) => ({
309
+ key: `${node.id}=${i}`,
310
+ label: p.name,
311
+ kind: node.kind,
312
+ children: [],
313
+ prop: { name: p.name, ...p.value === void 0 ? {} : { value: p.value } },
314
+ owner: node.id,
315
+ ...p.at?.line === void 0 ? {} : { at: { file: p.at.file ?? "", line: p.at.line, column: p.at.column ?? 0 } }
316
+ }));
317
+ const group = `${node.id}=props`;
318
+ if (listed.length && !defaulted.has(group)) {
319
+ defaulted.add(group);
320
+ shut.add(group);
321
+ }
322
+ const props = listed.length ? [{ key: group, label: "props", kind: node.kind, children: listed, props: true, owner: node.id }] : [];
323
+ const row2 = {
324
+ key: String(node.id),
325
+ label: node.label,
326
+ kind: node.kind,
327
+ id: node.id,
328
+ children: [...props, ...node.children.map(build)],
329
+ ...node.value ? { value: true } : {},
330
+ ...node.lazy ? { lazy: true } : {},
331
+ ...node.badge ? { badge: node.badge } : {}
332
+ };
333
+ byId.set(node.id, row2);
334
+ return row2;
335
+ };
336
+ const roots = nodes.map(build);
337
+ const domFor = options.domFor;
338
+ if (!domFor) return roots;
339
+ const placed = /* @__PURE__ */ new Set();
340
+ const done = /* @__PURE__ */ new Set();
341
+ const uses = /* @__PURE__ */ new Map();
342
+ const tally = (nodes2) => {
343
+ for (const node of nodes2) {
344
+ if (node.owner !== void 0) uses.set(node.owner, (uses.get(node.owner) ?? 0) + 1);
345
+ tally(node.children);
346
+ }
347
+ };
348
+ for (const row2 of byId.values()) if (row2.id !== void 0) tally(domFor(row2.id));
349
+ const readerOf = (id, skip) => {
350
+ const drawn = (from) => {
351
+ const seen = /* @__PURE__ */ new Set();
352
+ let at = from;
353
+ while (at !== void 0 && !seen.has(at)) {
354
+ seen.add(at);
355
+ const row2 = byId.get(at);
356
+ if (row2 && !row2.value && row2 !== skip) return row2;
357
+ at = graph.get(at)?.createdIn;
358
+ }
359
+ return void 0;
360
+ };
361
+ let best;
362
+ let deepest = -1;
363
+ for (const [nodeId, node] of graph) {
364
+ if (nodeId === id || !node.deps.has(id)) continue;
365
+ const row2 = drawn(nodeId);
366
+ if (!row2 || row2.id === void 0) continue;
367
+ const depth = graph.get(row2.id)?.depth ?? 0;
368
+ if (depth >= deepest) {
369
+ deepest = depth;
370
+ best = row2;
371
+ }
372
+ }
373
+ return best;
374
+ };
375
+ const again = (row2, key) => ({
376
+ ...row2,
377
+ key,
378
+ children: row2.children.map((child, i) => again(child, `${key}/${i}`))
379
+ });
380
+ const expand = (row2) => {
381
+ if (row2.value || row2.id === void 0) return row2;
382
+ if (done.has(row2)) return row2;
383
+ done.add(row2);
384
+ const dom = domFor(row2.id);
385
+ if (!dom.length) {
386
+ row2.children = row2.children.map(expand);
387
+ return row2;
388
+ }
389
+ const walk = (node, key) => {
390
+ const owner = node.owner === void 0 ? void 0 : byId.get(node.owner);
391
+ if (owner && owner !== row2) {
392
+ if (done.has(owner)) {
393
+ const count = uses.get(node.owner) ?? 0;
394
+ return count > 1 ? again(owner, key) : void 0;
395
+ }
396
+ placed.add(owner);
397
+ return expand(owner);
398
+ }
399
+ const kids = [];
400
+ for (const id of node.writes ?? []) {
401
+ const value = byId.get(id);
402
+ if (!value?.value || placed.has(value)) continue;
403
+ placed.add(value);
404
+ kids.push(value);
405
+ }
406
+ for (const id of node.inside ?? []) {
407
+ const tag = byId.get(id);
408
+ if (!tag || tag === row2 || placed.has(tag) || done.has(tag)) continue;
409
+ placed.add(tag);
410
+ kids.push(expand(tag));
411
+ }
412
+ node.children.forEach((child, i) => {
413
+ const kid = walk(child, `${key}/${i}`);
414
+ if (kid) kids.push(kid);
415
+ });
416
+ return {
417
+ key,
418
+ label: node.label,
419
+ kind: "element",
420
+ children: kids,
421
+ owner: row2.id,
422
+ ...node.writes?.length ? { writes: node.writes } : {},
423
+ ...node.at ? { at: node.at } : {},
424
+ ...node.extra ? { extra: node.extra } : {},
425
+ ...pluginElementBadge(node.extra) ? { badge: pluginElementBadge(node.extra) } : {},
426
+ ...node.element ? { element: node.element } : {}
427
+ };
428
+ };
429
+ const mount = dom.findIndex((node) => node.container);
430
+ if (mount !== -1) {
431
+ const node = dom[mount];
432
+ row2.children = row2.children.map(expand);
433
+ return {
434
+ key: `${row2.id}:${mount}`,
435
+ label: node.label,
436
+ kind: "element",
437
+ children: [row2],
438
+ owner: row2.id,
439
+ ...node.at ? { at: node.at } : {},
440
+ ...node.element ? { element: node.element } : {}
441
+ };
442
+ }
443
+ const rendered = dom.map((node, i) => walk(node, `${row2.id}:${i}`)).filter((node) => node !== void 0);
444
+ const rest = row2.children.filter((child) => !placed.has(child)).map(expand);
445
+ const lifted = [];
446
+ const spare = [];
447
+ const adopt = /* @__PURE__ */ new Map();
448
+ for (const child of rest) {
449
+ const reader = child.value && child.id !== void 0 ? readerOf(child.id, row2) : void 0;
450
+ if (reader) {
451
+ const held = adopt.get(reader);
452
+ if (held) held.push(child);
453
+ else adopt.set(reader, [child]);
454
+ placed.add(child);
455
+ continue;
456
+ }
457
+ (child.value || child.prop || child.props ? lifted : spare).push(child);
458
+ }
459
+ for (const [reader, values2] of adopt) reader.children.unshift(...values2);
460
+ row2.children = [...lifted, ...rendered, ...spare];
461
+ return row2;
462
+ };
463
+ const top = roots.map((root) => placed.has(root) ? root : expand(root));
464
+ return top.filter((_, i) => !placed.has(roots[i]));
465
+ }
466
+ const api = {
467
+ render(graph, selected = null) {
468
+ last = { graph, selected };
469
+ const roots = rowsFor(componentTree(graph, { values, filter }), graph);
470
+ const next = `${values}|${filter}|${[...shut].sort().join(",")}|${roots.map(function label(n) {
471
+ return `${n.key}:${n.label}:${n.children.map(label).join("+")}`;
472
+ }).join(",")}`;
473
+ if (next !== signature) {
474
+ signature = next;
475
+ shown = /* @__PURE__ */ new Map();
476
+ const index = (list2) => {
477
+ for (const node of list2) {
478
+ shown.set(node.key, node);
479
+ index(node.children);
480
+ }
481
+ };
482
+ index(roots);
483
+ list.innerHTML = roots.length ? roots.map((node) => row(node, 0)).join("") : `<span class="fx-tree-empty">${escape(filter ? `nothing named ${filter}` : empty)}</span>`;
484
+ }
485
+ if (selected !== marked) {
486
+ marked = selected;
487
+ paint();
488
+ }
489
+ },
490
+ clear() {
491
+ signature = "";
492
+ marked = null;
493
+ shut.clear();
494
+ list.innerHTML = `<span class="fx-tree-empty">${escape(empty)}</span>`;
495
+ },
496
+ destroy() {
497
+ host.removeEventListener("click", onClick);
498
+ host.removeEventListener("dblclick", onDouble);
499
+ host.removeEventListener("pointermove", onOver);
500
+ host.removeEventListener("pointerleave", onOut);
501
+ host.innerHTML = "";
502
+ }
503
+ };
504
+ return api;
505
+ }
506
+ var TREE_CSS = `
507
+ .fx-tree-row {
508
+ display: flex; align-items: center; gap: 4px;
509
+ padding: 1px 6px 1px calc(6px + var(--depth, 0) * 12px);
510
+ font-family: ui-monospace, monospace; font-size: 11.5px;
511
+ color: var(--fx-label, #e7e9ee); cursor: pointer; white-space: nowrap;
512
+ }
513
+ .fx-tree-row:hover { background: var(--fx-hover, #1b1e26); }
514
+ .fx-tree-row.on { background: var(--fx-sel, #232838); }
515
+ .fx-tree-twist {
516
+ width: 12px; padding: 0; border: 0; background: none; cursor: pointer;
517
+ color: var(--fx-muted, #8a90a2); font-size: 9px; line-height: 1;
518
+ }
519
+ .fx-tree-plugin {
520
+ font-size: 9px; color: var(--fx-accent, #5eead4);
521
+ border: 1px solid var(--fx-line, #262a35); border-radius: 6px; padding: 0 4px;
522
+ max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
523
+ }
524
+ .fx-tree-lazy {
525
+ font-size: 9px; letter-spacing: .04em; color: var(--fx-router, #7dd3fc);
526
+ border: 1px solid var(--fx-line, #262a35); border-radius: 6px; padding: 0 4px;
527
+ }
528
+ .fx-tree-count {
529
+ font-size: 9.5px; color: var(--fx-muted, #8a90a2);
530
+ border: 1px solid var(--fx-line, #262a35); border-radius: 6px; padding: 0 4px;
531
+ }
532
+ ${kindRules((k) => `.fx-tree-row.n-${k} .fx-tree-tag`, "color")}
533
+ .fx-tree-bar {
534
+ display: flex; align-items: center; gap: 5px; padding: 2px 8px 5px;
535
+ font-size: 10.5px; color: var(--fx-muted, #8a90a2); cursor: pointer; user-select: none;
536
+ }
537
+ .fx-tree-bar:hover { color: var(--fx-label, #e7e9ee); }
538
+ .fx-tree-bar input { width: 11px; height: 11px; margin: 0; accent-color: var(--fx-accent, #5eead4); }
539
+ .fx-tree-row.n-element:hover .fx-tree-tag { color: var(--fx-label, #e7e9ee); }
540
+ /* A declared value is listed inside the tag that made it, so it reads quieter than a tag. */
541
+ .fx-tree-row.is-value .fx-tree-tag { font-style: italic; opacity: .85; }
542
+ .fx-tree-search {
543
+ width: calc(100% - 16px); margin: 0 8px 4px; padding: 2px 6px;
544
+ font: inherit; font-size: 10.5px;
545
+ color: var(--fx-label, #e7e9ee); background: var(--fx-bg, #0f1115);
546
+ border: 1px solid var(--fx-line, #262a35); border-radius: 4px;
547
+ }
548
+ .fx-tree-search:focus { outline: none; border-color: var(--fx-accent, #5eead4); }
549
+ .fx-tree-empty { display: block; padding: 8px; font-size: 11px; color: var(--fx-muted, #8a90a2); }
550
+ /* A prop reads as what was written on the tag, so it is quieter than the tag itself. */
551
+ /* The row that folds a tag's props away. Dashed, because it stands for something written
552
+ rather than something rendered. */
553
+ .fx-tree-row.is-props .fx-tree-tag {
554
+ display: inline-flex; align-items: center; gap: 5px;
555
+ padding: 0 5px; border: 1px dashed var(--fx-line, #262a35); border-radius: 3px;
556
+ color: var(--fx-muted, #8a90a2); font-size: 10px; letter-spacing: .04em; text-transform: uppercase;
557
+ }
558
+ .fx-tree-row.is-props:hover .fx-tree-tag { border-color: var(--fx-accent, #5eead4); color: var(--fx-label, #e7e9ee); }
559
+ .fx-tree-props-count { opacity: .7; }
560
+
561
+ /* A prop is what was written on the tag above it, so it reads as an aside: the name in a
562
+ quiet chip, the value beside it, and no marker pretending it opens. */
563
+ .fx-tree-row.is-prop .fx-tree-tag { display: inline-flex; align-items: baseline; gap: 5px; min-width: 0; }
564
+ .fx-tree-prop-name {
565
+ flex: none; padding: 0 4px; border-radius: 3px;
566
+ background: var(--fx-chip, rgba(138, 144, 162, .14));
567
+ color: var(--fx-muted, #8a90a2); font-size: 10px; letter-spacing: .02em;
568
+ }
569
+ .fx-tree-row.is-prop:hover .fx-tree-prop-name { color: var(--fx-label, #e7e9ee); }
570
+ .fx-tree-prop-value {
571
+ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
572
+ color: var(--fx-label, #e7e9ee); opacity: .75;
573
+ }
574
+ `;
package/dist/tree.d.ts ADDED
@@ -0,0 +1,128 @@
1
+ /**
2
+ * What the page is made of, as a tree.
3
+ *
4
+ * The graph answers which value depends on which. It does not answer what the page is built
5
+ * from, and that is the question a tree is for: `<Router>` holding a `<Counter>` and an
6
+ * `<Another>`, each openable to see what it holds.
7
+ *
8
+ * Both are drawn from the same nodes. A component's place in the tree is `createdIn`, the
9
+ * node whose run made it, lifted through everything that is not itself a tag, since the
10
+ * compiler wraps each component in a memo and that memo is not something anyone wrote.
11
+ */
12
+ import type { NodeKind, RNode } from './instrument.js';
13
+ import type { DomNode } from './dom.js';
14
+ /** Kinds that are tags in the source, and so have a place in a tree of them. */
15
+ export declare const TREE_KINDS: ReadonlySet<NodeKind>;
16
+ export interface TreeNode {
17
+ id: number;
18
+ label: string;
19
+ kind: NodeKind;
20
+ children: TreeNode[];
21
+ /** A value the author declared rather than a tag they wrote. */
22
+ value?: boolean;
23
+ /** Came down as its own chunk. */
24
+ lazy?: boolean;
25
+ /** What a plugin wants said beside the label. */
26
+ badge?: string;
27
+ }
28
+ /**
29
+ * An element row, for a host that wants to say something about one.
30
+ *
31
+ * An element is not a node, so it has no id to select. What it does have is the tag it
32
+ * belongs to and the values writing into it, which between them are what there is to say.
33
+ */
34
+ export interface ElementPick {
35
+ /** The row key, which doubles as a path to the element. */
36
+ key: string;
37
+ /** The element as a selector: `div#app.card`. */
38
+ label: string;
39
+ owner?: number;
40
+ writes?: number[];
41
+ /** Where the element itself was written, when the build recorded it. */
42
+ at?: {
43
+ file: string;
44
+ line: number;
45
+ column: number;
46
+ };
47
+ /** What plugins attached to this element. */
48
+ extra?: Record<string, unknown>;
49
+ /** Only in a host that is in the page. */
50
+ element?: Element;
51
+ }
52
+ /** Whatever the host can say about the dom under a tag. Elements only when it has them. */
53
+ export type { DomNode } from './dom.js';
54
+ /**
55
+ * The tags in `graph`, each under the tag that made it.
56
+ *
57
+ * Ordered by id, which is creation order, so siblings read down the page the way they were
58
+ * rendered. A node whose ancestors are all untagged is a root.
59
+ */
60
+ export interface TreeOptions {
61
+ /**
62
+ * Also list the reactive values each tag made: the signals and memos the author wrote
63
+ * inside it. Machinery is left out: a node the framework named itself does not count, so
64
+ * a component shows `count` and not the memo it was wrapped in.
65
+ *
66
+ * Only an explicit `authored: false` is machinery. A graph read from the source says
67
+ * nothing either way, and everything in it was written by hand.
68
+ */
69
+ values?: boolean;
70
+ /**
71
+ * Keep only what this names, and whatever holds it. An ancestor is kept even when its own
72
+ * name does not match, since dropping it would leave the match with nothing above it.
73
+ */
74
+ filter?: string;
75
+ }
76
+ /**
77
+ * Put the tags that rendered back into page order, leaving everything else untouched.
78
+ *
79
+ * Sorting by id alone reads as creation order, which is wrong for a keyed list: rebuilding a
80
+ * row hands the replacement the highest id, so the row you edited jumped to the bottom.
81
+ *
82
+ * Only the slots those tags already occupy are reshuffled. Sorting the whole list by `order`
83
+ * instead sent every value to the end of its band, so editing a todo moved the `left` memo
84
+ * as well, which had nothing to do with the change.
85
+ */
86
+ export declare function inPageOrder<T extends {
87
+ order?: number;
88
+ }>(nodes: T[]): T[];
89
+ export declare function componentTree(graph: Map<number, RNode>, options?: TreeOptions): TreeNode[];
90
+ /** Every id in the tree, for a caller that wants to open or shut all of it. */
91
+ export declare function idsIn(nodes: readonly TreeNode[]): number[];
92
+ export interface ComponentTreeOptions {
93
+ /** Clicking a row. The host decides what selecting means, usually the inspector. */
94
+ onSelect?: (id: number) => void;
95
+ /**
96
+ * Clicking an element row. Elements have no id, so a host that leaves this out gets no
97
+ * selection for them at all, which is what made half the tree look inert.
98
+ */
99
+ onPick?: (pick: ElementPick) => void;
100
+ /** Double-clicking a row, for a host that can open the file it came from. */
101
+ onReveal?: (id: number) => void;
102
+ /** Double-clicking an element row. */
103
+ onRevealPick?: (pick: ElementPick) => void;
104
+ /**
105
+ * The row under the pointer, and null on the way out. For a host that can reach the page
106
+ * and show where the node writes. An element row gives the element itself, which needs no
107
+ * looking up.
108
+ */
109
+ onHover?: (id: number | null, element?: Element, path?: string) => void;
110
+ /**
111
+ * What each tag rendered. A host in the page walks its own elements; one reading a
112
+ * snapshot passes what the producer described. Omitted draws tags only.
113
+ */
114
+ domFor?: (id: number) => readonly DomNode[];
115
+ /** What to say when nothing has rendered yet. */
116
+ empty?: string;
117
+ /** Start with the values shown. @default false */
118
+ values?: boolean;
119
+ }
120
+ export interface ComponentTree {
121
+ /** Draw `graph`, with `selected` marked. Cheap to call every frame. */
122
+ render(graph: Map<number, RNode>, selected?: number | null): void;
123
+ clear(): void;
124
+ destroy(): void;
125
+ }
126
+ export declare function createComponentTree(host: HTMLElement, options?: ComponentTreeOptions): ComponentTree;
127
+ export declare const TREE_CSS: string;
128
+ //# sourceMappingURL=tree.d.ts.map