@svelterm/core 0.1.0 → 0.21.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 (164) hide show
  1. package/CHANGELOG.md +425 -0
  2. package/README.md +42 -29
  3. package/dist/src/cli/build.d.ts +13 -0
  4. package/dist/src/cli/build.js +119 -0
  5. package/dist/src/cli/bundle.d.ts +25 -0
  6. package/dist/src/cli/bundle.js +61 -0
  7. package/dist/src/cli/dev.d.ts +10 -0
  8. package/dist/src/cli/dev.js +152 -0
  9. package/dist/src/cli/devtools.d.ts +9 -0
  10. package/dist/src/cli/devtools.js +47 -0
  11. package/dist/src/cli/init.d.ts +8 -0
  12. package/dist/src/cli/init.js +153 -0
  13. package/dist/src/cli/main.d.ts +9 -0
  14. package/dist/src/cli/main.js +52 -0
  15. package/dist/src/cli/svt-bin.d.ts +2 -0
  16. package/dist/src/cli/svt-bin.js +6 -0
  17. package/dist/src/cli/svt.d.ts +14 -0
  18. package/dist/src/cli/svt.js +76 -0
  19. package/dist/src/components/text-buffer.js +8 -5
  20. package/dist/src/css/animation-runner.d.ts +15 -6
  21. package/dist/src/css/animation-runner.js +80 -29
  22. package/dist/src/css/animation.d.ts +12 -0
  23. package/dist/src/css/animation.js +21 -0
  24. package/dist/src/css/calc.js +4 -3
  25. package/dist/src/css/color.d.ts +19 -0
  26. package/dist/src/css/color.js +371 -62
  27. package/dist/src/css/compute.d.ts +30 -3
  28. package/dist/src/css/compute.js +272 -33
  29. package/dist/src/css/defaults.d.ts +1 -1
  30. package/dist/src/css/defaults.js +9 -0
  31. package/dist/src/css/easing.d.ts +9 -0
  32. package/dist/src/css/easing.js +95 -0
  33. package/dist/src/css/incremental.d.ts +1 -1
  34. package/dist/src/css/incremental.js +2 -2
  35. package/dist/src/css/interpolate.d.ts +13 -0
  36. package/dist/src/css/interpolate.js +41 -0
  37. package/dist/src/css/parser.js +59 -3
  38. package/dist/src/css/pseudo-elements.d.ts +9 -0
  39. package/dist/src/css/pseudo-elements.js +97 -0
  40. package/dist/src/css/selector.d.ts +17 -2
  41. package/dist/src/css/selector.js +128 -13
  42. package/dist/src/css/specificity.js +17 -6
  43. package/dist/src/css/values.d.ts +6 -1
  44. package/dist/src/css/values.js +13 -6
  45. package/dist/src/debug/context.d.ts +13 -0
  46. package/dist/src/debug/context.js +11 -0
  47. package/dist/src/debug/css.d.ts +12 -0
  48. package/dist/src/debug/css.js +28 -0
  49. package/dist/src/debug/dom.d.ts +17 -0
  50. package/dist/src/debug/dom.js +92 -0
  51. package/dist/src/devtools/DevTools.compiled.js +327 -0
  52. package/dist/src/devtools/DevTools.css.js +1 -0
  53. package/dist/src/devtools/client.d.ts +36 -0
  54. package/dist/src/devtools/client.js +76 -0
  55. package/dist/src/framelog.d.ts +54 -0
  56. package/dist/src/framelog.js +99 -0
  57. package/dist/src/headless.js +12 -4
  58. package/dist/src/index.d.ts +65 -3
  59. package/dist/src/index.js +609 -81
  60. package/dist/src/input/checkable.d.ts +8 -0
  61. package/dist/src/input/checkable.js +66 -0
  62. package/dist/src/input/details.d.ts +6 -0
  63. package/dist/src/input/details.js +34 -0
  64. package/dist/src/input/focus.d.ts +6 -0
  65. package/dist/src/input/focus.js +27 -9
  66. package/dist/src/input/keyboard.d.ts +2 -2
  67. package/dist/src/input/keyboard.js +32 -5
  68. package/dist/src/input/label.d.ts +8 -0
  69. package/dist/src/input/label.js +53 -0
  70. package/dist/src/input/modal.d.ts +9 -0
  71. package/dist/src/input/modal.js +28 -0
  72. package/dist/src/input/mouse.d.ts +2 -2
  73. package/dist/src/input/mouse.js +15 -2
  74. package/dist/src/input/select.d.ts +12 -0
  75. package/dist/src/input/select.js +63 -0
  76. package/dist/src/input/selection.d.ts +48 -0
  77. package/dist/src/input/selection.js +150 -0
  78. package/dist/src/layout/engine.d.ts +2 -0
  79. package/dist/src/layout/engine.js +1084 -142
  80. package/dist/src/layout/flex.js +4 -4
  81. package/dist/src/layout/size.js +3 -2
  82. package/dist/src/layout/text.d.ts +3 -2
  83. package/dist/src/layout/text.js +96 -17
  84. package/dist/src/layout/unicode.d.ts +20 -0
  85. package/dist/src/layout/unicode.js +121 -0
  86. package/dist/src/render/animation-clock.d.ts +51 -0
  87. package/dist/src/render/animation-clock.js +213 -0
  88. package/dist/src/render/ansi-text.d.ts +26 -0
  89. package/dist/src/render/ansi-text.js +131 -0
  90. package/dist/src/render/ansi.d.ts +18 -0
  91. package/dist/src/render/ansi.js +64 -19
  92. package/dist/src/render/border.js +166 -17
  93. package/dist/src/render/buffer.d.ts +1 -0
  94. package/dist/src/render/buffer.js +5 -2
  95. package/dist/src/render/color-depth.d.ts +8 -0
  96. package/dist/src/render/color-depth.js +59 -0
  97. package/dist/src/render/context.d.ts +1 -0
  98. package/dist/src/render/context.js +17 -21
  99. package/dist/src/render/cursor-emit.d.ts +18 -0
  100. package/dist/src/render/cursor-emit.js +50 -0
  101. package/dist/src/render/diff.d.ts +12 -0
  102. package/dist/src/render/diff.js +120 -0
  103. package/dist/src/render/generation.d.ts +9 -0
  104. package/dist/src/render/generation.js +14 -0
  105. package/dist/src/render/graphics-layer.d.ts +27 -0
  106. package/dist/src/render/graphics-layer.js +86 -0
  107. package/dist/src/render/image.d.ts +27 -0
  108. package/dist/src/render/image.js +113 -0
  109. package/dist/src/render/incremental-paint.d.ts +7 -3
  110. package/dist/src/render/incremental-paint.js +52 -79
  111. package/dist/src/render/inline.d.ts +59 -0
  112. package/dist/src/render/inline.js +219 -0
  113. package/dist/src/render/kitty-graphics.d.ts +24 -0
  114. package/dist/src/render/kitty-graphics.js +58 -0
  115. package/dist/src/render/paint-text.js +68 -22
  116. package/dist/src/render/paint.d.ts +8 -1
  117. package/dist/src/render/paint.js +328 -30
  118. package/dist/src/render/png.d.ts +13 -0
  119. package/dist/src/render/png.js +145 -0
  120. package/dist/src/render/scrollbar.d.ts +8 -2
  121. package/dist/src/render/scrollbar.js +71 -14
  122. package/dist/src/render/snapshot.js +3 -1
  123. package/dist/src/renderer/default.d.ts +7 -0
  124. package/dist/src/renderer/default.js +11 -0
  125. package/dist/src/renderer/index.d.ts +8 -2
  126. package/dist/src/renderer/index.js +4 -2
  127. package/dist/src/renderer/node.d.ts +109 -0
  128. package/dist/src/renderer/node.js +165 -1
  129. package/dist/src/terminal/capabilities.d.ts +33 -0
  130. package/dist/src/terminal/capabilities.js +66 -0
  131. package/dist/src/terminal/clipboard.d.ts +9 -0
  132. package/dist/src/terminal/clipboard.js +39 -0
  133. package/dist/src/terminal/io.d.ts +82 -0
  134. package/dist/src/terminal/io.js +155 -0
  135. package/dist/src/terminal/screen.d.ts +3 -10
  136. package/dist/src/terminal/screen.js +5 -28
  137. package/dist/src/terminal/stdin-router.d.ts +8 -5
  138. package/dist/src/terminal/stdin-router.js +22 -11
  139. package/dist/src/utils/node-map.d.ts +24 -0
  140. package/dist/src/utils/node-map.js +75 -0
  141. package/dist/src/vite/config.d.ts +62 -0
  142. package/dist/src/vite/config.js +191 -0
  143. package/docs/compatibility.md +67 -0
  144. package/docs/debug/devtools.md +40 -0
  145. package/docs/debug/svt.md +50 -0
  146. package/docs/distribution.md +106 -0
  147. package/docs/elements.md +120 -0
  148. package/docs/getting-started.md +177 -0
  149. package/docs/guide/css.md +187 -0
  150. package/docs/guide/input.md +143 -0
  151. package/docs/guide/layout.md +171 -0
  152. package/docs/guide/theming.md +94 -0
  153. package/docs/how-it-works.md +115 -0
  154. package/docs/inline-mode.md +77 -0
  155. package/docs/layout.md +106 -0
  156. package/docs/motion.md +91 -0
  157. package/docs/reference/README.md +65 -0
  158. package/docs/reference/css/properties/border-corner.md +82 -0
  159. package/docs/reference/css/properties/border-style.md +168 -0
  160. package/docs/reference.md +226 -0
  161. package/docs/selectors.md +80 -0
  162. package/docs/terminal-css.md +149 -0
  163. package/docs/terminals.md +83 -0
  164. package/package.json +28 -7
