xbintsc 0.5.41 → 0.5.42

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 (39) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/driver/version.d.ts +1 -1
  3. package/dist/src/driver/version.js +1 -1
  4. package/dist/tests/e2e/gui-data-url.test.d.ts +10 -0
  5. package/dist/tests/e2e/gui-data-url.test.js +127 -0
  6. package/dist/tests/e2e/gui-data-url.test.js.map +1 -0
  7. package/dist/tests/e2e/gui-effects.test.js +96 -0
  8. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  9. package/dist/tests/e2e/gui-paint.test.js +77 -0
  10. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  11. package/doc/gui/canvas.md +6 -2
  12. package/doc/gui/known-issues.md +15 -9
  13. package/doc/gui/media.md +11 -2
  14. package/doc/gui/rendering.md +43 -18
  15. package/doc/gui.md +5 -4
  16. package/doc/zh-CN/gui/canvas.md +6 -4
  17. package/doc/zh-CN/gui/known-issues.md +11 -8
  18. package/doc/zh-CN/gui/media.md +9 -2
  19. package/doc/zh-CN/gui/rendering.md +30 -11
  20. package/doc/zh-CN/gui.md +1 -1
  21. package/package.json +7 -7
  22. package/runtime/ext_gui/dom_api_canvas.cpp +42 -24
  23. package/runtime/ext_gui/gradient_values.cpp +44 -48
  24. package/runtime/ext_gui/gui_events.cpp +16 -13
  25. package/runtime/ext_gui/image.cpp +180 -25
  26. package/runtime/ext_gui/image.h +6 -4
  27. package/runtime/ext_gui/paint.cpp +53 -3
  28. package/runtime/ext_gui/paint.h +11 -1
  29. package/runtime/ext_gui/paint_background.cpp +92 -11
  30. package/runtime/ext_gui/paint_effects.cpp +2 -1
  31. package/runtime/ext_gui/paint_internal.h +5 -1
  32. package/runtime/ext_gui/renderer.cpp +81 -13
  33. package/runtime/ext_gui/renderer.h +19 -5
  34. package/runtime/ext_gui/renderer_internal.h +26 -0
  35. package/runtime/ext_gui/renderer_layer.cpp +552 -0
  36. package/runtime/ext_gui/style.h +29 -1
  37. package/runtime/ext_gui/style_print.cpp +10 -2
  38. package/runtime/ext_gui/style_values.cpp +10 -0
  39. package/src/driver/version.ts +1 -1
@@ -4,4 +4,4 @@
4
4
  * It lives apart from the command dispatch so the executable entry point stays
5
5
  * under the repository's per-file line budget; the text is data, not logic.
6
6
  */
