xbintsc 0.5.45 → 0.5.52

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 (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * End-to-end test for the `gui` extension — WebGL pipeline cache reuse.
3
+ *
4
+ * A pipeline is a function of the program, the vertex *layout* and the
5
+ * fixed-function state. It is not a function of which buffer currently holds
6
+ * the data, nor of the monotonic counters the engine bumps when a VAO is bound
7
+ * or a framebuffer is switched. Keying the pipeline cache on those counters
8
+ * made every draw create a fresh `SDL_GPUGraphicsPipeline` once a frame bound
9
+ * more than one layout (three.js binds one VAO per geometry): a measured
10
+ * ~250 pipeline creations per frame, which cost more than the whole rest of
11
+ * the frame.
12
+ *
13
+ * This program draws two triangles whose layouts are identical and whose
14
+ * buffers differ, alternating the bound buffer every frame. The layout never
15
+ * changes, so after the first frame the pipeline must come from the cache:
16
+ * `webglPipelineMisses` may not grow. Before the fix it grew by two per frame.
17
+ */
18
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
19
+ import { createGuiHarness, guiAvailable } from "./gui-helpers.js";
20
+ const harness = createGuiHarness();
21
+ const PAGE = `
22
+ <html><head><title>webgl pipeline cache</title></head>
23
+ <body style="margin:0">
24
+ <canvas id="c" width="64" height="64"></canvas>
25
+ </body></html>`;
26
+ describe.skipIf(!guiAvailable)("gui extension — WebGL pipeline cache", () => {
27
+ beforeAll(() => harness.setup());
28
+ afterAll(() => harness.cleanup());
29
+ it("reuses the pipeline when only the bound buffers change", () => {
30
+ const { value } = harness.compileAndRun("webgl-pipeline-cache", `
31
+ import { createWindow } from "gui";
32
+ import page from "./page.html";
33
+
34
+ const win = createWindow({ title: "pipeline cache", width: 320, height: 240 });
35
+ win.loadHTML(page);
36
+ const out = (name: string, value: any): void => console.log(name + "=" + value);
37
+
38
+ let gl: any = null;
39
+ let position = 0;
40
+ let buffers: any[] = [];
41
+
42
+ /* A triangle in its own buffer: same layout, different data. */
43
+ const makeBuffer = (center: number): any => {
44
+ const buffer: any = gl.createBuffer();
45
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
46
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
47
+ center - 0.4, -0.4, center + 0.4, -0.4, center, 0.5,
48
+ ]), gl.STATIC_DRAW);
49
+ return buffer;
50
+ };
51
+
52
+ let frame = 0;
53
+ let warm: any = null;
54
+
55
+ const tick = (): void => {
56
+ frame = frame + 1;
57
+ for (const buffer of buffers) {
58
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
59
+ gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
60
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
61
+ }
62
+ /* Frames 1-3 warm the cache (shader translation, the first pipelines and
63
+ * the one-time GPU setup). From there the layout never changes. */
64
+ if (frame === 3) warm = win.stats();
65
+ if (frame < 8) {
66
+ win.requestAnimationFrame(tick);
67
+ return;
68
+ }
69
+ const now: any = win.stats();
70
+ out("misses.warm", now.webglPipelineMisses - warm.webglPipelineMisses);
71
+ out("draws.warm", now.webglDraws - warm.webglDraws);
72
+ out("misses.total", now.webglPipelineMisses);
73
+ out("frames", frame);
74
+ win.close();
75
+ };
76
+
77
+ win.on("ready", () => {
78
+ const canvas: any = document.getElementById("c");
79
+ gl = canvas.getContext("webgl");
80
+ out("webgl", gl != null);
81
+ if (gl == null) {
82
+ win.close();
83
+ return;
84
+ }
85
+
86
+ const vs: any = gl.createShader(gl.VERTEX_SHADER);
87
+ gl.shaderSource(vs, "attribute vec2 a_pos;" +
88
+ "void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }");
89
+ gl.compileShader(vs);
90
+ const fs: any = gl.createShader(gl.FRAGMENT_SHADER);
91
+ gl.shaderSource(fs, "precision mediump float;" +
92
+ "void main() { gl_FragColor = vec4(0.2, 0.6, 1.0, 1.0); }");
93
+ gl.compileShader(fs);
94
+
95
+ const program: any = gl.createProgram();
96
+ gl.attachShader(program, vs);
97
+ gl.attachShader(program, fs);
98
+ gl.linkProgram(program);
99
+ out("link", gl.getProgramParameter(program, gl.LINK_STATUS));
100
+ gl.useProgram(program);
101
+
102
+ position = gl.getAttribLocation(program, "a_pos");
103
+ gl.enableVertexAttribArray(position);
104
+ buffers = [makeBuffer(-0.4), makeBuffer(0.4)];
105
+ win.requestAnimationFrame(tick);
106
+ });
107
+ win.on("close", () => console.log("closed"));
108
+ `, { files: { "page.html": PAGE }, autocloseMs: 15000, console: true });
109
+ expect(value("webgl")).toBe("true");
110
+ expect(value("link")).toBe("true");
111
+ expect(value("frames")).toBe("8");
112
+ /* The draw calls really happened. */
113
+ expect(Number(value("draws.warm"))).toBeGreaterThan(0);
114
+ /* ... and none of them built a pipeline: the layout is unchanged. */
115
+ expect(Number(value("misses.warm"))).toBe(0);
116
+ /* The cache stays bounded: a handful for the whole program. */
117
+ expect(Number(value("misses.total"))).toBeLessThan(8);
118
+ });
119
+ });
120
+ //# sourceMappingURL=gui-webgl-pipeline-cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gui-webgl-pipeline-cache.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-webgl-pipeline-cache.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;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,MAAM,IAAI,GAAG;;;;eAIE,CAAC;AAEhB,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAC1E,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,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CACrC,sBAAsB,EACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8EC,EACD,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CACpE,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,qCAAqC;QACrC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACvD,qEAAqE;QACrE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,+DAA+D;QAC/D,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,175 @@
1
+ import { join } from "node:path";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ const mocks = vi.hoisted(() => ({ findRuntimeLibrary: vi.fn() }));
4
+ /* Keep the archive lookup deterministic so both the success and the
5
+ * "not built yet" branches of `nativeObjects` can be exercised. */
6
+ vi.mock("../../src/driver/runtime-lib.js", () => ({
7
+ findRuntimeLibrary: mocks.findRuntimeLibrary,
8
+ runtimeLibDir: () => "/tmp/runtime/lib/darwin-x64",
9
+ }));
10
+ vi.mock("../../src/driver/paths.js", () => ({
11
+ findRuntimeDir: () => "/tmp/runtime",
12
+ platformSlug: () => "darwin-x64",
13
+ }));
14
+ import { guiExtension } from "../../src/extensions/gui/index.js";
15
+ import { guiLinkFeatures, hasGpuUsage, hasImageUsage, hasMediaUsage, hasShapingUsage, hasWebglUsage, } from "../../src/extensions/gui/features.js";
16
+ afterEach(() => {
17
+ vi.restoreAllMocks();
18
+ mocks.findRuntimeLibrary.mockReset();
19
+ });
20
+ /** Build a usage record with the fields a matcher reads. */
21
+ function usage(overrides = {}) {
22
+ return {
23
+ strings: [],
24
+ identifiers: new Set(),
25
+ getContext: [],
26
+ dynamicMarkup: false,
27
+ ...overrides,
28
+ };
29
+ }
30
+ /** The features in declaration order. */
31
+ const media = guiLinkFeatures.find((feature) => feature.name === "media");
32
+ const gpu = guiLinkFeatures.find((feature) => feature.name === "gpu");
33
+ const images = guiLinkFeatures.find((feature) => feature.name === "images");
34
+ const shaping = guiLinkFeatures.find((feature) => feature.name === "shaping");
35
+ const webgl = guiLinkFeatures.find((feature) => feature.name === "webgl");
36
+ describe("gui link features", () => {
37
+ it("declares media, gpu, images, shaping and webgl as optional halves of gui", () => {
38
+ expect(guiExtension.linkFeatures?.()).toBe(guiLinkFeatures);
39
+ expect(guiLinkFeatures.map((feature) => feature.name)).toEqual([
40
+ "media",
41
+ "gpu",
42
+ "images",
43
+ "shaping",
44
+ "webgl",
45
+ ]);
46
+ expect(media.description).toMatch(/FFmpeg/);
47
+ expect(gpu.description).toMatch(/glslang|GLSL/);
48
+ expect(images.description).toMatch(/stb_image|NanoSVG/);
49
+ expect(shaping.description).toMatch(/HarfBuzz|FreeType/);
50
+ expect(webgl.requires).toEqual(["gpu"]);
51
+ });
52
+ it("links the media bridge and the prebuilt archive", () => {
53
+ mocks.findRuntimeLibrary.mockReturnValue("/tmp/media.a");
54
+ expect(media.runtimeSources?.()).toEqual([join("/tmp/runtime", "ext_media", "media_bridge.c")]);
55
+ expect(media.nativeObjects?.()).toEqual(["/tmp/media.a"]);
56
+ expect(mocks.findRuntimeLibrary).toHaveBeenCalledWith("media");
57
+ });
58
+ it("links the gpu bridge without an archive (the code is in gui.a)", () => {
59
+ expect(gpu.runtimeSources?.()).toEqual([join("/tmp/runtime", "ext_gui", "gpu_bridge.c")]);
60
+ expect(gpu.nativeObjects?.()).toBeUndefined();
61
+ });
62
+ it("links the images bridge without an archive", () => {
63
+ expect(images.runtimeSources?.()).toEqual([join("/tmp/runtime", "ext_gui", "image_bridge.c")]);
64
+ expect(images.nativeObjects?.()).toBeUndefined();
65
+ });
66
+ it("links the shaping bridge without an archive", () => {
67
+ expect(shaping.runtimeSources?.()).toEqual([join("/tmp/runtime", "ext_gui", "text_bridge.c")]);
68
+ expect(shaping.nativeObjects?.()).toBeUndefined();
69
+ });
70
+ it("links the webgl bridge without an archive", () => {
71
+ expect(webgl.runtimeSources?.()).toEqual([join("/tmp/runtime", "ext_gui", "webgl_bridge.c")]);
72
+ expect(webgl.nativeObjects?.()).toBeUndefined();
73
+ });
74
+ it("fails with an actionable message when the media archive is missing", () => {
75
+ mocks.findRuntimeLibrary.mockReturnValue(undefined);
76
+ expect(() => media.nativeObjects?.()).toThrow(/media native library not found/);
77
+ expect(() => media.nativeObjects?.()).toThrow(/npm run media/);
78
+ expect(() => media.nativeObjects?.()).toThrow(/darwin-x64/);
79
+ });
80
+ it("adds FFmpeg's Win32 libraries on Windows only", () => {
81
+ vi.spyOn(process, "platform", "get").mockReturnValue("win32");
82
+ expect(media.linkerFlags?.()).toEqual(["-lbcrypt", "-lole32", "-luser32"]);
83
+ vi.restoreAllMocks();
84
+ vi.spyOn(process, "platform", "get").mockReturnValue("darwin");
85
+ expect(media.linkerFlags?.()).toEqual(["-lm"]);
86
+ });
87
+ });
88
+ describe("media usage detection", () => {
89
+ it("recognises <video>/<audio> tags in HTML strings", () => {
90
+ expect(hasMediaUsage(usage({ strings: ['<html><video src="x.mp4"></video>'] }))).toBe(true);
91
+ expect(hasMediaUsage(usage({ strings: ["<audio controls></audio>"] }))).toBe(true);
92
+ });
93
+ it("recognises createElement tag names and the Audio constructor", () => {
94
+ expect(hasMediaUsage(usage({ strings: ["video"] }))).toBe(true);
95
+ expect(hasMediaUsage(usage({ identifiers: new Set(["Audio"]) }))).toBe(true);
96
+ expect(hasMediaUsage(usage({ identifiers: new Set(["HTMLVideoElement"]) }))).toBe(true);
97
+ expect(hasMediaUsage(usage({ identifiers: new Set(["decodeAudioData"]) }))).toBe(true);
98
+ });
99
+ it("stays off for a DOM-only page and turns on for dynamic markup", () => {
100
+ expect(hasMediaUsage(usage({ strings: ["<div>hi</div>"], identifiers: new Set(["document"]) }))).toBe(false);
101
+ expect(hasMediaUsage(usage({ dynamicMarkup: true }))).toBe(true);
102
+ });
103
+ });
104
+ describe("gpu usage detection", () => {
105
+ it("turns on for a WebGL context", () => {
106
+ expect(hasGpuUsage(usage({ getContext: ["webgl2"] }))).toBe(true);
107
+ expect(hasGpuUsage(usage({ getContext: ["experimental-webgl"] }))).toBe(true);
108
+ });
109
+ it("turns on for a context kind decided at run time", () => {
110
+ expect(hasGpuUsage(usage({ getContext: [undefined] }))).toBe(true);
111
+ });
112
+ it("turns on for mix-blend-mode/backdrop-filter in HTML/CSS or the style property", () => {
113
+ expect(hasGpuUsage(usage({ strings: ["style=\"mix-blend-mode: multiply\""] }))).toBe(true);
114
+ expect(hasGpuUsage(usage({ identifiers: new Set(["mixBlendMode"]) }))).toBe(true);
115
+ expect(hasGpuUsage(usage({ strings: ["backdrop-filter: blur(4px)"] }))).toBe(true);
116
+ expect(hasGpuUsage(usage({ identifiers: new Set(["backdropFilter"]) }))).toBe(true);
117
+ });
118
+ it("stays off for a 2D canvas and plain DOM", () => {
119
+ expect(hasGpuUsage(usage({ getContext: ["2d"] }))).toBe(false);
120
+ expect(hasGpuUsage(usage({ strings: ["<canvas id=c></canvas>"] }))).toBe(false);
121
+ });
122
+ it("turns on for dynamic markup", () => {
123
+ expect(hasGpuUsage(usage({ dynamicMarkup: true }))).toBe(true);
124
+ });
125
+ });
126
+ describe("webgl usage detection", () => {
127
+ it("turns on only for a WebGL context or a webgl string", () => {
128
+ expect(hasWebglUsage(usage({ getContext: ["webgl2"] }))).toBe(true);
129
+ expect(hasWebglUsage(usage({ strings: ["webgl2"] }))).toBe(true);
130
+ expect(hasWebglUsage(usage({ getContext: [undefined] }))).toBe(true);
131
+ });
132
+ it("stays off for a 2D canvas, blend-only CSS and plain DOM", () => {
133
+ expect(hasWebglUsage(usage({ getContext: ["2d"] }))).toBe(false);
134
+ expect(hasWebglUsage(usage({ strings: ["mix-blend-mode: multiply"] }))).toBe(false);
135
+ expect(hasWebglUsage(usage({ strings: ["<canvas id=c></canvas>"] }))).toBe(false);
136
+ });
137
+ it("turns on for dynamic markup", () => {
138
+ expect(hasWebglUsage(usage({ dynamicMarkup: true }))).toBe(true);
139
+ });
140
+ });
141
+ describe("image usage detection", () => {
142
+ it("turns on for <img>, CSS backgrounds, SVG and drawImage", () => {
143
+ expect(hasImageUsage(usage({ strings: ["<img src=a.png>"] }))).toBe(true);
144
+ expect(hasImageUsage(usage({ strings: ["background-image: url(a.png)"] }))).toBe(true);
145
+ expect(hasImageUsage(usage({ strings: ["<svg viewBox='0 0 10 10'></svg>"] }))).toBe(true);
146
+ expect(hasImageUsage(usage({ identifiers: new Set(["drawImage"]) }))).toBe(true);
147
+ });
148
+ it("stays off for text-only DOM", () => {
149
+ expect(hasImageUsage(usage({ strings: ["<div>Hello</div>"] }))).toBe(false);
150
+ expect(hasImageUsage(usage({ getContext: ["2d"] }))).toBe(false);
151
+ });
152
+ it("turns on for dynamic markup", () => {
153
+ expect(hasImageUsage(usage({ dynamicMarkup: true }))).toBe(true);
154
+ });
155
+ });
156
+ describe("shaping usage detection", () => {
157
+ it("turns on for non-ASCII text and escaped/encoded codepoints", () => {
158
+ expect(hasShapingUsage(usage({ strings: ["<p>中文测试</p>"] }))).toBe(true);
159
+ expect(hasShapingUsage(usage({ strings: ["café"] }))).toBe(true);
160
+ expect(hasShapingUsage(usage({ strings: ["\\u4e2d\\u6587"] }))).toBe(true);
161
+ expect(hasShapingUsage(usage({ strings: ["&#20013;&#25991;"] }))).toBe(true);
162
+ });
163
+ it("turns on for author @font-face rules (custom fonts may be CFF/OTF)", () => {
164
+ expect(hasShapingUsage(usage({
165
+ strings: ['@font-face { font-family: "X"; src: url("./x.otf"); }'],
166
+ }))).toBe(true);
167
+ });
168
+ it("stays off for ASCII-only text", () => {
169
+ expect(hasShapingUsage(usage({ strings: ["<div>Hello</div>"] }))).toBe(false);
170
+ });
171
+ it("turns on for dynamic markup", () => {
172
+ expect(hasShapingUsage(usage({ dynamicMarkup: true }))).toBe(true);
173
+ });
174
+ });
175
+ //# sourceMappingURL=gui-features.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gui-features.test.js","sourceRoot":"","sources":["../../../tests/extensions/gui-features.test.ts"],"names":[],"mappings":"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"}
package/doc/ai/cli.md CHANGED
@@ -47,7 +47,9 @@ so restart it after installing a dependency.
47
47
  --out <dir> Output directory (default: build/)
48
48
  --emit <kind> exe | obj | ir (default: exe)
49
49
  -O0 .. -O3 Optimization level passed to clang (default: -O2)
50
- --ext <names> Enable bundled extensions, comma separated (e.g. node,gui,media)
50
+ --ext <names> Enable bundled extensions, comma separated (e.g. node,gui)
51
+ --feature <names> Force optional link features on (media,gpu,images,shaping,webgl,all)
52
+ --no-feature <n> Keep optional link features out even when detected
51
53
  --ext-native <m> Register a C++/Rust extension from a JSON manifest
52
54
  (comma separated for several)
53
55
  --config <path> Use this project config instead of discovering one
@@ -121,6 +123,8 @@ matching field. `--no-config` skips discovery entirely.
121
123
  | `pgoDir` | string | Profile directory (default `<outDir>/pgo`) |
122
124
  | `extensions` | string[] | Bundled extensions to enable, e.g. `["node"]` |
123
125
  | `extNative` | string[] | Native extension manifest paths |
126
+ | `features` | string[] | Optional link features to force on, e.g. `["media"]` / `["all"]` |
127
+ | `noFeatures` | string[] | Optional link features to keep out even when detected |
124
128
  | `force` | boolean | Ignore the incremental cache |
125
129
  | `app.name` / `app.icon` / `app.bundle` / `app.bundleId` | | Application metadata ([icon.md](../icon.md)) |
126
130
 
@@ -1,8 +1,10 @@
1
- # Extensions: Node modules, GUI, media, C++/Rust libraries
1
+ # Extensions: Node modules, GUI, link features, C++/Rust libraries
2
2
 
3
3
  The compiler core is platform-agnostic. Anything platform-specific — Node's
4
4
  `fs`, an HTML/CSS renderer, your own C++ library — arrives as an **extension**
5
5
  that contributes module bindings plus the C/C++ sources or objects to link.
6
+ Heavy optional halves of an extension (FFmpeg, glslang) are **link features**
7
+ selected from the program instead of separate extension flags.
6
8
 
7
9
  ## The rule that trips people up
8
10
 
@@ -74,8 +76,9 @@ blocks, because the runtime parser only reads inline `<style>`; and it rewrites
74
76
  a bundled three.js module's `WebGLRenderer` to use the real WebGL 2 backend:
75
77
  only its shader unroller is patched, and no Canvas2D fallback is bundled. It
76
78
  ships as a per-platform prebuilt `gui.a`/`gui.lib` because it is C++. `<img>` decodes through the engine
77
- itself; `<video>`/`<audio>` do **not** work with `gui` alone — they need the
78
- separate [`media`](#media--videoaudio-decoding) extension. `<canvas>` is
79
+ itself; `<video>`/`<audio>` decoding (FFmpeg) and GLSL translation (glslang) are
80
+ **optional link features** selected from the program — see
81
+ [Optional link features](#optional-link-features-media-gpu-images-shaping-webgl). `<canvas>` is
79
82
  supported too: `getContext("2d")` returns a context whose fills, paths,
80
83
  gradients, text and `drawImage` are rasterised by a self-written CPU rasteriser
81
84
  (plus line caps/joins, dashed strokes, `ellipse` and `roundRect`), and element
@@ -85,9 +88,13 @@ text, images and children in it) can be translated, scaled and rotated without
85
88
  reflowing anything — animate it per frame from `requestAnimationFrame` or with
86
89
  `transition: transform` (see `examples/gui/pelican-svg`). The CSS subset also
87
90
  covers `@media`, `::before`/`::after` generated content, multi-layer linear/radial
88
- backgrounds, `text-shadow`, `clip-path` and `filter` colour/shadow effects;
89
- `backdrop-filter` and true group blur are documented approximations or
90
- unsupported. If the archive is
91
+ backgrounds, `calc()`, `text-shadow`/`box-shadow` with a true Gaussian blur,
92
+ `clip-path`, `filter` colour effects, `backdrop-filter` compositing,
93
+ `@keyframes` and `transition`/`animation` timing functions, `letter-spacing`,
94
+ `text-transform`, inline-level `flex`/`grid`, `grid-area`, `aspect-ratio`, auto
95
+ margins, percentage `border-radius`, `outline`, tiled `background-size` for
96
+ `url()` layers, document CSS on SVG children and text-only vertical writing
97
+ mode. `filter: blur()` is still a documented approximation. If the archive is
91
98
  missing for your platform, the build fails with an actionable message; design
92
99
  and roadmap live in [../gui.md](../gui.md) (overview; the engine is documented by
93
100
  function under [`doc/gui/`](../gui/)), and the scripting surface in
@@ -101,7 +108,7 @@ Pages written for the browser also get the browser globals they reach for —
101
108
  (`AudioContext` with gain, oscillator, buffer-source, biquad, compressor,
102
109
  stereo-panner, constant-source and delay nodes, a real 48 kHz mixer, per-sample
103
110
  AudioParam automation, and `decodeAudioData` for WAV in-engine plus compressed
104
- formats through `media`) — plus a frame pump that
111
+ formats through the `media` feature) — plus a frame pump that
105
112
  starts at `loadHTML` and re-arms only while animation-frame callbacks remain, so
106
113
  a page that never calls `run()` still paints and then exits. **WebGL 1.0 and 2.0 are implemented**: `canvas.getContext("webgl")` /
107
114
  `"webgl2"` return a GPU context whose GLSL ES shaders are compiled to SPIR-V by
@@ -109,19 +116,47 @@ a bundled glslang — consumed by Vulkan, or cross-compiled to MSL by bundled
109
116
  SPIRV-Cross on Metal — and drawn through SDL_GPU; the page composites the colour
110
117
  texture without a CPU readback, and WebGL 2 adds VAOs, instancing, integer
111
118
  attributes, sampler objects, `texStorage2D` and buffer copies (see
112
- [../gui/webgl.md](../gui/webgl.md)). WebXR, `<video>`/`<audio>` without
113
- `media`, and streaming are **not** implemented:
114
- `typeof` answers `"undefined"` so a page takes its own fallback branch. `fetch`
119
+ [../gui/webgl.md](../gui/webgl.md)). WebXR and streaming are **not**
120
+ implemented: `typeof` answers `"undefined"` so a page takes its own fallback
121
+ branch. `fetch`
115
122
  is **not** a GUI extension feature: the core runtime provides it (`http://`,
116
123
  plus local `file://` and relative paths; no HTTPS or streaming), so GUI
