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
@@ -1,8 +1,9 @@
1
- # 扩展:Node 模块、GUI、媒体、C++/Rust 库
1
+ # 扩展:Node 模块、GUI、链接特性、C++/Rust 库
2
2
 
3
3
  编译器核心与平台无关。任何与平台相关的东西——Node 的 `fs`、一个 HTML/CSS 渲染器、
4
4
  你自己的 C++ 库——都以**扩展**的形式接入:它既贡献模块绑定,也贡献要一起编译/链接的
5
- C/C++ 源码或目标文件。
5
+ C/C++ 源码或目标文件。扩展里的重型可选部分(FFmpeg、glslang)做成**链接特性**,
6
+ 由程序用法选择,而不是单独的扩展开关。
6
7
 
7
8
  ## 最容易踩的规则
8
9
 
@@ -70,8 +71,9 @@ import { createWindow, run } from "gui";
70
71
  `<style>` 块(运行时解析器只读取内联 `<style>`);并让随包引入的 Three.js 的
71
72
  `WebGLRenderer` 先探测 WebGL 上下文:引擎有上下文时使用 Three.js 自己的渲染器,
72
73
  直接使用真实的 WebGL 2 后端(只修补其着色器循环展开,不再打包 Canvas2D 回退)。
73
- `<img>` 由引擎自身解码;而 `<video>`/`<audio>` 在只启用 `gui` 时**不可用**
74
- ——它们需要单独的 [`media`](#media--视频音频解码) 扩展。也支持 `<canvas>`:
74
+ `<img>` 由引擎自身解码;`<video>`/`<audio>` 解码(FFmpeg)与 GLSL 翻译(glslang)
75
+ 是**可选的链接特性**,由程序自动选择——见
76
+ [可选链接特性](#可选链接特性media-gpu-images-shaping-webgl)。也支持 `<canvas>`:
75
77
  `getContext("2d")` 返回的上下文,其填充、路径、
76
78
  渐变、文本与 `drawImage` 由自研 CPU 光栅器处理(另有线段端点/连接、虚线描边、
77
79
  `ellipse` 与 `roundRect`),元素句柄还暴露 `width`/`height`。CSS
@@ -79,8 +81,13 @@ import { createWindow, run } from "gui";
79
81
  图像与子元素)可以被平移、缩放与旋转而不触发任何重排——可以用
80
82
  `requestAnimationFrame` 逐帧驱动,或写成 `transition: transform`(见
81
83
  `examples/gui/pelican-svg`)。CSS 子集还覆盖 `@media`、`::before`/`::after`
82
- 生成内容、线性/径向多层背景、`text-shadow`、`clip-path` 与 `filter`
83
- 颜色/阴影效果;`backdrop-filter` 与真正的组模糊是有文档说明的近似或未支持。
84
+ 生成内容、线性/径向多层背景、`calc()`、真正的 `text-shadow`/`box-shadow`
85
+ 高斯模糊、`clip-path`、`filter` 颜色效果、`backdrop-filter` 合成、
86
+ `@keyframes` 与 `transition`/`animation` 时间函数、`letter-spacing`、
87
+ `text-transform`、行内级 `flex`/`grid`、`grid-area`、`aspect-ratio`、自动
88
+ 外边距、百分比 `border-radius`、`outline`、`url()` 图层的平铺
89
+ `background-size`、SVG 子元素上的文档 CSS,以及仅文本的竖排书写模式。`filter: blur()`
90
+ 仍是有文档说明的近似。
84
91
  因为主体是 C++,它以各平台预编译的 `gui.a`/`gui.lib` 形式分发。如果该
85
92
  平台的归档不存在,构建会以明确的提示失败;设计与路线图见 [../../zh-CN/gui.md](../../zh-CN/gui.md)(总览;引擎按功能拆分在 [`doc/zh-CN/gui/`](../../zh-CN/gui/) 下),
86
93
  脚本接口见 [../../zh-CN/gui-scripts.md](../../zh-CN/gui-scripts.md)。Canvas API
@@ -91,38 +98,57 @@ import { createWindow, run } from "gui";
91
98
  `getComputedStyle`、`alert`/`prompt`、`escape`、`Event` 家族,以及可用的 Web Audio
92
99
  实现(`AudioContext` 提供 gain、oscillator、buffer-source、biquad、compressor、
93
100
  stereo-panner、constant-source 与 delay 节点、真实的 48 kHz 混音器、逐采样的
94
- AudioParam 自动化,`decodeAudioData` 引擎内解码 WAV,压缩格式则通过 `media`)—— 另外还有帧泵:
101
+ AudioParam 自动化,`decodeAudioData` 引擎内解码 WAV,压缩格式则通过 `media` 特性)—— 另外还有帧泵:
95
102
  它在 `loadHTML` 时启动,仅在仍有动画帧回调时重新排程,因此从不调用 `run()` 的页面
96
103
  也能绘制并最终退出。**WebGL 1.0 与 2.0 已实现**:`canvas.getContext("webgl")` /
97
104
  `"webgl2"` 返回 GPU 上下文,其 GLSL ES 着色器由内置 glslang 编译为 SPIR-V
98
105
  (Vulkan 直接消费,Metal 上由内置 SPIRV-Cross 转成 MSL),
99
106
  并通过 SDL_GPU 绘制;页面直接合成颜色纹理而无须 CPU 读回,WebGL 2 还增加
100
107
  VAO、实例化、整数属性、采样器对象、`texStorage2D` 与缓冲拷贝(见
101
- [../../zh-CN/gui/webgl.md](../../zh-CN/gui/webgl.md))。WebXR、未启用
102
- `media` 时的 `<video>`/`<audio>` 与流式接口**尚未实现**:`typeof` 会回答
108
+ [../../zh-CN/gui/webgl.md](../../zh-CN/gui/webgl.md))。WebXR 与流式接口**尚未实现**:
109
+ `typeof` 会回答
103
110
  `"undefined"`,让页面走自己的降级分支。`fetch` 不是 GUI 扩展的功能——它由核心
104
111
  运行时提供(`http://`,以及本地 `file://` 与相对路径;不支持 HTTPS 与流式),