7
- export declare const HELP = "xbintsc 0.5.41 - TypeScript binary compiler\n\nUsage:\n xbintsc build <file.ts> [options] Compile to a native binary\n xbintsc run <file.ts> [-- args] Compile and execute\n xbintsc check <file.ts> Parse, bind and check without linking\n xbintsc watch <file.ts> [options] Rebuild whenever the source tree changes\n xbintsc emit <file.ts> Print LLVM IR\n xbintsc doctor Report the resolved toolchain\n xbintsc version Print the version\n xbintsc help Show this message\n\nOptions:\n -o, --output <path> Output path\n --out <dir> Output directory (default: build/)\n --emit <kind> exe | obj | ir (default: exe)\n -O0..-O3 Optimization level (default: -O2)\n --ext <names> Comma separated extensions (e.g. node,gui,media)\n --ext-native <m> Register a C++/Rust extension from a JSON manifest\n (comma separated for several)\n --config <path> Use a project config (default: auto-detect\n xbintsc.config.json)\n --no-config Do not read any project config\n --icon <path> Embed an application icon (PNG/ICO/ICNS)\n --bundle macOS: also produce a .app bundle\n --app-name <name> Bundle / display name\n --app-id <id> macOS bundle identifier (e.g. com.example.demo)\n --force Ignore the incremental cache\n --verbose Print progress information\n --time Print per-phase build timings\n --debug Emit debug info (DWARF/CodeView) and keep frame pointers\n --check Run the semantic checker (default)\n --no-check Skip the semantic checker; only erase type syntax\n --lto Compile program and runtime to LTO bitcode; link with lld\n --pgo-generate [dir] Build an instrumented binary writing profiles to dir\n --pgo-use [dir] Build with the profiles collected from --pgo-generate\n --json Print diagnostics as a JSON array on stdout\n --no-hints Do not append the \"hint: \u2026 see <doc>\" line to failures\n\nxbintsc compiles a subset of TypeScript to a native binary. Types are erased;\nonly inference-free facts (names, redeclarations, call arity) are checked.\nNode built-ins need --ext node, packages in node_modules are bundled at build\ntime, and clang 16+ is required to link.\n\nInstalled as a project dependency (npm):\n npx xbintsc doctor\n npx xbintsc build <entry.ts> --out build\n npx xbintsc run <entry.ts>\nThe first build compiles the C runtime; later builds reuse the cache.\n\nDocs: doc/ai/README.md (task guide) - doc/ai/language-support.md (subset) -\ndoc/ai/troubleshooting.md (failures) - llms.txt (index of every document).\nWhen installed from npm, these live under node_modules/xbintsc/doc/ and\nnode_modules/xbintsc/doc/npm-package.md is the project guide.\n";
7
+ export declare const HELP = "xbintsc 0.5.42 - TypeScript binary compiler\n\nUsage:\n xbintsc build <file.ts> [options] Compile to a native binary\n xbintsc run <file.ts> [-- args] Compile and execute\n xbintsc check <file.ts> Parse, bind and check without linking\n xbintsc watch <file.ts> [options] Rebuild whenever the source tree changes\n xbintsc emit <file.ts> Print LLVM IR\n xbintsc doctor Report the resolved toolchain\n xbintsc version Print the version\n xbintsc help Show this message\n\nOptions:\n -o, --output <path> Output path\n --out <dir> Output directory (default: build/)\n --emit <kind> exe | obj | ir (default: exe)\n -O0..-O3 Optimization level (default: -O2)\n --ext <names> Comma separated extensions (e.g. node,gui,media)\n --ext-native <m> Register a C++/Rust extension from a JSON manifest\n (comma separated for several)\n --config <path> Use a project config (default: auto-detect\n xbintsc.config.json)\n --no-config Do not read any project config\n --icon <path> Embed an application icon (PNG/ICO/ICNS)\n --bundle macOS: also produce a .app bundle\n --app-name <name> Bundle / display name\n --app-id <id> macOS bundle identifier (e.g. com.example.demo)\n --force Ignore the incremental cache\n --verbose Print progress information\n --time Print per-phase build timings\n --debug Emit debug info (DWARF/CodeView) and keep frame pointers\n --check Run the semantic checker (default)\n --no-check Skip the semantic checker; only erase type syntax\n --lto Compile program and runtime to LTO bitcode; link with lld\n --pgo-generate [dir] Build an instrumented binary writing profiles to dir\n --pgo-use [dir] Build with the profiles collected from --pgo-generate\n --json Print diagnostics as a JSON array on stdout\n --no-hints Do not append the \"hint: \u2026 see <doc>\" line to failures\n\nxbintsc compiles a subset of TypeScript to a native binary. Types are erased;\nonly inference-free facts (names, redeclarations, call arity) are checked.\nNode built-ins need --ext node, packages in node_modules are bundled at build\ntime, and clang 16+ is required to link.\n\nInstalled as a project dependency (npm):\n npx xbintsc doctor\n npx xbintsc build <entry.ts> --out build\n npx xbintsc run <entry.ts>\nThe first build compiles the C runtime; later builds reuse the cache.\n\nDocs: doc/ai/README.md (task guide) - doc/ai/language-support.md (subset) -\ndoc/ai/troubleshooting.md (failures) - llms.txt (index of every document).\nWhen installed from npm, these live under node_modules/xbintsc/doc/ and\nnode_modules/xbintsc/doc/npm-package.md is the project guide.\n";
@@ -9,4 +9,4 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export declare const COMPILER_VERSION = "0.5.41";
12
+ export declare const COMPILER_VERSION = "0.5.42";
@@ -9,5 +9,5 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export const COMPILER_VERSION = "0.5.41";
12
+ export const COMPILER_VERSION = "0.5.42";
13
13
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * End-to-end tests for `data:` image URLs: raster and SVG, base64 and
3
+ * percent-encoded payloads, in `<img>`, `canvas.drawImage(String, …)` and CSS
4
+ * `background-image`.
5
+ *
6
+ * The SVG below deliberately exercises the decoder, not NanoSVG's feature set:
7
+ * the engine rasterises NanoSVG's subset, so an SVG that uses filters still
8
+ * decodes but renders the shapes without the filter effect.
9
+ */
10
+ export {};
@@ -0,0 +1,127 @@
1
+ /**
2
+ * End-to-end tests for `data:` image URLs: raster and SVG, base64 and
3
+ * percent-encoded payloads, in `<img>`, `canvas.drawImage(String, …)` and CSS
4
+ * `background-image`.
5
+ *
6
+ * The SVG below deliberately exercises the decoder, not NanoSVG's feature set:
7
+ * the engine rasterises NanoSVG's subset, so an SVG that uses filters still
8
+ * decodes but renders the shapes without the filter effect.
9
+ */
10
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
11
+ import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
12
+ const harness = createGuiHarness();
13
+ /* 4x4 PNG: left half red, right half blue. */
14
+ const PNG = "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAFElEQVR42mP4z8DwH4Sh1H8G0gUALFAf4eNWqTEAAAAASUVORK5CYII=";
15
+ const SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="8" height="6"><rect width="8" height="6" fill="#ff0000"/></svg>';
16
+ const SVG_B64 = Buffer.from(SVG).toString("base64");
17
+ const SVG_ENC = encodeURIComponent(SVG);
18
+ /** The `feTurbulence` example from the request: `<filter>` is not in NanoSVG's
19
+ * subset, but the document still decodes (the rect renders, unfiltered). */
20
+ const NOISE = "data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.8'/%3E%3C/svg%3E";
21
+ const PAGE = `
22
+ <html><head><style>body { margin: 0; }</style></head><body>
23
+ <img id="vec" src="data:image/svg+xml,${SVG_ENC}">
24
+ <img id="vec64" src="data:image/svg+xml;base64,${SVG_B64}">
25
+ <img id="noise" src="${NOISE}">
26
+ <div id="bg" style="width:20px;height:20px"></div>
27
+ <canvas id="c" width="240" height="240"></canvas>
28
+ </body></html>`;
29
+ describe.skipIf(!guiAvailable)("gui extension — data URL images", () => {
30
+ beforeAll(() => harness.setup());
31
+ afterAll(() => harness.cleanup());
32
+ it("decodes SVG data URLs in <img>, drawImage and CSS backgrounds", () => {
33
+ const { value, stderr } = harness.compileAndRun("data-url-svg", `
34
+ import { createWindow, run } from "gui";
35
+
36
+ const win = createWindow({ title: "data-url", width: 500, height: 400 });
37
+ win.on("ready", () => {
38
+ const doc: any = document;
39
+ const ctx: any = doc.getElementById("c").getContext("2d");
40
+ const px = (x: number, y: number): string => {
41
+ const d = ctx.getImageData(x, y, 1, 1).data;
42
+ return d[0] + "," + d[1] + "," + d[2] + "," + d[3];
43
+ };
44
+ const clear = (): void => { ctx.clearRect(0, 0, 240, 240); };
45
+ const box = (id: string): string => {
46
+ const r: any = doc.getElementById(id).getBoundingClientRect();
47
+ return r.width + "x" + r.height;
48
+ };
49
+
50
+ const png = "data:image/png;base64,${PNG}";
51
+ const svgEnc = "data:image/svg+xml,${SVG_ENC}";
52
+ const svg64 = "data:image/svg+xml;base64,${SVG_B64}";
53
+ const noise = "${NOISE}";
54
+
55
+ console.log("vec-box=" + box("vec"));
56
+ console.log("vec64-box=" + box("vec64"));
57
+ console.log("noise-box=" + box("noise"));
58
+
59
+ // A raster data URL drawn from its string, without an <img>.
60
+ clear();
61
+ ctx.drawImage(png, 0, 0);
62
+ console.log("png-string=" + px(0, 0) + ":" + px(3, 0));
63
+
64
+ // SVG data URLs in both encodings, as a string and as an <img>.
65
+ clear();
66
+ ctx.drawImage(svgEnc, 0, 0);
67
+ console.log("svg-enc-string=" + px(0, 0) + ":" + px(7, 5));
68
+
69
+ clear();
70
+ ctx.drawImage(svg64, 0, 0);
71
+ console.log("svg64-string=" + px(0, 0));
72
+
73
+ clear();
74
+ ctx.drawImage(svgEnc, 0, 0, 16, 12);
75
+ console.log("svg-scaled=" + px(0, 0) + ":" + px(15, 11));
76
+
77
+ clear();
78
+ ctx.drawImage(doc.getElementById("vec"), 0, 0);
79
+ console.log("svg-img=" + px(0, 0));
80
+
81
+ clear();
82
+ ctx.drawImage(doc.getElementById("vec64"), 0, 0);
83
+ console.log("svg64-img=" + px(0, 0));
84
+
85
+ // The request's feTurbulence document: it decodes (opaque rect), while
86
+ // the filter itself remains outside NanoSVG's subset.
87
+ clear();
88
+ ctx.drawImage(noise, 0, 0, 32, 32);
89
+ console.log("noise-alpha=" + ctx.getImageData(0, 0, 1, 1).data[3]);
90
+
91
+ // SVG data URL as a CSS background.
92
+ const bg: any = doc.getElementById("bg");
93
+ bg.style.backgroundImage = 'url("' + svgEnc + '")';
94
+ console.log("bg-css=" + getComputedStyle(bg).backgroundImage.substring(0, 26));
95
+ let hits = 0;
96
+ for (const line of win.paintList().split("\\n")) {
97
+ if (line.indexOf("src=data:image/svg+xml") >= 0) hits++;
98
+ }
99
+ console.log("paint-svg=" + hits);
100
+
101
+ // Let one rendered frame decode the background texture (stderr reports
102
+ // a failure), then close.
103
+ requestAnimationFrame(() => {
104
+ requestAnimationFrame(() => {
105
+ win.close();
106
+ });
107
+ });
108
+ });
109
+ win.loadHTML(\`${PAGE}\`);
110
+ run();
111
+ `, { autocloseMs: 3000 });
112
+ expect(value("vec-box")).toBe("8x6");
113
+ expect(value("vec64-box")).toBe("8x6");
114
+ expect(value("noise-box")).toBe("180x180");
115
+ expect(value("png-string")).toBe("255,0,0,255:0,0,255,255");
116
+ expect(value("svg-enc-string")).toBe("255,0,0,255:255,0,0,255");
117
+ expect(value("svg64-string")).toBe("255,0,0,255");
118
+ expect(value("svg-scaled")).toBe("255,0,0,255:255,0,0,255");
119
+ expect(value("svg-img")).toBe("255,0,0,255");
120
+ expect(value("svg64-img")).toBe("255,0,0,255");
121
+ expect(Number(value("noise-alpha"))).toBeGreaterThan(0);
122
+ expect(value("bg-css")).toMatch(/^url\(data:image\/svg\+xml/);
123
+ expect(value("paint-svg")).toBe("4");
124
+ expect(stderr).not.toContain("could not decode image");
125
+ });
126
+ });
127
+ //# sourceMappingURL=gui-data-url.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gui-data-url.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-data-url.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;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,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,8CAA8C;AAC9C,MAAM,GAAG,GACP,0GAA0G,CAAC;AAC7G,MAAM,GAAG,GACP,gHAAgH,CAAC;AACnH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACpD,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;AAExC;4EAC4E;AAC5E,MAAM,KAAK,GACT,4SAA4S,CAAC;AAE/S,MAAM,IAAI,GAAG;;wCAE2B,OAAO;iDACE,OAAO;uBACjC,KAAK;;;eAGb,CAAC;AAEhB,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,iCAAiC,EAAE,GAAG,EAAE;IACrE,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,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,cAAc,EACd;;;;;;;;;;;;;;;;;6CAiBuC,GAAG;6CACH,OAAO;mDACD,OAAO;yBACjC,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAwDP,IAAI;;OAEpB,EACD,EAAE,WAAW,EAAE,IAAI,EAAE,CACtB,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAChE,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAC9D,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -138,6 +138,102 @@ describe.skipIf(!guiAvailable)("gui extension — effects", () => {
138
138
  // Radial bands plus the linear strips.
139
139
  expect(Number(value("count"))).toBeGreaterThan(30);
140
140
  });
