xbintsc 0.5.51 → 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 (122) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/driver/version.d.ts +1 -1
  3. package/dist/src/driver/version.js +1 -1
  4. package/dist/src/extensions/gui/features.d.ts +9 -6
  5. package/dist/src/extensions/gui/features.js +14 -9
  6. package/dist/src/extensions/gui/features.js.map +1 -1
  7. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  8. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  9. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  10. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  11. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  12. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  13. package/dist/tests/e2e/gui-effects.test.js +92 -11
  14. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  15. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  16. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  17. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  18. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  19. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  20. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  21. package/dist/tests/e2e/gui-helpers.d.ts +5 -0
  22. package/dist/tests/e2e/gui-helpers.js +4 -0
  23. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  24. package/dist/tests/e2e/gui-layout.test.js +3 -2
  25. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  26. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  27. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  28. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  29. package/dist/tests/e2e/gui-transform.test.js +3 -2
  30. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  31. package/dist/tests/extensions/gui-features.test.js +8 -1
  32. package/dist/tests/extensions/gui-features.test.js.map +1 -1
  33. package/doc/ai/extensions.md +10 -5
  34. package/doc/ai/zh-CN/extensions.md +9 -4
  35. package/doc/gui/animation.md +39 -9
  36. package/doc/gui/build.md +6 -5
  37. package/doc/gui/dom.md +20 -4
  38. package/doc/gui/known-issues.md +31 -23
  39. package/doc/gui/layout.md +30 -7
  40. package/doc/gui/rendering.md +84 -26
  41. package/doc/gui.md +62 -14
  42. package/doc/zh-CN/gui/animation.md +33 -10
  43. package/doc/zh-CN/gui/build.md +5 -5
  44. package/doc/zh-CN/gui/dom.md +16 -3
  45. package/doc/zh-CN/gui/known-issues.md +21 -12
  46. package/doc/zh-CN/gui/layout.md +22 -5
  47. package/doc/zh-CN/gui/rendering.md +61 -20
  48. package/doc/zh-CN/gui.md +53 -15
  49. package/package.json +7 -7
  50. package/runtime/ext_gui/animation_values.cpp +425 -0
  51. package/runtime/ext_gui/animation_values.h +64 -0
  52. package/runtime/ext_gui/css.cpp +172 -21
  53. package/runtime/ext_gui/css.h +75 -4
  54. package/runtime/ext_gui/css_match.cpp +4 -0
  55. package/runtime/ext_gui/css_sheet.cpp +218 -2
  56. package/runtime/ext_gui/document.cpp +42 -192
  57. package/runtime/ext_gui/document.h +25 -12
  58. package/runtime/ext_gui/document_animation.cpp +428 -0
  59. package/runtime/ext_gui/effect_values.cpp +245 -21
  60. package/runtime/ext_gui/gradient_values.cpp +24 -0
  61. package/runtime/ext_gui/grid_values.cpp +23 -0
  62. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  63. package/runtime/ext_gui/gui_events.cpp +16 -4
  64. package/runtime/ext_gui/gui_internal.h +3 -0
  65. package/runtime/ext_gui/image_api.h +3 -1
  66. package/runtime/ext_gui/image_feature.cpp +4 -2
  67. package/runtime/ext_gui/inline_svg.cpp +95 -8
  68. package/runtime/ext_gui/inline_svg.h +5 -1
  69. package/runtime/ext_gui/layout.cpp +3 -2
  70. package/runtime/ext_gui/layout.h +6 -0
  71. package/runtime/ext_gui/layout_flex.cpp +134 -29
  72. package/runtime/ext_gui/layout_flow.cpp +58 -7
  73. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  74. package/runtime/ext_gui/layout_grid.cpp +31 -0
  75. package/runtime/ext_gui/layout_inline.cpp +26 -2
  76. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  77. package/runtime/ext_gui/paint.cpp +67 -9
  78. package/runtime/ext_gui/paint.h +61 -8
  79. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  80. package/runtime/ext_gui/paint_background.cpp +175 -97
  81. package/runtime/ext_gui/paint_effects.cpp +12 -6
  82. package/runtime/ext_gui/paint_internal.h +27 -0
  83. package/runtime/ext_gui/paint_outline.cpp +44 -0
  84. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  85. package/runtime/ext_gui/renderer.cpp +15 -7
  86. package/runtime/ext_gui/renderer.h +10 -1
  87. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  88. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  89. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  90. package/runtime/ext_gui/renderer_image.cpp +6 -1
  91. package/runtime/ext_gui/renderer_internal.h +21 -0
  92. package/runtime/ext_gui/renderer_layer.cpp +27 -1
  93. package/runtime/ext_gui/renderer_text.cpp +2 -1
  94. package/runtime/ext_gui/style.cpp +62 -8
  95. package/runtime/ext_gui/style.h +111 -4
  96. package/runtime/ext_gui/style_cascade.cpp +83 -0
  97. package/runtime/ext_gui/style_internal.h +13 -1
  98. package/runtime/ext_gui/style_length.cpp +92 -0
  99. package/runtime/ext_gui/style_print.cpp +83 -2
  100. package/runtime/ext_gui/style_svg.cpp +40 -0
  101. package/runtime/ext_gui/style_text.cpp +302 -0
  102. package/runtime/ext_gui/style_ui.cpp +150 -0
  103. package/runtime/ext_gui/style_values.cpp +76 -62
  104. package/runtime/ext_gui/style_values_internal.h +23 -0
  105. package/runtime/ext_gui/text.cpp +4 -2
  106. package/runtime/ext_gui/text.h +11 -4
  107. package/runtime/ext_gui/text_cache.cpp +11 -2
  108. package/runtime/ext_gui/text_font_db.cpp +427 -0
  109. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  110. package/runtime/ext_gui/text_font_paths.h +39 -0
  111. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  112. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  113. package/runtime/ext_gui/text_fonts.cpp +348 -0
  114. package/runtime/ext_gui/text_fonts.h +78 -0
  115. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  116. package/runtime/ext_gui/text_hb.cpp +63 -179
  117. package/runtime/ext_gui/text_stb.cpp +49 -193
  118. package/runtime/ext_gui/transform.cpp +107 -9
  119. package/runtime/ext_gui/transform.h +5 -0
  120. package/runtime/ext_gui/window.cpp +1 -1
  121. package/src/driver/version.ts +1 -1
  122. package/src/extensions/gui/features.ts +13 -9
