xbintsc 0.5.21 → 0.5.23
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 +1 -1
- package/README.md +15 -5
- package/README.zh-CN.md +11 -4
- package/dist/src/cli/hints.js +11 -0
- package/dist/src/cli/hints.js.map +1 -1
- package/dist/src/cli/main.js +1 -1
- package/dist/src/driver/compiler.js +1 -0
- package/dist/src/driver/compiler.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/catalog.js +2 -1
- package/dist/src/extensions/catalog.js.map +1 -1
- package/dist/src/extensions/media/index.d.ts +27 -0
- package/dist/src/extensions/media/index.js +60 -0
- package/dist/src/extensions/media/index.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +12 -0
- package/dist/src/extensions/registry.js +16 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/tests/cli/hints.test.js +15 -0
- package/dist/tests/cli/hints.test.js.map +1 -1
- package/dist/tests/cli/process.test.js +12 -0
- package/dist/tests/cli/process.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 +72 -0
- package/dist/tests/e2e/gui-example-canvas.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +3 -0
- package/dist/tests/e2e/gui-helpers.js +2 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-media-disabled.test.d.ts +9 -0
- package/dist/tests/e2e/gui-media-disabled.test.js +42 -0
- package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -0
- package/dist/tests/e2e/gui-media.test.d.ts +6 -4
- package/dist/tests/e2e/gui-media.test.js +15 -12
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/unicode-output.test.d.ts +16 -0
- package/dist/tests/e2e/unicode-output.test.js +65 -0
- package/dist/tests/e2e/unicode-output.test.js.map +1 -0
- package/dist/tests/extensions/media.test.d.ts +1 -0
- package/dist/tests/extensions/media.test.js +58 -0
- package/dist/tests/extensions/media.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +8 -0
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/dist/tests/lexer/identifiers.test.js +12 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -1
- package/dist/tests/lexer/scanner.test.js +8 -0
- package/dist/tests/lexer/scanner.test.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +30 -0
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/ai/cli.md +1 -1
- package/doc/ai/extensions.md +35 -22
- package/doc/ai/troubleshooting.md +9 -0
- package/doc/ai/zh-CN/cli.md +1 -1
- package/doc/ai/zh-CN/extensions.md +22 -9
- package/doc/ai/zh-CN/troubleshooting.md +8 -0
- package/doc/gui/build.md +37 -21
- package/doc/gui/canvas.md +47 -21
- package/doc/gui/known-issues.md +7 -6
- package/doc/gui/media.md +26 -11
- package/doc/gui.md +50 -34
- package/doc/zh-CN/gui/build.md +28 -16
- package/doc/zh-CN/gui/canvas.md +36 -17
- package/doc/zh-CN/gui/known-issues.md +6 -5
- package/doc/zh-CN/gui/media.md +21 -10
- package/doc/zh-CN/gui.md +43 -34
- package/package.json +2 -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/gui_media.cpp +69 -2
- package/runtime/ext_gui/media.cpp +34 -488
- package/runtime/ext_gui/media.h +54 -19
- package/runtime/ext_gui/media_api.h +65 -0
- package/runtime/ext_media/media.cpp +507 -0
- package/runtime/ext_media/media_bridge.c +35 -0
- package/scripts/build-gui.ts +6 -72
- package/scripts/build-media.ts +245 -0
- package/src/cli/hints.ts +11 -0
- package/src/cli/main.ts +1 -1
- package/src/driver/compiler.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/catalog.ts +2 -1
- package/src/extensions/media/index.ts +67 -0
- package/src/extensions/registry.ts +24 -0
- package/src/index.ts +1 -0
|
@@ -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,72 @@
|
|
|
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. Probe its whole box: the
|
|
63
|
+
// ink offset depends on the platform font (YaHei vs STHeiti/PingFang), so a
|
|
64
|
+
// single point at the left edge is not stable across platforms.
|
|
65
|
+
expect(stdout).toContain("pixel-title=255,255,255,255");
|
|
66
|
+
// The road gradient, and the pelican body (not the sky colour).
|
|
67
|
+
expect(stdout).toMatch(/pixel-road=\d+,\d+,\d+,255/);
|
|
68
|
+
expect(stdout).toMatch(/pixel-pelican=\d+,\d+,\d+,255/);
|
|
69
|
+
expect(stdout).toContain("gui exited");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# 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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"}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* test PNG; the suite calls `setup()` in `beforeAll` and `cleanup()` in
|
|
19
19
|
* `afterAll`.
|
|
20
20
|
*/
|
|
21
|
+
import { type Extension } from "../../src/extensions/registry.js";
|
|
21
22
|
/**
|
|
22
23
|
* True when the GUI archive exists and the suite may run.
|
|
23
24
|
*
|
|
@@ -37,6 +38,8 @@ export interface GuiRunResult {
|
|
|
37
38
|
export interface GuiRunOptions {
|
|
38
39
|
/** Extra files written into the work directory before the build. */
|
|
39
40
|
files?: Record<string, string>;
|
|
41
|
+
/** Additional extensions to register next to `gui` (e.g. `media`). */
|
|
42
|
+
extensions?: readonly Extension[];
|
|
40
43
|
/**
|
|
41
44
|
* Milliseconds the frame loop may run before `XT_GUI_AUTOCLOSE_MS` closes
|
|
42
45
|
* every window. A program whose handlers close the window does not need this
|
|
@@ -60,6 +60,8 @@ export function createGuiHarness() {
|
|
|
60
60
|
const entry = join(workdir, `${name}.ts`);
|
|
61
61
|
writeFileSync(entry, source);
|
|
62
62
|
const extensions = createDefaultRegistry().register(guiExtension);
|
|
63
|
+
for (const extension of options.extensions ?? [])
|
|
64
|
+
extensions.register(extension);
|
|
63
65
|
const result = build(entry, {
|
|
64
66
|
emit: "exe",
|
|
65
67
|
outDir: join(workdir, `out-${name}`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gui-helpers.js","sourceRoot":"","sources":["../../../tests/e2e/gui-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"gui-helpers.js","sourceRoot":"","sources":["../../../tests/e2e/gui-helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<video>` / `<audio>` are part of the GUI DOM even when no decoder is linked.
|
|
3
|
+
*
|
|
4
|
+
* This suite compiles with `--ext gui` only (the harness never registers the
|
|
5
|
+
* `media` extension), which is the whole point of the split: a GUI program does
|
|
6
|
+
* not link FFmpeg unless it asks for it. The element still exists and its DOM
|
|
7
|
+
* API is callable, but opening a source fails and fires `error`.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<video>` / `<audio>` are part of the GUI DOM even when no decoder is linked.
|
|
3
|
+
*
|
|
4
|
+
* This suite compiles with `--ext gui` only (the harness never registers the
|
|
5
|
+
* `media` extension), which is the whole point of the split: a GUI program does
|
|
6
|
+
* not link FFmpeg unless it asks for it. The element still exists and its DOM
|
|
7
|
+
* API is callable, but opening a source fails and fires `error`.
|
|
8
|
+
*/
|
|
9
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
10
|
+
import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
|
|
11
|
+
describe.skipIf(!guiAvailable)("gui extension without media", () => {
|
|
12
|
+
const harness = createGuiHarness();
|
|
13
|
+
beforeAll(() => harness.setup());
|
|
14
|
+
afterAll(() => harness.cleanup());
|
|
15
|
+
it("reports error instead of decoding when the media extension is off", () => {
|
|
16
|
+
const { stdout } = harness.compileAndRun("media-off", `
|
|
17
|
+
import { createWindow, run } from "gui";
|
|
18
|
+
|
|
19
|
+
const win = createWindow({ title: "media off", width: 320, height: 200 });
|
|
20
|
+
win.on("ready", () => {
|
|
21
|
+
win.loadHTML('<video id="v" src="missing.mp4"></video>');
|
|
22
|
+
const v: any = win.document.getElementById("v");
|
|
23
|
+
v.addEventListener("loadedmetadata", () => {
|
|
24
|
+
console.log("unexpected-metadata");
|
|
25
|
+
win.close();
|
|
26
|
+
});
|
|
27
|
+
v.addEventListener("error", () => {
|
|
28
|
+
console.log("media-error");
|
|
29
|
+
win.close();
|
|
30
|
+
});
|
|
31
|
+
// The DOM surface is still there even without a decoder.
|
|
32
|
+
console.log("api=" + (v.paused === true) + ":" + (v.duration === 0));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
run();
|
|
36
|
+
`, { autocloseMs: 1500 });
|
|
37
|
+
expect(stdout).toContain("media-error");
|
|
38
|
+
expect(stdout).not.toContain("unexpected-metadata");
|
|
39
|
+
expect(stdout).toContain("api=true:true");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=gui-media-disabled.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gui-media-disabled.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-media-disabled.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,6BAA6B,EAAE,GAAG,EAAE;IACjE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CACtC,WAAW,EACX;;;;;;;;;;;;;;;;;;;;OAoBC,EACD,EAAE,WAAW,EAAE,IAAI,EAAE,CACtB,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* End-to-end tests for `<video>` / `<audio>` support
|
|
2
|
+
* End-to-end tests for `<video>` / `<audio>` support.
|
|
3
3
|
*
|
|
4
|
-
* These exercise the real decoding path (
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* These exercise the real decoding path (the `media` extension's vendored
|
|
5
|
+
* FFmpeg + the GUI's SDL audio sink) and the DOM media API. They are skipped
|
|
6
|
+
* unless both the `gui` and `media` archives exist: the `gui` extension alone
|
|
7
|
+
* is deliberately decoder-free. The fixtures live in `examples/resources`; when
|
|
8
|
+
* they are absent (a trim checkout) the suite is skipped rather than failing.
|
|
7
9
|
*/
|
|
8
10
|
export {};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* End-to-end tests for `<video>` / `<audio>` support
|
|
2
|
+
* End-to-end tests for `<video>` / `<audio>` support.
|
|
3
3
|
*
|
|
4
|
-
* These exercise the real decoding path (
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* These exercise the real decoding path (the `media` extension's vendored
|
|
5
|
+
* FFmpeg + the GUI's SDL audio sink) and the DOM media API. They are skipped
|
|
6
|
+
* unless both the `gui` and `media` archives exist: the `gui` extension alone
|
|
7
|
+
* is deliberately decoder-free. The fixtures live in `examples/resources`; when
|
|
8
|
+
* they are absent (a trim checkout) the suite is skipped rather than failing.
|
|
7
9
|
*/
|
|
8
10
|
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
9
11
|
import { tmpdir } from "node:os";
|
|
@@ -14,6 +16,8 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
|
14
16
|
import { build } from "../../src/driver/compiler.js";
|
|
15
17
|
import { createDefaultRegistry } from "../../src/extensions/registry.js";
|
|
16
18
|
import { guiExtension } from "../../src/extensions/gui/index.js";
|
|
19
|
+
import { mediaExtension } from "../../src/extensions/media/index.js";
|
|
20
|
+
import { findRuntimeLibrary } from "../../src/driver/runtime-lib.js";
|
|
17
21
|
import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
|
|
18
22
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
19
23
|
const resourceDir = join(root, "examples", "resources");
|
|
@@ -27,13 +31,12 @@ function mediaSrc(absolutePath) {
|
|
|
27
31
|
}
|
|
28
32
|
const resourcesAvailable = existsSync(moviePath) && existsSync(soundPath);
|
|
29
33
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* even with `xbintsc_GUI_TESTS=1`.
|
|
34
|
+
* The `media` archive is built separately (`npm run media`) and is POSIX-only
|
|
35
|
+
* for now. Without it the suite would compile GUI programs that report the
|
|
36
|
+
* expected `error` events, i.e. it would test the wrong thing.
|
|
34
37
|
*/
|
|
35
|
-
const mediaAvailable = process.platform !== "win32";
|
|
36
|
-
describe.skipIf(!guiAvailable || !resourcesAvailable || !mediaAvailable)("gui
|
|
38
|
+
const mediaAvailable = process.platform !== "win32" && findRuntimeLibrary("media") !== undefined;
|
|
39
|
+
describe.skipIf(!guiAvailable || !resourcesAvailable || !mediaAvailable)("gui + media extensions", () => {
|
|
37
40
|
const harness = createGuiHarness();
|
|
38
41
|
beforeAll(() => harness.setup());
|
|
39
42
|
afterAll(() => harness.cleanup());
|
|
@@ -81,7 +84,7 @@ describe.skipIf(!guiAvailable || !resourcesAvailable || !mediaAvailable)("gui ex
|
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
run();
|
|
84
|
-
`, { autocloseMs: 8000 });
|
|
87
|
+
`, { autocloseMs: 8000, extensions: [mediaExtension] });
|
|
85
88
|
expect(stdout).not.toContain("video-error");
|
|
86
89
|
expect(stdout).not.toContain("audio-error");
|
|
87
90
|
expect(value("duration")).toBe("true:true");
|
|
@@ -98,7 +101,7 @@ describe.skipIf(!guiAvailable || !resourcesAvailable || !mediaAvailable)("gui ex
|
|
|
98
101
|
try {
|
|
99
102
|
writeFileSync(join(workdir, "media.html"), readFileSync(join(exampleDir, "media.html"), "utf8"));
|
|
100
103
|
writeFileSync(join(workdir, "media.ts"), readFileSync(join(exampleDir, "media.ts"), "utf8"));
|
|
101
|
-
const extensions = createDefaultRegistry().register(guiExtension);
|
|
104
|
+
const extensions = createDefaultRegistry().register(guiExtension).register(mediaExtension);
|
|
102
105
|
const result = build(join(workdir, "media.ts"), {
|
|
103
106
|
emit: "exe",
|
|
104
107
|
outDir: join(workdir, "out"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gui-media.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-media.test.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gui-media.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-media.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression coverage for Chinese (and other non-ASCII) text reaching stdout.
|
|
3
|
+
*
|
|
4
|
+
* The driver reads a source file as UTF-8 bytes, the lexer stores a literal's
|
|
5
|
+
* bytes, and the runtime writes those bytes straight to `stdout`/`stderr`. A
|
|
6
|
+
* mis-encoded literal, a UTF-16 code-unit leak or a console writer that
|
|
7
|
+
* re-encodes the bytes changes the output sequence even when adjacent English
|
|
8
|
+
* text still looks fine — the issue reports exactly that:
|
|
9
|
+
*
|
|
10
|
+
* console.log('你好世界!');
|
|
11
|
+
* console.log('Hello World!');
|
|
12
|
+
*
|
|
13
|
+
* The program is run with its stdout/stderr captured as pipes, so these cases
|
|
14
|
+
* pin the bytes the terminal receives without depending on a local code page.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression coverage for Chinese (and other non-ASCII) text reaching stdout.
|
|
3
|
+
*
|
|
4
|
+
* The driver reads a source file as UTF-8 bytes, the lexer stores a literal's
|
|
5
|
+
* bytes, and the runtime writes those bytes straight to `stdout`/`stderr`. A
|
|
6
|
+
* mis-encoded literal, a UTF-16 code-unit leak or a console writer that
|
|
7
|
+
* re-encodes the bytes changes the output sequence even when adjacent English
|
|
8
|
+
* text still looks fine — the issue reports exactly that:
|
|
9
|
+
*
|
|
10
|
+
* console.log('你好世界!');
|
|
11
|
+
* console.log('Hello World!');
|
|
12
|
+
*
|
|
13
|
+
* The program is run with its stdout/stderr captured as pipes, so these cases
|
|
14
|
+
* pin the bytes the terminal receives without depending on a local code page.
|
|
15
|
+
*/
|
|
16
|
+
import { expect, it } from "vitest";
|
|
17
|
+
import { describeE2E } from "./harness.js";
|
|
18
|
+
/** The UTF-8 bytes of `text`, as hexadecimal, so a re-encoding cannot pass. */
|
|
19
|
+
function utf8Hex(text) {
|
|
20
|
+
return Buffer.from(text, "utf8").toString("hex");
|
|
21
|
+
}
|
|
22
|
+
const issueProgram = `console.log('你好世界!');
|
|
23
|
+
console.log('Hello World!');
|
|
24
|
+
`;
|
|
25
|
+
describeE2E("non-ASCII console output", (harness) => {
|
|
26
|
+
it("prints the issue's Chinese and English lines as exact UTF-8", () => {
|
|
27
|
+
const result = harness.runProgramFull(issueProgram, { name: "chinese_issue" });
|
|
28
|
+
expect(result.status).toBe(0);
|
|
29
|
+
expect(result.stderr).toBe("");
|
|
30
|
+
expect(result.stdout).toBe("你好世界!\nHello World!\n");
|
|
31
|
+
expect(utf8Hex(result.stdout)).toBe("e4bda0e5a5bde4b896e7958cefbc810a48656c6c6f20576f726c64210a");
|
|
32
|
+
});
|
|
33
|
+
it("prints Chinese literals, escapes, templates and identifiers byte-identically", () => {
|
|
34
|
+
const result = harness.runProgramFull(`
|
|
35
|
+
const 名字 = "世界";
|
|
36
|
+
console.log("你好," + 名字 + "!");
|
|
37
|
+
console.log("\\u4f60\\u597d\\uff0c\\u4e16\\u754c\\uff01");
|
|
38
|
+
console.log(\`你好,\${名字}!\`);
|
|
39
|
+
console.log(["中文", { 键: "值" }]);
|
|
40
|
+
console.log(JSON.stringify({ 你好: "世界" }));
|
|
41
|
+
`, { name: "chinese_values" });
|
|
42
|
+
expect(result.status).toBe(0);
|
|
43
|
+
expect(result.stderr).toBe("");
|
|
44
|
+
expect(result.stdout).toBe([
|
|
45
|
+
"你好,世界!",
|
|
46
|
+
"你好,世界!",
|
|
47
|
+
"你好,世界!",
|
|
48
|
+
"[ '中文', { '键': '值' } ]",
|
|
49
|
+
'{"你好":"世界"}',
|
|
50
|
+
"",
|
|
51
|
+
].join("\n"));
|
|
52
|
+
});
|
|
53
|
+
it("routes Chinese console levels to the matching stream", () => {
|
|
54
|
+
const result = harness.runProgramFull(`
|
|
55
|
+
console.log("日志:中文");
|
|
56
|
+
console.info("信息:中文");
|
|
57
|
+
console.warn("警告:中文");
|
|
58
|
+
console.error("错误:中文");
|
|
59
|
+
`, { name: "chinese_console_levels" });
|
|
60
|
+
expect(result.status).toBe(0);
|
|
61
|
+
expect(result.stdout).toBe("日志:中文\n信息:中文\n");
|
|
62
|
+
expect(result.stderr).toBe("警告:中文\n错误:中文\n");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=unicode-output.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unicode-output.test.js","sourceRoot":"","sources":["../../../tests/e2e/unicode-output.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,+EAA+E;AAC/E,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,YAAY,GAAG;;CAEpB,CAAC;AAEF,WAAW,CAAC,0BAA0B,EAAE,CAAC,OAAO,EAAE,EAAE;IAClD,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACpD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CACnC;;;;;;;OAOC,EACD,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAC3B,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACxB;YACE,QAAQ;YACR,QAAQ;YACR,QAAQ;YACR,wBAAwB;YACxB,aAAa;YACb,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CACnC;;;;;OAKC,EACD,EAAE,IAAI,EAAE,wBAAwB,EAAE,CACnC,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|