117
124
  programs can use it too. See [../implemented/fetch.md](../implemented/fetch.md).
118
125
 
119
- ### `media` — video/audio decoding
120
-
121
- Enable with `--ext media` **together with** `gui` (`--ext gui,media`; the
122
- registry rejects `media` on its own). It links a separate FFmpeg-backed
123
- `media.a`/`media.lib`, so adding `<video>`/`<audio>` playback does not make a
124
- GUI-only binary any larger.
126
+ ### Optional link features (`media`, `gpu`, `images`, `shaping`, `webgl`)
127
+
128
+ `gui` is one extension, but its heavy halves are optional **link features**. The
129
+ compiler scans the bundled HTML/CSS/TS (imported `.html` assets and their inlined
130
+ CSS are part of the bundle) and selects them per program; you do not pass an
131
+ extension flag:
132
+
133
+ - **`media`** — FFmpeg decoding for `<video>`/`<audio>` and compressed
134
+ `decodeAudioData`. Selected by a `<video>`/`<audio>` element,
135
+ `createElement("video"/"audio")`, `new Audio()`, `decodeAudioData`, or markup
136
+ built at run time. Links the FFmpeg-backed `media.a`/`media.lib`.
137
+ - **`gpu`** — the glslang/SPIRV-Cross GLSL translator shared by WebGL and
138
+ `mix-blend-mode`. Selected by `getContext("webgl"/"webgl2"/…)` (or a context
139
+ kind decided at run time), any `webgl` string, or `mix-blend-mode`.
140
+ - **`images`** — stb_image/NanoSVG decoding for `<img>`, inline `<svg>`, CSS
141
+ `background-image`/`url(...)`, `drawImage`/`createImageBitmap`/`Image`, and an
142
+ app icon. A text-only page links neither decoder.
143
+ - **`shaping`** — HarfBuzz + FreeType for full OpenType shaping (ligatures,
144
+ complex scripts) and CFF/OTF/variable fonts. Selected automatically for
145
+ non-ASCII text (or an escaped/entity codepoint) or an author `@font-face`
146
+ rule; a Latin-only page keeps the stb_truetype backend and links neither
147
+ library.
148
+ - **`webgl`** — the WebGL context and DOM bindings. Selected by a WebGL
149
+ `getContext(...)` or a `webgl` string; it requires `gpu`, so both activate
150
+ together. A page that never asks for WebGL links neither.
151
+
152
+ Override the scan with `--feature media,gpu,images,shaping,webgl` (or `all`) and keep a
153
+ feature out with `--no-feature media`. `--verbose` prints the selected set. A
154
+ program that only renders HTML/CSS/2D canvas links no FFmpeg, glslang, image
155
+ decoder, HarfBuzz/FreeType or WebGL context; a page that
156
+ builds HTML/CSS from runtime data should pass `--feature all` because the scan
157
+ cannot see it. If an auto-detected feature's archive is missing, the build warns
158
+ and the capability degrades at run time (media elements fire `error`); a forced
159
+ feature makes it an error.
125
160
 