105
112
  因此 GUI 程序同样可用。见 [../../implemented/fetch.md](../../implemented/fetch.md)。
106
113
 
107
- ### `media` —— 视频/音频解码
108
-
109
- 用 `--ext media` **与 `gui` 一起**开启(`--ext gui,media`;注册表会拒绝单独启用
110
- `media`)。它链接单独的 FFmpeg 后端 `media.a`/`media.lib`,因此加入
111
- `<video>`/`<audio>` 播放不会让只做界面的二进制变大。
114
+ ### 可选链接特性(media、gpu、images、shaping、webgl)
115
+
116
+ `gui` 仍然只有一个扩展,但其中的重型部分是可选的**链接特性**。编译器扫描打包后的
117
+ HTML/CSS/TS(导入的 `.html` 与内联 CSS 都属于 bundle),按程序自动选择;不需要额外
118
+ 扩展参数:
119
+
120
+ - **`media`** —— `<video>`/`<audio>` 与压缩格式 `decodeAudioData` 的 FFmpeg 解码。
121
+ 由 `<video>`/`<audio>` 元素、`createElement("video"/"audio")`、`new Audio()`、
122
+ `decodeAudioData` 或运行时拼出的 markup 触发,链接 FFmpeg 后端
123
+ `media.a`/`media.lib`。
124
+ - **`gpu`** —— WebGL 与 `mix-blend-mode` 共用的 glslang/SPIRV-Cross GLSL 翻译器。
125
+ 由 `getContext("webgl"/"webgl2"/…)`(或运行时决定的 kind)、任意 `webgl` 字符串、
126
+ `mix-blend-mode` 触发。
127
+ - **`images`** —— `<img>`、内联 `<svg>`、CSS `background-image`/`url(...)`、
128
+ `drawImage`/`createImageBitmap`/`Image` 与应用图标所需的 stb_image/NanoSVG 解码。
129
+ 纯文本页面两个解码器都不链接。
130
+ - **`shaping`** —— HarfBuzz + FreeType 提供的完整 OpenType 整形(连字、复杂文种)
131
+ 与 CFF/OTF/可变字体支持。非 ASCII 文本(或转义/实体码点)或作者的
132
+ `@font-face` 规则会自动选中;纯拉丁页面保持 stb_truetype 后端,两个库都不链接。
133
+ - **`webgl`** —— WebGL context 与 DOM 绑定。由 WebGL 的 `getContext(...)` 或
134
+ `webgl` 字符串触发;它依赖 `gpu`,两者一起激活。不用 WebGL 的页面两者都不链接。
135
+
136
+ 用 `--feature media,gpu,images,shaping,webgl`(或 `all`)覆盖扫描,用 `--no-feature media`
137
+ 强制不链接。`--verbose` 会打印选中的集合。只渲染 HTML/CSS/2D canvas 的程序既不链接
138
+ FFmpeg、glslang,也不链接图片解码器、HarfBuzz/FreeType 或 WebGL context;用运行时数据拼 HTML/CSS 的
139
+ 页面应传 `--feature all`,因为扫描看不到它。
140
+ 自动检测到的特性归档缺失时构建打印 warning,能力在运行时降级(媒体元素触发
141
+ `error`);显式强制的特性则报错。
112
142
 
113
143
  DOM API 与普通 HTML 媒体元素一致:`play()` / `pause()` / `load()`,以及
114
144
  `paused` / `ended` / `duration` / `currentTime` / `volume` / `muted` / `loop` /
115
145
  `autoplay` / `videoWidth` / `videoHeight` / `readyState` / `src` 属性,还有
116
146
  `loadedmetadata`/`play`/`pause`/`ended`/`timeupdate`/`error` 事件。没有内置
117
147
  控件——用元素和 CSS 自绘控件(见 `examples/gui/media.ts`)。视频帧与音频 PCM
118
- 都在主线程解码;视频走图像纹理路径。没有该扩展时,打开媒体源会失败并触发
119
- `error`(元素与 API 仍然存在)。归档会为每个宿主平台构建:POSIX 产出
120
- `media.a`,Windows 则用面向 MSVC 的 `clang-cl` 编译 FFmpeg 并产出 `media.lib`,
121
- 因此可与 `gui.lib` 链接。Windows 上构建它需要 POSIX shell(Git for Windows
122
- 自带的 bash)与原生 GNU make(Chocolatey `make`,或 `xbintsc_MAKE`)。见
123
- [../../zh-CN/gui/build.md](../../zh-CN/gui/build.md)。
124
- 没有归档、或宿主上还没运行 `npm run media` 时,媒体元素会触发 `error`;见
125
- [../../zh-CN/gui/known-issues.md](../../zh-CN/gui/known-issues.md)。
148
+ 都在主线程解码;视频走图像纹理路径。Windows 上构建归档需要 POSIX shell(Git for
149
+ Windows 自带的 bash)与原生 GNU make(Chocolatey `make`,或 `xbintsc_MAKE`)。见
150
+ [../../zh-CN/gui/build.md](../../zh-CN/gui/build.md) 与
151
+ [../../zh-CN/gui/media.md](../../zh-CN/gui/media.md)。
126
152
 
127
153
  ## 第三方 npm 包
128
154
 
@@ -113,14 +113,15 @@ npm 包会提供它(去掉 `--omit=optional` 重新安装),但普通源码
113
113
 
114
114
  ### `media native library not found at …`
115
115
 