@@ -0,0 +1,17 @@
1
+ /**
2
+ * DOM domain — tree inspection and live mutation over the debug
3
+ * protocol. Serialises the TermNode tree (tags, attributes, text), finds
4
+ * nodes by selector, reports box models, and edits attributes.
5
+ */
6
+ import { type DebugContext } from './context.js';
7
+ import type { DebugDomain } from './server.js';
8
+ export declare class DomDomain implements DebugDomain {
9
+ private ctx;
10
+ constructor(ctx: DebugContext);
11
+ handle(method: string, params: Record<string, any>): any;
12
+ private firstMatch;
13
+ private node;
14
+ private boxModel;
15
+ private setAttribute;
16
+ private removeAttribute;
17
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * DOM domain — tree inspection and live mutation over the debug
3
+ * protocol. Serialises the TermNode tree (tags, attributes, text), finds
4
+ * nodes by selector, reports box models, and edits attributes.
5
+ */
6
+ import { matchesSelector } from '../css/selector.js';
7
+ import { findNodeById } from './context.js';
8
+ export class DomDomain {
9
+ ctx;
10
+ constructor(ctx) {
11
+ this.ctx = ctx;
12
+ }
13
+ handle(method, params) {
14
+ switch (method) {
15
+ case 'getDocument':
16
+ return { root: serialise(this.ctx.root) };
17
+ case 'querySelector':
18
+ return { nodeId: this.firstMatch(params.selector)?.id ?? null };
19
+ case 'getBoxModel':
20
+ return this.boxModel(params.nodeId);
21
+ case 'setAttribute':
22
+ return this.setAttribute(params.nodeId, params.name, params.value);
23
+ case 'removeAttribute':
24
+ return this.removeAttribute(params.nodeId, params.name);
25
+ default:
26
+ throw new Error(`DOM.${method} not implemented`);
27
+ }
28
+ }
29
+ firstMatch(selector) {
30
+ const walk = (node) => {
31
+ if (node.nodeType === 'element' && matchesSelector(node, selector))
32
+ return node;
33
+ for (const child of node.children) {
34
+ const found = walk(child);
35
+ if (found)
36
+ return found;
37
+ }
38
+ return null;
39
+ };
40
+ for (const child of this.ctx.root.children) {
41
+ const found = walk(child);
42
+ if (found)
43
+ return found;
44
+ }
45
+ return null;
46
+ }
47
+ node(nodeId) {
48
+ const node = findNodeById(this.ctx.root, nodeId);
49
+ if (!node)
50
+ throw new Error(`No node with id ${nodeId}`);
51
+ return node;
52
+ }
53
+ boxModel(nodeId) {
54
+ const box = this.ctx.layout()?.get(nodeId);
55
+ if (!box)
56
+ throw new Error(`No layout for node ${nodeId}`);
57
+ return { x: box.x, y: box.y, width: box.width, height: box.height };
58
+ }
59
+ setAttribute(nodeId, name, value) {
60
+ const node = this.node(nodeId);
61
+ if (node.ctx)
62
+ node.ctx.onSetAttribute(node, name, value);
63
+ else
64
+ node.attributes.set(name, value);
65
+ this.ctx.requestRender?.();
66
+ return {};
67
+ }
68
+ removeAttribute(nodeId, name) {
69
+ const node = this.node(nodeId);
70
+ if (node.ctx)
71
+ node.ctx.onRemoveAttribute(node, name);
72
+ else
73
+ node.attributes.delete(name);
74
+ this.ctx.requestRender?.();
75
+ return {};
76
+ }
77
+ }
78
+ function serialise(node) {
79
+ const out = {
80
+ nodeId: node.id,
81
+ nodeType: node.nodeType,
82
+ children: node.children.map(serialise),
83
+ };
84
+ if (node.tag)
85
+ out.tag = node.tag;
86
+ if (node.nodeType === 'text')
87
+ out.text = node.text ?? '';
88
+ if (node.attributes.size > 0) {
89
+ out.attributes = Object.fromEntries(node.attributes);
90
+ }
91
+ return out;
92
+ }
@@ -0,0 +1,327 @@
1
+ import $renderer from '@svelterm/core';
2
+ import 'svelte/internal/disclose-version';
3
+ import * as $ from 'svelte/internal/client';
4
+ import { connectDebugClient, flattenTree } from './client.js';
5
+
6
+ var root = $.from_tree([
7
+ [
8
+ 'div',
9
+ null,
10
+ ['span', { class: 'marker svelte-1dtdv2u' }, ' '],
11
+ ' '
12
+ ]
13
+ ]);
14
+
15
+ var root_1 = $.from_tree([['div', { class: 'empty svelte-1dtdv2u' }, 'no nodes']]);
16
+ var root_2 = $.from_tree([['div', { class: 'prop svelte-1dtdv2u' }, ' ']]);
17
+
18
+ var root_3 = $.from_tree(
19
+ [
20
+ [
21
+ 'div',
22
+ { class: 'detail-title svelte-1dtdv2u' },
23
+ 'computed style'
24
+ ],
25
+ ' ',,
26
+ ],
27
+ 1
28
+ );
29
+
30
+ var root_4 = $.from_tree(
31
+ [
32
+ ['div', { class: 'detail-title svelte-1dtdv2u' }, 'box'],
33
+ ' ',
34
+ ['div', { class: 'prop svelte-1dtdv2u' }, ' '],
35
+ ' ',
36
+ ['div', { class: 'prop svelte-1dtdv2u' }, ' ']
37
+ ],
38
+ 1
39
+ );
40
+
41
+ var root_5 = $.from_tree([['div', { class: 'prop dim svelte-1dtdv2u' }, ' ']]);
42
+
43
+ var root_6 = $.from_tree([
44
+ ['div', { class: 'prop dim svelte-1dtdv2u' }, 'select a node']
45
+ ]);
46
+
47
+ var root_7 = $.from_tree([
48
+ [
49
+ 'div',
50
+ { class: 'devtools svelte-1dtdv2u' },
51
+ ['div', { class: 'header svelte-1dtdv2u' }, ' '],
52
+ ' ',
53
+ [
54
+ 'div',
55
+ { class: 'panes svelte-1dtdv2u' },
56
+ [
57
+ 'div',
58
+ { class: 'tree svelte-1dtdv2u', tabindex: '0' },,
59
+ ' ',,
60
+ ],
61
+ ' ',
62
+ [
63
+ 'div',
64
+ { class: 'detail svelte-1dtdv2u' },,
65
+ ' ',,
66
+ ' ',,
67
+ ' ',,
68
+ ]
69
+ ]
70
+ ]
71
+ ]);
72
+
73
+ export default function DevTools($$anchor, $$props) {
74
+ var $$pop_renderer = $.push_renderer($renderer);
75
+
76
+ $.push($$props, true);
77
+
78
+ let port = $.prop($$props, 'port', 3, 9444);
79
+ let status = $.state('connecting…');
80
+ let doc = $.state(null);
81
+ let collapsed = $.state($.proxy(new Set()));
82
+ let selected = $.state(0);
83
+ let detail = $.state(null);
84
+ let client = null;
85
+ let rows = $.derived(() => $.get(doc) ? flattenTree($.get(doc).root, $.get(collapsed)) : []);
86
+
87
+ async function refresh() {
88
+ if (!client) return;
89
+
90
+ try {
91
+ $.set(doc, await client.request('DOM.getDocument').then((r) => r), true);
92
+
93
+ if ($.get(selected) >= $.get(rows).length) $.set(selected, Math.max(0, $.get(rows).length - 1), true);
94
+
95
+ $.set(status, `${$.get(rows).length} nodes`);
96
+ await loadDetail();
97
+ } catch(err) {
98
+ $.set(status, 'error: ' + (err?.message ?? err));
99
+ }
100
+ }
101
+
102
+ function toggleCollapse(row, force) {
103
+ if (!row?.hasChildren) return;
104
+
105
+ const next = new Set($.get(collapsed));
106
+ const shouldCollapse = force ?? !next.has(row.node.nodeId);
107
+
108
+ if (shouldCollapse) next.add(row.node.nodeId); else next.delete(row.node.nodeId);
109
+
110
+ $.set(collapsed, next, true);
111
+ }
112
+
113
+ async function loadDetail() {
114
+ const row = $.get(rows)[$.get(selected)];
115
+
116
+ if (!client || !row) {
117
+ $.set(detail, null);
118
+
119
+ return;
120
+ }
121
+
122
+ if (row.node.nodeType !== 'element') {
123
+ $.set(detail, { note: 'no style for non-element' }, true);
124
+
125
+ return;
126
+ }
127
+
128
+ try {
129
+ const [style, box] = await Promise.all([
130
+ client.request('CSS.getComputedStyle', { nodeId: row.node.nodeId }).catch(() => null),
131
+ client.request('DOM.getBoxModel', { nodeId: row.node.nodeId }).catch(() => null)
132
+ ]);
133
+
134
+ $.set(detail, { style: style?.style, box }, true);
135
+ } catch {
136
+ $.set(detail, null);
137
+ }
138
+ }
139
+
140
+ // Style keys that are always their default and would just be noise.
141
+ const HIDE = new Set(['animationName', 'transitionProperty']);
142
+
143
+ function styleLines(style) {
144
+ if (!style) return [];
145
+
146
+ return Object.keys(style).filter((k) => !HIDE.has(k)).filter((k) => {
147
+ const v = style[k];
148
+
149
+ return v !== undefined && v !== null && v !== '' && v !== false && v !== 'default' && v !== 'none' && v !== 0 && v !== 'static' && v !== 'visible' && v !== 'normal';
150
+ }).map((k) => `${k}: ${style[k]}`);
151
+ }
152
+
153
+ function onkeydown(event) {
154
+ const key = event.data?.key ?? event.key;
155
+ const row = $.get(rows)[$.get(selected)];
156
+
157
+ if (key === 'ArrowDown') {
158
+ $.set(selected, Math.min($.get(rows).length - 1, $.get(selected) + 1), true);
159
+ loadDetail();
160
+ } else if (key === 'ArrowUp') {
161
+ $.set(selected, Math.max(0, $.get(selected) - 1), true);
162
+ loadDetail();
163
+ } else if (key === 'ArrowLeft') {
164
+ toggleCollapse(row, true);
165
+ } else if (key === 'ArrowRight') {
166
+ toggleCollapse(row, false);
167
+ } else if (key === 'Enter') {
168
+ toggleCollapse(row);
169
+ } else if (key === 'r') refresh();
170
+ }
171
+
172
+ async function connect(el) {
173
+ try {
174
+ client = await connectDebugClient(port());
175
+ $.set(status, 'connected');
176
+ await refresh();
177
+ } catch(err) {
178
+ $.set(status, `cannot connect on ${port()} — run the target app with run(App, { debug: true })`);
179
+ }
180
+
181
+ el.focus?.();
182
+
183
+ return () => client?.close();
184
+ }
185
+
186
+ var div = root_7();
187
+ var div_1 = $.child(div);
188
+ var text = $.child(div_1);
189
+
190
+ $.reset(div_1);
191
+
192
+ var div_2 = $.sibling(div_1, 2);
193
+ var div_3 = $.child(div_2);
194
+ var node = $.child(div_3);
195
+
196
+ $.each(node, 17, () => $.get(rows), $.index, ($$anchor, row, index) => {
197
+ var div_4 = root();
198
+ let classes;
199
+ var span = $.child(div_4);
200
+ var text_1 = $.child(span, true);
201
+
202
+ $.reset(span);
203
+
204
+ var text_2 = $.sibling(span, 1, true);
205
+
206
+ $.reset(div_4);
207
+
208
+ $.template_effect(() => {
209
+ classes = $.set_class(div_4, 0, 'node svelte-1dtdv2u', null, classes, { sel: index === $.get(selected) });
210
+ $.set_style(div_4, `padding-left: ${$.get(row).depth * 2}cell;`);
211
+ $.set_text(text_1, $.get(row).hasChildren ? $.get(row).collapsed ? '▸' : '▾' : ' ');
212
+ $.set_text(text_2, $.get(row).label);
213
+ });
214
+
215
+ $.append($$anchor, div_4);
216
+ });
217
+
218
+ var node_1 = $.sibling(node, 2);
219
+
220
+ {
221
+ var consequent = ($$anchor) => {
222
+ var div_5 = root_1();
223
+
224
+ $.append($$anchor, div_5);
225
+ };
226
+
227
+ $.if(node_1, ($$render) => {
228
+ if ($.get(rows).length === 0) $$render(consequent);
229
+ });
230
+ }
231
+
232
+ $.reset(div_3);
233
+ $.attach(div_3, () => connect);
234
+
235
+ var div_6 = $.sibling(div_3, 2);
236
+ var node_2 = $.child(div_6);
237
+
238
+ {
239
+ var consequent_1 = ($$anchor) => {
240
+ var fragment = root_3();
241
+ var node_3 = $.sibling($.first_child(fragment), 2);
242
+
243
+ $.each(node_3, 17, () => styleLines($.get(detail).style), $.index, ($$anchor, line) => {
244
+ var div_7 = root_2();
245
+ var text_3 = $.child(div_7, true);
246
+
247
+ $.reset(div_7);
248
+ $.template_effect(() => $.set_text(text_3, $.get(line)));
249
+ $.append($$anchor, div_7);
250
+ });
251
+
252
+ $.append($$anchor, fragment);
253
+ };
254
+
255
+ $.if(node_2, ($$render) => {
256
+ if ($.get(detail)?.style) $$render(consequent_1);
257
+ });
258
+ }
259
+
260
+ var node_4 = $.sibling(node_2, 2);
261
+
262
+ {
263
+ var consequent_2 = ($$anchor) => {
264
+ var fragment_1 = root_4();
265
+ var div_8 = $.sibling($.first_child(fragment_1), 2);
266
+ var text_4 = $.child(div_8);
267
+
268
+ $.reset(div_8);
269
+
270
+ var div_9 = $.sibling(div_8, 2);
271
+ var text_5 = $.child(div_9);
272
+
273
+ $.reset(div_9);
274
+
275
+ $.template_effect(() => {
276
+ $.set_text(text_4, `x ${$.get(detail).box.x ?? ''} y ${$.get(detail).box.y ?? ''}`);
277
+ $.set_text(text_5, `w ${$.get(detail).box.width ?? ''} h ${$.get(detail).box.height ?? ''}`);
278
+ });
279
+
280
+ $.append($$anchor, fragment_1);
281
+ };
282
+
283
+ $.if(node_4, ($$render) => {
284
+ if ($.get(detail)?.box) $$render(consequent_2);
285
+ });
286
+ }
287
+
288
+ var node_5 = $.sibling(node_4, 2);
289
+
290
+ {
291
+ var consequent_3 = ($$anchor) => {
292
+ var div_10 = root_5();
293
+ var text_6 = $.child(div_10, true);
294
+
295
+ $.reset(div_10);
296
+ $.template_effect(() => $.set_text(text_6, $.get(detail).note));
297
+ $.append($$anchor, div_10);
298
+ };
299
+
300
+ $.if(node_5, ($$render) => {
301
+ if ($.get(detail)?.note) $$render(consequent_3);
302
+ });
303
+ }
304
+
305
+ var node_6 = $.sibling(node_5, 2);
306
+
307
+ {
308
+ var consequent_4 = ($$anchor) => {
309
+ var div_11 = root_6();
310
+
311
+ $.append($$anchor, div_11);
312
+ };
313
+
314
+ $.if(node_6, ($$render) => {
315
+ if (!$.get(detail)) $$render(consequent_4);
316
+ });
317
+ }
318
+
319
+ $.reset(div_6);
320
+ $.reset(div_2);
321
+ $.reset(div);
322
+ $.template_effect(() => $.set_text(text, `svelterm devtools · ${$.get(status) ?? ''} · ↑↓ select · ←→ fold · r refresh · Ctrl+C quit`));
323
+ $.event('keydown', div, onkeydown);
324
+ $.append($$anchor, div);
325
+ $.pop();
326
+ $$pop_renderer();
327
+ }
@@ -0,0 +1 @@
1
+ export const css = "\n .devtools.svelte-1dtdv2u {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .header.svelte-1dtdv2u {\n background: light-dark(#e0e0d8, #26415c);\n color: light-dark(#0a3055, #cfe6ff);\n padding: 0 1cell;\n }\n\n .panes.svelte-1dtdv2u {\n display: flex;\n flex-grow: 1;\n }\n\n .tree.svelte-1dtdv2u {\n width: 50%;\n overflow: auto;\n border-right: single;\n border-color: light-dark(#bbbbbb, #444444);\n }\n\n .node.svelte-1dtdv2u {\n color: light-dark(#333333, #cccccc);\n }\n\n .marker.svelte-1dtdv2u {\n color: light-dark(#999999, #777777);\n }\n\n .node.sel.svelte-1dtdv2u {\n background: light-dark(#d5e5f5, #26415c);\n color: cyan;\n font-weight: bold;\n }\n\n .detail.svelte-1dtdv2u {\n width: 50%;\n padding: 0 1cell;\n }\n\n .detail-title.svelte-1dtdv2u {\n color: yellow;\n font-weight: bold;\n }\n\n .prop.svelte-1dtdv2u {\n color: light-dark(#333333, #cccccc);\n }\n\n .prop.dim.svelte-1dtdv2u,\n .empty.svelte-1dtdv2u {\n color: light-dark(#888888, #666666);\n padding: 0 1cell;\n }\n"
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Debug-protocol client for the DevTools TUI: a request/response wrapper
3
+ * over the WebSocket the debug server exposes. Pure logic (tree
4
+ * flattening) is separated so it can be unit-tested without a socket.
5
+ */
6
+ export interface DebugClient {
7
+ request(method: string, params?: Record<string, unknown>): Promise<any>;
8
+ close(): void;
9
+ }
10
+ export interface SerialNode {
11
+ nodeId: number;
12
+ nodeType: string;
13
+ tag?: string;
14
+ text?: string;
15
+ attributes?: Record<string, string>;
16
+ children: SerialNode[];
17
+ }
18
+ export interface FlatNode {
19
+ node: SerialNode;
20
+ depth: number;
21
+ /** A one-line label: `<div.card#main>` / `"text"` / `<!--comment-->`. */
22
+ label: string;
23
+ /** True if this node has children (so it can collapse). */
24
+ hasChildren: boolean;
25
+ /** True if collapsed — its subtree is hidden from the flattened list. */
26
+ collapsed: boolean;
27
+ }
28
+ /** Connect to a debug server on 127.0.0.1:port. */
29
+ export declare function connectDebugClient(port: number): Promise<DebugClient>;
30
+ /**
31
+ * Flatten the tree depth-first into indented, labelled rows. Nodes whose
32
+ * id is in `collapsed` render but their subtrees are hidden.
33
+ */
34
+ export declare function flattenTree(root: SerialNode, collapsed?: Set<number>): FlatNode[];
35
+ /** A compact one-line label for a node. */
36
+ export declare function labelFor(node: SerialNode): string;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Debug-protocol client for the DevTools TUI: a request/response wrapper
3
+ * over the WebSocket the debug server exposes. Pure logic (tree
4
+ * flattening) is separated so it can be unit-tested without a socket.
5
+ */
6
+ /** Connect to a debug server on 127.0.0.1:port. */
7
+ export async function connectDebugClient(port) {
8
+ const { WebSocket } = await import('ws');
9
+ const ws = new WebSocket(`ws://127.0.0.1:${port}`);
10
+ await new Promise((resolve, reject) => {
11
+ ws.on('open', () => resolve());
12
+ ws.on('error', (err) => reject(err));
13
+ });
14
+ let nextId = 1;
15
+ const pending = new Map();
16
+ ws.on('message', (data) => {
17
+ try {
18
+ const msg = JSON.parse(data.toString());
19
+ const entry = pending.get(msg.id);
20
+ if (!entry)
21
+ return;
22
+ pending.delete(msg.id);
23
+ if (msg.error)
24
+ entry.reject(new Error(msg.error.message));
25
+ else
26
+ entry.resolve(msg.result);
27
+ }
28
+ catch { /* ignore malformed */ }
29
+ });
30
+ return {
31
+ request(method, params = {}) {
32
+ const id = nextId++;
33
+ return new Promise((resolve, reject) => {
34
+ pending.set(id, { resolve, reject });
35
+ ws.send(JSON.stringify({ id, method, params }));
36
+ });
37
+ },
38
+ close() { ws.close(); },
39
+ };
40
+ }
41
+ /**
42
+ * Flatten the tree depth-first into indented, labelled rows. Nodes whose
43
+ * id is in `collapsed` render but their subtrees are hidden.
44
+ */
45
+ export function flattenTree(root, collapsed = new Set()) {
46
+ const out = [];
47
+ const walk = (node, depth) => {
48
+ const isCollapsed = collapsed.has(node.nodeId);
49
+ out.push({
50
+ node, depth, label: labelFor(node),
51
+ hasChildren: node.children.length > 0,
52
+ collapsed: isCollapsed,
53
+ });
54
+ if (isCollapsed)
55
+ return;
56
+ for (const child of node.children)
57
+ walk(child, depth + 1);
58
+ };
59
+ walk(root, 0);
60
+ return out;
61
+ }
62
+ /** A compact one-line label for a node. */
63
+ export function labelFor(node) {
64
+ if (node.nodeType === 'text') {
65
+ const text = (node.text ?? '').trim().replace(/\s+/g, ' ');
66
+ return text ? `"${truncate(text, 40)}"` : '(whitespace)';
67
+ }
68
+ if (node.nodeType === 'comment')
69
+ return '<!--comment-->';
70
+ const cls = node.attributes?.class ? '.' + node.attributes.class.split(/\s+/).join('.') : '';
71
+ const id = node.attributes?.id ? '#' + node.attributes.id : '';
72
+ return `<${node.tag ?? '?'}${id}${cls}>`;
73
+ }
74
+ function truncate(text, max) {
75
+ return text.length <= max ? text : text.slice(0, max - 1) + '…';
76
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * FrameLog — an append-only log of frames for inline mode. Each frame is
3
+ * a mounted component; archiving hands its rows to the terminal's
4
+ * scrollback and frees the component, so a long session's memory tracks
5
+ * what's live, not the whole history.
6
+ *
7
+ * The host element should be the first content on screen (frames release
8
+ * from the top). To update a frame after append, pass a `$state` object
9
+ * as its props and mutate it — or use `update()`, which assigns onto that
10
+ * same object.
11
+ */
12
+ import { TermNode } from './renderer/node.js';
13
+ interface InlineHooks {
14
+ /** Hand the top n screen rows of the live area to scrollback. */
15
+ releaseTop(n: number): void;
16
+ }
17
+ /** Called by run() in inline mode so FrameLogs can archive rows. */
18
+ export declare function registerInlineHooks(root: TermNode, hooks: InlineHooks): void;
19
+ interface FrameLogDeps {
20
+ mount: (component: any, options: any) => object;
21
+ unmount: (app: object) => void;
22
+ renderer: unknown;
23
+ }
24
+ export declare class FrameLog {
25
+ private host;
26
+ private deps;
27
+ private frames;
28
+ private nextId;
29
+ constructor(host: TermNode, deps?: FrameLogDeps);
30
+ /** Mount a component as a new frame at the bottom of the log. */
31
+ append<P extends Record<string, unknown>>(component: unknown, props: P): number;
32
+ /**
33
+ * Assign new values onto the frame's props object. Reaches the
34
+ * component only when the props were created with `$state`.
35
+ */
36
+ update(id: number, partial: Record<string, unknown>): void;
37
+ /**
38
+ * Archive every frame up to and including `id`: their rows stay on
39
+ * the terminal and scroll into history; their components unmount.
40
+ */
41
+ archive(id: number): void;
42
+ /** Remove a frame outright — its rows clear and the log reflows. */
43
+ remove(id: number): void;
44
+ /** IDs of the frames still live, in order. */
45
+ liveFrames(): number[];
46
+ private dispose;
47
+ private rootHooks;
48
+ }
49
+ /**
50
+ * A FrameLog bound to a host element — from a component, grab the node
51
+ * with `bind:this` and create the log once on mount.
52
+ */
53
+ export declare function createFrameLog(host: TermNode): FrameLog;
54
+ export {};