xbintsc 0.5.20 → 0.5.22
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.
- package/AGENTS.md +5 -3
- package/dist/src/driver/compiler.js +1 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/node/fs/index.d.ts +4 -3
- package/dist/src/extensions/node/fs/index.js +12 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs-promises/index.d.ts +5 -5
- package/dist/src/extensions/node/fs-promises/index.js +15 -6
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
- package/dist/tests/e2e/async-await.test.js +1 -1
- package/dist/tests/e2e/async-fs.test.d.ts +9 -0
- package/dist/tests/e2e/async-fs.test.js +119 -0
- package/dist/tests/e2e/async-fs.test.js.map +1 -0
- package/dist/tests/e2e/fetch.test.js +24 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas.test.js +159 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-example-canvas.test.d.ts +11 -0
- package/dist/tests/e2e/gui-example-canvas.test.js +70 -0
- package/dist/tests/e2e/gui-example-canvas.test.js.map +1 -0
- package/dist/tests/e2e/gui-inline-svg.test.d.ts +11 -0
- package/dist/tests/e2e/gui-inline-svg.test.js +160 -0
- package/dist/tests/e2e/gui-inline-svg.test.js.map +1 -0
- package/doc/ai/contributing.md +2 -1
- package/doc/ai/extensions.md +8 -6
- package/doc/ai/language-support.md +2 -1
- package/doc/ai/zh-CN/contributing.md +1 -1
- package/doc/ai/zh-CN/extensions.md +6 -4
- package/doc/ai/zh-CN/language-support.md +2 -1
- package/doc/gui/api.md +8 -0
- package/doc/gui/canvas.md +47 -21
- package/doc/gui/known-issues.md +10 -9
- package/doc/gui/media.md +32 -3
- package/doc/gui.md +31 -3
- package/doc/implemented/async.md +16 -3
- package/doc/implemented/fetch.md +38 -6
- package/doc/implemented/node/fs-promises.md +19 -8
- package/doc/implemented/node.md +1 -1
- package/doc/implemented.md +1 -1
- package/doc/unimplemented/deviations.md +1 -1
- package/doc/unimplemented/node/async.md +6 -3
- package/doc/unimplemented/node/fs-promises.md +10 -6
- package/doc/unimplemented/node/fs.md +7 -8
- package/doc/unimplemented/node.md +2 -2
- package/doc/unimplemented/toolchain.md +1 -1
- package/doc/zh-CN/gui/api.md +7 -0
- package/doc/zh-CN/gui/canvas.md +36 -17
- package/doc/zh-CN/gui/known-issues.md +8 -7
- package/doc/zh-CN/gui/media.md +26 -2
- package/doc/zh-CN/gui.md +24 -3
- package/doc/zh-CN/implemented/async.md +13 -2
- package/doc/zh-CN/implemented/fetch.md +23 -4
- package/doc/zh-CN/implemented/node/fs-promises.md +6 -3
- package/doc/zh-CN/implemented/node.md +1 -1
- package/doc/zh-CN/implemented.md +1 -1
- package/doc/zh-CN/unimplemented/deviations.md +1 -1
- package/doc/zh-CN/unimplemented/node/async.md +6 -3
- package/doc/zh-CN/unimplemented/node/fs-promises.md +4 -4
- package/doc/zh-CN/unimplemented/node/fs.md +2 -2
- package/doc/zh-CN/unimplemented/node.md +2 -2
- package/doc/zh-CN/unimplemented/toolchain.md +1 -1
- package/package.json +1 -1
- package/runtime/ext_gui/canvas.cpp +114 -199
- package/runtime/ext_gui/canvas.h +101 -2
- package/runtime/ext_gui/canvas_gradient.cpp +112 -0
- package/runtime/ext_gui/canvas_internal.h +6 -6
- package/runtime/ext_gui/canvas_raster.cpp +27 -5
- package/runtime/ext_gui/canvas_stroke.cpp +252 -0
- package/runtime/ext_gui/canvas_style.cpp +240 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +38 -4
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +232 -0
- package/runtime/ext_gui/dom_api_internal.h +31 -0
- package/runtime/ext_gui/dom_api_proto.cpp +24 -0
- package/runtime/ext_gui/inline_svg.cpp +219 -0
- package/runtime/ext_gui/inline_svg.h +41 -0
- package/runtime/ext_gui/layout.cpp +18 -0
- package/runtime/ext_gui/layout.h +7 -0
- package/runtime/ext_gui/layout_flow.cpp +4 -3
- package/runtime/ext_gui/paint.cpp +14 -0
- package/runtime/ext_gui/paint.h +5 -1
- package/runtime/ext_gui/renderer_image.cpp +53 -25
- package/runtime/ext_gui/style_ua.cpp +3 -0
- package/runtime/ext_gui/svg.cpp +122 -39
- package/runtime/ext_gui/svg.h +14 -3
- package/runtime/ext_gui/window.cpp +28 -0
- package/runtime/ext_node/fs/async_api.c +314 -0
- package/runtime/ext_node/fs/async_complete.c +99 -0
- package/runtime/ext_node/fs/async_fh.c +206 -0
- package/runtime/ext_node/fs/async_job.c +81 -0
- package/runtime/ext_node/fs/async_job.h +132 -0
- package/runtime/ext_node/fs/async_worker.c +534 -0
- package/runtime/ext_node/fs/async_worker_dir.c +439 -0
- package/runtime/ext_node/fs/async_worker_dir.h +33 -0
- package/runtime/ext_node/fs/async_worker_fd.c +74 -0
- package/runtime/ext_node/fs/dir.c +60 -15
- package/runtime/ext_node/fs/fs_common.h +9 -0
- package/runtime/ext_node/fs/fs_ops.c +18 -29
- package/runtime/ext_node/fs/parts/builtins.inc +34 -0
- package/runtime/ext_node/fs/parts/platform.inc +66 -0
- package/runtime/ext_node/fs/promises.c +44 -234
- package/runtime/rt_internal.h +2 -0
- package/runtime/xt_async_io.c +333 -0
- package/runtime/xt_async_io.h +47 -0
- package/runtime/xt_fetch/async.inc +295 -0
- package/runtime/xt_fetch/request.inc +6 -183
- package/runtime/xt_fetch/socket.inc +5 -4
- package/runtime/xt_fetch.c +6 -4
- package/runtime/xt_loop.c +44 -3
- package/src/driver/compiler.ts +1 -1
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/node/fs/index.ts +12 -3
- package/src/extensions/node/fs-promises/index.ts +15 -6
|
@@ -159,6 +159,165 @@ describe.skipIf(!guiAvailable)("gui extension — canvas", () => {
|
|
|
159
159
|
expect(value("fill")).toBe("#ff0000");
|
|
160
160
|
expect(value("text")).toBe("true");
|
|
161
161
|
});
|
|
162
|
+
it("supports gradients, ellipses, rounded rects, dashes and line styles", () => {
|
|
163
|
+
const { value } = harness.compileAndRun("canvas-modern", `
|
|
164
|
+
import { createWindow, run } from "gui";
|
|
165
|
+
|
|
166
|
+
const HTML = \`
|
|
167
|
+
<html><head><style>body { margin: 0; }</style></head><body>
|
|
168
|
+
<canvas id="c" width="240" height="260"></canvas>
|
|
169
|
+
</body></html>
|
|
170
|
+
\`;
|
|
171
|
+
|
|
172
|
+
const win = createWindow({ title: "canvas-modern", width: 400, height: 300 });
|
|
173
|
+
win.on("ready", () => {
|
|
174
|
+
const canvas: any = win.document.getElementById("c");
|
|
175
|
+
const ctx: any = canvas.getContext("2d");
|
|
176
|
+
const px = (x: number, y: number): string => {
|
|
177
|
+
const d = ctx.getImageData(x, y, 1, 1).data;
|
|
178
|
+
return d[0] + "," + d[1] + "," + d[2] + "," + d[3];
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
ctx.fillStyle = "#ffffff";
|
|
182
|
+
ctx.fillRect(0, 0, 240, 260);
|
|
183
|
+
|
|
184
|
+
// A horizontal red -> blue gradient, sampled well inside both ends.
|
|
185
|
+
const linear = ctx.createLinearGradient(0, 0, 100, 0);
|
|
186
|
+
linear.addColorStop(0, "#ff0000");
|
|
187
|
+
linear.addColorStop(1, "#0000ff");
|
|
188
|
+
ctx.fillStyle = linear;
|
|
189
|
+
ctx.fillRect(0, 0, 100, 20);
|
|
190
|
+
console.log("grad-left=" + px(5, 10));
|
|
191
|
+
console.log("grad-right=" + px(95, 10));
|
|
192
|
+
// The getter returns the same handle, not a copy.
|
|
193
|
+
console.log("same-gradient=" + (ctx.fillStyle === linear));
|
|
194
|
+
|
|
195
|
+
// A stop added after assignment is live: the right side turns blue.
|
|
196
|
+
const live = ctx.createLinearGradient(0, 0, 100, 0);
|
|
197
|
+
live.addColorStop(0, "#ff0000");
|
|
198
|
+
ctx.fillStyle = live;
|
|
199
|
+
live.addColorStop(1, "#0000ff");
|
|
200
|
+
ctx.fillRect(0, 30, 100, 20);
|
|
201
|
+
console.log("live-right=" + px(95, 40));
|
|
202
|
+
|
|
203
|
+
const radial = ctx.createRadialGradient(50, 70, 0, 50, 70, 35);
|
|
204
|
+
radial.addColorStop(0, "#ffffff");
|
|
205
|
+
radial.addColorStop(1, "#000000");
|
|
206
|
+
ctx.fillStyle = radial;
|
|
207
|
+
ctx.fillRect(0, 60, 100, 40);
|
|
208
|
+
console.log("radial-centre=" + px(50, 70));
|
|
209
|
+
console.log("radial-edge=" + px(88, 70));
|
|
210
|
+
|
|
211
|
+
ctx.fillStyle = "#00ff00";
|
|
212
|
+
ctx.beginPath();
|
|
213
|
+
ctx.ellipse(170, 70, 40, 15, 0, 0, Math.PI * 2);
|
|
214
|
+
ctx.fill();
|
|
215
|
+
console.log("ellipse-centre=" + px(170, 70));
|
|
216
|
+
console.log("ellipse-outside=" + px(170, 40));
|
|
217
|
+
|
|
218
|
+
ctx.fillStyle = "#ff00ff";
|
|
219
|
+
ctx.beginPath();
|
|
220
|
+
ctx.roundRect(10, 100, 70, 50, 14);
|
|
221
|
+
ctx.fill();
|
|
222
|
+
console.log("roundrect-centre=" + px(45, 125));
|
|
223
|
+
console.log("roundrect-corner=" + px(11, 101));
|
|
224
|
+
|
|
225
|
+
ctx.strokeStyle = "#000000";
|
|
226
|
+
ctx.lineWidth = 4;
|
|
227
|
+
ctx.lineCap = "butt";
|
|
228
|
+
ctx.setLineDash([10, 10]);
|
|
229
|
+
ctx.lineDashOffset = 0;
|
|
230
|
+
console.log("cap=" + ctx.lineCap);
|
|
231
|
+
console.log("join=" + ctx.lineJoin);
|
|
232
|
+
console.log("miter=" + ctx.miterLimit);
|
|
233
|
+
console.log("dash=" + ctx.getLineDash().join(","));
|
|
234
|
+
ctx.beginPath();
|
|
235
|
+
ctx.moveTo(120, 150);
|
|
236
|
+
ctx.lineTo(230, 150);
|
|
237
|
+
ctx.stroke();
|
|
238
|
+
console.log("dash-on=" + px(124, 150));
|
|
239
|
+
console.log("dash-off=" + px(136, 150));
|
|
240
|
+
|
|
241
|
+
ctx.setLineDash([]);
|
|
242
|
+
ctx.strokeStyle = "#ff0000";
|
|
243
|
+
ctx.lineWidth = 10;
|
|
244
|
+
ctx.lineCap = "square";
|
|
245
|
+
ctx.beginPath();
|
|
246
|
+
ctx.moveTo(120, 190);
|
|
247
|
+
ctx.lineTo(180, 190);
|
|
248
|
+
ctx.stroke();
|
|
249
|
+
console.log("square-cap=" + px(115, 190));
|
|
250
|
+
console.log("square-cap-far=" + px(105, 190));
|
|
251
|
+
|
|
252
|
+
// A miter fills the outer corner right to the tip; a bevel cuts it off.
|
|
253
|
+
ctx.strokeStyle = "#000000";
|
|
254
|
+
ctx.lineWidth = 20;
|
|
255
|
+
ctx.lineCap = "butt";
|
|
256
|
+
ctx.lineJoin = "miter";
|
|
257
|
+
ctx.beginPath();
|
|
258
|
+
ctx.moveTo(120, 215);
|
|
259
|
+
ctx.lineTo(120, 245);
|
|
260
|
+
ctx.lineTo(160, 245);
|
|
261
|
+
ctx.stroke();
|
|
262
|
+
console.log("miter-join=" + px(111, 254));
|
|
263
|
+
ctx.lineJoin = "bevel";
|
|
264
|
+
ctx.beginPath();
|
|
265
|
+
ctx.moveTo(180, 215);
|
|
266
|
+
ctx.lineTo(180, 245);
|
|
267
|
+
ctx.lineTo(220, 245);
|
|
268
|
+
ctx.stroke();
|
|
269
|
+
console.log("bevel-join=" + px(171, 254));
|
|
270
|
+
|
|
271
|
+
ctx.save();
|
|
272
|
+
ctx.translate(100, 100);
|
|
273
|
+
ctx.rotate(Math.PI / 2);
|
|
274
|
+
const t = ctx.getTransform();
|
|
275
|
+
console.log("nested-transform=" + Math.round(t.e) + "," + Math.round(t.f));
|
|
276
|
+
// Post-multiplied: the square sits at (90,100)..(100,110), not at the
|
|
277
|
+
// rotated origin. A pre-multiplied transform would put it elsewhere.
|
|
278
|
+
ctx.fillStyle = "#ff8800";
|
|
279
|
+
ctx.fillRect(0, 0, 10, 10);
|
|
280
|
+
console.log("rotated-rect=" + px(95, 105));
|
|
281
|
+
ctx.restore();
|
|
282
|
+
const r = ctx.getTransform();
|
|
283
|
+
console.log("restored-transform=" + r.a + "," + r.e + "," + r.f);
|
|
284
|
+
|
|
285
|
+
win.close();
|
|
286
|
+
});
|
|
287
|
+
win.loadHTML(HTML);
|
|
288
|
+
run();
|
|
289
|
+
`);
|
|
290
|
+
const channels = (label) => value(label).split(",").map(Number);
|
|
291
|
+
const left = channels("grad-left");
|
|
292
|
+
expect(left[0]).toBeGreaterThan(200);
|
|
293
|
+
expect(left[2]).toBeLessThan(60);
|
|
294
|
+
const right = channels("grad-right");
|
|
295
|
+
expect(right[0]).toBeLessThan(60);
|
|
296
|
+
expect(right[2]).toBeGreaterThan(200);
|
|
297
|
+
expect(value("same-gradient")).toBe("true");
|
|
298
|
+
expect(channels("live-right")[2]).toBeGreaterThan(200);
|
|
299
|
+
// The radial gradient is white at its centre and black at its edge.
|
|
300
|
+
expect(channels("radial-centre")[0]).toBeGreaterThan(230);
|
|
301
|
+
expect(channels("radial-edge")[0]).toBeLessThan(40);
|
|
302
|
+
expect(channels("ellipse-centre")).toEqual([0, 255, 0, 255]);
|
|
303
|
+
expect(channels("ellipse-outside")).toEqual([255, 255, 255, 255]);
|
|
304
|
+
expect(channels("roundrect-centre")).toEqual([255, 0, 255, 255]);
|
|
305
|
+
expect(channels("roundrect-corner")).toEqual([255, 255, 255, 255]);
|
|
306
|
+
expect(value("cap")).toBe("butt");
|
|
307
|
+
expect(value("join")).toBe("miter");
|
|
308
|
+
expect(value("miter")).toBe("10");
|
|
309
|
+
expect(value("dash")).toBe("10,10");
|
|
310
|
+
expect(channels("dash-on")).toEqual([0, 0, 0, 255]);
|
|
311
|
+
expect(channels("dash-off")).toEqual([255, 255, 255, 255]);
|
|
312
|
+
// A square cap extends half a line width past the endpoint.
|
|
313
|
+
expect(channels("square-cap")).toEqual([255, 0, 0, 255]);
|
|
314
|
+
expect(channels("square-cap-far")).toEqual([255, 255, 255, 255]);
|
|
315
|
+
expect(channels("miter-join")).toEqual([0, 0, 0, 255]);
|
|
316
|
+
expect(channels("bevel-join")).toEqual([255, 255, 255, 255]);
|
|
317
|
+
expect(value("nested-transform")).toBe("100,100");
|
|
318
|
+
expect(channels("rotated-rect")).toEqual([255, 136, 0, 255]);
|
|
319
|
+
expect(value("restored-transform")).toBe("1,0,0");
|
|
320
|
+
});
|
|
162
321
|
it("draws an image onto a canvas with drawImage", () => {
|
|
163
322
|
const src = harness.imageSrc(harness.logoPath);
|
|
164
323
|
const { value } = harness.compileAndRun("canvas-drawimage", `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gui-canvas.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-canvas.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gui-canvas.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-canvas.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end smoke test for `examples/gui/pelican-canvas` — the animated
|
|
3
|
+
* `<canvas>` drawing.
|
|
4
|
+
*
|
|
5
|
+
* The drawing lives in `scene.js`, loaded by `index.html` through a plain
|
|
6
|
+
* `<script src>`: a browser runs it as-is, while xbintsc AOT-compiles the same
|
|
7
|
+
* body via the gui `.html` loader. `main.ts` only hosts the native window and
|
|
8
|
+
* reports deterministic pixel probes, so this test can assert that the
|
|
9
|
+
* gradients, paths and text really rasterised.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end smoke test for `examples/gui/pelican-canvas` — the animated
|
|
3
|
+
* `<canvas>` drawing.
|
|
4
|
+
*
|
|
5
|
+
* The drawing lives in `scene.js`, loaded by `index.html` through a plain
|
|
6
|
+
* `<script src>`: a browser runs it as-is, while xbintsc AOT-compiles the same
|
|
7
|
+
* body via the gui `.html` loader. `main.ts` only hosts the native window and
|
|
8
|
+
* reports deterministic pixel probes, so this test can assert that the
|
|
9
|
+
* gradients, paths and text really rasterised.
|
|
10
|
+
*/
|
|
11
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { spawnSync } from "node:child_process";
|
|
15
|
+
import { Script } from "node:vm";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
18
|
+
import { build } from "../../src/driver/compiler.js";
|
|
19
|
+
import { createDefaultRegistry } from "../../src/extensions/registry.js";
|
|
20
|
+
import { guiExtension } from "../../src/extensions/gui/index.js";
|
|
21
|
+
import { guiAvailable } from "./gui-helpers.js";
|
|
22
|
+
/** The example lives at <repo>/examples/gui/pelican-canvas. */
|
|
23
|
+
const exampleDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "examples", "gui", "pelican-canvas");
|
|
24
|
+
let workdir = "";
|
|
25
|
+
describe("pelican canvas example — browser entry", () => {
|
|
26
|
+
it("loads the shared scene as a classic script", () => {
|
|
27
|
+
const html = readFileSync(join(exampleDir, "index.html"), "utf8");
|
|
28
|
+
expect(html).toContain('<script src="./scene.js"></script>');
|
|
29
|
+
const script = readFileSync(join(exampleDir, "scene.js"), "utf8");
|
|
30
|
+
// A browser compiles this as a classic script: no ESM, no TypeScript.
|
|
31
|
+
expect(() => new Script(script)).not.toThrow();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe.skipIf(!guiAvailable)("gui example — pelican canvas", () => {
|
|
35
|
+
beforeAll(() => {
|
|
36
|
+
workdir = mkdtempSync(join(tmpdir(), "xbintsc-pelican-canvas-"));
|
|
37
|
+
for (const file of ["index.html", "main.ts", "scene.js"]) {
|
|
38
|
+
writeFileSync(join(workdir, file), readFileSync(join(exampleDir, file), "utf8"));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
afterAll(() => {
|
|
42
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
43
|
+
});
|
|
44
|
+
it("compiles the canvas drawing and reports pixels", () => {
|
|
45
|
+
const extensions = createDefaultRegistry().register(guiExtension);
|
|
46
|
+
const result = build(join(workdir, "main.ts"), {
|
|
47
|
+
emit: "exe",
|
|
48
|
+
outDir: join(workdir, "out"),
|
|
49
|
+
cacheDir: join(workdir, ".cache"),
|
|
50
|
+
extensions,
|
|
51
|
+
});
|
|
52
|
+
expect(result.diagnostics.filter((diagnostic) => diagnostic.category === "error")).toEqual([]);
|
|
53
|
+
const executed = spawnSync(result.outputPath, [], {
|
|
54
|
+
encoding: "utf8",
|
|
55
|
+
env: { ...process.env, XT_GUI_AUTOCLOSE_MS: "2000" },
|
|
56
|
+
});
|
|
57
|
+
expect(executed.status, executed.stderr).toBe(0);
|
|
58
|
+
const stdout = executed.stdout ?? "";
|
|
59
|
+
expect(stdout).toContain("window=1000x640 (gui extension, SDL_GPU)");
|
|
60
|
+
expect(stdout).toContain("canvas-box=x=0 y=0 1000x640");
|
|
61
|
+
expect(stdout).toContain("canvas-size=1000x640");
|
|
62
|
+
// The title is white text on the gradient sky.
|
|
63
|
+
expect(stdout).toContain("pixel-title=255,255,255,255");
|
|
64
|
+
// The road gradient, and the pelican body (not the sky colour).
|
|
65
|
+
expect(stdout).toMatch(/pixel-road=\d+,\d+,\d+,255/);
|
|
66
|
+
expect(stdout).toMatch(/pixel-pelican=\d+,\d+,\d+,255/);
|
|
67
|
+
expect(stdout).toContain("gui exited");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=gui-example-canvas.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gui-example-canvas.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-example-canvas.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end tests for the `gui` extension — inline `<svg>` elements.
|
|
3
|
+
*
|
|
4
|
+
* An inline `<svg>` tree is not an SVG *file*: the document already holds the
|
|
5
|
+
* nodes, so the engine serializes the subtree back into an SVG document and
|
|
6
|
+
* rasterises it exactly like `<img src="logo.svg">`. These tests pin the
|
|
7
|
+
* replaced-element sizing, the display-list entry, the serializer's canonical
|
|
8
|
+
* SVG names (the HTML parser lowercases `viewBox`/`linearGradient`) and the
|
|
9
|
+
* fact that a DOM mutation re-serializes the artwork.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end tests for the `gui` extension — inline `<svg>` elements.
|
|
3
|
+
*
|
|
4
|
+
* An inline `<svg>` tree is not an SVG *file*: the document already holds the
|
|
5
|
+
* nodes, so the engine serializes the subtree back into an SVG document and
|
|
6
|
+
* rasterises it exactly like `<img src="logo.svg">`. These tests pin the
|
|
7
|
+
* replaced-element sizing, the display-list entry, the serializer's canonical
|
|
8
|
+
* SVG names (the HTML parser lowercases `viewBox`/`linearGradient`) and the
|
|
9
|
+
* fact that a DOM mutation re-serializes the artwork.
|
|
10
|
+
*/
|
|
11
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
12
|
+
import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
|
|
13
|
+
const harness = createGuiHarness();
|
|
14
|
+
describe.skipIf(!guiAvailable)("gui extension — inline svg", () => {
|
|
15
|
+
beforeAll(() => harness.setup());
|
|
16
|
+
afterAll(() => harness.cleanup());
|
|
17
|
+
it("sizes and paints an inline svg as a replaced element", () => {
|
|
18
|
+
const { stdout, stderr } = harness.compileAndRun("inline-svg", `
|
|
19
|
+
import { createWindow, run } from "gui";
|
|
20
|
+
|
|
21
|
+
const HTML = \`
|
|
22
|
+
<html><head><style>
|
|
23
|
+
body { margin: 0; }
|
|
24
|
+
#scaled { width: 64px; height: 48px; }
|
|
25
|
+
</style></head><body>
|
|
26
|
+
<svg id="natural" width="8" height="6" viewBox="0 0 8 6">
|
|
27
|
+
<rect width="8" height="6" fill="#ff0000"/>
|
|
28
|
+
<circle cx="4" cy="3" r="2" fill="#0000ff"/>
|
|
29
|
+
</svg>
|
|
30
|
+
<svg id="viewbox" viewBox="0 0 20 10">
|
|
31
|
+
<rect width="20" height="10" fill="#00ff00"/>
|
|
32
|
+
</svg>
|
|
33
|
+
<svg id="scaled" width="8" height="6" viewBox="0 0 8 6">
|
|
34
|
+
<rect width="8" height="6" fill="#ff0000"/>
|
|
35
|
+
</svg>
|
|
36
|
+
</body></html>
|
|
37
|
+
\`;
|
|
38
|
+
|
|
39
|
+
const win = createWindow({ title: "inline-svg", width: 400, height: 300 });
|
|
40
|
+
win.on("ready", () => {
|
|
41
|
+
const natural = win.getBoundingClientRect("#natural");
|
|
42
|
+
console.log("natural=" + natural.width + "x" + natural.height);
|
|
43
|
+
const viewbox = win.getBoundingClientRect("#viewbox");
|
|
44
|
+
console.log("viewbox=" + viewbox.width + "x" + viewbox.height);
|
|
45
|
+
const scaled = win.getBoundingClientRect("#scaled");
|
|
46
|
+
console.log("scaled=" + scaled.width + "x" + scaled.height);
|
|
47
|
+
console.log("display=" + win.computedStyle("#natural", "display"));
|
|
48
|
+
console.log("hit=" + win.hitTest(4, 3));
|
|
49
|
+
console.log("no-rects=" + (win.layoutTree().indexOf("<rect>") < 0));
|
|
50
|
+
console.log("paint-list-start");
|
|
51
|
+
console.log(win.paintList());
|
|
52
|
+
console.log("paint-list-end");
|
|
53
|
+
const info = win.inlineSvg("#natural");
|
|
54
|
+
console.log("markup=" + info.markup);
|
|
55
|
+
console.log("parsed=" + info.width + "x" + info.height + " shapes=" + info.shapes);
|
|
56
|
+
});
|
|
57
|
+
win.loadHTML(HTML);
|
|
58
|
+
run();
|
|
59
|
+
`);
|
|
60
|
+
// width/height attributes give the intrinsic size; a bare viewBox is the
|
|
61
|
+
// fallback; an explicit CSS size wins over both.
|
|
62
|
+
expect(stdout).toContain("natural=8x6");
|
|
63
|
+
expect(stdout).toContain("viewbox=20x10");
|
|
64
|
+
expect(stdout).toContain("scaled=64x48");
|
|
65
|
+
// The `<svg>` box itself is the hit-test target; its children are vector
|
|
66
|
+
// art, not HTML boxes, so the walk stays at the svg element.
|
|
67
|
+
expect(stdout).toContain("hit=svg#natural");
|
|
68
|
+
expect(stdout).toContain("display=inline-block");
|
|
69
|
+
// The svg subtree produces no HTML boxes: only the replaced box is laid out.
|
|
70
|
+
expect(stdout).toContain("no-rects=true");
|
|
71
|
+
// A source newline between two inline elements is a hard line break in this
|
|
72
|
+
// engine (like `<br>`), so the three boxes stack in document order.
|
|
73
|
+
const list = stdout.slice(stdout.indexOf("paint-list-start"), stdout.indexOf("paint-list-end"));
|
|
74
|
+
expect(list).toContain("image x=0.0 y=0.0 w=8.0 h=6.0 inline-svg=");
|
|
75
|
+
expect(list).toContain("image x=0.0 y=6.0 w=20.0 h=10.0 inline-svg=");
|
|
76
|
+
expect(list).toContain("image x=0.0 y=16.0 w=64.0 h=48.0 inline-svg=");
|
|
77
|
+
// The serialized document restores the case-sensitive SVG names the HTML
|
|
78
|
+
// parser lowercased, and only the svg subtree is included.
|
|
79
|
+
const markup = stdout
|
|
80
|
+
.split("\n")
|
|
81
|
+
.find((line) => line.startsWith("markup="))
|
|
82
|
+
.slice("markup=".length);
|
|
83
|
+
expect(markup.startsWith('<svg xmlns="http://www.w3.org/2000/svg" id="natural" width="8" height="6" viewBox="0 0 8 6">')).toBe(true);
|
|
84
|
+
expect(markup).toContain('<rect width="8" height="6" fill="#ff0000"></rect>');
|
|
85
|
+
expect(markup).toContain('<circle cx="4" cy="3" r="2" fill="#0000ff"></circle>');
|
|
86
|
+
expect(markup).not.toContain("viewbox=");
|
|
87
|
+
expect(stdout).toContain("parsed=8x6 shapes=2");
|
|
88
|
+
// Rasterising a parsed document must actually succeed.
|
|
89
|
+
expect(stderr).not.toContain("could not rasterise inline SVG");
|
|
90
|
+
});
|
|
91
|
+
it("parses gradients, re-serializes on mutation and restyles the box", () => {
|
|
92
|
+
const { stdout, stderr } = harness.compileAndRun("inline-svg-mutate", `
|
|
93
|
+
import { createWindow, run } from "gui";
|
|
94
|
+
|
|
95
|
+
const HTML = \`
|
|
96
|
+
<html><head><style>
|
|
97
|
+
body { margin: 0; }
|
|
98
|
+
#icon { background: #123456; border: 2px solid #00ff00; }
|
|
99
|
+
</style></head><body>
|
|
100
|
+
<svg id="icon" width="24" height="24" viewBox="0 0 24 24">
|
|
101
|
+
<defs>
|
|
102
|
+
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="24" y2="24">
|
|
103
|
+
<stop offset="0%" stop-color="#ff0000"/>
|
|
104
|
+
<stop offset="100%" stop-color="#0000ff"/>
|
|
105
|
+
</linearGradient>
|
|
106
|
+
</defs>
|
|
107
|
+
<rect id="box" width="24" height="24" fill="url(#grad)"/>
|
|
108
|
+
</svg>
|
|
109
|
+
</body></html>
|
|
110
|
+
\`;
|
|
111
|
+
|
|
112
|
+
const win = createWindow({ title: "inline-svg-mutate", width: 400, height: 300 });
|
|
113
|
+
win.on("ready", () => {
|
|
114
|
+
const first = win.inlineSvg("#icon");
|
|
115
|
+
console.log("shapes=" + first.shapes);
|
|
116
|
+
console.log("gradient=" + (first.markup.indexOf("linearGradient") >= 0));
|
|
117
|
+
console.log("units=" + (first.markup.indexOf('gradientUnits="userSpaceOnUse"') >= 0));
|
|
118
|
+
console.log("stops=" + first.markup.split("<stop").length);
|
|
119
|
+
|
|
120
|
+
const icon = win.document.getElementById("icon");
|
|
121
|
+
icon.setAttribute("width", "48");
|
|
122
|
+
const rect = win.document.querySelector("rect");
|
|
123
|
+
rect.setAttribute("fill", "#00ff00");
|
|
124
|
+
const grown = win.getBoundingClientRect("#icon");
|
|
125
|
+
const second = win.inlineSvg("#icon");
|
|
126
|
+
console.log("grown=" + grown.width + "x" + grown.height);
|
|
127
|
+
console.log("moved=" + (second.markup.indexOf('fill="#00ff00"') >= 0));
|
|
128
|
+
|
|
129
|
+
icon.innerHTML = '<circle cx="12" cy="12" r="10" fill="#ffff00"/>';
|
|
130
|
+
const third = win.inlineSvg("#icon");
|
|
131
|
+
console.log("replaced=" + (third.markup.indexOf("<circle") >= 0) + " shapes=" + third.shapes);
|
|
132
|
+
console.log("paint-list-start");
|
|
133
|
+
console.log(win.paintList());
|
|
134
|
+
console.log("paint-list-end");
|
|
135
|
+
});
|
|
136
|
+
win.loadHTML(HTML);
|
|
137
|
+
run();
|
|
138
|
+
`);
|
|
139
|
+
expect(stdout).toContain("shapes=1");
|
|
140
|
+
// The HTML parser lowercases `linearGradient`; the serializer restores it,
|
|
141
|
+
// or NanoSVG would silently drop the gradient definition.
|
|
142
|
+
expect(stdout).toContain("gradient=true");
|
|
143
|
+
expect(stdout).toContain("units=true");
|
|
144
|
+
// start + two stops = three occurrences of "<stop".
|
|
145
|
+
expect(stdout).toContain("stops=3");
|
|
146
|
+
// A presentation-attribute mutation changes the intrinsic size, and a paint
|
|
147
|
+
// mutation changes the serialized document the renderer rasterises.
|
|
148
|
+
expect(stdout).toContain("grown=52x28");
|
|
149
|
+
expect(stdout).toContain("moved=true");
|
|
150
|
+
// `innerHTML` swaps the artwork for a new subtree.
|
|
151
|
+
expect(stdout).toContain("replaced=true shapes=1");
|
|
152
|
+
const list = stdout.slice(stdout.indexOf("paint-list-start"), stdout.indexOf("paint-list-end"));
|
|
153
|
+
// The svg element is still an ordinary styled box: background + 4 borders.
|
|
154
|
+
expect(list).toContain("color=#123456ff");
|
|
155
|
+
expect((list.match(/#00ff00ff/g) ?? []).length).toBe(4);
|
|
156
|
+
expect(list).toContain("w=48.0 h=24.0 inline-svg=");
|
|
157
|
+
expect(stderr).not.toContain("could not rasterise inline SVG");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=gui-inline-svg.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gui-inline-svg.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-inline-svg.test.ts"],"names":[],"mappings":"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"}
|
package/doc/ai/contributing.md
CHANGED
|
@@ -110,7 +110,8 @@ diff scratch/ref.ll scratch/gen2.ll
|
|
|
110
110
|
therefore never allocate per access.
|
|
111
111
|
- The GC is a non-moving mark-sweep collector: new heap objects go through
|
|
112
112
|
`xt_alloc`, and a value must be reachable from an explicit root slot, a
|
|
113
|
-
registered root provider (the event loop, the microtask queue
|
|
113
|
+
registered root provider (the event loop, the microtask queue, the async I/O
|
|
114
|
+
job roots) or the
|
|
114
115
|
conservative C-stack scan while it is live. A value held only in a C local that
|
|
115
116
|
is not on the scanned stack is a bug.
|
|
116
117
|
|
package/doc/ai/extensions.md
CHANGED
|
@@ -36,7 +36,7 @@ resolve to the same implementation.
|
|
|
36
36
|
| Module | Notes |
|
|
37
37
|
| --- | --- |
|
|
38
38
|
| `fs` | **synchronous API only** (`readFileSync`, `writeFileSync`, …); `fs/promises` is separate |
|
|
39
|
-
| `fs/promises` | promise-based file APIs |
|
|
39
|
+
| `fs/promises` | promise-based file APIs; the blocking I/O runs on the async I/O worker pool and settles on the reactor |
|
|
40
40
|
| `path` | also hooks namespace dispatch, so `path.join(...)` works |
|
|
41
41
|
| `os` | |
|
|
42
42
|
| `process` | `process.cwd()`, `argv`, `env`, …; namespace dispatch too |
|
|
@@ -54,8 +54,9 @@ Coverage per module (exact functions and options) is in
|
|
|
54
54
|
[../unimplemented/node.md](../unimplemented/node.md).
|
|
55
55
|
|
|
56
56
|
Remember the runtime model before building on `net`/`http`: the event loop runs
|
|
57
|
-
**after** the program body, but `async`/`await`
|
|
58
|
-
|
|
57
|
+
**after** the program body, but `async`/`await` suspends on pending promises, so a
|
|
58
|
+
promise settled from a socket callback, a timer or an offloaded file/HTTP job can
|
|
59
|
+
be awaited. `net.connect` itself is still blocking. See
|
|
59
60
|
[language-support.md](./language-support.md).
|
|
60
61
|
|
|
61
62
|
### `gui` — HTML/CSS window
|
|
@@ -75,9 +76,10 @@ media API (`play`/`pause`/`currentTime`/`volume`/…) plus the
|
|
|
75
76
|
program drive playback. There are no built-in controls — draw your own chrome
|
|
76
77
|
with elements and CSS (see `examples/gui/media.ts`). Video frames and audio PCM
|
|
77
78
|
are decoded on the main thread. `<canvas>` is supported too: `getContext("2d")`
|
|
78
|
-
returns a context whose fills, paths, text and `drawImage` are
|
|
79
|
-
self-written CPU rasteriser
|
|
80
|
-
`
|
|
79
|
+
returns a context whose fills, paths, gradients, text and `drawImage` are
|
|
80
|
+
rasterised by a self-written CPU rasteriser (plus line caps/joins, dashed
|
|
81
|
+
strokes, `ellipse` and `roundRect`), and element handles expose `canvas` drawing
|
|
82
|
+
plus `width`/`height`. CSS `transform`/`transform-origin` work as paint-time
|
|
81
83
|
effects, so a box (and the text, images and children in it) can be translated,
|
|
82
84
|
scaled and rotated without reflowing anything — animate it per frame from
|
|
83
85
|
`requestAnimationFrame` or with `transition: transform` (see
|
|
@@ -75,7 +75,8 @@ Canonical detail (always current, much longer):
|
|
|
75
75
|
| `setTimeout` and callbacks | ✅ | timers run on the core event loop; `await new Promise(r => setTimeout(r, ms))` works |
|
|
76
76
|
| `queueMicrotask` / `setImmediate` / `performance.now` | ✅ | microtasks share the promise FIFO queue; `setImmediate` is a zero-delay timer with numeric ids; `performance.now` is monotonic fractional milliseconds |
|
|
77
77
|
| Sockets / servers (`http`, `net`, `dgram`) | ✅ | callbacks run on the event loop and can settle a promise you await (`net` / `http` examples included in the tests) |
|
|
78
|
-
| `fs/promises` |
|
|
78
|
+
| `fs/promises` | ✅ | file, directory and `FileHandle` operations run on the async I/O worker pool and settle on the reactor, so timers and sockets keep running while they wait |
|
|
79
|
+
| Global `fetch` | ✅ | HTTP/1.1 client whose connect/send/receive runs on the same worker pool; `await fetch(...)` yields and redirects are followed on the reactor. `http://` only (no TLS) and the body is buffered before resolving |
|
|
79
80
|
| Worker threads | 🚫 | workers are child processes |
|
|
80
81
|
| `async` generators | ✅ | suspend at `await` and `yield`; every `.next()` returns a promise; `for await...of` supported |
|
|
81
82
|
|
|
@@ -93,7 +93,7 @@ diff scratch/ref.ll scratch/gen2.ll
|
|
|
93
93
|
- 字符串字面量会被驻留:每个不同字面量只在 `main` 中创建一次,存入 GC 根全局,
|
|
94
94
|
每次使用只做一次 load;属性名因此不再逐次分配。
|
|
95
95
|
- GC 是非移动标记清扫:新堆对象走 `xt_alloc`;值存活期间必须能从显式根槽、已注册的
|
|
96
|
-
|
|
96
|
+
根提供者(事件循环、微任务队列、异步 I/O job 根)或 C 栈保守扫描到达。只存在于未被扫描的 C 局部
|
|
97
97
|
变量里的值就是 bug。
|
|
98
98
|
|
|
99
99
|
## 修改运行时
|
|
@@ -35,7 +35,7 @@ xbintsc run app.ts --ext node
|
|
|
35
35
|
| 模块 | 说明 |
|
|
36
36
|
| --- | --- |
|
|
37
37
|
| `fs` | **仅同步 API**(`readFileSync`、`writeFileSync` 等);`fs/promises` 是独立模块 |
|
|
38
|
-
| `fs/promises` | 基于 Promise 的文件 API |
|
|
38
|
+
| `fs/promises` | 基于 Promise 的文件 API;阻塞 I/O 跑在异步 I/O 线程池上并在 reactor 上 settle |
|
|
39
39
|
| `path` | 同时接入命名空间分发,`path.join(...)` 可用 |
|
|
40
40
|
| `os` | |
|
|
41
41
|
| `process` | `process.cwd()`、`argv`、`env` 等;也接入命名空间分发 |
|
|
@@ -53,8 +53,9 @@ xbintsc run app.ts --ext node
|
|
|
53
53
|
[../../zh-CN/unimplemented/node.md](../../zh-CN/unimplemented/node.md)。
|
|
54
54
|
|
|
55
55
|
在 `net`/`http` 上搭东西之前,先记住运行时模型:事件循环在**程序主体之后**才运行,
|
|
56
|
-
但 `async`/`await`
|
|
57
|
-
|
|
56
|
+
但 `async`/`await` 会在 pending Promise 上挂起,因此由套接字回调、定时器或 offload 的
|
|
57
|
+
文件/HTTP job settle 的 Promise 都可以 await。`net.connect` 本身仍是阻塞的。见
|
|
58
|
+
[language-support.md](./language-support.md)。
|
|
58
59
|
|
|
59
60
|
### `gui` —— HTML/CSS 窗口
|
|
60
61
|
|
|
@@ -70,7 +71,8 @@ import { createWindow, run } from "gui";
|
|
|
70
71
|
`loadedmetadata`/`play`/`pause`/`ended`/`timeupdate`/`error` 事件可让程序驱动播放。
|
|
71
72
|
没有内置控件——用元素和 CSS 自绘控件(见 `examples/gui/media.ts`);视频帧与音频 PCM
|
|
72
73
|
都在主线程解码。也支持 `<canvas>`:`getContext("2d")` 返回的上下文,其填充、路径、
|
|
73
|
-
|
|
74
|
+
渐变、文本与 `drawImage` 由自研 CPU 光栅器处理(另有线段端点/连接、虚线描边、
|
|
75
|
+
`ellipse` 与 `roundRect`),元素句柄还暴露 `width`/`height`。CSS
|
|
74
76
|
`transform`/`transform-origin` 作为绘制期效果可用,因此一个盒子(以及其中的文本、
|
|
75
77
|
图像与子元素)可以被平移、缩放与旋转而不触发任何重排——可以用
|
|
76
78
|
`requestAnimationFrame` 逐帧驱动,或写成 `transition: transform`(见
|
|
@@ -72,7 +72,8 @@ xbintsc 编译 TypeScript 的一个**实用子集**。本页用来判断你能
|
|
|
72
72
|
| `setTimeout` 与回调 | ✅ | 定时器运行在核心事件循环上;`await new Promise(r => setTimeout(r, ms))` 可用 |
|
|
73
73
|
| `queueMicrotask` / `setImmediate` / `performance.now` | ✅ | 微任务与 Promise 共用 FIFO 队列;`setImmediate` 是零延迟定时器,返回数字 id;`performance.now` 返回单调递增的毫秒小数 |
|
|
74
74
|
| 套接字/服务端(`http`、`net`、`dgram`) | ✅ | 回调在事件循环上执行,可以 settle 你正在 await 的 Promise(测试含 `net`/`http` 示例) |
|
|
75
|
-
| `fs/promises` |
|
|
75
|
+
| `fs/promises` | ✅ | 文件、目录与 `FileHandle` 操作跑在异步 I/O 线程池上,并在 reactor 上 settle,等待期间定时器与套接字继续运行 |
|
|
76
|
+
| 全局 `fetch` | ✅ | HTTP/1.1 客户端,connect/send/receive 跑在同一线程池;`await fetch(...)` 会让出,重定向在 reactor 上处理。仅 `http://`(无 TLS),resolve 前缓冲整个响应体 |
|
|
76
77
|
| worker 线程 | 🚫 | worker 是子进程 |
|
|
77
78
|
| `async` 生成器 | ✅ | 可在 `await` 与 `yield` 处挂起;每次 `.next()` 返回 Promise;支持 `for await...of` |
|
|
78
79
|
|
package/doc/gui/api.md
CHANGED
|
@@ -65,6 +65,7 @@ win.documentTree() // serialized DOM (debugging)
|
|
|
65
65
|
win.layoutTree() // serialized layout boxes (debugging)
|
|
66
66
|
win.paintCount() // number of shapes in the display list
|
|
67
67
|
win.paintList() // serialized display list (debugging)
|
|
68
|
+
win.inlineSvg(selector) // serialized inline <svg>: { markup, width, height, shapes }
|
|
68
69
|
win.measureText(text, fontSize?, family?) // shaped advance width in pixels
|
|
69
70
|
win.fontMetrics(fontSize?, family?) // { ascent, descent, lineHeight, ready }
|
|
70
71
|
win.hitTest(x, y) // deepest element descriptor, or ""
|
|
@@ -73,6 +74,13 @@ win.advance(ms) // step the CSS transition clock (testing
|
|
|
73
74
|
win.stats() // engine/runtime counters (memory debugging)
|
|
74
75
|
```
|
|
75
76
|
|
|
77
|
+
`win.inlineSvg(selector)` returns the standalone SVG document the engine builds
|
|
78
|
+
from the first `<svg>` element matching `selector` (`markup`), together with the
|
|
79
|
+
intrinsic size and shape count NanoSVG parses out of it; it is `undefined` when
|
|
80
|
+
nothing matches or the element is not an `<svg>`. It makes the serializer's
|
|
81
|
+
canonical casing (`viewBox`, `linearGradient`, `gradientUnits`, …) testable
|
|
82
|
+
without a GPU — the HTML parser delivers those names lowercased.
|
|
83
|
+
|
|
76
84
|
`win.stats()` returns `{ heapBytes, heapAllocations, treeNodes, detachedRoots,`
|
|
77
85
|
`detachedNodes, handles, handleSlots, listeners }`: the live runtime heap, the
|
|
78
86
|
size of the DOM tree, the detached-node pool, and the element-handle tables. In a
|