116
- `<video>`/`<audio>` 播放需要可选的 `media` 归档(`media.a`,Windows 上是
116
+ `<video>`/`<audio>` 解码需要可选的 `media` 链接特性及其归档(`media.a`,Windows 上是
117
117
  `media.lib`)。发布为其构建时,`@xbintsc/<os>-<arch>` npm 包会提供它;否则用
118
- `npm run media` 单独构建。构建它,或去掉 `--ext media`;
119
- 仅启用 `gui` 的构建仍可链接,媒体元素只会触发 `error`。`media` 扩展还依赖
120
- `gui`,单独 `--ext media` 会被拒绝并提示应传的 flag。Windows 上 FFmpeg 的
118
+ `npm run media` 构建。自动选中该特性而归档缺失时,构建打印 **warning** 后继续,
119
+ 媒体元素只会触发 `error`;显式 `--feature media`(或弃用的 `--ext media`)则直接
120
+ 报错。可以用 `--no-feature media` 主动关闭。Windows 上 FFmpeg 的
121
121
  构建还需要 POSIX shell(Git for Windows 的 bash)与原生 GNU make
122
122
  (Chocolatey `make`,或 `xbintsc_MAKE`)。脚本会指出缺失的是哪一个。见
123
- [../../zh-CN/gui/build.md](../../zh-CN/gui/build.md)。
123
+ [../../zh-CN/gui/build.md](../../zh-CN/gui/build.md) 与
124
+ [../../zh-CN/gui/build.md#链接特性](../../zh-CN/gui/build.md#链接特性)。
124
125
 
125
126
  ### `Icon file not found` / 图标工具缺失
126
127
 
@@ -30,21 +30,27 @@ running transition is recorded and re-applied every frame until it finishes;
30
30
  the engine advances the clock with real frame deltas (`XtDocument::advance`),
31
31
  and `win.advance(ms)` lets tests step it deterministically.
32
32
 
33
- - Supported properties: `background-color`, `color`, `border-color`,
34
- `border-radius` and `transform`; `transition: all` covers them. A
35
- layout-affecting property (width, position, …) is still not animated — that
36
- would reflow every frame — but `transform` is paint-time, so animating it costs
37
- one matrix per animated box per frame ([Layout](./layout.md#transforms-m15)).
33
+ - Supported properties: `background-color`, `color`, `border-color`, `opacity`,
34
+ `border-radius`, `transform`, `text-shadow`, `box-shadow`, every box offset
35
+ and size length (`width`/`height`/`top`/`left`/`margin-*`/`padding-*`/…), the
36
+ gap/flex-basis lengths and `filter` (matching filter lists interpolate their
37
+ blur lengths, amounts and drop-shadow colours). `transition: all` covers the
38
+ whole list. Lengths
39
+ interpolate when their units line up (a mixed-unit change is CSS's discrete
40
+ fallback); `opacity`, colours and `transform` interpolate normally.
38
41
  - Both the `transition` shorthand and the `transition-property` / `-duration` /
39
42
  `-delay` / `-timing-function` longhands are parsed; time values accept `s` and
40
- `ms`; timing functions are `linear`, `ease`, `ease-in`, `ease-out` and
41
- `ease-in-out` (`ease` is a smoothstep approximation).
43
+ `ms`; `calc()` is accepted inside the shorthand and longhands.
44
+ - Timing functions are `linear`, `ease`, `ease-in`, `ease-out`, `ease-in-out`,
45
+ `cubic-bezier(x1, y1, x2, y2)`, `steps(n, start|end)` and their `jump-*`
46
+ spellings. The engine solves the cubic Bézier numerically; keyword curves use
47
+ the CSS control points.
42
48
  - Retargeting mid-flight starts a new transition from the current interpolated
43
49
  value, so reversing a hover animates smoothly from wherever it was.
44
50
  - `computedStyle()` reports the displayed (interpolated) value, so transitions
45
51
  are directly observable in tests.
46
52
 
47
- `transform` lists interpolate **element-wise** (`document.cpp`,
53
+ `transform` lists interpolate **element-wise** (`animation_values.cpp`,
48
54
  `xt_transform_interpolate`), which is what makes the common pair
49
55
  `translateY(-4px) rotate(-0.35deg)` <-> `none` glide instead of jumping: the
50
56
  missing side is filled with identity ops of the same kind. Two lists whose shapes
@@ -52,6 +58,29 @@ or units do not line up (`rotate(10deg)` vs `rotate(10rad)`, or a different numb
52
58
  of functions) are not interpolated at all — the transition snaps to the target,
53
59
  which is CSS's discrete fallback rather than a wrong-looking sweep.
54
60
 
61
+ ## CSS animations (`@keyframes`)
62
+
63
+ `@keyframes` rules are parsed into `StyleSheet::keyframes` (offsets from
64
+ `from`/`to`/percent selectors, sorted). `animation` and every `animation-*`
65
+ longhand are supported: name, duration, delay, timing function, iteration count
66
+ (including `infinite`), direction (`normal`/`reverse`/`alternate`/
67
+ `alternate-reverse`), fill mode (`none`/`forwards`/`backwards`/`both`) and
68
+ `paused`/`running`.
69
+
70
+ `XtDocument` starts one active animation per element/name when the computed
71
+ style first declares it, restarts it when the shorthand changes, and leaves it
72
+ alone across unrelated restyles. Each frame it resolves the local time to an
73
+ iteration progress, applies the timing function **per keyframe interval**, and
74
+ interpolates every animatable property. A property that a middle keyframe does
75
+ not mention keeps interpolating between the nearest keyframes that do declare
76
+ it (so a transform declared at 0% and 100% runs straight through a 50% step that
77
+ only changes `opacity`). Missing 0%/100% steps use the element's underlying
78
+ style. `forwards`/`backwards` hold the edge value outside the active time, and
79
+ `computedStyle()` reports the animated value.
80
+
81
+ Animations and transitions share `animation_values.cpp`, so both understand the
82
+ same property set, shadow lists and `transform` identity filling.
83
+
55
84
  ## CSS transforms (M15)
56
85
 
57
86
  `transform` and `transform-origin` are supported as *paint-time* effects
@@ -93,5 +122,6 @@ Both are exercised by `examples/gui/pelican-svg` (per-frame sway plus a
93
122
  hover transition on the caption) and pinned by
94
123
  `tests/e2e/gui-transform.test.ts`.
95
124
 
96
- Still to do for animation: `@keyframes` animations and `cubic-bezier(...)`.
125
+ `calc()` can be used in any animated length (for example
126
+ `transition: width 300ms ease-out; width: calc(100% - 20px)`).
97
127
 
package/doc/gui/api.md CHANGED
@@ -137,7 +137,8 @@ evaluated per sample, so a ramp keeps moving while the page is idle.
137
137
  procedurally. `decodeAudioData(bytes)` returns a promise for a decoded
138
138
  AudioBuffer: WAV (PCM 8/16/24/32-bit, IEEE float 32/64, A-law and µ-law) is
139
139
  decoded by the engine itself; MP3, AAC/M4A, Vorbis, Opus and FLAC need the
140
- optional `media` extension (`--ext gui,media`), whose FFmpeg decoder is used as
140
+ optional `media` link feature (auto-selected when the program calls
141
+ `decodeAudioData`), whose FFmpeg decoder is used as
141
142
  the fallback. `bytes` may be an ArrayBuffer, a typed array, a Node `Buffer`, or
142
143
  the raw byte string `fs.readFileSync` returns (this runtime has no dedicated
143
144
  binary type, and that string's storage is exactly the file bytes). The result
package/doc/gui/build.md CHANGED
@@ -8,13 +8,33 @@
8
8
  `runtime/ext_media/` and is built separately.
9
9
  - `npm run gui` (`scripts/build-gui.ts`) fetches a pinned SDL3 (`SDL3_TAG`,
10
10
  default `release-3.2.10`), builds a static SDL3, fetches and builds static
11
- FreeType (`FREETYPE_VERSION`) and HarfBuzz (`HARFBUZZ_VERSION`), and fetches
12
- and builds static **glslang** (`GLSLANG_VERSION`, default `16.6.0`) against
13
- the pinned SPIR-V headers (`SPIRV_HEADERS_VERSION`), and **SPIRV-Cross**
14
- (`SPIRV_CROSS_VERSION`, default `vulkan-sdk-1.4.313.0`). glslang is the
15
- runtime GLSL ES compiler behind WebGL and SPIRV-Cross turns its SPIR-V into
16
- MSL for the Metal backend (see [WebGL](./webgl.md)); both are merged into the
17
- archive like the other libraries. The script then compiles the engine and
11
+ **FreeType** (`FREETYPE_VERSION`, default `2.13.3`) and **HarfBuzz**
12
+ (`HARFBUZZ_VERSION`, default `10.1.0`) for the optional `shaping` feature,
13
+ and fetches and builds static **glslang** (`GLSLANG_VERSION`, default
14
+ `16.6.0`) against the pinned SPIR-V headers (`SPIRV_HEADERS_VERSION`), and —
15
+ **on macOS only** — **SPIRV-Cross** (`SPIRV_CROSS_VERSION`, default
16
+ `vulkan-sdk-1.4.313.0`). glslang is the runtime GLSL ES compiler behind WebGL
17
+ and SPIRV-Cross turns its SPIR-V into MSL for the Metal backend (see
18
+ [WebGL](./webgl.md)); both are merged into the archive like the other
19
+ libraries. Metal is the only SDL_GPU backend that consumes MSL, so on
20
+ Windows/Linux the script neither fetches nor merges SPIRV-Cross:
21
+ `webgl_msl.cpp` compiles to nothing there and the ~2.5 MiB it contributes
22
+ would be unreachable. The default text path uses the bundled
23
+ **stb_truetype** (`runtime/ext_gui/stb_truetype.h`); HarfBuzz + FreeType are
24
+ only extracted when the `shaping` feature is active.
25
+ Every library (SDL3, FreeType, HarfBuzz, glslang, SPIRV-Cross and the engine)
26
+ is compiled with `-ffunction-sections -fdata-sections`, and SDL is configured
27
+ without the subsystems the engine never calls (`SDL_JOYSTICK`, `SDL_HAPTIC`,
28
+ `SDL_SENSOR`, `SDL_CAMERA`, `SDL_RENDER`, `SDL_OPENGL`/`SDL_OPENGLES`,
29
+ `SDL_OFFSCREEN`, `SDL_HIDAPI`). HarfBuzz omits the APIs the shaping backend
30
+ never uses (`HB_NO_DRAW`/`PAINT`/`COLOR`/`NAME`/`STYLE`/`METRICS`/…, while
31
+ keeping `CFF`/`OPEN`/`VAR` for OTF and variable fonts) and is built with
32
+ `HB_NO_PRAGMA_GCC_DIAGNOSTIC_ERROR`, because `src/hb.hh` raises its warning
33
+ set to errors and two lean defines leave macOS/CoreText helpers unreferenced
34
+ (`HB_NO_MMAP` and `HB_NO_OT_FONT_GLYPH_NAMES`). The linker then drops
35
+ the unreachable functions (`--gc-sections`, `-dead_strip`, or link.exe's
36
+ default `/OPT:REF`). The
37
+ script then compiles the engine and
18
38
  merges everything into `runtime/lib/<os>-<arch>/gui.a` (or
19
39
  `gui.lib` with the MSVC ABI on Windows), same convention as `core.a`,
20
40
  statically bundling all three. The merge uses `libtool` on macOS and
@@ -25,13 +45,17 @@
25
45
  minimal static build with only the demuxers/parsers/decoders the media
26
46
  elements need, compiles `runtime/ext_media/media.cpp` against it and merges
27
47
  everything into `runtime/lib/<os>-<arch>/media.a` (`media.lib` with the MSVC
28
- ABI on Windows). `gui.a` does **not** link FFmpeg: a program enables playback
29
- explicitly with `--ext media`.
48
+ ABI on Windows). `gui.a` does **not** link FFmpeg: the compiler selects the
49
+ `media` link feature for a program that may play media and only then links
50
+ `media.a` (see [Link features](#link-features)).
30
51
  - `src/extensions/gui/index.ts` exposes the engine archive through
31
52
  `nativeObjects()` (via `findRuntimeLibrary`, so `.a`/`.lib` both work) and the
32
- OS frameworks through `linkerFlags()`. `src/extensions/media/index.ts` does
33
- the same for `media.a`/`media.lib` and compiles the registration bridge
34
- (`runtime/ext_media/media_bridge.c`) as a normal runtime source.
53
+ OS frameworks through `linkerFlags()`. Its `linkFeatures()` (in
54
+ `src/extensions/gui/features.ts`) declares `media`, `gpu`, `images`, `shaping` and `webgl`; the driver
55
+ compiles their bridges only when a feature is active.
56
+ - glslang (and SPIRV-Cross on macOS) is merged into `gui.a` like SDL3, but the
57
+ engine reaches it only through the `gpu` feature's dispatch table. A program
58
+ that never compiles GLSL does not extract those archive members.
35
59
  - `scripts/build-gui.ts` compiles with the *same* clang the driver resolves
36
60
  (`src/driver/toolchain-provider.ts`); on Windows that is the MSVC-targeting
37
61
  LLVM bundled with Visual Studio, and its sibling `clang++`/`llvm-ar` are used
@@ -59,11 +83,12 @@
59
83
  - CI builds `gui.a`/`gui.lib` and `media.a`/`media.lib` before assembling the
60
84
  release archive so they ship inside `runtime/lib/<slug>/` (the release tarball
61
85
  copies the whole `runtime/` tree). The same folders are packed into the
62
- optional `@xbintsc/<slug>` npm package, so `--ext gui`/`--ext media` work from
86
+ optional `@xbintsc/<slug>` npm package, so `--ext gui` (with automatic
87
+ `media`/`gpu`/`images`/`shaping`/`webgl` selection) works from
63
88
  an npm install on the hosts the release builds for. The archives are built and
64
89
  the examples are run on Linux (under Xvfb, with the lavapipe software Vulkan
65
90
  driver) and macOS;
66
- the Windows x64 job builds both archives and links a `gui,media` program, so
91
+ the Windows x64 job builds both archives and links a `gui` program, so
67
92
  the MSVC build is validated even though *running* the examples there stays
68
93
  provisional (the hosted runner has no GPU/Vulkan driver). The macOS run is
69
94
  provisional too: the Intel runner's virtualised GPU lacks
@@ -82,8 +107,9 @@
82
107
  - The existing cache fingerprint already hashes `nativeObjects()` contents and
83
108
  `runtimeSources()` content, so rebuilding either archive invalidates cached
84
109
  binaries automatically.
85
- - CMake build trees live *next to* their sources (`vendor/sdl-build`,
86
- `vendor/freetype-build`, `vendor/harfbuzz-build`), never inside them: a
110
+ - CMake build trees live *next to* their sources (`vendor/sdl-build-lean`,
111
+ `vendor/freetype-build-lean`, `vendor/harfbuzz-build-lean`,
112
+ `vendor/glslang-build-lean`, `vendor/spirv-cross-build-lean`), never inside them: a
87
113
  `CMakeCache.txt` records the machine it was configured on, and SDL's video
88
114
  backends are `option()`s, so the first configure to run wins. A cached
89
115
  `vendor/SDL` tree that also carried `build/` therefore restored one machine's
@@ -95,6 +121,97 @@
95
121
  `xbintsc_SKIP_HEADER_CHECK=1` when they live under a prefix that check does
96
122
  not know).
97
123
 
124
+ ## Link features
125
+
126
+ `gui` is the only user-facing extension, but two heavy halves are **optional link
127
+ features** selected per program:
128
+
129
+ | Feature | What it links | Selected when the program may use it |
130
+ | --- | --- | --- |
131
+ | `media` | `media_bridge.c` + `media.a` (FFmpeg) | an HTML `<video>`/`<audio>` tag, `createElement("video"/"audio")`, `new Audio()`, `decodeAudioData`, or runtime-built markup |
132
+ | `gpu` | `gpu_bridge.c` (the implementation is already in `gui.a`) | a `getContext("webgl"/"webgl2"/"experimental-webgl")` (or a kind decided at run time), any `webgl` string, `mix-blend-mode`/`mixBlendMode`, or runtime-built markup |
133
+ | `images` | `image_bridge.c` (the implementation is already in `gui.a`) | `<img>`, inline `<svg>`, `background-image`/`url(...)`, a `.svg` path, `drawImage`/`createImageBitmap`/`Image`, or runtime-built markup |
134
+ | `shaping` | `text_bridge.c` (the implementation is already in `gui.a`) | non-ASCII text (CJK/Cyrillic/Greek/…), an escaped/entity codepoint, an author `@font-face` rule (custom fonts may be CFF/OTF), or runtime-built markup |
135
+ | `webgl` | `webgl_bridge.c` (the implementation is already in `gui.a`; requires `gpu`) | a WebGL `getContext(...)`, a `webgl` string, or runtime-built markup |
136
+
137
+ The driver scans the bundled AST (`src/driver/usage.ts`): every string literal
138
+ (including imported `.html` and the CSS inlined from it), identifiers and
139
+ property names, each `getContext(...)` kind, and whether HTML/CSS can be built
140
+ from data the compiler cannot see. Simple `const` aliases are resolved;
141
+ anything unresolved makes the program “dynamic markup” and enables the optional
142
+ features conservatively.
143
+
144
+ When a feature is active, the driver compiles its C **bridge only for that
145
+ build** and links it before `gui.a`:
146
+
147
+ - `media` → `runtime/ext_media/media_bridge.c`; its constructor installs the
148
+ decoder table and references `xt_media_backend_*`, which is what extracts the
149
+ FFmpeg members from `media.a`.
150
+ - `gpu` → `runtime/ext_gui/gpu_bridge.c`; its constructor calls
151
+ `xt_gpu_feature_register` in `gpu_feature.cpp`, which installs the
152
+ `XtGpuShaderApi` table and references `compile_glsl` / `cross_compile_msl` /
153
+ the reflection helpers, extracting glslang (and SPIRV-Cross on macOS).
154
+ - `images` → `runtime/ext_gui/image_bridge.c`; its constructor installs
155
+ `XtImageApi` via `image_feature.cpp`, which references stb_image and NanoSVG.
156
+ Layout, paint, the canvas `drawImage` path and the app-icon path call through
157
+ that table, so a text-only page links neither decoder.
158
+ - `shaping` → `runtime/ext_gui/text_bridge.c`; its constructor installs
159
+ `TextApi` via `text_hb.cpp`, which references HarfBuzz and FreeType. Without
160
+ it the engine uses the built-in stb_truetype backend, so a Latin-only page
161
+ links neither library.
162
+ - `webgl` → `runtime/ext_gui/webgl_bridge.c`; its constructor installs
163
+ `XtWebglApi` via `webgl_feature.cpp`, which references the WebGL context and
164
+ DOM bindings. The core reaches `getContext`, the canvas width/height setters,
165
+ `createImageBitmap`, the host types and the renderer through that table, so a
166
+ page with no WebGL links none of `webgl_*`/`dom_api_webgl*`.
167
+
168
+ A bridge is the “strong reference” that makes the linker search the archive; with
169
+ no bridge, the engine's table stays null and the implementation members are never
170
+ extracted. That is why the large libraries can stay in the existing archives
171
+ without a new package layout.
172
+
173
+ Overrides: `--feature media,gpu,images,shaping,webgl` (or `all`) forces a
174
+ feature on; `--no-feature media` keeps it out even when detection finds a use.
175
+ `webgl` requires `gpu`, and the resolver activates both together. An app icon
176
+ forces `images` because the icon is decoded through the same pipeline. `--ext media`
177
+ is a deprecated alias for `--ext gui --feature media`. If an auto-detected
178
+ feature's archive is missing, the build warns and continues (the element/context
179
+ degrades as documented); a forced feature makes it an error.
180
+
181
+ > Detection is static by design. A page that builds HTML/CSS from `fetch`,
182
+ > `readFileSync` or other runtime data should pass `--feature all` (or the
183
+ > specific feature). `--verbose` prints the selected set, e.g.
184
+ > `xbintsc: link features: none`.
185
+
186
+ ### Binary size
187
+
188
+ The lean build is what makes a DOM-only program small. On Windows x64 a
189
+ `hello`-sized program drops from roughly 6.4 MiB to about 3.1 MiB once SDL is
190
+ configured as above, function/data sections let `/OPT:REF` discard what the page
191
+ never reaches, the WebGL/images link features stay off, and text uses the
192
+ bundled stb_truetype instead of HarfBuzz + FreeType. The remaining weight is
193
+ mostly the static CRT, the lean SDL (~1 MiB), stb_truetype (~0.1 MiB), and the
194
+ DOM/CSS/layout/paint engine itself (~1.2 MiB). Linux and macOS use their own
195
+ linker sections (`--gc-sections`/`-dead_strip`) and differ somewhat, but the
196
+ relative ranking is the same.
197
+
198
+ WebGL and images/SVG are link features (roughly 0.4 MiB of WebGL context/DOM
199
+ code and 0.23 MiB of stb_image/NanoSVG that a DOM-only page does not link).
200
+ Canvas2D, Web Audio and the `mix-blend-mode` compositor are not gated yet; a
201
+ program that uses none of them still pays for their API surface (roughly
202
+ 0.1–0.2 MiB each).
203
+
204
+ The text trade-off is deliberate. The default backend is stb_truetype: only
205
+ TrueType outlines and a per-codepoint mapping plus the font's `kern` table, so
206
+ CFF/OTF and variable fonts, OpenType ligatures and complex-script shaping
207
+ (Arabic/Indic/Thai) are not available. The fallback chain still selects another
208
+ face for codepoints the primary lacks. Selecting the optional **`shaping`**
209
+ feature (automatically for non-ASCII text or an author `@font-face` rule, or
210
+ with `--feature shaping`) links HarfBuzz + FreeType back in (~1.2 MiB) and
211
+ restores full OpenType shaping and CFF/OTF/variable-font support. A Latin-only
212
+ DOM page therefore stays near 3.1 MiB; a Chinese/complex-script page is closer
213
+ to 4.3 MiB.
214
+
98
215
  ## Running the example
99
216
 
100
217
  ```sh
@@ -133,10 +250,11 @@ its README for controls and the geometry report it prints.
133
250
 
134
251
  `media.ts` (with `media.html`) demonstrates `<video>`/`<audio>`: it overlays a
135
252
  CSS progress bar on the video and drives playback with `play()` / `pause()` /
136
- `currentTime` / `volume`. It needs the separate `media` extension, so build and
137
- enable both. Media `src` paths resolve relative to the process working
253
+ `currentTime` / `volume`. The compiler sees the elements in `media.html` and
254
+ links the `media` feature automatically; build `media.a` once with
255
+ `npm run media`. Media `src` paths resolve relative to the process working
138
256
  directory, so run it from the repository root:
139
- `xbintsc run examples/gui/media.ts --ext gui,media`.
257
+ `xbintsc run examples/gui/media.ts --ext gui`.
140
258
 
141
259
  Set `XT_GUI_AUTOCLOSE_MS=<n>` to close all windows after `n` milliseconds,
142
260
  which the e2e test (`tests/e2e/gui-*.test.ts`) uses to run headlessly.
package/doc/gui/canvas.md CHANGED
@@ -191,7 +191,7 @@ non-finite entries are ignored, an all-zero list means a solid line).
191
191
 
192
192
  Text: `measureText(text)` (returns `{ width }`), `fillText(text, x, y,
193
193
  maxWidth?)` and `strokeText(text, x, y, maxWidth?)`. Text is shaped with
194
- HarfBuzz and rasterised with FreeType (the same stack as HTML text) at the
194
+ stb_truetype (shaped and rasterised) (the same stack as HTML text) at the
195
195
  transform's approximate scale. A positive `maxWidth` condenses advances and
196
196
  glyph bitmaps horizontally so the run is at most that wide (as the canvas API
197
197
  specifies) instead of being ignored.
package/doc/gui/dom.md CHANGED
@@ -21,11 +21,26 @@ commas, brackets or parentheses without splitting the list. Selectors: type,
21
21
  `.class`, `#id`, attribute (`=`, `~=`, `|=`, `^=`, `$=`, `*=`), the four
22
22
  combinators (descendant, child, adjacent and general sibling), the pseudo-classes
23
23
  `:first-child`, `:last-child`, `:only-child`, `:empty`, `:root`, `:not(...)`,
24
- `:nth-child(an+b)`, `:disabled`, `:checked`, plus the stateful `:hover` and
25
- `:focus` (see [Input](./input.md)), and the pseudo-elements `::before`/`::after`
24
+ `:nth-child(an+b)`, `:disabled`, `:checked`, plus the stateful `:hover`,
25
+ `:focus`, `:focus-visible` (currently an alias of `:focus`) and `:active` (see
26
+ [Input](./input.md)), and the pseudo-elements `::before`/`::after`
26
27
  (legacy `:before`/`:after` too). Values: lengths (`px`, `%`, `em`, `rem`, `vw`,
27
28
  `vh`, `pt`, `pc`, `in`, `cm`, `mm`, `q`), colors (hex, `rgb()`/`rgba()`, a named
28
- subset), numbers, keywords and shorthands (`margin`/`padding`/`border`/`flex`).
29
+ subset), numbers, keywords and shorthands (`margin`/`padding`/`border`/`flex`/
30
+ `font`/`outline`). `display: inline-flex`/`inline-grid` make a flex/grid
31
+ container inline-level; `grid-area`, `aspect-ratio`, `letter-spacing`,
32
+ `text-transform`, `writing-mode` (`vertical-rl`/`vertical-lr` text),
33
+ `vertical-align` (length form), percentage `border-radius`, `background-size`
34
+ (size + default repeat for `url()` layers) and `currentColor` are parsed too.
35
+ `font-variant-numeric`, `text-wrap`, `text-rendering`, `touch-action`,
36
+ `user-select`, `overscroll-behavior`, `scroll-behavior`, `isolation` and
37
+ `vector-effect` are accepted but have no engine behaviour (the text stack exposes no OpenType features and the engine
38
+ has no touch/selection/smooth-scroll subsystem); `cursor` maps to the SDL system
39
+ cursor. A document rule may style SVG children with the presentation properties
40
+ `fill`, `fill-opacity`, `fill-rule`, `stroke`, `stroke-opacity`, `stroke-width`,
41
+ `stroke-dasharray`, `stroke-dashoffset`, `stroke-linecap` and `stroke-linejoin`;
42
+ the serializer writes them into the inline `<svg>` markup as NanoSVG-readable
43
+ inline styles. `skewX()`/`skewY()`/`skew()` transform functions are supported.
29
44
  `transform` (`translate`/`translate3d`/`translateX`/`translateY`, `scale`/
30
45
  `scale3d`/`scaleX`/`scaleY`, `rotate`/`rotateZ`, `matrix`; the `3d` spellings'
31
46
  `z` argument is dropped) and `transform-origin` are parsed into the computed
@@ -33,7 +48,8 @@ style as a list of operations plus two (unresolved) origin lengths; an
33
48
  unsupported function invalidates the whole declaration
34
49
  ([Layout](./layout.md#transforms-m15)). M19 adds `background-image` layer lists
35
50
  (linear/radial), `clip-path` (`inset`/`circle`/`ellipse`/`polygon`), the
36
- `filter`/`backdrop-filter` function lists, `text-shadow` and `content`.
51
+ `filter`/`backdrop-filter` function lists, `text-shadow`, `box-shadow` and
52
+ `content`.
37
53
 
38
54
  **Cascade & computed style** (`runtime/ext_gui/style.{h,cpp}`): a small built-in
39
55
  UA stylesheet, author rules sorted by `(!important, specificity, source order)`,
@@ -22,6 +22,16 @@
22
22
  shader unroller; `WebGLRenderer` runs on the real backend and there is no
23
23
  Canvas2D fallback. Shadow maps (comparative `sampler2DShadow` targeting depth
24
24
  textures), cube textures/environment maps and HDR render targets are supported.
25
+ - **Comparative samplers can miscompile on Intel Macs.** On an Intel GPU (seen
26
+ on Intel Iris Plus Graphics with macOS 26), the Metal shader compiler drops a
27
+ fragment function that calls `sample_compare`: the draw produces no pixels at
28
+ all, so `texture(sampler2DShadow, …)` reads black and shadow maps disappear.
29
+ The MSL is correct (`depth2d<float>` + `sample_compare`), the sampler carries
30
+ the comparison op, and the same program renders correctly once Metal GPU
31
+ shader validation is enabled — running with `MTL_SHADER_VALIDATION=1` makes
32
+ `tests/e2e/gui-webgl2-shadow-sampler.test.ts` pass on such a machine. That
33
+ points at the driver's shader compiler rather than the engine, so the
34
+ workaround is the environment variable; other Apple GPUs may not show it.
25
35
 
26
36
 
27
37
 
@@ -54,29 +64,37 @@
54
64
  *class change* on a flex container has also been observed to crash the same
55
65
  way; `examples/gui/pelican-bike` reveals its game-over overlay with `opacity`
56
66
  instead.
57
- - **CSS coverage is a deliberate subset.** Grid, `clamp()`/`min()`/`max()`/`env()`,
58
- `var()` custom properties, the `inset` shorthand, `box-sizing`, `visibility` and
59
- `pointer-events`, `@media` queries, generated content (`::before`/`::after`),
60
- multi-layer linear/radial backgrounds, `text-shadow`, `clip-path`
61
- (`inset`/`circle`/`ellipse`/`polygon`) and `filter` colour functions are
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;
70
- `filter: blur()` is a nine-tap per-shape spread rather than a true group blur,
71
- so overlapping descendants can darken; `drop-shadow` is approximated from box
72
- silhouettes; at most two nested `clip-path` shapes are intersected (deeper
73
- nesting keeps the outer two); and images/canvases are not colour-filtered
74
- except for the brightness/opacity tint the image shader can carry. Canvas
75
- textures are sampled nearest-neighbour. A background linear gradient remains
76
- solid bands (18 strips, 64 while repeating), so a large soft ramp may show
77
- slight banding; a radial gradient is rasterised per pixel into a texture
78
- (capped at 1024 px per edge, then stretched, so a hard stop in a very large
79
- box comes out slightly soft).
67
+ - **CSS coverage is a deliberate subset.** Grid, `calc()` (including
68
+ nested arithmetic and `calc()` inside `min()`/`max()`/`clamp()`),
69
+ `clamp()`/`min()`/`max()`/`env()`, `var()` custom properties, the `inset`
70
+ shorthand, `box-sizing`, `visibility` and `pointer-events`, `@media` queries,
71
+ generated content (`::before`/`::after`), multi-layer linear/radial
72
+ backgrounds, `text-shadow` (true Gaussian), `box-shadow` (outer/inset, spread,
73
+ multiple layers), `clip-path` (`inset`/`circle`/`ellipse`/`polygon`),
74
+ `filter` colour functions, `@keyframes` animations, the full `transition`
75
+ timing-function set, `letter-spacing`, `text-transform`, inline-level
76
+ `flex`/`grid`, `grid-area`, `aspect-ratio`, auto margins, percentage
77
+ `border-radius`, `outline`, `background-size` tiling for `url()` layers and
78
+ document CSS on SVG children (`fill`/`stroke`/…) are implemented. Known
79
+ limits: `background-clip`, `background-position` and `background-repeat`
80
+ remain unimplemented (`background-size` covers the tiled default);
81
+ `font-variant-numeric`, `text-wrap`, `text-rendering`, `touch-action`,
82
+ `user-select`, `overscroll-behavior`, `scroll-behavior`, `isolation` and
83
+ `vector-effect` are parsed but inert; vertical writing mode is text-only (nested/mixed content falls back to
84
+ horizontal); group `opacity` is
85
+ a per-shape alpha rather than a true group composite; the blend and
86
+ `backdrop-filter` compositors are unavailable on the D3D12/DXIL backend, where
87
+ groups fall back to normal source-over painting and the backdrop filter is
88
+ skipped. `filter: blur()` is still a nine-tap per-shape spread rather than a
89
+ true group blur, so overlapping descendants can darken; `drop-shadow` is
90
+ approximated from box silhouettes; at most two nested `clip-path` shapes are
91
+ intersected (deeper nesting keeps the outer two); and images/canvases are not
92
+ colour-filtered except for the brightness/opacity tint the image shader can
93
+ carry. Canvas textures are sampled nearest-neighbour. Background gradients
94
+ (linear and radial) are rasterised per pixel into a texture and then stretched
95
+ when the box is larger than the texture (1024 px per edge for a radial ramp,
96
+ 4096 px along the line for a linear one, which is only one texel thick across
97
+ it), so a hard stop in a very large box can come out slightly soft.
80
98
  - **SVG is NanoSVG's subset.** SVG *files* (`<img src="logo.svg">`) and inline
81
99
  `<svg>` trees share the vendored NanoSVG parser/rasteriser
82
100
  (`runtime/ext_gui/svg.cpp`, `inline_svg.cpp`), which renders
@@ -111,6 +129,22 @@
111
129
  bottleneck. Inline `style="…"` attributes are re-parsed on each restyle of
112
130
  the element that carries them.
113
131
 
132
+ - **Text shaping is a simple shaper.** stb_truetype handles Latin/CJK/Cyrillic/
133
+ Greek with basic `kern` and per-codepoint fallback, but it does not run
134
+ OpenType ligatures or complex-script shaping (Arabic/Indic/Thai), and it only
135
+ accepts TrueType outlines (no CFF/OTF or variable fonts). Colour emoji are not
136
+ rendered as colour. This is the price of the ~3 MiB DOM-only binary; the
137
+ optional `shaping` feature restores HarfBuzz + FreeType (and full
138
+ complex-script shaping).
139
+ - **Link-feature detection is static.** `media`/`gpu`/`images`/`shaping`/`webgl` are selected from the
140
+ bundled HTML/CSS/TS (including imported `.html` and the CSS inlined from it).
141
+ A page that builds markup or CSS from runtime data (`fetch`, `readFileSync`,
142
+ a value returned by a function) cannot be seen; the scanner falls back to
143
+ enabling both when it can prove the sink is dynamic, but an opaque value that
144
+ it cannot classify needs an explicit `--feature media`/`gpu`/`images`/`shaping`/`webgl` (or `all`).
145
+ `--verbose` prints the selected set. See
146
+ [Link features](./build.md#link-features).
147
+
114
148
  ## Open questions
115
149
 
116
150
  - Whether Linux ships X11, Wayland, or both in the first cut. (Decision:
@@ -130,7 +164,8 @@
130
164
  - Windows: an MSVC-compatible `gui.lib` (COFF objects + `ar -M`/`llvm-ar`) is
131
165
  produced by `scripts/build-gui.ts`. The Windows x64 CI job builds it with the
132
166
  Visual Studio toolchain (`.github/actions/vs-toolchain` imports `vcvarsall`
133
- and uses VS's bundled LLVM), links a `gui,media` program, and ships it in the
167
+ and uses VS's bundled LLVM), links a `gui` program that exercises the `media`
168
+ link feature, and ships it in the
134
169
  x64 release. *Running* the examples on the hosted runner stays provisional
135
170
  because it has no GPU/Vulkan driver (SDL_GPU selects the **Vulkan** backend
136
171
  there, so no DXIL/`dxc` toolchain is needed). Windows arm64 still skips both
@@ -139,12 +174,14 @@
139
174
  - **Windows: x64 builds in CI, arm64 and runtime validation stay provisional.**
140
175
  `media.lib` is produced by `scripts/build-media.ts` with FFmpeg's MSVC
141
176
  toolchain (`--toolchain=msvc --cc=clang-cl`) so its COFF objects and static CRT
142
- match `gui.lib`, and `--ext media` links and plays there. Building it needs a
177
+ match `gui.lib`, and the auto-selected `media` feature links and plays there.
178
+ Building it needs a
143
179
  POSIX shell to run FFmpeg's `configure` and GNU make; the shell comes from Git
144
180
  for Windows (already needed to clone SDL3), and make is a native GNU make
145
181
  (Chocolatey's `make`, or `xbintsc_MAKE`). CI installs make explicitly,
146
182
  builds `gui.lib` + `media.lib` on Windows x64 (using the same Visual Studio
147
- `vcvarsall` + bundled LLVM as a local build), links a `gui,media` program, and
183
+ `vcvarsall` + bundled LLVM as a local build), links a `gui` program that
184
+ exercises the feature, and
148
185
  ships both archives in the Windows x64 release; the example *run* remains
149
186
  provisional because hosted runners have no GPU/Vulkan driver. Windows arm64
150
187
  still skips both archives, and `<video controls>` is likewise deferred — the