141
+ it("tiles repeating gradients across the whole box", () => {
142
+ const { value, stdout } = harness.compileAndRun("repeating-gradient", `
143
+ import { createWindow, run } from "gui";
144
+
145
+ const HTML = \`
146
+ <html><head><style>
147
+ body { margin: 0; }
148
+ #r {
149
+ width: 100px; height: 40px;
150
+ background: repeating-linear-gradient(90deg,
151
+ #ff0000 0%, #ff0000 10%, #0000ff 10%, #0000ff 20%);
152
+ }
153
+ </style></head><body><div id="r"></div></body></html>
154
+ \`;
155
+
156
+ const win = createWindow({ title: "repeating-gradient", width: 400, height: 300 });
157
+ win.on("ready", () => {
158
+ console.log("image=" + win.computedStyle("#r", "background-image"));
159
+ console.log("list-start");
160
+ console.log(win.paintList());
161
+ console.log("list-end");
162
+ win.close();
163
+ });
164
+ win.loadHTML(HTML);
165
+ run();
166
+ `);
167
+ const list = stdout.slice(stdout.indexOf("list-start"), stdout.indexOf("list-end"));
168
+ // The computed value keeps the repeating function name ...
169
+ expect(value("image")).toBe("repeating-linear-gradient()");
170
+ // ... and the whole 100px box is tiled: a 20% period gives five red and
171
+ // five blue bands, not one ramp that holds the final colour.
172
+ expect((list.match(/#ff0000ff/g) ?? []).length).toBeGreaterThan(4);
173
+ expect((list.match(/#0000ffff/g) ?? []).length).toBeGreaterThan(4);
174
+ });
175
+ it("resolves pixel offsets in repeating gradients", () => {
176
+ const { value, stdout } = harness.compileAndRun("repeating-gradient-px", `
177
+ import { createWindow, run } from "gui";
178
+
179
+ const HTML = \`
180
+ <html><head><style>
181
+ body { margin: 0; }
182
+ #r {
183
+ width: 100px; height: 20px;
184
+ background: repeating-linear-gradient(90deg,
185
+ #123456 0px 10px, #abcdef 10px 20px);
186
+ }
187
+ </style></head><body><div id="r"></div></body></html>
188
+ \`;
189
+
190
+ const win = createWindow({ title: "repeating-gradient-px", width: 400, height: 300 });
191
+ win.on("ready", () => {
192
+ console.log("image=" + win.computedStyle("#r", "background-image"));
193
+ console.log("list-start");
194
+ console.log(win.paintList());
195
+ console.log("list-end");
196
+ win.close();
197
+ });
198
+ win.loadHTML(HTML);
199
+ run();
200
+ `);
201
+ const list = stdout.slice(stdout.indexOf("list-start"), stdout.indexOf("list-end"));
202
+ expect(value("image")).toBe("repeating-linear-gradient()");
203
+ expect((list.match(/#123456ff/g) ?? []).length).toBeGreaterThan(4);
204
+ expect((list.match(/#abcdefff/g) ?? []).length).toBeGreaterThan(4);
205
+ });
206
+ it("tiles repeating radial gradients", () => {
207
+ const { value, stdout } = harness.compileAndRun("repeating-radial-gradient", `
208
+ import { createWindow, run } from "gui";
209
+
210
+ const HTML = \`
211
+ <html><head><style>
212
+ body { margin: 0; }
213
+ #r {
214
+ width: 40px; height: 40px;
215
+ background: repeating-radial-gradient(circle at 50% 50%,
216
+ #ff0000 0%, #ff0000 5%, #0000ff 5%, #0000ff 10%);
217
+ }
218
+ </style></head><body><div id="r"></div></body></html>
219
+ \`;
220
+
221
+ const win = createWindow({ title: "repeating-radial-gradient", width: 400, height: 300 });
222
+ win.on("ready", () => {
223
+ console.log("image=" + win.computedStyle("#r", "background-image"));
224
+ console.log("list-start");
225
+ console.log(win.paintList());
226
+ console.log("list-end");
227
+ win.close();
228
+ });
229
+ win.loadHTML(HTML);
230
+ run();
231
+ `);
232
+ const list = stdout.slice(stdout.indexOf("list-start"), stdout.indexOf("list-end"));
233
+ expect(value("image")).toBe("repeating-radial-gradient()");
234
+ expect(list).toContain("#ff0000ff");
235
+ expect(list).toContain("#0000ffff");
236
+ });
141
237
  it("paints text-shadow copies behind the text", () => {
142
238
  const { value, stdout } = harness.compileAndRun("text-shadow", `
143
239
  import { createWindow, run } from "gui";
@@ -1 +1 @@
1
- {"version":3,"file":"gui-effects.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-effects.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-effects.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-effects.test.ts"],"names":[],"mappings":"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"}
@@ -187,5 +187,82 @@ describe.skipIf(!guiAvailable)("gui extension — paint", () => {
187
187
  // Anything above 0 is painted in full (there is no partial compositing yet).
188
188
  expect(list).toContain("#ff0000ff");
189
189
  });
190
+ it("applies fractional `opacity` to every painted colour in the subtree", () => {
191
+ const { value, stdout } = harness.compileAndRun("opacity-half", `
192
+ import { createWindow, run } from "gui";
193
+
194
+ const HTML = \`
195
+ <html><head><style>
196
+ body { margin: 0; background-color: #000000; }
197
+ #half { width: 40px; height: 20px; background: #ff0000; opacity: 0.5; }
198
+ #nested { width: 20px; height: 10px; background: #00ff00; color: #00ff00; opacity: 0.5; }
199
+ #label { color: #ffff00; }
200
+ </style></head><body>
201
+ <div id="half"><div id="nested"><span id="label">faded</span></div></div>
202
+ </body></html>
203
+ \`;
204
+
205
+ const win = createWindow({ title: "opacity-half", width: 400, height: 300 });
206
+ win.on("ready", () => {
207
+ console.log("opacity=" + win.computedStyle("#nested", "opacity"));
208
+ console.log("list-start");
209
+ console.log(win.paintList());
210
+ console.log("list-end");
211
+ win.close();
212
+ });
213
+ win.loadHTML(HTML);
214
+ run();
215
+ `);
216
+ const list = stdout.slice(stdout.indexOf("list-start"), stdout.indexOf("list-end"));
217
+ expect(value("opacity")).toBe("0.5");
218
+ // #half's red background, then #nested's 0.5 on top of it, then the text's
219
+ // inherited 0.25: alpha is multiplied down the paint chain instead of
220
+ // treating any value above zero as opaque.
221
+ expect(list).toContain("color=#ff000080");
222
+ expect(list).toContain("color=#00ff0040");
223
+ expect(list).toContain("color=#ffff0040");
224
+ });
225
+ it("wraps `mix-blend-mode` boxes in a compositing layer", () => {
226
+ const { value, stdout, stderr } = harness.compileAndRun("mix-blend-mode", `
227
+ import { createWindow, run } from "gui";
228
+
229
+ const HTML = \`
230
+ <html><head><style>
231
+ body { margin: 0; }
232
+ #card { width: 100px; height: 100px; background: #ffffff; }
233
+ #multiply { width: 50px; height: 50px; background: #ff0000; mix-blend-mode: multiply; }
234
+ #overlay { width: 20px; height: 20px; background: #00ff00; mix-blend-mode: overlay; }
235
+ </style></head><body>
236
+ <div id="card"><div id="multiply"><div id="overlay"></div></div></div>
237
+ </body></html>
238
+ \`;
239
+
240
+ const win = createWindow({ title: "mix-blend-mode", width: 400, height: 300 });
241
+ win.on("ready", () => {
242
+ console.log("multiply=" + win.computedStyle("#multiply", "mix-blend-mode"));
243
+ console.log("overlay=" + win.computedStyle("#overlay", "mix-blend-mode"));
244
+ console.log("list-start");
245
+ console.log(win.paintList());
246
+ console.log("list-end");
247
+ win.close();
248
+ });
249
+ win.loadHTML(HTML);
250
+ run();
251
+ `);
252
+ const list = stdout.slice(stdout.indexOf("list-start"), stdout.indexOf("list-end"));
253
+ expect(value("multiply")).toBe("multiply");
254
+ expect(value("overlay")).toBe("overlay");
255
+ // The card paints normally; the multiply element and its overlay child are
256
+ // separate nested groups (the renderer composites each as a unit).
257
+ expect(list).toContain("layer blend=multiply");
258
+ expect(list).toContain("layer blend=overlay");
259
+ expect(list).toContain("end-layer");
260
+ expect(list).toContain("color=#ffffffff");
261
+ expect(list).toContain("color=#ff0000ff");
262
+ expect(list).toContain("color=#00ff00ff");
263
+ // The offscreen blend pipeline and its runtime-compiled shader must not
264
+ // fall back on this GPU backend.
265
+ expect(stderr).not.toContain("mix-blend-mode");
266
+ });
190
267
  });
191
268
  //# sourceMappingURL=gui-paint.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gui-paint.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-paint.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-paint.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-paint.test.ts"],"names":[],"mappings":"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"}
package/doc/gui/canvas.md CHANGED
@@ -206,7 +206,10 @@ engine's 9-argument form is destination-first) forms. A full affine transform
206
206
  (rotation/skew/scale) is honoured: the destination rectangle becomes a
207
207
  parallelogram and every device pixel is inverse-mapped to the source rectangle.
208
208
  `source` may be an `<img>` element (decoded through the image module), another
209
- `<canvas>`, an `OffscreenCanvas`, or an `ImageBitmap`. Sampling is bilinear when
209
+ `<canvas>`, an `OffscreenCanvas`, an `ImageBitmap`, or — an engine extension — a
210
+ string path/`data:` URL that is decoded directly, so an SVG or bitmap data URL
211
+ can be drawn without an `<img>`. A vector source is rasterised at the destination
212
+ size (`dw`/`dh`), keeping a scaled SVG crisp. Sampling is bilinear when
210
213
  `imageSmoothingEnabled` is `true` (the default) and nearest-neighbour when it is
211
214
  `false`.
212
215
 
@@ -230,7 +233,8 @@ from a microtask (the bytes are on `__xt_gui_blob_data`; there is no
230
233
  shape and `transferToImageBitmap()` snapshots the bitmap.
231
234
 
232
235
  CSS backgrounds: `background-image: url(...)` accepts a file path or a `data:`
233
- URL, so a canvas can be used as a CSS background by exporting it first
236
+ URL (a raster payload or an SVG document, base64 or percent-encoded), so a
237
+ canvas can be used as a CSS background by exporting it first
234
238
  (`element.style.backgroundImage = "url(" + canvas.toDataURL() + ")"`). An image
235
239
  layer is stretched to the element's border box; `background-size`,
236
240
  `background-position` and tiling are not modelled.
@@ -59,18 +59,22 @@
59
59
  `pointer-events`, `@media` queries, generated content (`::before`/`::after`),
60
60
  multi-layer linear/radial backgrounds, `text-shadow`, `clip-path`
61
61
  (`inset`/`circle`/`ellipse`/`polygon`) and `filter` colour functions are
62
- implemented. Known limits: `mix-blend-mode`, `background-clip`,
63
- `background-position`/`size`/`repeat`, repeating gradients and `backdrop-filter`
64
- compositing are not implemented (`backdrop-filter` parses and cascades, but
65
- sampling the backdrop needs an offscreen layer, so it paints nothing);
62
+ implemented. Known limits: `background-clip`,
63
+ `background-position`/`size`/`repeat` and `backdrop-filter` compositing are not
64
+ implemented (`backdrop-filter` parses and cascades, but sampling the backdrop
65
+ needs an offscreen layer, so it paints nothing); `mix-blend-mode` and repeating
66
+ gradients are implemented (see [Rendering](./rendering.md)), but group
67
+ `opacity` is a per-shape alpha rather than a true group composite, and the
68
+ blend compositor is unavailable on the D3D12/DXIL backend, where groups fall
69
+ back to normal source-over painting;
66
70
  `filter: blur()` is a nine-tap per-shape spread rather than a true group blur,
67
71
  so overlapping descendants can darken; `drop-shadow` is approximated from box
68
72
  silhouettes; at most two nested `clip-path` shapes are intersected (deeper
69
73
  nesting keeps the outer two); and images/canvases are not colour-filtered
70
74
  except for the brightness/opacity tint the image shader can carry. Canvas
71
75
  textures are sampled nearest-neighbour. Background gradients remain solid
72
- bands (18 for linear, 32 concentric ellipses for radial), so a soft gradient
73
- may show slight banding.
76
+ bands (18 for linear, 32 concentric ellipses for radial; 64 for repeating
77
+ gradients), so a soft gradient may show slight banding.
74
78
  - **SVG is NanoSVG's subset.** SVG *files* (`<img src="logo.svg">`) and inline
75
79
  `<svg>` trees share the vendored NanoSVG parser/rasteriser
76
80
  (`runtime/ext_gui/svg.cpp`, `inline_svg.cpp`), which renders
@@ -78,9 +82,11 @@
78
82
  gradients and class-based `<style>` rules. `<text>`, `<use>`, filters, masks
79
83
  and clip paths are not rendered, and CSS applied by the document cascade does
80
84
  not reach inside the artwork (fill/stroke are SVG presentation attributes,
81
- not CSS properties here). Vector art that needs those has to be rasterised
82
- beforehand, drawn from boxes, or restricted to the supported syntax. See
83
- [Media](./media.md#images-m6a).
85
+ not CSS properties here). An SVG whose only artwork comes from a filter
86
+ (`feTurbulence` noise, blur, …) therefore shows the unfiltered shapes — often
87
+ a solid rect — rather than the filter result. Vector art that needs those has
88
+ to be rasterised beforehand, drawn from boxes, or restricted to the supported
89
+ syntax. See [Media](./media.md#images-m6a).
84
90
  - **Rendering is still whole-document per change.** A DOM mutation recomputes
85
91
  the cascade and lays out the complete document again (there is no incremental
86
92
  subtree invalidation), and a change that only affects paint (a hover colour,
package/doc/gui/media.md CHANGED
@@ -43,6 +43,15 @@ subtree, whose DOM nodes are serialized into a document first
43
43
  without limit.
44
44
  - The parse is cached per path; the parse and rasteriser state are released in
45
45
  `xt_svg_shutdown` (`xt_image_shutdown`).
46
+ - `data:` URLs are decoded in memory and work wherever a path does:
47
+ `<img src>`, CSS `background-image` and `canvas.drawImage(url, …)`. Both the
48
+ `;base64` form and a percent-encoded (or plain) payload are accepted, and the
49
+ `base64` flag is case-insensitive. The MIME type only classifies the payload:
50
+ `image/svg+xml` (or an SVG sniff when the type is generic or absent) goes
51
+ through NanoSVG — a data-URL SVG is rasterised at the drawn size exactly like
52
+ an SVG file — while PNG/JPEG/BMP/GIF/TGA go through `stb_image`. NanoSVG's
53
+ subset still applies: an SVG whose only artwork comes from `<filter>`
54
+ (`feTurbulence`, …) decodes and lays out, but the filter itself is ignored.
46
55
 
47
56
  Document CSS styles the `<svg>` **box** — size, margins, background, borders,
48
57
  `transform`, opacity, `display` — exactly as it styles any other element. The
@@ -55,8 +64,8 @@ renderer caches one texture per markup-and-size pair, so a program that rewrites
55
64
  its artwork every frame keeps a texture per variant; prefer animating the box
56
65
  (`transform`, `opacity`) or swapping between a small set of documents.
57
66
 
58
- Still to do for images: CSS `background-image: url(...)`, `data:` URIs,
59
- `object-fit` and 9-slice borders.
67
+ Still to do for images: `object-fit` and 9-slice borders. (CSS
68
+ `background-image: url(...)` and `data:` URLs are supported.)
60
69
 
61
70
  Both kinds of SVG share NanoSVG's subset: `path`, `rect`, `circle`, `ellipse`,
62
71
  `line`, `polyline`, `polygon`, groups (`g`), `defs` with linear/radial gradients
@@ -15,12 +15,14 @@ up to two indices into the display list's triangulated `clip-path` polygons, so
15
15
  `overflow`/`clip-path` clipping and `z-index` ordering survive into the renderer.
16
16
  `DisplayList::dump()` feeds `paintList()`/`paintCount()`.
17
17
 
18
- `opacity` is honoured only at its extremes: a box whose computed `opacity` is
19
- `0` (and its whole subtree) paints nothing, and anything above `0` paints fully
20
- opaque. A shape carries its colour's alpha only, so there is no compositing of a
21
- partly transparent box — the `opacity` transitions the CSS parser accepts are
22
- therefore not visible either. Hiding and revealing a layer with `opacity: 0` on
23
- the hidden state is what `examples/gui/pelican-bike`'s game-over overlay does.
18
+ `opacity` multiplies the alpha of every colour painted by the box and its
19
+ descendants (the values multiply down the paint chain), so fractional opacity and
20
+ the `opacity` transitions the CSS parser accepts are visible. It is a
21
+ per-shape approximation, not an offscreen group: overlapping children of a
22
+ translucent box composite individually, whereas a browser composes the group
23
+ once and then applies the alpha. `opacity: 0` still skips the whole subtree.
24
+ Hiding and revealing a layer with `opacity: 0` on the hidden state is what
25
+ `examples/gui/pelican-bike`'s game-over overlay does.
24
26
 
25
27
  `runtime/ext_gui/renderer.{h,cpp}` turns that list into batched vertex buffers
26
28
  per window (shapes, glyph quads and images) and merges consecutive ops that
@@ -80,14 +82,23 @@ The window background (`setBackground`) is the render-pass clear colour.
80
82
  comma-separated layer list is painted bottom-up (the first declaration layer is
81
83
  topmost), and both gradient kinds are supported:
82
84
 
83
- - a **linear** gradient is sampled at 18 points and emitted as solid rectangles;
84
- `to <side>`, `deg`/`grad`/`rad`/`turn`, percentage and multi-stop colours work
85
- (the band walk is axis-aligned, so an oblique angle uses the nearest axis);
86
- - a **radial** gradient is 32 concentric ellipses. Each is a unit-radius rounded
87
- rectangle mapped through the shape pipeline's transform slot, so a circle or
88
- ellipse keeps the distance-field antialiasing. Shape/size keywords
89
- (`circle`/`ellipse`, `closest-side`/`farthest-side`/`closest-corner`/
90
- `farthest-corner`, explicit radii) and `at <position>` are parsed.
85
+ - a **linear** gradient is sampled at 18 points (64 when repeating) and emitted
86
+ as solid rectangles; `to <side>`, `deg`/`grad`/`rad`/`turn`, percentage or
87
+ length (`px`/`em`/…) offsets, the `color <start> <end>` shorthand and
88
+ multi-stop colours work (the band walk is axis-aligned, so an oblique angle
89
+ uses the nearest axis);
90
+ - a **radial** gradient is 32 concentric ellipses (64 when repeating). Each is a
91
+ unit-radius rounded rectangle mapped through the shape pipeline's transform
92
+ slot, so a circle or ellipse keeps the distance-field antialiasing.
93
+ Shape/size keywords (`circle`/`ellipse`, `closest-side`/`farthest-side`/
94
+ `closest-corner`/`farthest-corner`, explicit radii) and `at <position>` are
95
+ parsed.
96
+
97
+ `repeating-linear-gradient()` and `repeating-radial-gradient()` tile the stop
98
+ pattern from 0 to the last stop's offset across the whole box; the stop list is
99
+ not forced to end at 100%, so `red 0%, blue 10%` repeats every 10%. A normal
100
+ gradient whose last stop is below 100% holds that colour for the rest of the box,
101
+ as in CSS.
91
102
 
92
103
  The background colour sits under every layer. Gradients are partially
93
104
  transparent if their stops are, and the band approximation can show banding and
@@ -117,10 +128,24 @@ nesting keeps the outermost two.
117
128
  colour, so a box and its descendants see the filter. `blur()` and
118
129
  `drop-shadow()` are approximations at the display-list level: blur spreads nine
119
130
  translucent copies per shape, drop-shadow is a box/text silhouette. This keeps
120
- the single render pass — there is no offscreen render target — at the cost of
121
- group semantics: overlapping descendants of a blurred/filtered group can darken,
122
- and `backdrop-filter` cannot sample what is behind the element, so it parses and
123
- cascades but paints nothing.
131
+ the ordinary painter single-pass at the cost of group semantics: overlapping
132
+ descendants of a blurred/filtered group can darken, and `backdrop-filter` cannot
133
+ sample what is behind the element, so it parses and cascades but paints nothing.
134
+
135
+ **`mix-blend-mode`** is the exception that does need an offscreen layer. A box
136
+ with a non-`normal` mode is wrapped in `LayerBegin`/`LayerEnd` markers (visible
137
+ in `paintList()` as `layer blend=…` / `end-layer`); the renderer draws that group
138
+ to a full-viewport offscreen texture and composites it over the scene with a
139
+ small GLSL blend shader. All sixteen CSS modes (`multiply`, `screen`, `overlay`,
140
+ `darken`, `lighten`, `color-dodge`, `color-burn`, `hard-light`, `soft-light`,
141
+ `difference`, `exclusion`, `hue`, `saturation`, `color`, `luminosity`) are
142
+ implemented with the compositing-spec formulas. The shader is compiled at run
143
+ time by the bundled glslang (SPIR-V for Vulkan) and cross-compiled to MSL by
144
+ SPIRV-Cross (Metal); the two scene textures per nesting level are reused across
145
+ frames and ping-pong so the backdrop stays sampleable while it is written.
146
+ Backends that consume neither format — the D3D12/DXIL path, which has no shader
147
+ compiler here — keep painting the group with ordinary source-over blending.
148
+ `backdrop-filter` still paints nothing.
124
149
 
125
150
  ## Text stack (M4b)
126
151
 
package/doc/gui.md CHANGED
@@ -40,10 +40,11 @@ them) can be translated, scaled and rotated without reflowing anything — and
40
40
  [Animation](./gui/animation.md#css-transforms-m15).
41
41
  **M17** adds real **WebGL 1.0**, and **M18** adds **WebGL 2.0**: `<canvas>.getContext("webgl2")` accepts `#version 300 es` shaders, vertex-array objects, instanced draws and sampler objects, all compiled to SPIR-V by the bundled glslang (and to MSL by the bundled SPIRV-Cross on Metal) and rendered through SDL_GPU. Geometry, lighting, texture sampling and blending run on the GPU and the page composites the colour texture without a readback — see [WebGL](./gui/webgl.md).
42
42
  **M19** fills in the decorative CSS layer: `@media` queries, generated content
43
- (`::before`/`::after`), multi-layer linear/radial backgrounds, `text-shadow`,
44
- `clip-path` and the `filter` colour/shadow effects. `backdrop-filter` and true
45
- group blur need an offscreen compositor and remain documented approximations or
46
- unsupported — see [Rendering](./gui/rendering.md#backgrounds-generated-content-and-effects-m19).
43
+ (`::before`/`::after`), multi-layer linear/radial/repeating backgrounds,
44
+ `text-shadow`, `clip-path`, `mix-blend-mode` (through an offscreen compositor)
45
+ and the `filter` colour/shadow effects. `backdrop-filter` still needs a backdrop
46
+ snapshot, and true group blur remains an approximation — see
47
+ [Rendering](./gui/rendering.md#backgrounds-generated-content-and-effects-m19).
47
48
  **M20** makes large documents interactive: `@media` is evaluated once per rule,
48
49
  layout and paint share a bounded shaping cache, and a frame only does (and
49
50
  presents) the work a change requires — see