xbintsc 0.3.68 → 0.3.79

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 (100) hide show
  1. package/README.md +59 -4
  2. package/README.zh-CN.md +52 -2
  3. package/dist/tests/e2e/gui-advanced.test.d.ts +6 -0
  4. package/dist/tests/e2e/gui-advanced.test.js +261 -0
  5. package/dist/tests/e2e/gui-advanced.test.js.map +1 -0
  6. package/dist/tests/e2e/gui-canvas.test.d.ts +9 -0
  7. package/dist/tests/e2e/gui-canvas.test.js +233 -0
  8. package/dist/tests/e2e/gui-canvas.test.js.map +1 -0
  9. package/dist/tests/e2e/gui-example.test.d.ts +1 -1
  10. package/dist/tests/e2e/gui-example.test.js +2 -2
  11. package/dist/tests/e2e/gui-helpers.d.ts +3 -0
  12. package/dist/tests/e2e/gui-helpers.js +5 -0
  13. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  14. package/dist/tests/e2e/gui-layout.test.js +73 -0
  15. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  16. package/dist/tests/e2e/gui-media.test.d.ts +8 -0
  17. package/dist/tests/e2e/gui-media.test.js +119 -0
  18. package/dist/tests/e2e/gui-media.test.js.map +1 -0
  19. package/dist/tests/e2e/gui-paint.test.js +40 -0
  20. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  21. package/doc/ai/extensions.md +16 -3
  22. package/doc/ai/zh-CN/extensions.md +10 -3
  23. package/doc/gui/animation.md +46 -0
  24. package/doc/gui/api.md +92 -0
  25. package/doc/gui/build.md +91 -0
  26. package/doc/gui/canvas.md +98 -0
  27. package/doc/gui/dom.md +123 -0
  28. package/doc/gui/input.md +22 -0
  29. package/doc/gui/known-issues.md +66 -0
  30. package/doc/gui/layout.md +72 -0
  31. package/doc/gui/media.md +87 -0
  32. package/doc/gui/rendering.md +98 -0
  33. package/doc/gui/runtime.md +88 -0
  34. package/doc/gui-scripts.md +7 -3
  35. package/doc/gui.md +80 -612
  36. package/doc/implemented.md +1 -0
  37. package/doc/zh-CN/gui/animation.md +46 -0
  38. package/doc/zh-CN/gui/api.md +88 -0
  39. package/doc/zh-CN/gui/build.md +84 -0
  40. package/doc/zh-CN/gui/canvas.md +80 -0
  41. package/doc/zh-CN/gui/dom.md +114 -0
  42. package/doc/zh-CN/gui/input.md +21 -0
  43. package/doc/zh-CN/gui/known-issues.md +50 -0
  44. package/doc/zh-CN/gui/layout.md +60 -0
  45. package/doc/zh-CN/gui/media.md +71 -0
  46. package/doc/zh-CN/gui/rendering.md +81 -0
  47. package/doc/zh-CN/gui/runtime.md +76 -0
  48. package/doc/zh-CN/gui-scripts.md +6 -2
  49. package/doc/zh-CN/gui.md +64 -532
  50. package/doc/zh-CN/implemented.md +1 -0
  51. package/doc/zh-CN/post/xbintsc-build-pelican.md +2 -2
  52. package/llms.txt +15 -2
  53. package/package.json +1 -1
  54. package/runtime/ext_gui/canvas.cpp +580 -0
  55. package/runtime/ext_gui/canvas.h +170 -0
  56. package/runtime/ext_gui/canvas_internal.h +59 -0
  57. package/runtime/ext_gui/canvas_raster.cpp +254 -0
  58. package/runtime/ext_gui/document.cpp +24 -1
  59. package/runtime/ext_gui/document.h +11 -0
  60. package/runtime/ext_gui/dom_api.cpp +3 -0
  61. package/runtime/ext_gui/dom_api_canvas.cpp +548 -0
  62. package/runtime/ext_gui/dom_api_internal.h +73 -0
  63. package/runtime/ext_gui/dom_api_media.cpp +274 -0
  64. package/runtime/ext_gui/dom_api_proto.cpp +80 -0
  65. package/runtime/ext_gui/gui.cpp +6 -0
  66. package/runtime/ext_gui/gui_canvas.cpp +95 -0
  67. package/runtime/ext_gui/gui_engine.h +49 -0
  68. package/runtime/ext_gui/gui_events.cpp +21 -5
  69. package/runtime/ext_gui/gui_media.cpp +193 -0
  70. package/runtime/ext_gui/image.cpp +31 -12
  71. package/runtime/ext_gui/image.h +20 -7
  72. package/runtime/ext_gui/layout.cpp +52 -5
  73. package/runtime/ext_gui/layout.h +33 -1
  74. package/runtime/ext_gui/layout_flex.cpp +263 -99
  75. package/runtime/ext_gui/layout_flow.cpp +85 -9
  76. package/runtime/ext_gui/layout_flow_internal.h +84 -5
  77. package/runtime/ext_gui/layout_inline.cpp +54 -37
  78. package/runtime/ext_gui/media.cpp +514 -0
  79. package/runtime/ext_gui/media.h +104 -0
  80. package/runtime/ext_gui/nanosvg.h +3278 -0
  81. package/runtime/ext_gui/nanosvgrast.h +1472 -0
  82. package/runtime/ext_gui/paint.cpp +123 -38
  83. package/runtime/ext_gui/paint.h +36 -6
  84. package/runtime/ext_gui/renderer.cpp +113 -10
  85. package/runtime/ext_gui/renderer.h +49 -9
  86. package/runtime/ext_gui/renderer_image.cpp +203 -77
  87. package/runtime/ext_gui/renderer_internal.h +6 -8
  88. package/runtime/ext_gui/renderer_text.cpp +11 -26
  89. package/runtime/ext_gui/style.h +11 -0
  90. package/runtime/ext_gui/style_print.cpp +11 -0
  91. package/runtime/ext_gui/style_ua.cpp +7 -0
  92. package/runtime/ext_gui/style_values.cpp +40 -1
  93. package/runtime/ext_gui/svg.cpp +182 -0
  94. package/runtime/ext_gui/svg.h +46 -0
  95. package/runtime/ext_gui/text.cpp +156 -25
  96. package/runtime/ext_gui/text.h +11 -2
  97. package/runtime/ext_gui/window.cpp +4 -1
  98. package/scripts/build-gui.ts +148 -6
  99. package/scripts/check-file-length.ts +5 -1
  100. package/scripts/install-gui-deps.sh +28 -0
