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
@@ -136,6 +136,18 @@ static bool substituteVariables(const std::string &value,
136
136
  }
137
137
 
138
138
  void finalizeStyle(XtStyle *style, const ApplyContext &context) {
139
+ /* `letter-spacing` is resolved against the element's own font size. */
140
+ if (style->letter_spacing.unit != Unit::Px && style->letter_spacing.unit != Unit::Number) {
141
+ style->letter_spacing.value = xt_style_resolve_absolute(
142
+ style->letter_spacing, style->font_size, style->font_size, context.rootFontSize,
143
+ context.viewportWidth, context.viewportHeight);
144
+ style->letter_spacing.unit = Unit::Px;
145
+ }
146
+ /* `outline-color` defaults to the element's `color`. */
147
+ if (style->outline_style != BorderStyle::None && !style->has_outline_color) {
148
+ style->outline_color = style->color;
149
+ style->has_outline_color = true;
150
+ }
139
151
  switch (style->line_height.unit) {
140
152
  case Unit::Em:
141
153
  case Unit::Percent:
@@ -145,6 +157,7 @@ void finalizeStyle(XtStyle *style, const ApplyContext &context) {
145
157
  case Unit::Clamp:
146
158
  case Unit::Min:
147
159
  case Unit::Max:
160
+ case Unit::Calc:
148
161
  style->line_height.value = xt_style_resolve_absolute(
149
162
  style->line_height, style->font_size, style->font_size, context.rootFontSize,
150
163
  context.viewportWidth, context.viewportHeight);
@@ -172,6 +185,11 @@ void inheritFrom(XtStyle *style, const XtStyle *parent) {
172
185
  style->text_align = parent->text_align;
173
186
  style->white_space = parent->white_space;
174
187
  style->text_shadow = parent->text_shadow;
188
+ style->text_transform = parent->text_transform;
189
+ style->letter_spacing = parent->letter_spacing;
190
+ style->font_variant_numeric = parent->font_variant_numeric;
191
+ style->writing_mode = parent->writing_mode;
192
+ style->cursor = parent->cursor;
175
193
  }
176
194
 
177
195
  void applyCandidates(XtStyle *style, std::vector<Candidate> *candidates,
@@ -187,15 +205,43 @@ void applyCandidates(XtStyle *style, std::vector<Candidate> *candidates,
187
205
  style->variables[candidate.declaration->property] = candidate.declaration->value;
188
206
  }
189
207
  }
190
- for (const Candidate &candidate : *candidates) {
191
- if (isCustomProperty(candidate.declaration->property)) continue;
192
- std::string value = candidate.declaration->value;
193
- if (value.find("var(") != std::string::npos) {
208
+ auto resolvedValue = [&](const Candidate &candidate, std::string *out) {
209
+ *out = candidate.declaration->value;
210
+ if (out->find("var(") != std::string::npos) {
194
211
  std::string resolved;
195
- if (!substituteVariables(value, style->variables, 0, &resolved)) continue;
196
- value = resolved;
212
+ if (!substituteVariables(*out, style->variables, 0, &resolved)) return false;
213
+ *out = resolved;
214
+ }
215
+ return true;
216
+ };
217
+ /* `color` is computed before the other declarations: `currentColor` in them
218
+ * refers to this element's final colour regardless of source order. */
219
+ for (const Candidate &candidate : *candidates) {
220
+ if (isCustomProperty(candidate.declaration->property) ||
221
+ candidate.declaration->property != "color") {
222
+ continue;
223
+ }
224
+ std::string value;
225
+ if (!resolvedValue(candidate, &value)) continue;
226
+ if (value == "inherit") {
227
+ xt_style_apply_inherit(style, nodeContext.parent, "color");
228
+ continue;
197
229
  }
198
- xt_style_apply_declaration(style, candidate.declaration->property, value, nodeContext);
230
+ xt_style_apply_declaration(style, "color", value, nodeContext);
231
+ }
232
+ for (const Candidate &candidate : *candidates) {
233
+ const std::string &property = candidate.declaration->property;
234
+ if (isCustomProperty(property) || property == "color") continue;
235
+ std::string value;
236
+ if (!resolvedValue(candidate, &value)) continue;
237
+ if (value == "inherit") {
238
+ xt_style_apply_inherit(style, nodeContext.parent, property);
239
+ continue;
240
+ }
241
+ if (xt_css_lower(value).find("current") != std::string::npos) {
242
+ value = xt_style_replace_current_color(value, style->color);
243
+ }
244
+ xt_style_apply_declaration(style, property, value, nodeContext);
199
245
  }
200
246
  }
201
247
 
@@ -348,6 +394,7 @@ struct ComputeScratch {
348
394
  * `style="transition: …"`), so the document knows whether it needs the
349
395
  * transition snapshot path at all. */
350
396
  bool has_transitions = false;
397
+ bool has_animations = false;
351
398
  };
352
399
 
353
400
  void pushCandidates(std::vector<Candidate> *candidates, ComputeScratch *scratch,
@@ -377,6 +424,7 @@ void computePseudo(const Node *origin, PseudoElement side, const XtStyle &origin
377
424
 
378
425
  ApplyContext nodeContext = context;
379
426
  nodeContext.parentFontSize = originStyle.font_size;
427
+ nodeContext.parent = &originStyle;
380
428
 
381
429
  std::string pseudoName = side == PseudoElement::Before ? "before" : "after";
382
430
  std::vector<Candidate> &candidates = scratch->candidates;
@@ -402,6 +450,7 @@ void computePseudo(const Node *origin, PseudoElement side, const XtStyle &origin
402
450
  applyCandidates(&style, &candidates, nodeContext);
403
451
  finalizeStyle(&style, nodeContext);
404
452
  if (!style.transitions.empty()) scratch->has_transitions = true;
453
+ if (!style.animations.empty()) scratch->has_animations = true;
405
454
 
406
455
  bool generated = false;
407
456
  std::string text;
@@ -428,6 +477,7 @@ void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &shee
428
477
 
429
478
  ApplyContext nodeContext = context;
430
479
  nodeContext.parentFontSize = (parent != nullptr) ? parent->font_size : context.rootFontSize;
480
+ nodeContext.parent = parent;
431
481
 
432
482
  std::vector<Candidate> &candidates = scratch->candidates;
433
483
  candidates.clear();
@@ -455,6 +505,7 @@ void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &shee
455
505
  applyCandidates(&style, &candidates, nodeContext);
456
506
  finalizeStyle(&style, nodeContext);
457
507
  if (!style.transitions.empty()) scratch->has_transitions = true;
508
+ if (!style.animations.empty()) scratch->has_animations = true;
458
509
 
459
510
  /* `emplace` keeps the address of an existing entry stable, which is what
460
511
  * `box->style` pointers rely on. */
@@ -479,7 +530,7 @@ void computeNode(const Node *node, const XtStyle *parent, const StyleSheet &shee
479
530
  void xt_style_compute(const Node *root, const StyleSheet &sheet,
480
531
  std::unordered_map<const Node *, XtStyle> &out, float viewportWidth,
481
532
  float viewportHeight, const MatchState &state, PseudoNodeSet *pseudos,
482
- bool *hasTransitions) {
533
+ bool *hasTransitions, bool *hasAnimations) {
483
534
  out.clear();
484
535
  bool wantsPseudo = pseudos != nullptr && sheet.has_pseudo_elements;
485
536
  if (wantsPseudo && root != nullptr) {
@@ -512,6 +563,9 @@ void xt_style_compute(const Node *root, const StyleSheet &sheet,
512
563
  if (hasTransitions != nullptr) {
513
564
  *hasTransitions = *hasTransitions || scratch.has_transitions;
514
565
  }
566
+ if (hasAnimations != nullptr) {
567
+ *hasAnimations = *hasAnimations || scratch.has_animations;
568
+ }
515
569
  }
516
570
 
517
571
  } // namespace xtgui
@@ -10,6 +10,7 @@
10
10
  #ifndef XT_GUI_STYLE_H
11
11
  #define XT_GUI_STYLE_H
12
12
 
13
+ #include <map>
13
14
  #include <memory>
14
15
  #include <string>
15
16
  #include <unordered_map>
@@ -36,7 +37,64 @@ enum class WhiteSpace { Normal, NoWrap, Pre, PreWrap, PreLine };
36
37
  enum class Overflow { Visible, Hidden, Scroll, Auto };
37
38
  enum class BorderStyle { None, Solid, Dashed, Dotted };
38
39
  enum class FontStyle { Normal, Italic };
39
- enum class TimingFunction { Linear, Ease, EaseIn, EaseOut, EaseInOut };
40
+
41
+ /** CSS `text-transform`; only plain ASCII case mapping is applied. */
42
+ enum class TextTransform { None, Uppercase, Lowercase, Capitalize };
43
+
44
+ /** CSS `writing-mode`. Only text-level vertical layout is implemented. */
45
+ enum class WritingMode { HorizontalTB, VerticalRL, VerticalLR };
46
+
47
+ /** CSS `cursor` values the engine maps to an SDL system cursor. */
48
+ enum class CursorShape { Auto, Default, Pointer, Text, Move, Wait, Help, NotAllowed, Crosshair };
49
+
50
+ /** Apply a computed `text-transform` to a text node's UTF-8 contents. */
51
+ std::string xt_apply_text_transform(const std::string &text, TextTransform transform);
52
+
53
+ /** Resolve a `border-radius` against a border box (percent uses min(w, h)). */
54
+ float xt_style_border_radius_px(const Length &radius, float width, float height, float em,
55
+ float root, float vw, float vh);
56
+
57
+ /** One `background-size` entry for an image layer. */
58
+ struct BackgroundSize {
59
+ Length width{0.0f, Unit::Auto};
60
+ Length height{0.0f, Unit::Auto};
61
+ };
62
+
63
+ /** A CSS timing function. `Ease`/`Linear`/… are the keywords; `CubicBezier` and
64
+ * the two `Steps` kinds carry their parameters. */
65
+ struct TimingFunction {
66
+ enum class Kind {
67
+ Linear,
68
+ Ease,
69
+ EaseIn,
70
+ EaseOut,
71
+ EaseInOut,
72
+ CubicBezier,
73
+ StepsStart,
74
+ StepsEnd
75
+ };
76
+ Kind kind = Kind::Ease;
77
+ float x1 = 0.25f;
78
+ float y1 = 0.1f;
79
+ float x2 = 0.25f;
80
+ float y2 = 1.0f;
81
+ int steps = 1;
82
+ };
83
+
84
+ enum class AnimationDirection { Normal, Reverse, Alternate, AlternateReverse };
85
+ enum class AnimationFillMode { None, Forwards, Backwards, Both };
86
+
87
+ /** One `animation` entry: a `@keyframes` name plus how it runs. */
88
+ struct AnimationSpec {
89
+ std::string name = "none";
90
+ float duration = 0.0f; // seconds
91
+ float delay = 0.0f; // seconds
92
+ float iterations = 1.0f; // `infinite` is stored as -1
93
+ TimingFunction timing;
94
+ AnimationDirection direction = AnimationDirection::Normal;
95
+ AnimationFillMode fill = AnimationFillMode::None;
96
+ bool paused = false;
97
+ };
40
98
 
41
99
  /** CSS `mix-blend-mode`. The order matches the composite shader's `u_mode`
42
100
  * enum and the keyword table in `style_values.cpp`. */
@@ -64,7 +122,7 @@ struct TransitionSpec {
64
122
  std::string property = "all";
65
123
  float duration = 0.0f; // seconds
66
124
  float delay = 0.0f; // seconds
67
- TimingFunction timing = TimingFunction::Ease;
125
+ TimingFunction timing;
68
126
  };
69
127
 
70
128
  /** One track of a `grid-template-columns` / `grid-template-rows` list.
@@ -161,6 +219,17 @@ struct TextShadow {
161
219
  bool has_color = false;
162
220
  };
163
221
 
222
+ /** One `box-shadow` entry (`inset` keyword, spread included). */
223
+ struct BoxShadow {
224
+ Length dx;
225
+ Length dy;
226
+ Length blur;
227
+ Length spread;
228
+ Color color; // `has_color == false` means currentColor
229
+ bool has_color = false;
230
+ bool inset = false;
231
+ };
232
+
164
233
  struct Edges {
165
234
  Length top;
166
235
  Length right;
@@ -178,12 +247,20 @@ struct XtStyle {
178
247
  std::unordered_map<std::string, std::string> variables;
179
248
  Position position = Position::Static;
180
249
  Edges margin;
250
+ /** Which margins were explicitly declared `auto` (the `Edges` default unit is
251
+ * Auto, so the raw value cannot distinguish "unset" from "auto"). */
252
+ bool margin_left_auto = false;
253
+ bool margin_right_auto = false;
254
+ bool margin_top_auto = false;
255
+ bool margin_bottom_auto = false;
181
256
  Edges padding;
182
257
  Edges border_width;
183
258
  BorderStyle border_style = BorderStyle::None;
184
259
  Color border_color;
185
260
  bool has_border_color = false;
186
- float border_radius = 0.0f;
261
+ /** `border-radius` stays symbolic so a `50%` radius can resolve against the
262
+ * box in layout (the engine has one radius, so percentages use min(w, h)). */
263
+ Length border_radius{0.0f, Unit::Px};
187
264
 
188
265
  Length width;
189
266
  Length height;
@@ -203,6 +280,8 @@ struct XtStyle {
203
280
  bool has_background = false;
204
281
  /** `background-image` layers, in declaration order (first = topmost). */
205
282
  std::vector<BackgroundLayer> background_layers;
283
+ /** Per-layer `background-size`; empty keeps the historical stretch. */
284
+ std::vector<BackgroundSize> background_sizes;
206
285
  /** `clip-path`; `Type::None` leaves the box unclipped. */
207
286
  ClipPathSpec clip_path;
208
287
  /** `filter` / `backdrop-filter` function lists (not inherited). */
@@ -210,6 +289,8 @@ struct XtStyle {
210
289
  std::vector<FilterOp> backdrop_filters;
211
290
  /** `text-shadow` entries (inherited, as in CSS). */
212
291
  std::vector<TextShadow> text_shadow;
292
+ /** `box-shadow` entries (not inherited); declaration order = topmost first. */
293
+ std::vector<BoxShadow> box_shadow;
213
294
  /** Generated `content` of a `::before`/`::after` box. The cascade resolves
214
295
  * `attr()` and string escapes before layout; `has_content` distinguishes
215
296
  * `content: ""` from no generated box. */
@@ -224,6 +305,14 @@ struct XtStyle {
224
305
 
225
306
  TextAlign text_align = TextAlign::Start; // inherited
226
307
  WhiteSpace white_space = WhiteSpace::Normal; // inherited
308
+ TextTransform text_transform = TextTransform::None; // inherited
309
+ /** Resolved to pixels in the cascade (em is the element's own font size). */
310
+ Length letter_spacing{0.0f, Unit::Px}; // inherited
311
+ bool font_variant_numeric = false; // inherited; accepted, no OpenType features
312
+ WritingMode writing_mode = WritingMode::HorizontalTB; // inherited
313
+ Length vertical_align; // not inherited; atomic inline boxes only
314
+ /** `aspect-ratio` as width / height, or 0 when unset. */
315
+ float aspect_ratio = 0.0f;
227
316
 
228
317
  FlexDirection flex_direction = FlexDirection::Row;
229
318
  FlexWrap flex_wrap = FlexWrap::NoWrap;
@@ -251,6 +340,21 @@ struct XtStyle {
251
340
  Length flex_basis;
252
341
 
253
342
  float opacity = 1.0f;
343
+ /** `display: inline-flex` / `inline-grid`: a flex/grid container that is
344
+ * still inline-level (laid out atomically by its line box). */
345
+ bool inline_level = false;
346
+ /** `isolation`: parsed for compatibility; group isolation is not modelled. */
347
+ bool isolation = false;
348
+ CursorShape cursor = CursorShape::Auto;
349
+ /** `outline` (painted outside the border box). */
350
+ Length outline_width;
351
+ BorderStyle outline_style = BorderStyle::None;
352
+ Color outline_color;
353
+ bool has_outline_color = false;
354
+ Length outline_offset;
355
+ /** Computed SVG presentation properties (`fill`, `stroke`, …) injected into
356
+ * serialized inline `<svg>` markup. Only populated on SVG elements. */
357
+ std::map<std::string, std::string> svg_props;
254
358
  /** `mix-blend-mode` (not inherited: it creates a compositing group). */
255
359
  MixBlendMode mix_blend_mode = MixBlendMode::Normal;
256
360
  Overflow overflow = Overflow::Visible;
@@ -268,6 +372,8 @@ struct XtStyle {
268
372
 
269
373
  /** `transition` specs; not inherited. Applied by `XtDocument`. */
270
374
  std::vector<TransitionSpec> transitions;
375
+ /** `animation` specs; not inherited. Applied by `XtDocument`. */
376
+ std::vector<AnimationSpec> animations;
271
377
  };
272
378
 
273
379
  /** Initial (non-inherited) style; inherited fields are overridden from the parent. */
@@ -297,7 +403,8 @@ class PseudoNodeSet {
297
403
  void xt_style_compute(const Node *root, const StyleSheet &sheet,
298
404
  std::unordered_map<const Node *, XtStyle> &out, float viewportWidth,
299
405
  float viewportHeight, const MatchState &state = MatchState(),
300
- PseudoNodeSet *pseudos = nullptr, bool *hasTransitions = nullptr);
406
+ PseudoNodeSet *pseudos = nullptr, bool *hasTransitions = nullptr,
407
+ bool *hasAnimations = nullptr);
301
408
 
302
409
  /** Format a computed property for diagnostics/tests (`computedStyle(sel, prop)`). */
303
410
  std::string xt_style_property_to_string(const XtStyle &style, const std::string &property);
@@ -0,0 +1,83 @@
1
+ /*
2
+ * xbintsc GUI engine — cascade value helpers.
3
+ *
4
+ * CSS-wide `inherit` for the shorthands that appear in real stylesheets, and
5
+ * `currentColor` substitution. They live apart from `style.cpp` so the cascade
6
+ * core stays under the repository's per-file line budget.
7
+ */
8
+
9
+ #include "style_internal.h"
10
+
11
+ #include <cctype>
12
+ #include <cstdio>
13
+ #include <string>
14
+
15
+ namespace xtgui {
16
+
17
+ void xt_style_apply_inherit(XtStyle *style, const XtStyle *parent, const std::string &property) {
18
+ if (parent == nullptr) return;
19
+ if (property == "color") {
20
+ style->color = parent->color;
21
+ } else if (property == "clip-path") {
22
+ style->clip_path = parent->clip_path;
23
+ } else if (property == "font") {
24
+ style->font_size = parent->font_size;
25
+ style->font_weight = parent->font_weight;
26
+ style->font_family = parent->font_family;
27
+ style->font_style = parent->font_style;
28
+ style->line_height = parent->line_height;
29
+ style->letter_spacing = parent->letter_spacing;
30
+ style->font_variant_numeric = parent->font_variant_numeric;
31
+ }
32
+ }
33
+
34
+ std::string xt_style_replace_current_color(const std::string &value, const Color &color) {
35
+ std::string out;
36
+ char quote = 0;
37
+ auto matches = [&value](size_t at) {
38
+ if (at + 12 > value.size()) return false;
39
+ for (size_t i = 0; i < 12; i++) {
40
+ char c = value[at + i];
41
+ if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
42
+ if (c != "currentcolor"[i]) return false;
43
+ }
44
+ if (at > 0) {
45
+ char before = value[at - 1];
46
+ if (std::isalnum((unsigned char)before) || before == '-' || before == '_') return false;
47
+ }
48
+ if (at + 12 < value.size()) {
49
+ char after = value[at + 12];
50
+ if (std::isalnum((unsigned char)after) || after == '-' || after == '_') return false;
51
+ }
52
+ return true;
53
+ };
54
+ for (size_t i = 0; i < value.size();) {
55
+ char c = value[i];
56
+ if (quote != 0) {
57
+ out.push_back(c);
58
+ if (c == quote) quote = 0;
59
+ i++;
60
+ continue;
61
+ }
62
+ if (c == '"' || c == '\'') {
63
+ quote = c;
64
+ out.push_back(c);
65
+ i++;
66
+ continue;
67
+ }
68
+ if (matches(i)) {
69
+ char buffer[96];
70
+ std::snprintf(buffer, sizeof(buffer), "rgba(%ld, %ld, %ld, %.3f)",
71
+ (long)(color.r * 255.0f + 0.5f), (long)(color.g * 255.0f + 0.5f),
72
+ (long)(color.b * 255.0f + 0.5f), (double)color.a);
73
+ out += buffer;
74
+ i += 12;
75
+ continue;
76
+ }
77
+ out.push_back(c);
78
+ i++;
79
+ }
80
+ return out;
81
+ }
82
+
83
+ } // namespace xtgui
@@ -15,16 +15,28 @@ struct ApplyContext {
15
15
  float parentFontSize = 16.0f;
16
16
  float viewportWidth = 0.0f;
17
17
  float viewportHeight = 0.0f;
18
+ /** Parent computed style; needed by `inherit` and the `font` shorthand. */
19
+ const XtStyle *parent = nullptr;
18
20
  };
19
21
 
20
22
  /** Resolve a length to pixels given the current font/viewport context. */
21
23
  float xt_style_resolve_absolute(const Length &length, float base, float em, float root, float vw,
22
24
  float vh);
23
25
 
26
+ /** CSS-wide `inherit` for the shorthands the engine handles. */
27
+ void xt_style_apply_inherit(XtStyle *style, const XtStyle *parent, const std::string &property);
28
+
29
+ /** Replace `currentcolor` outside quoted strings with `color`'s rgba spelling. */
30
+ std::string xt_style_replace_current_color(const std::string &value, const Color &color);
31
+
24
32
  /** Apply the built-in user-agent rules for one element. */
25
33
  void xt_style_apply_user_agent(const Node *node, XtStyle *style);
26
34
 
27
- /** Apply one parsed declaration (`property: value`) onto a computed style. */
35
+ /** Resolve a `border-radius` against a border box (percent uses min(w, h)). */
36
+ float xt_style_border_radius_px(const Length &radius, float width, float height, float em,
37
+ float root, float vw, float vh);
38
+
39
+ /** Apply a parsed declaration to a computed style. */
28
40
  void xt_style_apply_declaration(XtStyle *style, const std::string &property,
29
41
  const std::string &rawValue, const ApplyContext &context);
30
42
 
@@ -0,0 +1,92 @@
1
+ /*
2
+ * xbintsc GUI engine — resolution of computed CSS lengths.
3
+ *
4
+ * `Length` keeps a symbolic expression (`calc()`, `min()`/`max()`/`clamp()`,
5
+ * percentages and relative units) until layout knows the containing block, the
6
+ * element's font size and the viewport. This unit is that one entry point; it
7
+ * lives apart from declaration parsing to keep `style_values.cpp` focused.
8
+ */
9
+
10
+ #include "style_internal.h"
11
+
12
+ namespace xtgui {
13
+
14
+ float xt_style_resolve_absolute(const Length &length, float base, float em, float root, float vw,
15
+ float vh) {
16
+ switch (length.unit) {
17
+ case Unit::Clamp: {
18
+ if (length.args.size() < 3) return 0.0f;
19
+ float low = xt_style_resolve_absolute(length.args[0], base, em, root, vw, vh);
20
+ float value = xt_style_resolve_absolute(length.args[1], base, em, root, vw, vh);
21
+ float high = xt_style_resolve_absolute(length.args[2], base, em, root, vw, vh);
22
+ return value < low ? low : (value > high ? high : value);
23
+ }
24
+ case Unit::Min: {
25
+ float best = 0.0f;
26
+ bool first = true;
27
+ for (const Length &arg : length.args) {
28
+ float value = xt_style_resolve_absolute(arg, base, em, root, vw, vh);
29
+ if (first || value < best) best = value;
30
+ first = false;
31
+ }
32
+ return best;
33
+ }
34
+ case Unit::Max: {
35
+ float best = 0.0f;
36
+ bool first = true;
37
+ for (const Length &arg : length.args) {
38
+ float value = xt_style_resolve_absolute(arg, base, em, root, vw, vh);
39
+ if (first || value > best) best = value;
40
+ first = false;
41
+ }
42
+ return best;
43
+ }
44
+ case Unit::Calc: {
45
+ if (length.args.size() < 2) return 0.0f;
46
+ float left = xt_style_resolve_absolute(length.args[0], base, em, root, vw, vh);
47
+ float right = xt_style_resolve_absolute(length.args[1], base, em, root, vw, vh);
48
+ switch (length.op) {
49
+ case '+':
50
+ return left + right;
51
+ case '-':
52
+ return left - right;
53
+ case '*':
54
+ return left * right;
55
+ case '/':
56
+ /* Division by zero makes the whole `calc()` invalid in CSS; the
57
+ * declaration is dropped, which here means "no value contributed". */
58
+ return right == 0.0f ? 0.0f : left / right;
59
+ default:
60
+ return left;
61
+ }
62
+ }
63
+ case Unit::Px:
64
+ case Unit::Number:
65
+ return length.value;
66
+ case Unit::Percent:
67
+ return base * length.value / 100.0f;
68
+ case Unit::Em:
69
+ return em * length.value;
70
+ case Unit::Rem:
71
+ return root * length.value;
72
+ case Unit::Vw:
73
+ return vw * length.value / 100.0f;
74
+ case Unit::Vh:
75
+ return vh * length.value / 100.0f;
76
+ default:
77
+ return 0.0f;
78
+ }
79
+ }
80
+
81
+ float xt_style_border_radius_px(const Length &radius, float width, float height, float em,
82
+ float root, float vw, float vh) {
83
+ float base = width < height ? width : height;
84
+ if (base < 0.0f) base = 0.0f;
85
+ float value = xt_style_resolve_absolute(radius, base, em, root, vw, vh);
86
+ if (value < 0.0f) value = 0.0f;
87
+ float half = (width < height ? width : height) / 2.0f;
88
+ if (half < 0.0f) half = 0.0f;
89
+ return value > half ? half : value;
90
+ }
91
+
92
+ } // namespace xtgui
@@ -18,6 +18,21 @@ static std::string formatNumber(float number) {
18
18
  return buffer;
19
19
  }
20
20
 
21
+ static std::string formatCalcBody(const Length &length);
22
+ static std::string formatLength(const Length &length);
23
+
24
+ /** A nested `calc()` operand is parenthesised instead of spelling `calc(calc(…))`,
25
+ * except where operator precedence already groups it the same way. */
26
+ static std::string formatCalcOperand(const Length &length, char parent_op) {
27
+ if (length.unit == Unit::Calc) {
28
+ int parent_precedence = (parent_op == '*' || parent_op == '/') ? 2 : 1;
29
+ int own_precedence = (length.op == '*' || length.op == '/') ? 2 : 1;
30
+ if (own_precedence > parent_precedence) return formatCalcBody(length);
31
+ return "(" + formatCalcBody(length) + ")";
32
+ }
33
+ return formatLength(length);
34
+ }
35
+
21
36
  static std::string formatLength(const Length &length) {
22
37
  switch (length.unit) {
23
38
  case Unit::Auto:
@@ -50,11 +65,24 @@ static std::string formatLength(const Length &length) {
50
65
  text += ")";
51
66
  return text;
52
67
  }
68
+ case Unit::Calc:
69
+ return "calc(" + formatCalcBody(length) + ")";
53
70
  default:
54
71
  return "";
55
72
  }
56
73
  }
57
74
 
75
+ static std::string formatCalcBody(const Length &length) {
76
+ if (length.args.size() < 2) return "";
77
+ char op = length.op != 0 ? length.op : '?';
78
+ std::string text = formatCalcOperand(length.args[0], op);
79
+ text += " ";
80
+ text.push_back(op);
81
+ text += " ";
82
+ text += formatCalcOperand(length.args[1], op);
83
+ return text;
84
+ }
85
+
58
86
  static std::string formatColor(const Color &color) {
59
87
  if (color.a >= 1.0f) {
60
88
  char buffer[64];
@@ -123,6 +151,22 @@ static std::string formatFilterList(const std::vector<FilterOp> &filters) {
123
151
  return out;
124
152
  }
125
153
 
154
+ static std::string formatBoxShadows(const std::vector<BoxShadow> &shadows) {
155
+ if (shadows.empty()) return "none";
156
+ std::string out;
157
+ for (const BoxShadow &shadow : shadows) {
158
+ if (!out.empty()) out += ", ";
159
+ if (shadow.inset) out += "inset ";
160
+ out += formatLength(shadow.dx) + " " + formatLength(shadow.dy);
161
+ bool zeroBlur = shadow.blur.unit == Unit::Px && shadow.blur.value == 0.0f;
162
+ bool zeroSpread = shadow.spread.unit == Unit::Px && shadow.spread.value == 0.0f;
163
+ if (!zeroBlur || !zeroSpread) out += " " + formatLength(shadow.blur);
164
+ if (!zeroSpread) out += " " + formatLength(shadow.spread);
165
+ if (shadow.has_color) out += " " + formatColor(shadow.color);
166
+ }
167
+ return out;
168
+ }
169
+
126
170
  static std::string formatTextShadows(const std::vector<TextShadow> &shadows) {
127
171
  if (shadows.empty()) return "none";
128
172
  std::string out;
@@ -189,8 +233,16 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
189
233
  static const char *const floatNames[] = {"none", "left", "right"};
190
234
  static const char *const clearNames[] = {"none", "left", "right", "both"};
191
235
  static const char *const alignTextNames[] = {"start", "center", "end", "justify"};
236
+ static const char *const textTransformNames[] = {"none", "uppercase", "lowercase", "capitalize"};
237
+ static const char *const writingModeNames[] = {"horizontal-tb", "vertical-rl", "vertical-lr"};
238
+ static const char *const cursorNames[] = {"auto", "default", "pointer", "text", "move",
239
+ "wait", "help", "not-allowed", "crosshair"};
240
+ static const char *const borderStyleNames[] = {"none", "solid", "dashed", "dotted"};
192
241
 
193
- if (property == "display") return displayNames[(int)style.display];
242
+ if (property == "display") {
243
+ if (style.inline_level) return style.display == Display::Grid ? "inline-grid" : "inline-flex";
244
+ return displayNames[(int)style.display];
245
+ }
194
246
  if (property == "position") return positionNames[(int)style.position];
195
247
  if (property == "color") return formatColor(style.color);
196
248
  if (property == "background-color" || property == "background") {
@@ -221,6 +273,34 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
221
273
  return style.line_height.unit == Unit::Auto ? "normal" : formatLength(style.line_height);
222
274
  }
223
275
  if (property == "text-align") return alignTextNames[(int)style.text_align];
276
+ if (property == "text-transform") return textTransformNames[(int)style.text_transform];
277
+ if (property == "letter-spacing") return formatLength(style.letter_spacing);
278
+ if (property == "writing-mode") return writingModeNames[(int)style.writing_mode];
279
+ if (property == "vertical-align") return formatLength(style.vertical_align);
280
+ if (property == "font-variant-numeric") return style.font_variant_numeric ? "tabular-nums" : "normal";
281
+ if (property == "cursor") return cursorNames[(int)style.cursor];
282
+ if (property == "aspect-ratio") return style.aspect_ratio > 0 ? formatNumber(style.aspect_ratio) : "auto";
283
+ if (property == "grid-area") {
284
+ return formatNumber((float)style.grid_row_start) + " / " +
285
+ formatNumber((float)style.grid_column_start) + " / " +
286
+ formatNumber((float)style.grid_row_end) + " / " +
287
+ formatNumber((float)style.grid_column_end);
288
+ }
289
+ if (property == "outline-width") return formatLength(style.outline_width);
290
+ if (property == "outline-style") return borderStyleNames[(int)style.outline_style];
291
+ if (property == "outline-color") {
292
+ return style.has_outline_color ? formatColor(style.outline_color) : "currentcolor";
293
+ }
294
+ if (property == "outline-offset") return formatLength(style.outline_offset);
295
+ if (property == "background-size") {
296
+ if (style.background_sizes.empty()) return "auto";
297
+ std::string out;
298
+ for (const BackgroundSize &size : style.background_sizes) {
299
+ if (!out.empty()) out += ", ";
300
+ out += formatLength(size.width) + " " + formatLength(size.height);
301
+ }
302
+ return out;
303
+ }
224
304
  if (property == "flex-direction") return flexNames[(int)style.flex_direction];
225
305
  if (property == "flex-wrap") return wrapNames[(int)style.flex_wrap];
226
306
  if (property == "justify-content") return justifyNames[(int)style.justify_content];
@@ -234,7 +314,7 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
234
314
  if (property == "flex-grow") return formatNumber(style.flex_grow);
235
315
  if (property == "flex-shrink") return formatNumber(style.flex_shrink);
236
316
  if (property == "flex-basis") return formatLength(style.flex_basis);
237
- if (property == "border-radius") return formatNumber(style.border_radius) + "px";
317
+ if (property == "border-radius") return formatLength(style.border_radius);
238
318
  if (property == "opacity") return formatNumber(style.opacity);
239
319
  if (property == "mix-blend-mode") {
240
320
  static const char *const names[] = {
@@ -250,6 +330,7 @@ std::string xt_style_property_to_string(const XtStyle &style, const std::string
250
330
  if (property == "filter") return formatFilterList(style.filters);
251
331
  if (property == "backdrop-filter") return formatFilterList(style.backdrop_filters);
252
332
  if (property == "text-shadow") return formatTextShadows(style.text_shadow);
333
+ if (property == "box-shadow") return formatBoxShadows(style.box_shadow);
253
334
  if (property == "background-image") {
254
335
  if (style.background_layers.empty()) return "none";
255
336
  std::string out;