@@ -30,27 +30,49 @@ transition 的属性时,会记录一个正在运行的 transition,并在每
30
30
  结束;引擎用真实帧间隔推进时钟(`XtDocument::advance`),`win.advance(ms)`
31
31
  让测试可以确定性地推进。
32
32
 
33
- - 支持的属性:`background-color`、`color`、`border-color`、
34
- `border-radius` 与 `transform`;`transition: all` 覆盖它们。影响布局的
35
- 属性(width、position 等)仍不做动画——那会每帧重新布局——但 `transform`
36
- 是绘制期效果,因此为它做动画的代价只是每个被动画的盒子每帧一个矩阵
37
- (见[布局](./layout.md#变换m15))。
33
+ - 支持的属性:`background-color`、`color`、`border-color`、`opacity`、
34
+ `border-radius`、`transform`、`text-shadow`、`box-shadow`、所有盒模型
35
+ 偏移/尺寸长度(`width`/`height`/`top`/`left`/`margin-*`/`padding-*`/…)、
36
+ gap/flex-basis 长度,以及 `filter`(形状一致的滤镜列表会插值模糊长度、数值与
37
+ drop-shadow 颜色);`transition: all` 覆盖整个列表。单位一致的长度会插值
38
+ (单位变化按 CSS 的离散回退处理),`opacity`、颜色与 `transform` 正常插值。
38
39
  - `transition` 简写与 `transition-property` / `-duration` / `-delay` /
39
- `-timing-function` 长写都会被解析;时间值接受 `s` 与 `ms`;缓动函数为
40
- `linear`、`ease`、`ease-in`、`ease-out` 与 `ease-in-out`(`ease` 是
41
- smoothstep 近似)。
40
+ `-timing-function` 长写都会被解析;时间值接受 `s` 与 `ms`;简写/长写内
41
+ 都接受 `calc()`。
42
+ - 缓动函数支持 `linear`、`ease`、`ease-in`、`ease-out`、`ease-in-out`、
43
+ `cubic-bezier(x1, y1, x2, y2)`、`steps(n, start|end)` 及其 `jump-*` 拼写;
44
+ 三次贝塞尔用数值求解,关键字曲线使用 CSS 控制点。
42
45
  - 在动画中途重定向会从当前插值启动一个新 transition,因此反转悬停会从它
43
46
  当前所在位置平滑动画。
44
47
  - `computedStyle()` 报告显示(已插值)的值,因此 transition 在测试中可直接
45
48
  观察。
46
49
 
47
- `transform` 列表按**逐项**插值(`document.cpp`、`xt_transform_interpolate`),
50
+ `transform` 列表按**逐项**插值(`animation_values.cpp`、`xt_transform_interpolate`),
48
51
  这正是让常见的一对 `translateY(-4px) rotate(-0.35deg)` 与 `none` 之间平滑
49
52
  滑行而不是跳变的原因:缺失的一侧会用同类型的单位(identity)操作补齐。两个
50
53
  列表形状或单位对不上时(`rotate(10deg)` 对 `rotate(10rad)`,或函数个数不同)
51
54
  完全不做插值——transition 直接吸附到目标值,即 CSS 的离散回退,而不是一段
52
55
  看起来错误的扫过。
53
56
 
57
+ ## CSS 动画(`@keyframes`)
58
+
59
+ `@keyframes` 会被解析进 `StyleSheet::keyframes`(`from`/`to`/百分比选择器产生的
60
+ 偏移会排序)。支持 `animation` 简写及全部 `animation-*` 长写:名称、时长、
61
+ 延迟、时间函数、迭代次数(含 `infinite`)、方向
62
+ (`normal`/`reverse`/`alternate`/`alternate-reverse`)、填充模式
63
+ (`none`/`forwards`/`backwards`/`both`)与 `paused`/`running`。
64
+
65
+ 当计算样式首次声明某个元素/名称时,`XtDocument` 启动一个活动动画;简写变化时
66
+ 重新启动,无关的 restyle 不会打断它。每帧把本地时间解析为迭代进度,**逐个
67
+ 关键帧区间**应用时间函数,并插值所有可动画属性。中间关键帧没有提到的属性会
68
+ 在最近两个声明了它的关键帧之间继续插值(因此 0% 与 100% 声明的 transform
69
+ 即使 50% 只改 `opacity` 也会贯穿全程)。缺失的 0%/100% 步骤使用元素的底层
70
+ 样式;`forwards`/`backwards` 在活动时间之外保持边缘值,`computedStyle()`
71
+ 报告动画后的值。
72
+
73
+ 动画与 transition 共享 `animation_values.cpp`,因此属性集合、阴影列表与
74
+ `transform` 的 identity 补齐行为一致。
75
+
54
76
  ## CSS transform(M15)
55
77
 
56
78
  `transform` 与 `transform-origin` 作为*绘制期*效果得到支持
@@ -89,5 +111,6 @@ const tick = (t: number): void => {
89
111
  两种方式都由 `examples/gui/pelican-svg` 演示(逐帧摇摆 + 悬停时说明文字的
90
112
  transition),并由 `tests/e2e/gui-transform.test.ts` 固定住。
91
113
 
92
- 动画仍需完成:`@keyframes` 动画与 `cubic-bezier(...)`。
114
+ 任意可动画长度都可以使用 `calc()`(例如
115
+ `transition: width 300ms ease-out; width: calc(100% - 20px)`)。
93
116
 
@@ -109,7 +109,7 @@
109
109
  | `media` | `media_bridge.c` + `media.a`(FFmpeg) | HTML 里有 `<video>`/`<audio>`、`createElement("video"/"audio")`、`new Audio()`、`decodeAudioData`,或运行时拼出的 HTML |
110
110
  | `gpu` | `gpu_bridge.c`(实现本身已在 `gui.a`) | `getContext("webgl"/"webgl2"/"experimental-webgl")`(或运行时决定的 kind)、任意 `webgl` 字符串、`mix-blend-mode`/`mixBlendMode`,或运行时拼出的 HTML |
111
111
  | `images` | `image_bridge.c`(实现本身已在 `gui.a`) | `<img>`、内联 `<svg>`、`background-image`/`url(...)`、`.svg` 路径、`drawImage`/`createImageBitmap`/`Image`,或运行时拼出的 HTML |
112
- | `shaping` | `text_bridge.c`(实现本身已在 `gui.a`) | 非 ASCII 文本(CJK/西里尔/希腊文等)、转义或实体码点,或运行时拼出的 HTML |
112
+ | `shaping` | `text_bridge.c`(实现本身已在 `gui.a`) | 非 ASCII 文本(CJK/西里尔/希腊文等)、转义或实体码点、作者的 `@font-face` 规则(自定义字体可能是 CFF/OTF),或运行时拼出的 HTML |
113
113
  | `webgl` | `webgl_bridge.c`(实现本身已在 `gui.a`,依赖 `gpu`) | WebGL 的 `getContext(...)`、`webgl` 字符串,或运行时拼出的 HTML |
114
114
 
115
115
  driver 会扫描打包后的 AST(`src/driver/usage.ts`):所有字符串字面量(包括导入的
@@ -167,10 +167,10 @@ stb_image/NanoSVG,DOM-only 页面不链接)。Canvas2D、Web Audio 与
167
167
  文本替换是有意的取舍。默认后端是 stb_truetype:只接受 TrueType 轮廓,整形是逐码点
168
168
  映射加字体的 `kern` 表,因此 CFF/OTF 与可变字体、OpenType 连字以及复杂文种
169
169
  (阿拉伯/印度/泰文)整形不可用;fallback 链仍会在主字体缺字时切换到其他字体。
170
- 选中可选的 **`shaping`** 特性(非 ASCII 文本会自动选中,也可用
171
- `--feature shaping`)会重新链接 HarfBuzz + FreeType(约 1.2 MiB),恢复完整的
172
- OpenType 整形与 CFF/OTF/可变字体支持。纯拉丁 DOM 页面因此保持约 3.1 MiB;
173
- 中文/复杂文种页面更接近 4.3 MiB。
170
+ 选中可选的 **`shaping`** 特性(非 ASCII 文本或作者的 `@font-face` 规则会自动
171
+ 选中,也可用 `--feature shaping`)会重新链接 HarfBuzz + FreeType(约 1.2 MiB),
172
+ 恢复完整的 OpenType 整形与 CFF/OTF/可变字体支持。纯拉丁 DOM 页面因此保持约
173
+ 3.1 MiB;中文/复杂文种页面更接近 4.3 MiB。
174
174
 
175
175
  ## 运行示例
176
176
 
@@ -20,11 +20,24 @@ doctype 跳过,实体解码(命名 + 十进制/十六进制),空元素
20
20
  (`=`、`~=`、`|=`、`^=`、`$=`、`*=`)、四种组合器(后代、子、相邻兄弟与
21
21
  通用兄弟)、伪类 `:first-child`、`:last-child`、`:only-child`、`:empty`、
22
22
  `:root`、`:not(...)`、`:nth-child(an+b)`、`:disabled`、`:checked`、有状态的
23
- `:hover` 与 `:focus`(见[输入](./input.md)),以及伪元素
23
+ `:hover`、`:focus`、`:focus-visible`(目前等同 `:focus`)与 `:active`
24
+ (见[输入](./input.md)),以及伪元素
24
25
  `::before`/`::after`(也接受旧式 `:before`/`:after`)。值:长度
25
26
  (`px`、`%`、`em`、`rem`、`vw`、`vh`、`pt`、`pc`、`in`、`cm`、`mm`、`q`)、
26
27
  颜色(十六进制、`rgb()`/`rgba()`、一个命名子集)、数字、关键字与简写
27
- (`margin`/`padding`/`border`/`flex`)。`transform`
28
+ (`margin`/`padding`/`border`/`flex`/`font`/`outline`)。
29
+ `display: inline-flex`/`inline-grid` 生成行内级 flex/grid 容器;`grid-area`、
30
+ `aspect-ratio`、`letter-spacing`、`text-transform`、`writing-mode`
31
+ (`vertical-rl`/`vertical-lr` 文本)、`vertical-align`(长度形式)、百分比
32
+ `border-radius`、`background-size`(`url()` 图层按尺寸与默认 repeat 平铺)与
33
+ `currentColor` 都已支持。`font-variant-numeric`、`text-wrap`、`text-rendering`、
34
+ `touch-action`、`user-select`、`overscroll-behavior`、`scroll-behavior`、
35
+ `isolation`、`vector-effect` 会被接受但不产生引擎行为(文本栈不暴露 OpenType 特性,引擎也没有触摸/选择/平滑滚动子系统);
36
+ `cursor` 映射到 SDL 系统光标。文档 CSS 可用 `fill`、`fill-opacity`、`fill-rule`、
37
+ `stroke`、`stroke-opacity`、`stroke-width`、`stroke-dasharray`、
38
+ `stroke-dashoffset`、`stroke-linecap`、`stroke-linejoin` 给 SVG 子元素上色;
39
+ 序列化器会把它们写成 NanoSVG 可读的内联样式。`skewX()`/`skewY()`/`skew()`
40
+ 变换函数也已支持。`transform`
28
41
  (`translate`/`translate3d`/`translateX`/`translateY`、
29
42
  `scale`/`scale3d`/`scaleX`/`scaleY`、`rotate`/`rotateZ`、`matrix`;`3d`
30
43
  拼写的 `z` 参数会被丢弃)与 `transform-origin`
@@ -32,7 +45,7 @@ doctype 跳过,实体解码(命名 + 十进制/十六进制),空元素
32
45
  函数会让整条声明失效(见[布局](./layout.md#变换m15))。M19 增加了
33
46
  `background-image` 层列表(线性/径向)、`clip-path`
34
47
  (`inset`/`circle`/`ellipse`/`polygon`)、`filter`/`backdrop-filter` 函数列表、
35
- `text-shadow` 与 `content`。
48
+ `text-shadow`、`box-shadow` 与 `content`。
36
49
 
37
50
  **层叠与计算样式**(`runtime/ext_gui/style.{h,cpp}`):一份小的内置 UA
38
51
  样式表,作者规则按 `(!important, 特异性, 源码顺序)` 排序,然后是内联
@@ -48,21 +48,30 @@
48
48
  `tests/e2e/gui-example-browser.test.ts` 中。
49
49
  - **`display: none -> flex` 的重排。** 通过类名切换让隐藏元素显示在 flex 容器上时,也曾观察
50
50
  到同样的崩溃;`examples/gui/pelican-bike` 改用 `opacity` 显示游戏结束遮罩。
51
- - **CSS 覆盖范围是有意为之的子集。** 已实现 Grid、`clamp()`/`min()`/`max()`/`env()`、
51
+ - **CSS 覆盖范围是有意为之的子集。** 已实现 Grid、`calc()`(含嵌套算术及
52
+ `min()`/`max()`/`clamp()` 内的 `calc()`)、`clamp()`/`min()`/`max()`/`env()`、
52
53
  `var()` 自定义属性、`inset` 简写、`box-sizing`、`visibility`/`pointer-events`、
53
54
  `@media` 查询、生成内容(`::before`/`::after`)、线性/径向多层背景、
54
- `text-shadow`、`clip-path`(`inset`/`circle`/`ellipse`/`polygon`)以及 `filter`
55
- 的颜色函数。仍有限制:`background-clip`、`background-position`/`size`/`repeat`
56
- 与 `backdrop-filter` 合成都未实现(`backdrop-filter` 会解析并参与层叠,但采样
57
- 背景需要离屏图层,因此不绘制);`mix-blend-mode` 与重复渐变已实现(见
58
- [渲染](./rendering.md)),但组 `opacity` 是按图形 alpha 近似而非真正的组合成,
59
- 且 D3D12/DXIL 后端没有混合合成器,那里会退回普通 source-over 绘制;
60
- `filter: blur()` 是按图形做九次偏移的近似,而不是真正的组模糊,重叠的子元素会加深;
61
- `drop-shadow` 用盒子轮廓近似;最多只交叉两层嵌套 `clip-path`(更深时保留外侧两层);
55
+ `text-shadow`(真正的高斯)、`box-shadow`(外阴影/内阴影、spread、多层)、
56
+ `clip-path`(`inset`/`circle`/`ellipse`/`polygon`)、`filter` 颜色函数、
57
+ `@keyframes` 动画、完整的 `transition` 时间函数、`letter-spacing`、
58
+ `text-transform`、行内级 `flex`/`grid`、`grid-area`、`aspect-ratio`、自动
59
+ 外边距、百分比 `border-radius`、`outline`、`url()` 图层的 `background-size`
60
+ 平铺,以及 SVG 子元素上的文档 CSS(`fill`/`stroke`/…)均已实现。仍有限制:
61
+ `background-clip`、`background-position`、`background-repeat` 未实现
62
+ (`background-size` 只覆盖默认平铺场景);`font-variant-numeric`、
63
+ `text-wrap`、`text-rendering`、`touch-action`、`user-select`、
64
+ `overscroll-behavior`、`scroll-behavior`、`isolation`、`vector-effect`
65
+ 可解析但不产生行为;竖排书写模式仅
66
+ 支持文本(嵌套/混排内容回退横排);组 `opacity` 是按图形 alpha
67
+ 近似而非真正的组合成;D3D12/DXIL 后端没有混合与 `backdrop-filter` 合成器,
68
+ 那里会退回普通 source-over 绘制并跳过背景滤镜。`filter: blur()` 仍是按图形做
69
+ 九次偏移的近似,而不是真正的组模糊,重叠的子元素会加深;`drop-shadow` 用盒子
70
+ 轮廓近似;最多只交叉两层嵌套 `clip-path`(更深时保留外侧两层);
62
71
  图像/画布除图像着色器能携带的亮度与透明度染色外不做颜色滤镜。Canvas 纹理使用
63
- 最近邻采样。背景线性渐变仍是纯色带(18 条,重复渐变 64 条),较大的柔和渐变
64
- 可能出现轻微条带;径向渐变则逐像素光栅化成纹理(每边最大 1024 px,超出后拉伸,
65
- 因此超大盒子上的硬色标会略微变软)。
72
+ 最近邻采样。背景渐变(线性与径向)都逐像素光栅化成纹理,盒子大于纹理时再拉伸
73
+ (径向每边最大 1024 px;线性沿渐变线最大 4096 px,且沿渐变线之外只有一像素厚),
74
+ 因此超大盒子上的硬色标会略微变软。
66
75
  - **SVG 是 NanoSVG 的子集。** SVG *文件*(`<img src="logo.svg">`)与内联
67
76
  `<svg>` 子树共用 vendored 的 NanoSVG 解析/光栅化器(`runtime/ext_gui/svg.cpp`、
68
77
  `inline_svg.cpp`),它会渲染 `path`/`rect`/`circle`/`ellipse`/`line`/
@@ -22,7 +22,10 @@
22
22
  技术栈测量(见[文本技术栈](./rendering.md))。
23
23
  - **Flexbox** —— `row`/`column`(以及 `-reverse` 变体),支持 `gap`、
24
24
  `flex-basis`/`flex-grow`/`flex-shrink`、`justify-content` 与 `align-items`
25
- (当交叉轴尺寸确定时包括 `stretch`)。`flex-wrap: wrap` 会把项断成多行,
25
+ (当交叉轴尺寸确定时包括 `stretch`)。两个方向的主轴尺寸都先取
26
+ `flex-basis`(为 `auto` 时用测量尺寸):有剩余空间按 `flex-grow` 分配,
27
+ 空间不足按 `flex-shrink` 收缩;单个项的 `align-self` 可覆盖容器的
28
+ `align-items`。`flex-wrap: wrap` 会把项断成多行,
26
29
  `align-content`(`stretch`/`flex-start`/`flex-end`/`center`/`space-between`/
27
30
  `space-around`)在交叉轴上分配这些行,`wrap-reverse` 则翻转交叉轴。
28
31
  flex 项的外边距参与其外框尺寸,因此
@@ -33,11 +36,25 @@
33
36
  `repeat(...)`)、`gap`/`row-gap`/`column-gap`、按行自动放置、简单的
34
37
  `grid-column`/`grid-row` 线定位,以及 `align/justify-items` 与
35
38
  `align/justify-self`(含 `place-items`/`place-self`)。隐式行/列由项目撑开;
36
- 单单元格网格配合 `place-items: center` 可把卡片居中到整个覆盖层。项目测量是
39
+ 网格高度确定时,先布置固定行与 auto 行,剩余高度再按 `fr` 分给 `fr` 行,
40
+ 因此 `aspect-ratio` 可以驱动 `repeat(n, 1fr)` 网格。单单元格网格配合
41
+ `place-items: center` 可把卡片居中到整个覆盖层。项目测量是
37
42
  两遍布局(先固有尺寸、再最终放置),因此两遍之间会清空文本片段。
38
- - **CSS 值函数与自定义属性** —— `clamp(min, value, max)`、`min(...)` 与
39
- `max(...)` 在计算样式中保留参数,布局时结合元素字号与视口(`vw`/`vh`)求值;
40
- 桌面窗口中 `env(safe-area-inset-*)` 解析为 0。`var(--name[, fallback])` 在
43
+ - **行内级容器、自动外边距与宽高比** —— `display: inline-flex`/`inline-grid`
44
+ 仍用 flex/grid 算法排列子元素,但作为单个原子行内盒参与父级行流
45
+ (`vertical-align` 的长度值会平移它)。块级盒的 `margin-left: auto` /
46
+ `margin-right: auto` 会吸收行内剩余空间(双 auto 则居中);flex 项主轴的 auto
47
+ 外边距在 `justify-content` 之前吸收该行剩余空间,因此可把页脚或按钮提示推到
48
+ 末端。`aspect-ratio` 在 `height: auto` 时由已用宽度推导高度(反向的由高推宽
49
+ 未实现)。`border-radius` 支持百分比,并按 border box 较小边解析。
50
+ - **竖排书写模式** —— `writing-mode: vertical-rl|vertical-lr` 按每列一个字符
51
+ 步进排字:CJK/假名/谚文字符保持直立,其他文字在绘制时旋转 90°。高度确定时
52
+ 另起一列;flex 容器的 row/column 轴随书写模式互换。这是面向文本的实现:
53
+ 竖排容器中的嵌套块与横竖混排内容仍回退为横排。
54
+ - **CSS 值函数与自定义属性** —— `calc()` 算术(含嵌套 `calc()` 以及其它函数
55
+ 内部的 `calc()`)、`clamp(min, value, max)`、`min(...)` 与 `max(...)` 在计算
56
+ 样式中保留表达式,布局时结合元素字号与视口(`vw`/`vh`)求值;桌面窗口中
57
+ `env(safe-area-inset-*)` 解析为 0。`var(--name[, fallback])` 在
41
58
  层叠期间从继承的自定义属性替换。`inset` 是四值 `top/right/bottom/left`
42
59
  简写;`box-sizing: border-box` 会从显式宽高里扣除 padding 与边框;
43
60
  `visibility: hidden` 跳过绘制与命中测试;`pointer-events: none` 使盒子不接收输入
@@ -63,15 +63,22 @@
63
63
 
64
64
  窗口背景(`setBackground`)是渲染通道的清除色。
65
65
 
66
+ `url()` 图层上的 `background-size` 会按平铺尺寸为每块生成一个图像四边形
67
+ (默认 `repeat`,上限 4096 块),而不是把图片拉伸到整个盒子;百分比尺寸相对
68
+ 盒子解析,`auto` 保留盒子的对应尺寸。`outline` 在边框之后、内容之前绘制边框盒
69
+ 之外的四条矩形(含 offset)。竖排文本通过把字形四边形旋转 90° 绘制
70
+ (CJK 字形保持直立并在列内居中)。
71
+
66
72
  ### 背景、生成内容与特效(M19)
67
73
 
68
74
  `background-color` 与每一条 `background-image` 层都会绘制。逗号分隔的层列表按
69
75
  从底到上的顺序绘制(声明中的第一层在最上面),两种渐变均受支持:
70
76
 
71
- - **线性**渐变在 18 个位置采样(重复渐变 64 个)并输出为纯色矩形;支持
77
+ - **线性**渐变沿 CSS 渐变线(过盒子中心、按声明角度,盒子在该方向上的投影即
78
+ 渐变线长度,因此百分比与 `calc()` 内的百分比都相对该长度解算)逐像素解算,
79
+ 与径向渐变相同。渐变只沿线变化,所以纹理只有一行(或一列)设备像素;支持
72
80
  `to <side>`、`deg`/`grad`/`rad`/`turn`、百分比或长度(`px`/`em`/…)偏移、
73
- `color <start> <end>` 简写与多色标(色带沿最近轴行走,因此斜角会退化为
74
- 最近的轴向);
81
+ `color <start> <end>` 简写与多色标;
75
82
  - **径向**渐变由 `renderer_gradient.cpp` 逐像素解算成一张 RGBA 纹理,作为单个
76
83
  图像四边形绘制(`PaintGradient`)。若沿用嵌套色带,由于每条色带都会覆盖整个
77
84
  内部区域,alpha 会叠加多次——淡出到 `transparent` 的暗角会把整个场景涂成实心
@@ -80,21 +87,39 @@
80
87
  的椭圆保持盒子的宽高比并缩放到刚好接触对应边或角,与 CSS 一致。色标在预乘
81
88
  alpha 空间中插值,最后一个色标会延伸覆盖结束形状之外的剩余盒子。
82
89
 
90
+ 两种渐变共用这条逐像素路径:用一串纯色矩形绘制时,半透明色标会逐条叠加 alpha,
91
+ 而且宽度小于一条色带的色标无法表达——`transparent … calc(100% - 1px),
92
+ rgba(…) calc(100% - 1px)` 这样的 1px 分割线要么落在两次采样之间(直接消失),
93
+ 要么被撑成一整条色带。`calc()` 给出的色标位置会保留相对渐变线的百分比,解算后
94
+ 为负的偏移也停留在声明的位置,而不会被当成「未声明偏移」重新分配。
95
+
83
96
  `repeating-linear-gradient()` 与 `repeating-radial-gradient()` 会在整个盒子上
84
97
  从 0 平铺到最后一个色标的偏移量;色标列表不会被强制结束在 100%,因此
85
98
  `red 0%, blue 10%` 每 10% 重复一次。普通渐变的最后一个色标若小于 100%,则剩余
86
99
  区域保持该颜色,与 CSS 一致。
87
100
 
88
- 背景色位于所有层下方。若渐变色标带透明度,渐变也会半透明;径向渐变逐像素精确,
89
- 线性渐变仍采样 18 条色带,较大的柔和渐变可能出现条带。
101
+ 背景色位于所有层下方。若渐变色标带透明度,渐变也会半透明;两种渐变都逐像素精确,
102
+ 因此硬色标与 1px 细节保持锐利,不再出现条带。
90
103
 
91
104
  **生成内容**不需要新的显示列表类型:`::before`/`::after` 是 `style.cpp` 中
92
105
  每次重算样式时重建的普通布局盒,因此其背景、边框、文本与 transform 都走同一条
93
106
  绘制路径。`content` 支持字符串、`attr()` 与 open/close quote。
94
107
 
95
- **`text-shadow`** 会作为阴影 `PaintText` run 排在真实 run 之前,因此文本管线
96
- 无需新 shader 就能绘制。带模糊的阴影是围绕偏移量展开的九个半透明副本(与
97
- `filter: blur()` 相同的近似)。
108
+ **`text-shadow`** 在不带模糊时作为阴影 `PaintText` run 排在真实 run 之前,文本
109
+ 管线无需新 shader 就能清晰绘制。带模糊的阴影是真正的高斯:`paint_shadow.cpp`
110
+ 光栅化字形覆盖度,用可分离核做卷积(`sigma = blur / 2`,截断在 ±3σ),再发出
111
+ 一个带色调的图像四边形。掩码按 文本/字体/模糊 缓存,因此动画颜色只改变色调。
112
+
113
+ **`box-shadow`** 使用同一套高斯掩码:圆角矩形轮廓被光栅化(spread 扩张或收缩)、
114
+ 卷积,再合成到背景之后;`inset` 阴影反转掩码、裁剪到 padding box 并绘制在背景
115
+ 之上。零模糊的外阴影仍使用精确的圆角矩形。声明列表按最后一层最先绘制,与 CSS
116
+ 一致。
117
+
118
+ **`backdrop-filter`** 与 `mix-blend-mode` 共用离屏机制:绘制器到达该盒子时,
119
+ 渲染器把此前场景复制到暂存纹理,用全屏 shader 应用高斯和一个合并的颜色矩阵
120
+ (brightness/contrast/grayscale/sepia/invert/saturate/hue-rotate/opacity 相乘),
121
+ 再通过盒子的圆角边框盒掩码合成回来。因此 `backdrop-filter` 在 `paintList()` 中
122
+ 显示为 `backdrop …` op。
98
123
 
99
124
  **`clip-path`** 在 `paint_clip.cpp` 中解析为设备空间多边形(每个点都应用盒子的
100
125
  transform),用耳切法三角化,并存入 `DisplayList::clip_paths`。每个 op 的
@@ -107,8 +132,8 @@ shader 改动,但边缘是硬边(没有解析抗锯齿)。支持两层嵌
107
132
  `invert`、`saturate`、`hue-rotate`、`opacity`)会折进每个绘制颜色,因此盒子与
108
133
  其后代都会看到滤镜。`blur()` 与 `drop-shadow()` 是显示列表层面的近似:模糊把每个
109
134
  图形展开成九个半透明副本,投影用盒子/文字轮廓。这样普通绘制保持单渲染通道,代价
110
- 是组语义:被模糊/滤镜的组内重叠后代可能加深;`backdrop-filter` 无法采样元素背后
111
- 的内容,因此会解析并参与层叠,但不绘制。
135
+ 是组语义:被模糊/滤镜的组内重叠后代可能加深。`backdrop-filter` 是例外,它有
136
+ 自己的离屏快照/滤镜通道(见上)。
112
137
 
113
138
  **`mix-blend-mode`** 是唯一真正需要离屏图层的例外。非 `normal` 的盒子会被
114
139
  `LayerBegin`/`LayerEnd` 标记包住(在 `paintList()` 中显示为 `layer blend=…` /
@@ -120,26 +145,41 @@ shader 合成到场景上。全部十六种 CSS 模式(`multiply`、`screen`
120
145
  再由 SPIRV-Cross 交叉编译为 MSL(Metal);每个嵌套层级的两张场景纹理跨帧复用,
121
146
  并通过 ping-pong 让背景在写入时仍可采样。既不吃 SPIR-V 也不吃 MSL 的后端
122
147
  (这里的 D3D12/DXIL 路径没有 shader 编译器)仍用普通 source-over 混合绘制该组。
123
- `backdrop-filter` 依旧不绘制。
148
+ `backdrop-filter` 使用同一条运行时编译路径;shader 后端不可用时会跳过它,页面
149
+ 其余内容仍照常绘制。
124
150
 
125
151
  ## 文本技术栈(M4b)
126
152
 
127
153
  `runtime/ext_gui/text.{h,cpp}` 是前端,通过一个小型后端表分发。默认后端是内置的
128
154
  **stb_truetype**(`text_stb.cpp`、`runtime/ext_gui/stb_truetype.h`)。可选的
129
155
  **`shaping`** 链接特性会安装 `text_hb.cpp`(HarfBuzz + FreeType 后端),非 ASCII
130
- 文本会自动选中它。
156
+ 文本或作者的 `@font-face` 规则会自动选中它。两个后端共享 `text_font_db.cpp` 中的
157
+ 字体数据库(发现与索引)、`text_fonts.cpp` 中的 CSS 匹配器和
158
+ `text_font_sfnt.cpp` 中的 sfnt 元数据读取器,因此同一个 CSS 样式无论链接哪个整形
159
+ 后端都会解析到同一个真实字面。
131
160
 
132
- - 字体从常见系统路径解析(macOS 上的 Helvetica/Arial、Linux 上的
133
- DejaVu/Liberation、Windows 上的 Segoe UI/Arial),可用 `XT_GUI_FONT`
134
- (以及 `XT_GUI_FONT_MONO`)覆盖,并按 `(字体类别, 字号)` 缓存。目前只使用
135
- 常规直立字面;字重/斜体选择是后续细化。
161
+ - 字体从已安装的系统字面解析。`font-family` 列表按大小写不敏感匹配;
162
+ `sans-serif`、`serif`、`monospace`(以及 `system-ui`/`ui-*` 别名)映射到平台
163
+ 默认族,其他族名会一次性扫描平台字体目录。`font-weight`(1–1000)和
164
+ `font-style: italic` 按 CSS Fonts 的字重匹配顺序选择该族真实的粗体/斜体字面;
165
+ 缺少对应字重时选择最接近的字面,族没有斜体字面时使用其直立字面——不做合成
166
+ 粗体或斜体。字面按 `(文件, 集合索引, 字号)` 缓存。`XT_GUI_FONT` /
167
+ `XT_GUI_FONT_MONO` 仍可覆盖通用族的默认值。
168
+ - 支持作者 **`@font-face`**:加载本地 sfnt 字面(`.ttf`、`.ttc`、`.otf`、
169
+ `.otc`)。`src` 列表从左到右尝试;`local(...)`、远程 URL 和 `data:` 条目会被
170
+ 跳过。相对 URL 按本地 `file:` 路径处理,相对进程工作目录解析(与图片资源的
171
+ 策略一致);也接受 `file://` URL(包括百分号编码路径)。`font-weight` /
172
+ `font-style` 描述符选择字面,支持 `font-weight: 100 900` 这类范围;同族同
173
+ 描述符的后一条规则覆盖前一条。CSS 匹配声明中的族名,即使它与字体文件内部的
174
+ 族名不同。所有来源都不可读时,该族会继续回退到 CSS 列表中的下一个族。
136
175
  - 默认候选优先选择 **CJK 覆盖的 TrueType/TTC** 字面(Windows 的 Microsoft
137
176
  YaHei、macOS 的 PingFang/STHeiti、Linux 的 Noto Sans CJK/WenQuanYi),
138
- 再是纯拉丁字面。stb_truetype 无法解析的候选(例如 CFF/OTF 集合)会被跳过,
139
- 继续尝试下一个。
177
+ 再是纯拉丁字面。同一族的字重/样式变体会跨文件解析;stb_truetype 无法解析的
178
+ 候选(例如 CFF/OTF 集合)会被跳过,`shaping` 后端则会加载它。
140
179
  - **主字面缺少的码点会回退到其他字面。** 引擎维护一条小的回退链——先平台的
141
180
  符号字体(Segoe UI Symbol、Apple Symbols、Noto Sans Symbols、DejaVu Sans),
142
- 再是另一字体类别——并为每个码点解析出回退链中第一个拥有该字形的字面。
181
+ 再是默认的 sans、serif 和 monospace 字面——并为每个码点解析出回退链中第一个
182
+ 拥有该字形的字面。
143
183
  run 以每个字面一段的方式整形,因此混合 run(拉丁文加箭头、制表符或几何图形,
144
184
  或 CJK 中的拉丁文)会绘制每个字形,而不是把缺失的字形当作 `.notdef` 丢弃。
145
185
  每个整形后的字形携带自己的字面(`ShapedGlyph::font`),渲染器据此从正确的
@@ -178,7 +218,8 @@ DOM-only 二进制约 3 MiB。可选的 **`shaping`** 链接特性会安装 Harf
178
218
  `(face, text) -> 字形 + 前进宽度`。它使用两代、每代最多 2 MiB 字形 / 8,192 条
179
219
  条目;某一代写满时丢弃**备用**代并切换过去,因此热路径上没有 LRU 记账。
180
220
  一个词只整形一次,之后每次布局和重绘都复用它。主字体查找也按
181
- (字体族类别,字号)缓存。
221
+ (字体族、字重、样式、字号)缓存;字体数据库本身只在第一次遇到非通用族名时
222
+ 扫描一次平台目录。
182
223
  - **空写不生效。** 当节点已经显示该值时,`textContent` 与 `style.<属性>` 不做任何
183
224
  事:文档不会被标脏,也就没有重算样式、重新布局、重绘或上传。因此每帧都写入相同
184
225
  计时文本、相同血条宽度或相同 transform 的 HUD 不产生任何开销 —— 在此之前,每次
package/doc/zh-CN/gui.md CHANGED
@@ -2,7 +2,7 @@
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** 真正播放
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
6
  `<video>`/`<audio>`——可选的 **`media` 链接特性**用 vendored 的最小 FFmpeg 在主线程解码,
7
7
  视频帧走图像纹理路径、音频进入 SDL 流,DOM 媒体 API 暴露
8
8
  `play`/`pause`/`currentTime`/… 与媒体事件。编译器会从程序里判断是否需要它
@@ -14,7 +14,7 @@
14
14
  渲染器,因此盒子(以及其中的文本、图像与子元素)可以被平移、缩放与旋转而
15
15
  不触发任何重排——再加上 `transition: transform`,这些姿态可以动画(自绘的示例
16
16
  `examples/gui/pelican-svg` 正是用它让矢量画动起来)。参见
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` 颜色/阴影效果。`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)。本总览记录了锁定决策、架构、里程碑计划与当前进度;引擎本身按功能拆分在以上各页中。
18
18
 
19
19
  ## 目录
20
20
 
@@ -127,8 +127,10 @@
127
127
  - **M4a — 显示列表 + GPU 图形** ✅ 背景/边框显示列表、圆角矩形 SDL_GPU
128
128
  管线(`paint.*`、`renderer.*`)。
129
129
  - **M4b-1 — 文本技术栈 + 度量** ✅ 文本模块(`text.*`)与字体解析/缓存、
130
- 供布局使用的基于整形的文本度量(`measureText`/`fontMetrics`);最初用
131
- HarfBuzz + FreeType,现在用内置 stb_truetype。
130
+ 供布局使用的基于整形的文本度量(`measureText`/`fontMetrics`);CSS 族名列表、
131
+ 字重和斜体样式会选择真实安装的字面,作者的 `@font-face` 规则会加载本地字体
132
+ 文件(相对 URL 按 `file:` 路径相对进程工作目录解析)。最初用 HarfBuzz +
133
+ FreeType,现在用内置 stb_truetype。
132
134
  - **M4b-2 — 字形渲染** ✅ stb_truetype 光栅化、共享的 shelf 打包字形图集、
133
135
  显示列表中的带纹理文本四边形与感知 HiDPI 的光栅缩放。渐变仍待完成。
134
136
  5. **M5 — 输入 + 事件** ✅
@@ -140,9 +142,9 @@
140
142
  - **M6a — 图像** ✅ stb_image 解码(位图)与 NanoSVG 光栅化(SVG 文件,按
141
143
  绘制像素尺寸)、`<img>` 替换元素布局、按文件 GPU 纹理与带纹理四边形
142
144
  (`image.*`、`svg.*`、`paint.*`、`renderer.*`)。
143
- - **M6b — transition/动画** ✅ `transition` 简写 + 长写、对
144
- `background-color`/`color`/`border-color`/`border-radius` 的动画、重定向
145
- 与 `win.advance(ms)`。`@keyframes` 仍待完成。
145
+ - **M6b — transition/动画** ✅ `transition` 简写 + 长写、重定向、
146
+ `cubic-bezier()`/`steps()`、`win.advance(ms)` 与(M21)`@keyframes`
147
+ 及全部动画长写。
146
148
  7. **M7 — CI 与发布** ✅(Linux/macOS 构建) / 🚧(运行 + Windows arm64)
147
149
  - `compile-examples` 在 Linux 与 macOS 上构建 `gui.a` 与 `media.a`(必需),
148
150
  然后在 Linux 上的 Xvfb + lavapipe 下运行示例与 `tests/e2e/gui-*.test.ts`
@@ -273,15 +275,16 @@
273
275
  特性);`matchMedia()` 使用同一个求值器。
274
276
  - 生成内容:`::before`/`::after` 选择器、继承的伪样式、`content` 字符串/
275
277
  转义/`attr()`,以及不会泄漏到 DOM 遍历的合成布局盒。
276
- - 多层 `background-image`:线性渐变自下而上按纯色带绘制(18 条),径向渐变
277
- 逐像素光栅化成一张图像纹理(`renderer_gradient.cpp`)。
278
- - `text-shadow` 作为阴影文本 run,并带九次采样模糊近似。
278
+ - 多层 `background-image`:线性与径向渐变都逐像素光栅化成一张图像纹理
279
+ (`renderer_gradient.cpp`),硬色标与亚像素细节(1px 分割线)因此能精确绘制。
280
+ - `text-shadow` 与 `box-shadow`(外/内阴影、spread、多层)用形状掩码上的
281
+ 真正可分离高斯模糊(M21)。
279
282
  - `clip-path` 多边形(`inset`/`circle`/`ellipse`/`polygon`)解析到设备空间,
280
283
  用耳切法三角化,并在 `renderer_clip.cpp` 中与图形几何求交——不需要模板/
281
284
  shader 改动。
282
285
  - `filter` 颜色函数、九次采样 `blur()` 与轮廓 `drop-shadow()`;
283
- `backdrop-filter` 会解析并参与层叠,但需要离屏背景通道(见
284
- [已知问题](./gui/known-issues.md))。
286
+ `backdrop-filter` 对场景做快照、应用高斯与一个合并颜色矩阵,再通过
287
+ 边框盒掩码合成(M21)。
285
288
  - e2e 覆盖见 `tests/e2e/gui-effects.test.ts`;参见
286
289
  [绘制](./gui/rendering.md#背景生成内容与特效m19)。
287
290
  20. **M20 — 渲染性能** ✅
@@ -294,6 +297,27 @@
294
297
  冗余的渲染状态绑定。
295
298
  - `win.stats()`/`XT_GUI_PERF` 计数器与 `tests/e2e/gui-perf.test.ts`;参见
296
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`。
297
321
 
298
322
  ## 进度日志
299
323
 
@@ -423,9 +447,23 @@
423
447
  `gradient_values.cpp` 建模背景层列表(线性/径向、形状/尺寸/位置/色标),
424
448
  `paint_background.cpp` 解算并绘制它(径向层变成一个 `PaintGradient`,由
425
449
  `renderer_gradient.cpp` 光栅化成纹理),`paint_effects.cpp` 应用滤镜颜色函数
426
- 与九次采样模糊/投影并生成 `text-shadow` run,`paint_clip.cpp`/
427
- `renderer_clip.cpp` 对三角化的 `clip-path` 多边形裁剪几何。e2e 见
428
- `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`。
429
467
  - **M20** ✅ 渲染性能。`@media` 每次重算样式时每条规则只求值一次;没有
430
468
  `::before`/`::after` 的样式表完全跳过生成内容;只有样式表包含
431
469
  `:hover`/`:focus` 规则时才因这两个状态重算样式(`style.cpp`、
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xbintsc",
3
- "version": "0.5.51",
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.51",
76
- "@xbintsc/linux-arm64": "0.5.51",
77
- "@xbintsc/darwin-x64": "0.5.51",
78
- "@xbintsc/darwin-arm64": "0.5.51",
79
- "@xbintsc/win32-x64": "0.5.51",
80
- "@xbintsc/win32-arm64": "0.5.51"
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"