package/README.md CHANGED
@@ -12,7 +12,7 @@ The design goals are:
12
12
  1. **Incremental compilation** — builds are content-addressed and skipped when nothing changed.
13
13
  2. **Well-factored modules** — lexer, parser, binder, codegen, driver, extensions each live in their own directory with their own tests.
14
14
  3. **Extensive tests** — per-module unit tests plus end-to-end tests that compile and run real binaries.
15
- 4. **Pluggable extensions** — Node `fs`, Bun APIs, … are optional compile modules, not core code.
15
+ 4. **Pluggable extensions** — Node `fs`, the `gui` HTML/CSS renderer , … are optional compile modules, not core code.
16
16
  5. **Self-hosting** — the compiler compiles itself: `src/cli/main.ts` builds into a native `xbintsc` binary whose emitted LLVM IR is a fixpoint across generations. CI builds these binaries for every platform and a release attaches them.
17
17
  6. **Multi-platform** — macOS, Linux and Windows on x64 and arm64 (Apple Silicon / AArch64), validated by GitHub Actions on native runners.
18
18
 
@@ -231,6 +231,60 @@ const { ir } = compileString("console.log(1 + 1);");
231
231
  const result = build("program.ts", { emit: "exe", outDir: "build" });
232
232
  ```
233
233
 
234
+ ## GUI applications (HTML/CSS)
235
+
236
+ xbintsc can also build **native desktop applications whose UI is HTML/CSS**. The
237
+ optional `gui` extension links a self-hosted, GPU-accelerated HTML/CSS renderer
238
+ (its own HTML parser, CSS cascade, layout and SDL_GPU compositor — no system
239
+ WebView, no bundled browser) into the executable and exposes it as an importable
240
+ module:
241
+
242
+ ```ts
243
+ // app.ts
244
+ import { createWindow, run } from "gui";
245
+ // The .html asset loader AOT-compiles the document's scripts (see below)
246
+ import page from "./page.html";
247
+
248
+ const win = createWindow({ title: "Demo", width: 900, height: 600 });
249
+ win.on("click", (e: any) => console.log(e.x, e.y, e.target));
250
+ win.loadHTML(page);
251
+ run(); // main loop, until every window closes
252
+ ```
253
+
254
+ ```bash
255
+ # Source checkout: build the engine archive once (fetches and builds SDL3,
256
+ # FreeType and HarfBuzz), then run the bundled example
257
+ npm run gui
258
+ npx tsx src/cli/main.ts run examples/gui/hello.ts --ext gui
259
+
260
+ # A prebuilt Linux/macOS release archive already ships the engine:
261
+ xbintsc run app.ts --ext gui
262
+ ```
263
+
264
+ A document's `<script>` bodies are **AOT-compiled** by xbintsc into the binary —
265
+ inline scripts (a bare `<script>` is already treated as TypeScript) and external
266
+ `<script src="./app.ts">` files alike — so there is still no JavaScript engine at
267
+ runtime. Element handles, DOM mutation, events and the rest of the scripting
268
+ surface are documented in [doc/gui-scripts.md](./doc/gui-scripts.md).
269
+
270
+ In a project, list the extension in `xbintsc.config.json`
271
+ (`"extensions": ["gui"]`) and the flag becomes unnecessary, exactly as for
272
+ `node`. `XT_GUI_AUTOCLOSE_MS=<ms>` closes all windows after a delay, which is how
273
+ the end-to-end tests run the examples headlessly.
274
+
275
+ Two examples live in the repository: [`examples/gui/hello.ts`](./examples/gui/hello.ts)
276
+ (an HTML/CSS window plus a reactive counter) and
277
+ [`examples/gui/pelican-bike`](./examples/gui/pelican-bike), a playable 2D game
278
+ whose document also opens unchanged in a browser.
279
+
280
+ Platforms and full details: [doc/gui.md](./doc/gui.md) is the engine overview
281
+ (architecture and roadmap); the per-function documents live under
282
+ [doc/gui/](./doc/gui) — API, DOM/CSS, layout, rendering, media, input, animation,
283
+ runtime, build and known issues. Linux and macOS are built and exercised in CI
284
+ and their release archives ship the engine (`gui.a`); the Windows archive (`gui.lib`, D3D12) is
285
+ produced by the same script but is still provisional and is not shipped in the
286
+ release yet.
287
+
234
288
  ## Incremental compilation
235
289
 
236
290
  The driver keys each build on the entry source hash, compiler version, options,
@@ -240,9 +294,10 @@ sources are compiled once and cached on the same principle.
240
294
 
241
295
  ## Extensions
242
296
 
243
- An extension is a plain object (`src/extensions/registry.ts`). The `node`
244
- extension is itself split into one folder per Node module, each pairing its
245
- exports with the C sources that implement them:
297
+ An extension is a plain object (`src/extensions/registry.ts`). Two are bundled:
298
+ [`gui`](#gui-applications-htmlcss), a GPU HTML/CSS renderer
299
+ (see [doc/gui.md](./doc/gui.md)), and `node`, itself split into one folder per
300
+ Node module, each pairing its exports with the C sources that implement them:
246
301
 
247
302
  ```
