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
@@ -24,7 +24,7 @@ if (gl == null) {
24
24
  - 上下文记录到自己的 SDL_GPU 命令缓冲。`clear`/`draw*` 保持一个渲染 pass 打开;只有缓冲区/纹理上传或切换 framebuffer 才会结束它,并以 `LOAD` 开始新 pass,因此绘图缓冲在多次绘制之间保留,和 WebGL 一样;没有上传的绘制不会中断 pass。
25
25
  - 每帧一次,在页面自己的渲染 pass 之前,引擎提交所有 WebGL 命令缓冲。随后图像管线把颜色纹理当作普通 `PaintImage` 采样,没有 CPU 拷贝。
26
26
  - WebGL 原点在左下,SDL_GPU 渲染目标原点在左上,因此帧缓冲按自上而下存储:转换 `viewport`/`scissor`,`readPixels` 再把行翻回去,合成器看到的是正立的纹理。
27
- - 固定功能状态变化(`blendFunc`、`depthFunc`、`cullFace`、`colorMask` 等)会改变所需的 `SDL_GPUGraphicsPipeline`,因此管线按(程序、顶点布局、状态)惰性创建,并缓存在程序上。
27
+ - 固定功能状态变化(`blendFunc`、`depthFunc`、`cullFace`、`colorMask` 等)会改变所需的 `SDL_GPUGraphicsPipeline`,因此管线惰性创建并缓存在程序上。键是这次绘制的**内容**:程序、图元模式、顶点布局的哈希(属性位置、格式、偏移、缓冲 pitch 与输入速率)、反射出的固定功能状态、附件格式。它刻意**不**包含上下文的单调版本计数器(`attribs_version_`、`framebuffer_version_`)与 GPU 缓冲句柄:这些值在每次绑定 VAO、切换帧缓冲或重建缓冲时都会前进,却不改变管线;用它们做键会让每帧绑定多个布局的场景(three.js 每帧为每个几何体绑定一个 VAO)每帧重建全部管线 —— 实测每帧 248 次 `SDL_CreateGPUGraphicsPipeline`,且程序级缓存无界增长。`tests/e2e/gui-webgl-pipeline-cache.test.ts` 固定了稳态:布局相同、缓冲不同的两次绘制在首帧之后不再创建管线。
28
28
 
29
29
  ## 性能
30
30
 
@@ -118,7 +118,7 @@ transform feedback(入口为空操作:SDL_GPU 没有 transform feedback 阶
118
118
 
119
119
  管线按 SDL_GPU 设备接受的着色器格式创建:Vulkan 上用 **SPIR-V**(`SDL_GPU_SHADERFORMAT_SPIRV`,Windows/Linux 选择的后端),macOS 上用 **MSL**(`SDL_GPU_SHADERFORMAT_MSL`,Metal 后端)。两个后端共用同一个 GLSL 前端,因此在一个后端能编译的着色器在另一个上也能编译。如果设备的后端两种格式都不接受——Direct3D 12 需要 DXIL——`getContext("webgl")` 与 `getContext("webgl2")` 都会返回 `null`;2D canvas 与页面其余部分仍然正常。
120
120
 
121
- `npm run gui` 会拉取并构建 glslang 与 SPIRV-Cross,合并进 `gui.a` / `gui.lib`(见[打包与构建](./build.md));只用 2D canvas 或 DOM 的程序除了归档体积之外没有运行时开销。
121
+ `npm run gui` 会拉取并构建 glslang 并合并进 `gui.a` / `gui.lib`;SPIRV-Cross **只在 macOS 上**合并,因为其他 SDL_GPU 后端都不消费 MSL(见[打包与构建](./build.md))。两者都通过可选的 **`gpu` 链接特性**访问:编译器在程序可能调用 `getContext("webgl…")` 或使用 `mix-blend-mode` 时选中它,只有那时 bridge 才会把翻译器成员从 `gui.a` 里取出。WebGL context 与 DOM 绑定是单独的 **`webgl` 特性**,它依赖 `gpu`;只用 2D canvas 或 DOM 的程序既不链接 glslang、SPIRV-Cross,也不链接任何 `webgl_*` 成员(见[链接特性](./build.md#链接特性))。
122
122
 
123
123
  ## 测试
124
124
 
@@ -140,4 +140,4 @@ transform feedback(入口为空操作:SDL_GPU 没有 transform feedback 阶
140
140
 
141
141
  着色器改写器会把 three.js 的 `#pragma unroll_loop_start` 循环展开为常量索引(否则其采样器数组无法通过 Vulkan GLSL),`.js` 资源加载器则避免渲染器自带的展开器重复处理。`WebGLRenderer` 在本引擎真正的 WebGL 2 后端上原样运行,**不再打包 Canvas2D 回退**。已用 three.js r160 通过 SDL_GPU 验证:`MeshBasicMaterial`、`MeshStandardMaterial`、`MeshPhongMaterial`、`CanvasTexture`、线性雾、环境光/平行光/点光/聚光/半球光、`Sprite`、`Line`、`Points` 与 `InstancedMesh`。
142
142
 
143
- 本引擎子集之外的能力仍无法模拟:transform feedback 与 MSAA。立方体贴图/环境贴图、深度纹理、HDR/浮点渲染目标、压缩纹理与比较型 `sampler2DShadow` 采样都已可用(three.js 阴影贴图可在两个后端使用硬件深度比较)。需要 transform feedback 或 MSAA 的场景无法正确渲染;此前 Canvas2D 回退能显示的内容现在全部走 GPU。
143
+ 本引擎子集之外的能力仍无法模拟:transform feedback 与 MSAA。立方体贴图/环境贴图、深度纹理、HDR/浮点渲染目标、压缩纹理与比较型 `sampler2DShadow` 采样都已可用(three.js 阴影贴图可在两个后端使用硬件深度比较,Intel Mac 上的驱动问题见[已知问题](./known-issues.md))。需要 transform feedback 或 MSAA 的场景无法正确渲染;此前 Canvas2D 回退能显示的内容现在全部走 GPU。
package/doc/zh-CN/gui.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  > 语言 / Language:[English](../gui.md) | **简体中文**
4
4
 
5
- 状态:**M20** —— 功能(M1–M15)已全部完成:HTML 解析、CSS 选择器匹配、层叠(cascade)、计算样式与布局(块级、行内、Flexbox 与 CSS 定位)均已就位,引擎还会**绘制**:它构建一个由矩形、图像与排版后的文本 run 组成的显示列表,并通过 SDL_GPU 渲染。输入事件会经过命中测试并投递给原生 TS 处理器,`:hover`/`:focus` 会被动态匹配,`<img>` 依据其固有尺寸确定大小并从纹理绘制——位图(PNG/JPEG/…)与 SVG 文件皆可,后者按绘制尺寸光栅化,因此矢量图保持清晰——CSS transition 会为绘制属性做动画。**M16** 让内联 `<svg>` 子树获得同样的处理:子树被序列化回 SVG 文档,并按绘制设备尺寸光栅化,因此图标不再需要单独的文件。**M8** 增加了交互式 DOM:具有稳定标识的元素句柄、变更操作(`appendChild`、`textContent`、`classList`、`style` 等)以及支持捕获/冒泡的元素级事件。**M9** 会提前编译 `<script>` 主体(内联与 `<script src>`)——参见 `gui-scripts.md`。**M10** 增加 `requestAnimationFrame` 以及若干 DOM 辅助方法。**M11** 增加 `position: relative`/`absolute`/`fixed` 及其偏移,`examples/gui/pelican-bike`(一个可玩的 2D 游戏)就构建在它之上。**M12** 明确了 DOM 节点生命周期(哪些变更释放、哪些保留),并让引擎内存有界:参见[节点生命周期](./gui/dom.md)与[内存与 GC](./gui/runtime.md)。**M13** 真正播放
6
- `<video>`/`<audio>`——可选的 `media` 扩展用 vendored 的最小 FFmpeg 在主线程解码,
5
+ 状态:**M22** —— 功能(M1–M15)已全部完成:HTML 解析、CSS 选择器匹配、层叠(cascade)、计算样式与布局(块级、行内、Flexbox 与 CSS 定位)均已就位,引擎还会**绘制**:它构建一个由矩形、图像与排版后的文本 run 组成的显示列表,并通过 SDL_GPU 渲染。输入事件会经过命中测试并投递给原生 TS 处理器,`:hover`/`:focus` 会被动态匹配,`<img>` 依据其固有尺寸确定大小并从纹理绘制——位图(PNG/JPEG/…)与 SVG 文件皆可,后者按绘制尺寸光栅化,因此矢量图保持清晰——CSS transition 会为绘制属性做动画。**M16** 让内联 `<svg>` 子树获得同样的处理:子树被序列化回 SVG 文档,并按绘制设备尺寸光栅化,因此图标不再需要单独的文件。**M8** 增加了交互式 DOM:具有稳定标识的元素句柄、变更操作(`appendChild`、`textContent`、`classList`、`style` 等)以及支持捕获/冒泡的元素级事件。**M9** 会提前编译 `<script>` 主体(内联与 `<script src>`)——参见 `gui-scripts.md`。**M10** 增加 `requestAnimationFrame` 以及若干 DOM 辅助方法。**M11** 增加 `position: relative`/`absolute`/`fixed` 及其偏移,`examples/gui/pelican-bike`(一个可玩的 2D 游戏)就构建在它之上。**M12** 明确了 DOM 节点生命周期(哪些变更释放、哪些保留),并让引擎内存有界:参见[节点生命周期](./gui/dom.md)与[内存与 GC](./gui/runtime.md)。**M13** 真正播放
6
+ `<video>`/`<audio>`——可选的 **`media` 链接特性**用 vendored 的最小 FFmpeg 在主线程解码,
7
7
  视频帧走图像纹理路径、音频进入 SDL 流,DOM 媒体 API 暴露
8
- `play`/`pause`/`currentTime`/… 与媒体事件。只启用 `gui` 不会链接 FFmpeg。浏览器全局还带有真正可用的 `AudioContext`:`runtime/ext_gui/audio_mixer.cpp` 中的 48 kHz 混音器,以及 `decodeAudioData`(WAV 由引擎内解码,压缩格式经 `media`)——见 [GUI API](./gui/api.md#web-audio)。**M14** 增加了 `<canvas>`,配一个自研的 2D 软件光栅器(抗锯齿
8
+ `play`/`pause`/`currentTime`/… 与媒体事件。编译器会从程序里判断是否需要它
9
+ (`<video>`/`<audio>` 元素、`new Audio()`、`decodeAudioData`,或运行时拼出的 HTML),
10
+ 因此只做 DOM/CSS 的程序不会链接 FFmpeg。浏览器全局还带有真正可用的 `AudioContext`:`runtime/ext_gui/audio_mixer.cpp` 中的 48 kHz 混音器,以及 `decodeAudioData`(WAV 由引擎内解码,压缩格式经 `media` 特性)——见 [GUI API](./gui/api.md#web-audio)。**M14** 增加了 `<canvas>`,配一个自研的 2D 软件光栅器(抗锯齿
9
11
  填充、路径、文本与 `drawImage`),并经图像纹理路径上传——参见
10
12
  [Canvas](./gui/canvas.md)。**M15** 增加了 CSS `transform` /
11
13
  `transform-origin`:每个布局盒一个合成好的 `Matrix2D`,由显示列表一路带到
12
14
  渲染器,因此盒子(以及其中的文本、图像与子元素)可以被平移、缩放与旋转而
13
15
  不触发任何重排——再加上 `transition: transform`,这些姿态可以动画(自绘的示例
14
16
  `examples/gui/pelican-svg` 正是用它让矢量画动起来)。参见
15
- [布局](./gui/layout.md#变换m15)与[动画](./gui/animation.md#css-transformm15)。**M17** 增加了真正的 **WebGL 1.0**,**M18** 增加了 **WebGL 2.0**:`<canvas>.getContext("webgl2")` 接受 `#version 300 es` 着色器、顶点数组对象、实例化绘制与采样器对象,全部由内置 glslang 编译为 SPIR-V(Metal 上再由内置 SPIRV-Cross 转成 MSL)并通过 SDL_GPU 渲染,几何、光照、纹理采样与混合都在 GPU 上完成,页面直接合成颜色纹理而无须读回——见 [WebGL](./gui/webgl.md)。**M19** 补齐 CSS 装饰层:`@media` 查询、生成内容(`::before`/`::after`)、线性/径向/重复多层背景、`text-shadow`、`clip-path`、`mix-blend-mode`(通过离屏合成器)与 `filter` 颜色/阴影效果。`backdrop-filter` 仍需要背景快照,真正的组模糊也仍是近似——见[绘制](./gui/rendering.md#背景生成内容与特效m19)。**M20** 让大文档保持可交互:`@media` 每条规则只求值一次,布局与绘制共享一个有界的整形缓存,一帧只做(也只提交)变更所要求的工作——见[性能](./gui/rendering.md#性能m20)。本总览记录了锁定决策、架构、里程碑计划与当前进度;引擎本身按功能拆分在以上各页中。
17
+ [布局](./gui/layout.md#变换m15)与[动画](./gui/animation.md#css-transformm15)。**M17** 增加了真正的 **WebGL 1.0**,**M18** 增加了 **WebGL 2.0**:`<canvas>.getContext("webgl2")` 接受 `#version 300 es` 着色器、顶点数组对象、实例化绘制与采样器对象,全部由内置 glslang 编译为 SPIR-V(Metal 上再由内置 SPIRV-Cross 转成 MSL)并通过 SDL_GPU 渲染,几何、光照、纹理采样与混合都在 GPU 上完成,页面直接合成颜色纹理而无须读回——见 [WebGL](./gui/webgl.md)。glslang 是可选的 **`gpu` 链接特性**,与 `mix-blend-mode` 共享;WebGL context/DOM 绑定是单独的 **`webgl` 特性**(依赖 `gpu`),两者都不用时不链接。**M19** 补齐 CSS 装饰层:`@media` 查询、生成内容(`::before`/`::after`)、线性/径向/重复多层背景、`text-shadow`、`clip-path`、`mix-blend-mode`(通过离屏合成器)与 `filter` 颜色效果——见[绘制](./gui/rendering.md#背景生成内容与特效m19)。**M20** 让大文档保持可交互:`@media` 每条规则只求值一次,布局与绘制共享一个有界的整形缓存,一帧只做(也只提交)变更所要求的工作——见[性能](./gui/rendering.md#性能m20)。**M21** 补齐取值/动画缺口:凡接受长度处都支持 `calc()` 算术、真正的 `text-shadow`/`box-shadow` 高斯模糊、背景合成、`@keyframes` 与完整的 transition 时间函数——见[绘制](./gui/rendering.md#背景生成内容与特效m19)与[动画](./gui/animation.md#css-动画keyframes)。本总览记录了锁定决策、架构、里程碑计划与当前进度;引擎本身按功能拆分在以上各页中。
16
18
 
17
19
  ## 目录
18
20
 
@@ -52,7 +54,8 @@
52
54
  | 4 | 引擎**非自托管**(它是 C/C++,不是 TS),但不得影响编译器的平台无关设计。以按平台预构建的归档形式交付,通过 `nativeObjects` 链接。 |
53
55
  | 5 | **事件循环是通用的**:运行时暴露一个通用主循环钩子与一个轮询原语;任何 GUI 特定的东西都不会进入 `runtime/`。 |
54
56
  | 6 | 核心对象模型支持**多窗口**。 |
55
- | 7 | 第三方底层库**静态链接进扩展归档**,使发布版保持自包含;链接时只额外加入操作系统框架。`gui.a` 携带 SDL3/FreeType/HarfBuzz,可选的 `media.a` 携带 FFmpeg,仅在 `--ext media` 时链接。 |
57
+ | 7 | 第三方底层库**静态链接进扩展归档**,使发布版保持自包含;链接时只额外加入操作系统框架。`gui.a` 携带 SDL3、用于可选 `shaping` 特性的 FreeType/HarfBuzz、glslang 和(仅 macOS)SPIRV-Cross;默认文本路径使用内置 stb_truetype。glslang 与 SPIRV-Cross 只有在编译器选中 **`gpu` 链接特性**时才会被从归档里取出。Metal 是唯一消费 MSL 的 SDL_GPU 后端,其他平台合并 SPIRV-Cross 只会多出约 2.5 MiB 没有任何驱动会请求的代码。WebGL context/DOM 代码是 **`webgl` 特性**(依赖 `gpu`),stb_image/NanoSVG 是 **`images` 特性**。可选的 `media.a` 携带 FFmpeg,仅在选中 **`media` 链接特性**时链接。所有特性都由程序的静态用法决定,可用 `--feature`/`--no-feature` 覆盖。 |
58
+ | 8 | **可选的重型部分做成链接特性,而不是独立扩展。** FFmpeg 与 glslang 由打包后的 HTML/CSS/TS 里可能用到的东西决定;只有特性启用时才会编译一个很小的 C bridge,它安装分发表并迫使链接器取出实现归档。这样用户仍然只看到 `gui` 一个扩展,而只做 DOM 的程序两套栈都不链接。 |
56
59
 
57
60
  ## 架构
58
61
 
@@ -72,12 +75,13 @@
72
75
  │
73
76
  ▼
74
77
  窗口 + 输入(SDL3)── 多窗口
75
- 文本(HarfBuzz + FreeType)── 字形图集 / SDF
78
+ 文本(stb_truetype)── 字形图集
76
79
  图像(stb_image;SVG 经 NanoSVG)
77
- 媒体 DOM + 音频 sink ── 可选的 media.a(FFmpeg)安装解码器
80
+ 媒体 DOM + 音频 sink ── 可选的 media 链接特性(media.a / FFmpeg)安装解码器
78
81
  ```
79
82
 
80
- 引擎从不与编译器通信。编译器只看到一个带有 `nativeObjects()`、`linkerFlags()` 和 `modules()` 的 `Extension`。
83
+ 引擎从不与编译器通信。编译器只看到一个带有 `nativeObjects()`、`linkerFlags()`、
84
+ `modules()` 和 `linkFeatures()` 的 `Extension`;FFmpeg 与 glslang 由后者按需选择。
81
85
 
82
86
  ## 技术栈(已确认)
83
87
 
@@ -85,14 +89,18 @@
85
89
  | --- | --- | --- |
86
90
  | 窗口 + 输入 + 多窗口 | **SDL3** | 跨平台窗口、HiDPI、IME、剪贴板、拖放、关闭/隐藏事件 |
87
91
  | GPU | **SDL_GPU**(SDL3) | 一条覆盖 Metal / Vulkan / D3D12 的渲染路径;避免三套后端 |
88
- | 文本整形 | **HarfBuzz** | 正确处理复杂文种的整形 |
89
- | 字形光栅化 | **FreeType** | 字形轮廓 → GPU 图集 / SDF |
92
+ | 文本整形 | **stb_truetype** 简单整形;可选 **`shaping`** 特性链接 HarfBuzz + FreeType | 默认让 DOM-only 二进制约 3 MiB;`shaping` 恢复连字/复杂文种整形 |
93
+ | 字形光栅化 | **stb_truetype**;仅 `shaping` 下用 FreeType | 默认 TrueType 轮廓 → 灰度图集 |
90
94
  | 图像 | 位图用 **stb_image**(vendored 头文件),SVG 用 **NanoSVG**(vendored 头文件) | 起步阶段用单头文件;SVG 文件按需光栅化 |
91
- | 视频 + 音频 | **FFmpeg**(静态 vendored,最小化构建,可选 `media` 扩展) | 解码常用容器/编码;不依赖 WebView 或平台媒体框架 |
95
+ | 视频 + 音频 | **FFmpeg**(静态 vendored,最小化构建,可选 `media` 链接特性) | 解码常用容器/编码;不依赖 WebView 或平台媒体框架 |
92
96
 
93
- 固定版本:SDL3 `release-3.2.10`、FreeType `2.13.3`、HarfBuzz `10.1.0`、
94
- FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
95
- `FREETYPE_VERSION` / `HARFBUZZ_VERSION` / `FFMPEG_VERSION` 覆盖)。
97
+ 固定版本:SDL3 `release-3.2.10`、FreeType `2.13.3`、HarfBuzz `10.1.0`
98
+ (供可选的 `shaping` 特性使用)、glslang `16.6.0`、SPIRV-Cross
99
+ `vulkan-sdk-1.4.313.0`、FFmpeg `7.1`(供可选的 `media` 归档使用;可用
100
+ `SDL3_TAG` / `FREETYPE_VERSION` / `HARFBUZZ_VERSION` / `GLSLANG_VERSION` /
101
+ `SPIRV_CROSS_VERSION` / `FFMPEG_VERSION` 覆盖)。默认文本后端是内置的
102
+ `stb_truetype.h`;它只接受 TrueType 轮廓(不支持 CFF/OTF 或可变字体)。
103
+ `shaping` 特性会换成 HarfBuzz + FreeType,提供完整 OpenType 整形。
96
104
 
97
105
  如果否决 SDL3,备选是 **GLFW + OpenGL 3.3**(更简单,但 OpenGL 在 macOS 上
98
106
  已弃用,且不提供现代 GPU 抽象)。如果引擎用 Rust 编写,则用 **wgpu-native**。
@@ -118,10 +126,12 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
118
126
  4. **M4 — 绘制 + 文本 + 显示列表**
119
127
  - **M4a — 显示列表 + GPU 图形** ✅ 背景/边框显示列表、圆角矩形 SDL_GPU
120
128
  管线(`paint.*`、`renderer.*`)。
121
- - **M4b-1 — 文本技术栈 + 度量** ✅ HarfBuzz + FreeType 链接进 `gui.a`、
122
- 字体解析/缓存、供布局使用的基于整形的文本度量
123
- (`text.*`、`measureText`/`fontMetrics`)。
124
- - **M4b-2 — 字形渲染** ✅ FreeType 光栅化、共享的 shelf 打包字形图集、
129
+ - **M4b-1 — 文本技术栈 + 度量** ✅ 文本模块(`text.*`)与字体解析/缓存、
130
+ 供布局使用的基于整形的文本度量(`measureText`/`fontMetrics`);CSS 族名列表、
131
+ 字重和斜体样式会选择真实安装的字面,作者的 `@font-face` 规则会加载本地字体
132
+ 文件(相对 URL 按 `file:` 路径相对进程工作目录解析)。最初用 HarfBuzz +
133
+ FreeType,现在用内置 stb_truetype。
134
+ - **M4b-2 — 字形渲染** ✅ stb_truetype 光栅化、共享的 shelf 打包字形图集、
125
135
  显示列表中的带纹理文本四边形与感知 HiDPI 的光栅缩放。渐变仍待完成。
126
136
  5. **M5 — 输入 + 事件** ✅
127
137
  - 命中测试(`LayoutTree::hitTest`)、带负载投递给 TS 处理器的指针/滚轮/
@@ -132,9 +142,9 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
132
142
  - **M6a — 图像** ✅ stb_image 解码(位图)与 NanoSVG 光栅化(SVG 文件,按
133
143
  绘制像素尺寸)、`<img>` 替换元素布局、按文件 GPU 纹理与带纹理四边形
134
144
  (`image.*`、`svg.*`、`paint.*`、`renderer.*`)。
135
- - **M6b — transition/动画** ✅ `transition` 简写 + 长写、对
136
- `background-color`/`color`/`border-color`/`border-radius` 的动画、重定向
137
- 与 `win.advance(ms)`。`@keyframes` 仍待完成。
145
+ - **M6b — transition/动画** ✅ `transition` 简写 + 长写、重定向、
146
+ `cubic-bezier()`/`steps()`、`win.advance(ms)` 与(M21)`@keyframes`
147
+ 及全部动画长写。
138
148
  7. **M7 — CI 与发布** ✅(Linux/macOS 构建) / 🚧(运行 + Windows arm64)
139
149
  - `compile-examples` 在 Linux 与 macOS 上构建 `gui.a` 与 `media.a`(必需),
140
150
  然后在 Linux 上的 Xvfb + lavapipe 下运行示例与 `tests/e2e/gui-*.test.ts`
@@ -144,11 +154,12 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
144
154
  因此两者都会随现有 runtime 归档一起发布(`package-release` 复制整个
145
155
  `runtime/`),也会打进可选的 `@xbintsc/<os>-<arch>` npm 包。Windows x64
146
156
  已包含;Windows arm64 仍跳过。
147
- - `vendor/`(GUI 归档的 SDL3/FreeType/HarfBuzz,以及 `media` 的 FFmpeg,
157
+ - `vendor/`(GUI 归档的 SDL3/FreeType/HarfBuzz/glslang,以及 `media` 的 FFmpeg,
148
158
  缓慢的部分)按 OS/arch 缓存,以 `scripts/build-gui.ts` /
149
159
  `scripts/build-media.ts` 为键。Windows x64 有独立的源码 + FFmpeg 安装前缀
150
160
  缓存。
151
- - Windows x64 在 CI 中构建两个归档并链接一个 `gui,media` 程序;托管 runner
161
+ - Windows x64 在 CI 中构建两个归档并链接一个会触发 `media` 链接特性的 `gui`
162
+ 程序(另有一条显式 `--feature media` 的构建);托管 runner
152
163
  没有 GPU/Vulkan 驱动,因此 GUI/媒体示例的*运行*仍为试验性(失败只记录
153
164
  日志而不标注运行)。Windows arm64 仍跳过两个归档(见
154
165
  [待决问题](./gui/known-issues.md))。CI 通过
@@ -198,14 +209,16 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
198
209
  - `win.stats()` 暴露引擎计数器;e2e 覆盖见
199
210
  `tests/e2e/gui-retention.test.ts`(见[内存与 GC](./gui/runtime.md))。
200
211
  13. **M13 — 视频与音频** ✅
201
- - `<video>` / `<audio>` 通过可选的 `media` 扩展(`runtime/ext_media/media.cpp`)
202
- 使用静态 vendored 的最小 FFmpeg 解码,帧沿用现有图像管线上传到 GPU 纹理,
212
+ - `<video>` / `<audio>` 通过可选的 `media` **链接特性**(`runtime/ext_media/media.cpp`)
213
+ 使用静态 vendored 的最小 FFmpeg 解码。编译器从程序里检测到媒体用法时才编译
214
+ `media_bridge.c` 并链接 `media.a`,只做 DOM/CSS 的程序不会链接 FFmpeg。
215
+ 帧沿用现有图像管线上传到 GPU 纹理,
203
216
  音频由 GUI 的音频 sink 在主线程推送进 SDL 流,并在元素句柄上暴露 DOM
204
217
  媒体 API 与 `loadedmetadata`/`play`/`pause`/`ended`/`timeupdate`/`error`
205
218
  事件(`dom_api_media.cpp`、`dom_api_proto.cpp`)。由 TS + CSS 控制;没有
206
219
  内置控件。示例见 `examples/gui/media.*`;e2e 见
207
- `tests/e2e/gui-media.test.ts`(启用 `media`)与
208
- `tests/e2e/gui-media-disabled.test.ts`(不启用)。
220
+ `tests/e2e/gui-media.test.ts`(自动选中 `media`)与
221
+ `tests/e2e/gui-media-disabled.test.ts`(`--no-feature media`)。
209
222
  14. **M14 — `<canvas>` 2D** ✅
210
223
  - `<canvas>` 是一个替换元素(`xtgui::Canvas2D`、`layout.*`),
211
224
  `getContext("2d")` 句柄与绘制 API 位于 `dom_api_canvas.cpp`,自研的抗锯齿
@@ -262,15 +275,16 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
262
275
  特性);`matchMedia()` 使用同一个求值器。
263
276
  - 生成内容:`::before`/`::after` 选择器、继承的伪样式、`content` 字符串/
264
277
  转义/`attr()`,以及不会泄漏到 DOM 遍历的合成布局盒。
265
- - 多层 `background-image`:线性渐变自下而上按纯色带绘制(18 条),径向渐变
266
- 逐像素光栅化成一张图像纹理(`renderer_gradient.cpp`)。
267
- - `text-shadow` 作为阴影文本 run,并带九次采样模糊近似。
278
+ - 多层 `background-image`:线性与径向渐变都逐像素光栅化成一张图像纹理
279
+ (`renderer_gradient.cpp`),硬色标与亚像素细节(1px 分割线)因此能精确绘制。
280
+ - `text-shadow` 与 `box-shadow`(外/内阴影、spread、多层)用形状掩码上的
281
+ 真正可分离高斯模糊(M21)。
268
282
  - `clip-path` 多边形(`inset`/`circle`/`ellipse`/`polygon`)解析到设备空间,
269
283
  用耳切法三角化,并在 `renderer_clip.cpp` 中与图形几何求交——不需要模板/
270
284
  shader 改动。
271
285
  - `filter` 颜色函数、九次采样 `blur()` 与轮廓 `drop-shadow()`;
272
- `backdrop-filter` 会解析并参与层叠,但需要离屏背景通道(见
273
- [已知问题](./gui/known-issues.md))。
286
+ `backdrop-filter` 对场景做快照、应用高斯与一个合并颜色矩阵,再通过
287
+ 边框盒掩码合成(M21)。
274
288
  - e2e 覆盖见 `tests/e2e/gui-effects.test.ts`;参见
275
289
  [绘制](./gui/rendering.md#背景生成内容与特效m19)。
276
290
  20. **M20 — 渲染性能** ✅
@@ -283,6 +297,27 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
283
297
  冗余的渲染状态绑定。
284
298
  - `win.stats()`/`XT_GUI_PERF` 计数器与 `tests/e2e/gui-perf.test.ts`;参见
285
299
  [性能](./gui/rendering.md#性能m20)。
300
+ 21. **M21 — CSS 取值/动画完整性** ✅
301
+ - `calc()` 被解析为未求值表达式树,由布局(`toPx`)与绘制共同求解;支持
302
+ 嵌套算术、`min`/`max`/`clamp` 内的 `calc()`,以及阴影/transform/filter
303
+ 中的 calc。
304
+ - `box-shadow`(外/内阴影、spread、多层)与带模糊的 `text-shadow` 被
305
+ 光栅化为高斯掩码,缓存后作为一个带色调的图像四边形合成。
306
+ - `backdrop-filter` 对已绘制场景做快照,用运行时编译的 shader 通过边框盒
307
+ 掩码合成高斯 + 颜色矩阵。
308
+ - `@keyframes` 与全部 `animation-*` 长写(延迟、迭代、方向、填充模式、
309
+ paused/running)与 transition 共享属性插值器;transition 现在还覆盖
310
+ opacity、布局长度、box-shadow 以及
311
+ `cubic-bezier()`/`steps()`/`step-start`/`step-end`。
312
+ - e2e:`tests/e2e/gui-css-features.test.ts`。
313
+ 22. **M22 — 拳击界面的 CSS 覆盖** ✅
314
+ - `letter-spacing`、`text-transform`、`font` 简写、长度形式的
315
+ `vertical-align`、`writing-mode`(仅文本竖排)、百分比 `border-radius`、
316
+ `inline-flex`/`inline-grid`、`grid-area`、`aspect-ratio`、自动外边距、
317
+ `outline` + `:focus-visible`/`:active`、`currentColor`、`skewX()`/
318
+ `skewY()`、`url()` 图层的 `background-size` 平铺,以及 SVG 子元素上的
319
+ 文档 CSS(`fill`/`stroke`/…)均已生效。
320
+ - e2e:`tests/e2e/gui-css-coverage.test.ts`。
286
321
 
287
322
  ## 进度日志
288
323
 
@@ -295,9 +330,9 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
295
330
  Flexbox、`getBoundingClientRect`/`layoutTree`、e2e 覆盖。
296
331
  - **M4a** ✅ 显示列表(`paint.*`)与带 MSL 圆角矩形管线的 SDL_GPU 2D 渲染器
297
332
  (`renderer.*`)、`paintList`/`paintCount`、e2e 覆盖。
298
- - **M4b-1** ✅ FreeType + HarfBuzz 获取/构建/合并进 `gui.a`、带字体解析的文本
299
- 模块(`text.*`)、HarfBuzz 整形与 FreeType 度量、布局中的真实文本度量、
300
- `measureText`/`fontMetrics`、e2e 覆盖。
333
+ - **M4b-1** ✅ 文本模块(`text.*`)、字体解析、整形与度量、布局中的真实文本度量、
334
+ `measureText`/`fontMetrics`、e2e 覆盖。最初用 FreeType + HarfBuzz,现在用
335
+ stb_truetype(见技术栈表与 [构建](./gui/build.md#二进制体积)中的体积取舍)。
301
336
  - **M4b-2** ✅ `renderer.*` 中的字形图集 + 带纹理文本管线、`paint.*` 中的
302
337
  整形文本 run、`text.*` 中的 `xt_text_shape_run`/`xt_text_rasterize`、
303
338
  HiDPI 光栅缩放、e2e 覆盖。
@@ -347,13 +382,15 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
347
382
  失效;收集器的触发阈值改为跟随存活集,而不再只升不降。`win.stats()` 暴露
348
383
  引擎计数器,`tests/e2e/gui-retention.test.ts` 对其做断言;每帧重写精灵的游戏
349
384
  循环现在内存恒定(见[内存与 GC](./gui/runtime.md))。
350
- - **M13** ✅ `<video>` / `<audio>` 支持。可选的 `media` 扩展 vendored 并构建一个
351
- 最小化的静态 FFmpeg(`scripts/build-media.ts`),合并进 `media.a`;
385
+ - **M13** ✅ `<video>` / `<audio>` 支持。可选的 `media` **链接特性** vendored 并构建一个
386
+ 最小化的静态 FFmpeg(`scripts/build-media.ts`),合并进 `media.a`。编译器从程序里
387
+ 选中该特性后才编译 `media_bridge.c` 并链接 `media.a`;只做 DOM/CSS 的程序不会链接
388
+ FFmpeg。
352
389
  `runtime/ext_media/media.cpp` 在主线程把视频解码为 RGBA 帧、把音频解码为
353
390
  48 kHz 立体声 PCM,`runtime/ext_gui/gui_media.cpp` 持有播放器、实现 SDL 音频
354
391
  sink 并在每帧推进媒体时钟与事件,`renderer_image.cpp` 把每个新视频帧沿图像纹理
355
392
  路径上传,`dom_api_media.cpp` 在元素句柄上暴露 `play`/`pause`/`load` 与媒体
356
- 属性。`gui.a` 不含 FFmpeg;只启用 `media` 而不启用 `gui` 会被注册表拒绝。
393
+ 属性。`gui.a` 不含 FFmpeg。
357
394
  `examples/gui/media.*` 演示 TS + CSS 控件的模式,
358
395
  `tests/e2e/gui-media.test.ts` 对其做断言。
359
396
  - **M14** ✅ `<canvas>` 支持。新的替换元素(`layout.*`)从 `width`/`height`
@@ -410,9 +447,23 @@ FFmpeg `7.1`(供可选的 `media` 归档使用;可用 `SDL3_TAG` /
410
447
  `gradient_values.cpp` 建模背景层列表(线性/径向、形状/尺寸/位置/色标),
411
448
  `paint_background.cpp` 解算并绘制它(径向层变成一个 `PaintGradient`,由
412
449
  `renderer_gradient.cpp` 光栅化成纹理),`paint_effects.cpp` 应用滤镜颜色函数
413
- 与九次采样模糊/投影并生成 `text-shadow` run,`paint_clip.cpp`/
414
- `renderer_clip.cpp` 对三角化的 `clip-path` 多边形裁剪几何。e2e 见
415
- `tests/e2e/gui-effects.test.ts`。
450
+ 与九次采样模糊/投影,`paint_shadow.cpp` 光栅化高斯文字/盒子阴影,
451
+ `paint_backdrop.cpp` + `renderer_layer.cpp` 合成 `backdrop-filter`,
452
+ `paint_clip.cpp`/`renderer_clip.cpp` 对三角化的 `clip-path` 多边形裁剪几何。
453
+ e2e 见 `tests/e2e/gui-effects.test.ts`。
454
+ - **M22** ✅ 拳击界面的 CSS 覆盖。层叠/声明解析新增 `letter-spacing`、
455
+ `text-transform`、`font` 简写、`vertical-align`、`writing-mode`、百分比
456
+ `border-radius`、`outline`、`background-size`、`currentColor` 与 SVG 表现
457
+ 属性;布局新增行内级 flex/grid、`grid-area`、`aspect-ratio`、自动外边距与
458
+ 仅文本竖排;绘制新增 outline 边框条、按尺寸平铺的图像背景与旋转的竖排文字;
459
+ `:focus-visible` 目前等同 `:focus`,`:active` 由指针按下/抬起驱动。e2e:
460
+ `tests/e2e/gui-css-coverage.test.ts`。
461
+ - **M21** ✅ CSS 取值/动画完整性。`calc()` 被解析为未求值表达式树,由布局/
462
+ 绘制求解;`box-shadow` 与带模糊的 `text-shadow` 使用可缓存为纹理的可分离
463
+ 高斯掩码;`backdrop-filter` 获得离屏快照通道;`@keyframes`(含全部
464
+ `animation-*` 长写、延迟、迭代、方向与填充模式)与 transition 共享属性插值器,
465
+ transition 现在还覆盖 opacity、布局长度、box-shadow 以及
466
+ `cubic-bezier()`/`steps()`。e2e 见 `tests/e2e/gui-css-features.test.ts`。
416
467
  - **M20** ✅ 渲染性能。`@media` 每次重算样式时每条规则只求值一次;没有
417
468
  `::before`/`::after` 的样式表完全跳过生成内容;只有样式表包含
418
469
  `:hover`/`:focus` 规则时才因这两个状态重算样式(`style.cpp`、
@@ -28,6 +28,8 @@ xbintsc help 帮助
28
28
  --emit <kind> exe | obj | ir(默认 exe)
29
29
  -O0..-O3 优化级别(默认 -O2)
30
30
  --ext <names> 逗号分隔扩展(如 node)
31
+ --feature <names> 强制开启可选链接特性(media,gpu,images,shaping,webgl,all)
32
+ --no-feature <n> 即使检测到也不链接该可选特性
31
33
  --ext-native <m> 从 JSON manifest 注册 C++/Rust 扩展
32
34
  --config <path> 使用项目配置(默认自动查找 xbintsc.config.json)
33
35
  --no-config 不读取任何项目配置
@@ -2,10 +2,16 @@
2
2
 
3
3
  > 属于[已实现语法与功能](../implemented.md) · [English](../../implemented/extensions.md)
4
4
 
5
- 实现位置:`src/extensions/registry.ts`、`src/extensions/node/`
5
+ 实现位置:`src/extensions/registry.ts`、`src/extensions/node/`、`src/extensions/link-feature.ts`、`src/driver/usage.ts`、`src/driver/link-features.ts`
6
6
 
7
- - 扩展是普通对象:`runtimeSources()`(额外 C 源)、`linkerFlags()`(额外链接参数)、`builtins()`(全局标识符 → 运行时符号,统一 `(argc, argv)` ABI)与 `modules()`(导入说明符 → 命名导出 / 命名空间)。
8
- - `ExtensionRegistry`:注册 / 注销 / 查询 / 汇总 builtins、modules、runtime 源、链接参数。
7
+ - 扩展是普通对象:`runtimeSources()`(额外 C 源)、`linkerFlags()`(额外链接参数)、`builtins()`(全局标识符 → 运行时符号,统一 `(argc, argv)` ABI)、`modules()`(导入说明符 → 命名导出 / 命名空间)与 `linkFeatures()`(可选的重型能力)。
8
+ - `ExtensionRegistry`:注册 / 注销 / 查询 / 汇总 builtins、modules、runtime 源、链接参数与链接特性。
9
+ - **可选链接特性**(`media`、`gpu`、`images`、`shaping`、`webgl`):
10
+ - 特性声明 `runtimeSources()`(只在启用时编译的 C bridge)、`nativeObjects()`(只在启用时链接的归档)、`linkerFlags()`、可选的 `requires`(依赖的另一个特性)与 `used(usage)`。
11
+ - driver 从打包后的 AST 构造 `ProgramUsage`(`src/driver/usage.ts` 的 `collectUsage`):字符串字面量(含导入 HTML 与内联 CSS)、标识符/属性名、`getContext(...)` 的 kind,以及 markup 是否可能在运行时拼出。简单的 `const` 别名会被解析;无法解析的 markup 让程序走保守回退。
12
+ - `resolveLinkFeatures` 应用 `--feature` / `--no-feature`(以及 `all`),展开 `requires`,把自动检测到但不可用的特性报为 warning、把强制开启却不可用的报为 error,并返回要链接的 bridge/归档/参数。
13
+ - bridge 就是强引用:构造函数安装特性的分发表,其引用迫使链接器取出实现;没有 bridge 时表保持 null,实现成员永不被链接。
14
+ - 当前客户端:`media`(FFmpeg,`runtime/ext_media/media_bridge.c` + `media.a`)、`gpu`(glslang/SPIRV-Cross,`runtime/ext_gui/gpu_bridge.c` + `gui.a` 内的 `gpu_feature.cpp`)、`images`(stb_image/NanoSVG,`runtime/ext_gui/image_bridge.c` + `image_feature.cpp`)、`shaping`(HarfBuzz/FreeType,`runtime/ext_gui/text_bridge.c` + `text_hb.cpp`)与 `webgl`(context/DOM 绑定,`runtime/ext_gui/webgl_bridge.c` + `webgl_feature.cpp`,依赖 `gpu`)。
9
15
  - 内置核心扩展 `core`:暴露 `print`(映射 `xt_println`),始终注册。
10
16
  - Node 扩展 `node`:
11
17
  - 模块化组织:`src/extensions/node/fs/` + `runtime/ext_node/fs/read_file.c`
@@ -63,7 +63,8 @@ TypeScript 编译器。
63
63
 
64
64
  如果 npm 跳过了这个可选包(`--omit=optional`、不安装可选依赖的包管理器,或发布
65
65
  未覆盖的宿主平台),驱动会回退到从随包源码编译 C 运行时并缓存目标文件;这样仍能
66
- 工作,但首次构建更慢,`--ext gui` / `--ext media` 也需要归档存在。
66
+ 工作,但首次构建更慢,`--ext gui`(以及自动选择的 `media`/`gpu`/`images`/`shaping`/`webgl` 链接特性)也需要
67
+ 归档存在。
67
68
 
68
69
  ## 环境要求
69
70
 
@@ -372,7 +373,7 @@ jobs:
372
373
  | --- | --- | --- |
373
374
  | 需要 Node.js | 是,22+ | 否 |
374
375
  | 首次构建 | 链接可选平台包里的预编译运行时 | 直接链接预编译运行时 |
375
- | GUI / media 扩展 | Linux/macOS 与 Windows x64(发布为这些平台构建)包含 | Linux/macOS 与 Windows x64 压缩包包含 |
376
+ | GUI 引擎 + media 归档 | Linux/macOS 与 Windows x64(发布为这些平台构建)包含 | Linux/macOS 与 Windows x64 压缩包包含 |
376
377
  | 安装方式 | `npm install` + lockfile / provenance | 下载、校验 `.sha256`、解压 |
377
378
  | 编程 API | 有 | 无(仅 CLI) |
378
379
  | 交叉编译 | 否——只构建宿主平台 | 否——只构建宿主平台 |
@@ -407,7 +408,7 @@ npm 包通过可选平台包获得同一套预编译运行时,只有在跳过
407
408
  | 第一次构建很慢 | 可选包 `@xbintsc/<os>-<arch>` 被跳过(或该宿主没有预编译包),正在编译并缓存运行时 C 源码。用 `npm ls @xbintsc/<os>-<arch>` 检查,并去掉 `--omit=optional` 重新安装;`--time` 可确认。 |
408
409
  | 写 `.xbintsc/` 权限错误 | 用 `xbintsc_CACHE_DIR` 指向可写目录,或在可写目录中运行。 |
409
410
  | `module 'fs' is provided by the 'node' extension` | 加 `--ext node`,或在配置里写 `"extensions": ["node"]`。 |
410
- | `GUI native library not found` / `media native library not found` | 携带 `gui.a`/`gui.lib` 或 `media.a`/`media.lib` 的可选平台包被跳过,或该宿主没有预编译的 GUI/media 归档(Windows arm64)。去掉 `--omit=optional` 重新安装,或使用 Linux/macOS/Windows x64 发布压缩包,或从源码检出构建。 |
411
+ | `GUI native library not found` / `media native library not found` | 携带 `gui.a`/`gui.lib` 或 `media.a`/`media.lib` 的可选平台包被跳过,或该宿主没有预编译的 GUI/media 归档(Windows arm64)。去掉 `--omit=optional` 重新安装,或使用 Linux/macOS/Windows x64 发布压缩包,或从源码检出构建。自动检测到的 `media` 特性缺归档只会 warning;`--feature media` 才会报错。 |
411
412
  | `require("xbintsc")` 失败 | 包只提供 ESM;使用 `import`(或动态 `import()`)。 |
412
413
  | Windows 链接时报缺少 MSVC 库 | 在 Native Tools 命令提示符中运行,安装 VS C++ 生成工具,并检查 `npx xbintsc doctor`。 |
413
414
  | `npm ci --omit=dev` 后构建找不到 `xbintsc` | 在构建阶段安装 devDependencies,或把 `xbintsc` 移到 `dependencies`。 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xbintsc",
3
- "version": "0.5.45",
3
+ "version": "0.5.52",
4
4
  "description": "xbintsc - a TypeScript binary compiler (TypeScript -> LLVM IR -> native binary)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -72,12 +72,12 @@
72
72
  "vitest": "^2.1.8"
73
73
  },
74
74
  "optionalDependencies": {
75
- "@xbintsc/linux-x64": "0.5.45",
76
- "@xbintsc/linux-arm64": "0.5.45",
77
- "@xbintsc/darwin-x64": "0.5.45",
78
- "@xbintsc/darwin-arm64": "0.5.45",
79
- "@xbintsc/win32-x64": "0.5.45",
80
- "@xbintsc/win32-arm64": "0.5.45"
75
+ "@xbintsc/linux-x64": "0.5.52",
76
+ "@xbintsc/linux-arm64": "0.5.52",
77
+ "@xbintsc/darwin-x64": "0.5.52",
78
+ "@xbintsc/darwin-arm64": "0.5.52",
79
+ "@xbintsc/win32-x64": "0.5.52",
80
+ "@xbintsc/win32-arm64": "0.5.52"
81
81
  },
82
82
  "engines": {
83
83
  "node": ">=22"