126
161
  The DOM API is the same as for any HTML media element: `play()` / `pause()` /
127
162
  `load()`, the `paused` / `ended` / `duration` / `currentTime` / `volume` /
@@ -129,16 +164,11 @@ The DOM API is the same as for any HTML media element: `play()` / `pause()` /
129
164
  `src` properties, and `loadedmetadata`/`play`/`pause`/`ended`/`timeupdate`/
130
165
  `error` events. There are no built-in controls — draw your own chrome with
131
166
  elements and CSS (see `examples/gui/media.ts`). Video frames and audio PCM are
132
- decoded on the main thread; video goes through the image texture path. Without
133
- the extension, opening a media source fails and fires `error` (the element and
134
- its API still exist). The archive is built for every host: POSIX produces
135
- `media.a`, while Windows produces `media.lib` from FFmpeg compiled with the
136
- MSVC-targeting `clang-cl`, so it links with `gui.lib`. Building it on Windows
137
- needs a POSIX shell (Git for Windows' bundled bash) and a native GNU make
138
- (Chocolatey `make`, or `xbintsc_MAKE`). See
139
- [../gui/build.md](../gui/build.md). Without the archive, or on a host where
140
- `npm run media` has not run, media elements report `error`; see
141
- [../gui/known-issues.md](../gui/known-issues.md).
167
+ decoded on the main thread; video goes through the image texture path. Building
168
+ the archive on Windows needs a POSIX shell (Git for Windows' bundled bash) and a
169
+ native GNU make (Chocolatey `make`, or `xbintsc_MAKE`). See
170
+ [../gui/build.md](../gui/build.md) and
171
+ [../gui/media.md](../gui/media.md).
142
172
 
143
173
  ## Third-party npm packages
144
174
 
@@ -123,15 +123,18 @@ optional `@xbintsc/<os>-<arch>` npm package ships it (reinstall without
123
123
 
124
124
  ### `media native library not found at …`
125
125
 
126
- `<video>`/`<audio>` playback needs the optional `media` archive (`media.a`, or
127
- `media.lib` on Windows). The `@xbintsc/<os>-<arch>` npm package ships it where
128
- the release builds it; otherwise build it separately with `npm run media`. Build
129
- it, or drop `--ext media`; a GUI-only build still links and media elements simply fire
130
- `error`. The `media` extension also requires `gui`, and `--ext media` alone is
131
- rejected with the flags to pass. On Windows the FFmpeg build additionally needs
126
+ `<video>`/`<audio>` decoding needs the optional `media` link feature and its
127
+ archive (`media.a`, or `media.lib` on Windows). The `@xbintsc/<os>-<arch>` npm
128
+ package ships it where the release builds it; otherwise build it with
129
+ `npm run media`. When the feature is selected automatically and the archive is
130
+ missing, the build prints a **warning** and continues; media elements then simply
131
+ fire `error`. An explicit `--feature media` (or the deprecated `--ext media`)
132
+ makes it a hard error instead. Use `--no-feature media` to opt out on purpose.
133
+ On Windows the FFmpeg build additionally needs
132
134
  a POSIX shell (Git for Windows' bash) and a native GNU make (Chocolatey `make`,
133
135
  or `xbintsc_MAKE`). The script names whichever is missing. See
134
- [../gui/build.md](../gui/build.md).
136
+ [../gui/build.md](../gui/build.md) and
137
+ [../gui/build.md#link-features](../gui/build.md#link-features).
135
138
 
136
139
  ### `Icon file not found` / icon tools missing
137
140
 
@@ -40,7 +40,9 @@
40
40
  --out <dir> 输出目录(默认 build/)
41
41
  --emit <kind> exe | obj | ir(默认 exe)
42
42
  -O0 .. -O3 传给 clang 的优化级别(默认 -O2)
43
- --ext <names> 开启内置扩展,逗号分隔(如 node,gui,media)
43
+ --ext <names> 开启内置扩展,逗号分隔(如 node,gui)
44
+ --feature <names> 强制开启可选链接特性(media,gpu,images,shaping,webgl,all)
45
+ --no-feature <n> 即使检测到也不链接该可选特性
44
46
  --ext-native <m> 通过 JSON 清单注册 C++/Rust 扩展(多个用逗号分隔)
45
47
  --config <path> 使用指定项目配置,不做自动发现
46
48
  --no-config 完全不读取项目配置
@@ -102,6 +104,8 @@ profile 数据可能独立于源码变化。
102
104
  | `pgoDir` | string | Profile 目录(默认 `<outDir>/pgo`) |
103
105
  | `extensions` | string[] | 开启的内置扩展,如 `["node"]` |
104
106
  | `extNative` | string[] | 原生扩展清单路径 |
107
+ | `features` | string[] | 强制开启的可选链接特性,如 `["media"]` / `["all"]` |
108
+ | `noFeatures` | string[] | 即使检测到也保持关闭的可选链接特性 |
105
109
  | `force` | boolean | 忽略增量缓存 |
106
110
  | `app.name` / `app.icon` / `app.bundle` / `app.bundleId` | | 应用元信息([icon.md](../../zh-CN/icon.md)) |
107
111