248
303
  src/extensions/node/ runtime/ext_node/
package/README.zh-CN.md CHANGED
@@ -11,7 +11,7 @@
11
11
  1. **增量编译** —— 构建以内容寻址,没有变化时直接跳过。
12
12
  2. **模块划分清晰** —— 词法、语法、绑定、代码生成、驱动、扩展各自独立目录,并有各自的测试。
13
13
  3. **测试充分** —— 每个模块单元测试,外加真正编译并运行二进制的端到端测试。
14
- 4. **可插拔扩展** —— Node `fs`、Bun API 等是可选的编译模块,而非核心代码。
14
+ 4. **可插拔扩展** —— Node `fs`、`gui` HTML/CSS 渲染器 等是可选的编译模块,而非核心代码。
15
15
  5. **自举(self-hosting)** —— 编译器可编译自身:`src/cli/main.ts` 能构建为原生 `xbintsc` 二进制,其产出的 LLVM IR 跨代达到不动点。CI 会为各平台构建这些二进制,Release 则附上这些产物。
16
16
  6. **多平台** —— 在 x64 与 arm64(Apple Silicon / AArch64)上支持 macOS、Linux 与 Windows,并由 GitHub Actions 在各自架构的原生 runner 上验证。
17
17
 
@@ -183,6 +183,54 @@ const { ir } = compileString("console.log(1 + 1);");
183
183
  const result = build("program.ts", { emit: "exe", outDir: "build" });
184
184
  ```
185
185
 
186
+ ## GUI 应用(HTML/CSS)
187
+
188
+ xbintsc 也能构建 **界面用 HTML/CSS 编写的原生桌面应用**。可选的 `gui` 扩展会把一个
189
+ 自研的、GPU 加速的 HTML/CSS 渲染器(自有 HTML 解析器、CSS 层叠、布局与 SDL_GPU
190
+ 合成器,不使用系统 WebView,也不内嵌浏览器)链接进可执行文件,并作为可 `import`
191
+ 的模块暴露出来:
192
+
193
+ ```ts
194
+ // app.ts
195
+ import { createWindow, run } from "gui";
196
+ // .html 资产加载器会把文档里的脚本提前编译(见下文)
197
+ import page from "./page.html";
198
+
199
+ const win = createWindow({ title: "Demo", width: 900, height: 600 });
200
+ win.on("click", (e: any) => console.log(e.x, e.y, e.target));
201
+ win.loadHTML(page);
202
+ run(); // 主循环,直到所有窗口关闭
203
+ ```
204
+
205
+ ```bash
206
+ # 源码检出:先构建一次引擎归档(会获取并构建 SDL3、FreeType 与 HarfBuzz),
207
+ # 然后运行仓库自带示例
208
+ npm run gui
209
+ npx tsx src/cli/main.ts run examples/gui/hello.ts --ext gui
210
+
211
+ # 预编译的 Linux/macOS 发布包已经内含该引擎:
212
+ xbintsc run app.ts --ext gui
213
+ ```
214
+
215
+ 文档里的 `<script>` 脚本会被 xbintsc **提前编译(AOT)**进二进制——内联脚本
216
+ (裸 `<script>` 本身就按 TypeScript 处理)与外部 `<script src="./app.ts">` 文件
217
+ 都是如此——因此运行时依然没有 JavaScript 引擎。元素句柄、DOM 变更、事件与脚本接口
218
+ 见 [doc/zh-CN/gui-scripts.md](./doc/zh-CN/gui-scripts.md)。
219
+
220
+ 在项目里把扩展写进 `xbintsc.config.json`(`"extensions": ["gui"]`)即可省去
221
+ `--ext gui`,与 `node` 完全一样。设置 `XT_GUI_AUTOCLOSE_MS=<ms>` 可在指定毫秒后
222
+ 自动关闭所有窗口,端到端测试正是用它无头运行示例。
223
+
224
+ 仓库里有两个示例:[`examples/gui/hello.ts`](./examples/gui/hello.ts)(HTML/CSS 窗口
225
+ 加一个响应式计数器)与 [`examples/gui/pelican-bike`](./examples/gui/pelican-bike)
226
+ ——一个可玩的 2D 游戏,它的同一份文档在浏览器里也能直接打开。
227
+
228
+ 平台与完整说明:[doc/zh-CN/gui.md](./doc/zh-CN/gui.md) 是引擎总览(架构与
229
+ 路线图);按功能拆分的文档位于 [doc/zh-CN/gui/](./doc/zh-CN/gui) —— API、
230
+ DOM/CSS、布局、绘制、媒体、输入、动画、运行时、构建与已知问题。Linux 与 macOS 已在 CI 中构建
231
+ 并实际运行,其发布包内含引擎(`gui.a`);Windows 的 `gui.lib`(D3D12)由同一脚本产出,但仍是
232
+ 暂定状态,尚未随发布包提供。
233
+
186
234
  ## 增量编译
187
235
 
188
236
  驱动以入口源码哈希、编译器版本、编译选项、平台与启用的扩展集合作为每次构建的
@@ -191,7 +239,9 @@ const result = build("program.ts", { emit: "exe", outDir: "build" });
191
239
 
192
240
  ## 扩展
193
241
 
194
- 扩展是一个普通对象(`src/extensions/registry.ts`)。`node` 扩展本身按每个 Node
242
+ 扩展是一个普通对象(`src/extensions/registry.ts`)。内置扩展有两个:`gui`,
243
+ 它贡献的是一个 GPU HTML/CSS 渲染器(见下文「GUI 应用(HTML/CSS)」一节与
244
+ [doc/zh-CN/gui.md](./doc/zh-CN/gui.md));以及 `node`,它本身按每个 Node
195
245
  模块拆分目录,每个模块把它对外导出的绑定与实现它们的 C 源配对:
196
246
 
197
247
  ```
@@ -0,0 +1,6 @@
1
+ /**
2
+ * End-to-end tests for the `gui` extension — the layout/paint features added in
3
+ * this milestone: margin collapsing, flex wrapping, z-index, overflow clipping
4
+ * and floats.
5
+ */
6
+ export {};
@@ -0,0 +1,261 @@
1
+ /**
2
+ * End-to-end tests for the `gui` extension — the layout/paint features added in
3
+ * this milestone: margin collapsing, flex wrapping, z-index, overflow clipping
4
+ * and floats.
5
+ */
6
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
7
+ import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
8
+ const harness = createGuiHarness();
9
+ /** The shared `rect(selector)` helper embedded in each test program. */
10
+ const RECT_HELPER = `
11
+ const rect = (selector) => {
12
+ const r = win.getBoundingClientRect(selector);
13
+ return r.x + "," + r.y + "," + r.width + "," + r.height;
14
+ };
15
+ `;
16
+ describe.skipIf(!guiAvailable)("gui extension — advanced layout/paint", () => {
17
+ beforeAll(() => harness.setup());
18
+ afterAll(() => harness.cleanup());
19
+ it("collapses adjoining vertical margins between siblings", () => {
20
+ const { value } = harness.compileAndRun("margin", `
21
+ import { createWindow, run } from "gui";
22
+
23
+ const HTML = \`
24
+ <html><head><style>
25
+ body { margin: 0; }
26
+ .a { height: 20px; margin-bottom: 30px; }
27
+ .b { height: 20px; margin-top: 10px; margin-bottom: 25px; }
28
+ .c { height: 20px; margin-top: 5px; margin-bottom: 30px; }
29
+ .d { height: 20px; margin-top: 15px; }
30
+ </style></head><body>
31
+ <div class="a" id="a"></div>
32
+ <div class="b" id="b"></div>
33
+ <div class="c" id="c"></div>
34
+ <div class="d" id="d"></div>
35
+ </body></html>
36
+ \`;
37
+
38
+ const win = createWindow({ title: "margin", width: 400, height: 300 });
39
+ win.on("ready", () => {
40
+ ${RECT_HELPER}
41
+ for (const id of ["a", "b", "c", "d"]) console.log(id + "=" + rect("#" + id));
42
+ win.close();
43
+ });
44
+ win.loadHTML(HTML);
45
+ run();
46
+ `);
47
+ // a ends at 20; collapse(30, 10) = 30, so b starts at 50 (not 60).
48
+ expect(value("a")).toBe("0,0,400,20");
49
+ expect(value("b")).toBe("0,50,400,20");
50
+ // b ends at 70; collapse(25, 5) = 25, so c starts at 95 (not 100).
51
+ expect(value("c")).toBe("0,95,400,20");
52
+ // c ends at 115; collapse(30, 15) = 30, so d starts at 145.
53
+ expect(value("d")).toBe("0,145,400,20");
54
+ });
55
+ it("counts a last child's bottom margin in the parent's height", () => {
56
+ const { value } = harness.compileAndRun("margin-parent", `
57
+ import { createWindow, run } from "gui";
58
+
59
+ const HTML = \`
60
+ <html><head><style>
61
+ body { margin: 0; }
62
+ #outer { padding-bottom: 0; }
63
+ #inner { height: 20px; margin-bottom: 25px; }
64
+ #after { height: 10px; }
65
+ </style></head><body>
66
+ <div id="outer"><div id="inner"></div></div>
67
+ <div id="after"></div>
68
+ </body></html>
69
+ \`;
70
+
71
+ const win = createWindow({ title: "margin-parent", width: 400, height: 300 });
72
+ win.on("ready", () => {
73
+ ${RECT_HELPER}
74
+ console.log("outer=" + rect("#outer"));
75
+ console.log("after=" + rect("#after"));
76
+ win.close();
77
+ });
78
+ win.loadHTML(HTML);
79
+ run();
80
+ `);
81
+ // The inner box ends at 20; its 25px bottom margin stays inside the outer
82
+ // (parent/child bottom-margin collapsing is not implemented), so the outer
83
+ // is 45 tall and #after starts at 45.
84
+ expect(value("outer")).toBe("0,0,400,45");
85
+ expect(value("after")).toBe("0,45,400,10");
86
+ });
87
+ it("wraps flex lines when they exceed the main size", () => {
88
+ const { value } = harness.compileAndRun("flex-wrap", `
89
+ import { createWindow, run } from "gui";
90
+
91
+ const HTML = \`
92
+ <html><head><style>
93
+ body { margin: 0; }
94
+ #row { display: flex; flex-wrap: wrap; width: 100px; }
95
+ #row div { width: 40px; height: 10px; }
96
+ </style></head><body>
97
+ <div id="row">
98
+ <div id="a"></div><div id="b"></div>
99
+ <div id="c"></div><div id="d"></div>
100
+ </div>
101
+ </body></html>
102
+ \`;
103
+
104
+ const win = createWindow({ title: "flex-wrap", width: 400, height: 300 });
105
+ win.on("ready", () => {
106
+ ${RECT_HELPER}
107
+ for (const id of ["a", "b", "c", "d"]) console.log(id + "=" + rect("#" + id));
108
+ console.log("row=" + rect("#row"));
109
+ win.close();
110
+ });
111
+ win.loadHTML(HTML);
112
+ run();
113
+ `);
114
+ // 40 + 40 fits on line 1; c and d wrap onto line 2.
115
+ expect(value("a")).toBe("0,0,40,10");
116
+ expect(value("b")).toBe("40,0,40,10");
117
+ expect(value("c")).toBe("0,10,40,10");
118
+ expect(value("d")).toBe("40,10,40,10");
119
+ // Two 10px lines make the container 20px tall.
120
+ expect(value("row")).toBe("0,0,100,20");
121
+ });
122
+ it("honours `flex-wrap: wrap-reverse`", () => {
123
+ const { value } = harness.compileAndRun("flex-wrap-reverse", `
124
+ import { createWindow, run } from "gui";
125
+
126
+ const HTML = \`
127
+ <html><head><style>
128
+ body { margin: 0; }
129
+ #row { display: flex; flex-wrap: wrap-reverse; align-content: flex-start; width: 100px; height: 40px; }
130
+ #row div { width: 40px; height: 10px; }
131
+ </style></head><body>
132
+ <div id="row">
133
+ <div id="a"></div><div id="b"></div>
134
+ <div id="c"></div><div id="d"></div>
135
+ </div>
136
+ </body></html>
137
+ \`;
138
+
139
+ const win = createWindow({ title: "wrap-reverse", width: 400, height: 300 });
140
+ win.on("ready", () => {
141
+ ${RECT_HELPER}
142
+ for (const id of ["a", "b", "c", "d"]) console.log(id + "=" + rect("#" + id));
143
+ win.close();
144
+ });
145
+ win.loadHTML(HTML);
146
+ run();
147
+ `);
148
+ // The cross axis is flipped: the first line (a, b) sits below the second.
149
+ expect(value("a")).toBe("0,30,40,10");
150
+ expect(value("b")).toBe("40,30,40,10");
151
+ expect(value("c")).toBe("0,20,40,10");
152
+ expect(value("d")).toBe("40,20,40,10");
153
+ });
154
+ it("paints `z-index` siblings in stacking order", () => {
155
+ const { stdout, value } = harness.compileAndRun("zindex", `
156
+ import { createWindow, run } from "gui";
157
+
158
+ const HTML = \`
159
+ <html><head><style>
160
+ body { margin: 0; }
161
+ .box { position: absolute; top: 0; left: 0; width: 50px; height: 50px; }
162
+ #green { background: #00ff00; z-index: 2; }
163
+ #red { background: #ff0000; z-index: 1; }
164
+ </style></head><body>
165
+ <div class="box" id="green"></div>
166
+ <div class="box" id="red"></div>
167
+ </body></html>
168
+ \`;
169
+
170
+ const win = createWindow({ title: "zindex", width: 400, height: 300 });
171
+ win.on("ready", () => {
172
+ const list = win.paintList();
173
+ console.log("order-start");
174
+ console.log(list);
175
+ console.log("order-end");
176
+ console.log("red-before-green=" + (list.indexOf("#ff0000ff") < list.indexOf("#00ff00ff")));
177
+ win.close();
178
+ });
179
+ win.loadHTML(HTML);
180
+ run();
181
+ `);
182
+ expect(value("red-before-green")).toBe("true");
183
+ // The paint list still carries both backgrounds.
184
+ expect(stdout).toContain("#ff0000ff");
185
+ expect(stdout).toContain("#00ff00ff");
186
+ });
187
+ it("clips a subtree with `overflow: hidden`", () => {
188
+ const { value, stdout } = harness.compileAndRun("overflow", `
189
+ import { createWindow, run } from "gui";
190
+
191
+ const HTML = \`
192
+ <html><head><style>
193
+ body { margin: 0; }
194
+ #clip { width: 50px; height: 50px; overflow: hidden; }
195
+ #inner { width: 200px; height: 20px; background: #00ff00; }
196
+ </style></head><body>
197
+ <div id="clip"><div id="inner"></div></div>
198
+ </body></html>
199
+ \`;
200
+
201
+ const win = createWindow({ title: "overflow", width: 400, height: 300 });
202
+ win.on("ready", () => {
203
+ const r = win.getBoundingClientRect("#inner");
204
+ console.log("inner-rect=" + r.x + "," + r.y + "," + r.width + "," + r.height);
205
+ console.log("paint-list-start");
206
+ console.log(win.paintList());
207
+ console.log("paint-list-end");
208
+ win.close();
209
+ });
210
+ win.loadHTML(HTML);
211
+ run();
212
+ `);
213
+ const list = stdout.slice(stdout.indexOf("paint-list-start"), stdout.indexOf("paint-list-end"));
214
+ // Clipping does not change layout: the inner box is still 200px wide.
215
+ expect(value("inner-rect")).toBe("0,0,200,20");
216
+ // Its background is drawn inside the 50x50 padding-box clip.
217
+ expect(list).toContain("clip=0.0,0.0,50.0,50.0 rect x=0.0 y=0.0 w=200.0 h=20.0");
218
+ });
219
+ it("wraps inline content around a left float", () => {
220
+ const { value, stdout } = harness.compileAndRun("float", `
221
+ import { createWindow, run } from "gui";
222
+
223
+ const HTML = \`
224
+ <html><head><style>
225
+ body { margin: 0; }
226
+ #w { width: 100px; font-size: 20px; }
227
+ #f { float: left; width: 40px; height: 40px; background: #ff0000; }
228
+ #after { clear: both; height: 10px; }
229
+ </style></head><body>
230
+ <div id="w"><div id="f"></div>hello world foo bar baz<div id="after"></div></div>
231
+ </body></html>
232
+ \`;
233
+
234
+ const win = createWindow({ title: "float", width: 400, height: 300 });
235
+ win.on("ready", () => {
236
+ const f = win.getBoundingClientRect("#f");
237
+ console.log("float=" + f.x + "," + f.y + "," + f.width + "," + f.height);
238
+ const w = win.getBoundingClientRect("#w");
239
+ console.log("w-height=" + w.height);
240
+ console.log("paint-list-start");
241
+ console.log(win.paintList());
242
+ console.log("paint-list-end");
243
+ const a = win.getBoundingClientRect("#after");
244
+ console.log("after-y=" + a.y);
245
+ win.close();
246
+ });
247
+ win.loadHTML(HTML);
248
+ run();
249
+ `);
250
+ // The float sits at the left edge and is 40x40.
251
+ expect(value("float")).toBe("0,0,40,40");
252
+ // The container grows to contain the float.
253
+ expect(Number(value("w-height"))).toBeGreaterThanOrEqual(40);
254
+ const list = stdout.slice(stdout.indexOf("paint-list-start"), stdout.indexOf("paint-list-end"));
255
+ // The first text line is pushed right of the float.
256
+ expect(list).toContain("text x=40.0");
257
+ // `clear: both` pushes the following block below the float.
258
+ expect(Number(value("after-y"))).toBeGreaterThanOrEqual(40);
259
+ });
260
+ });
261
+ //# sourceMappingURL=gui-advanced.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gui-advanced.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-advanced.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * End-to-end tests for `<canvas>` support in the `gui` extension.
3
+ *
4
+ * A `<canvas>` is a replaced element laid out like an `<img>`, but its pixels
5
+ * come from a CPU rasteriser instead of a decoder. The script draws through the
6
+ * 2D context and the engine turns the bitmap into a GPU texture through the same
7
+ * image path as `media_id`/`src` (see `renderer_image.cpp`).
8
+ */
9
+ export {};