xbintsc 0.5.31 → 0.5.32

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 (181) hide show
  1. package/README.md +16 -10
  2. package/dist/src/binder/references.js +14 -1
  3. package/dist/src/binder/references.js.map +1 -1
  4. package/dist/src/cli/help.d.ts +1 -1
  5. package/dist/src/codegen/generator/module.d.ts +1 -0
  6. package/dist/src/codegen/generator/module.js +54 -0
  7. package/dist/src/codegen/generator/module.js.map +1 -1
  8. package/dist/src/codegen/generator/statements/variables.js +6 -4
  9. package/dist/src/codegen/generator/statements/variables.js.map +1 -1
  10. package/dist/src/driver/version.d.ts +1 -1
  11. package/dist/src/driver/version.js +1 -1
  12. package/dist/src/extensions/gui/css.d.ts +26 -0
  13. package/dist/src/extensions/gui/css.js +100 -0
  14. package/dist/src/extensions/gui/css.js.map +1 -0
  15. package/dist/src/extensions/gui/html.js +9 -2
  16. package/dist/src/extensions/gui/html.js.map +1 -1
  17. package/dist/src/extensions/gui/index.js +16 -2
  18. package/dist/src/extensions/gui/index.js.map +1 -1
  19. package/dist/src/extensions/gui/three.d.ts +16 -0
  20. package/dist/src/extensions/gui/three.js +22 -0
  21. package/dist/src/extensions/gui/three.js.map +1 -0
  22. package/dist/src/extensions/media/index.d.ts +6 -5
  23. package/dist/src/extensions/media/index.js +12 -14
  24. package/dist/src/extensions/media/index.js.map +1 -1
  25. package/dist/tests/e2e/compile-builtins.test.js +24 -0
  26. package/dist/tests/e2e/compile-builtins.test.js.map +1 -1
  27. package/dist/tests/e2e/compile-objects-classes.test.js +23 -0
  28. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  29. package/dist/tests/e2e/gui-browser.test.js +1 -1
  30. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  31. package/dist/tests/e2e/gui-effects.test.d.ts +6 -0
  32. package/dist/tests/e2e/gui-effects.test.js +262 -0
  33. package/dist/tests/e2e/gui-effects.test.js.map +1 -0
  34. package/dist/tests/e2e/gui-example-webgl.test.d.ts +10 -0
  35. package/dist/tests/e2e/gui-example-webgl.test.js +75 -0
  36. package/dist/tests/e2e/gui-example-webgl.test.js.map +1 -0
  37. package/dist/tests/e2e/gui-example-webgl2.test.d.ts +8 -0
  38. package/dist/tests/e2e/gui-example-webgl2.test.js +72 -0
  39. package/dist/tests/e2e/gui-example-webgl2.test.js.map +1 -0
  40. package/dist/tests/e2e/gui-layout.test.js +48 -0
  41. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  42. package/dist/tests/e2e/gui-media.test.js +5 -4
  43. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  44. package/dist/tests/e2e/gui-webgl.test.d.ts +11 -0
  45. package/dist/tests/e2e/gui-webgl.test.js +179 -0
  46. package/dist/tests/e2e/gui-webgl.test.js.map +1 -0
  47. package/dist/tests/e2e/gui-webgl2.test.d.ts +10 -0
  48. package/dist/tests/e2e/gui-webgl2.test.js +238 -0
  49. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -0
  50. package/dist/tests/extensions/gui.test.js +41 -0
  51. package/dist/tests/extensions/gui.test.js.map +1 -1
  52. package/dist/tests/extensions/media.test.js +7 -3
  53. package/dist/tests/extensions/media.test.js.map +1 -1
  54. package/doc/ai/extensions.md +29 -9
  55. package/doc/ai/troubleshooting.md +12 -8
  56. package/doc/ai/zh-CN/extensions.md +26 -8
  57. package/doc/ai/zh-CN/troubleshooting.md +10 -6
  58. package/doc/gui/api.md +3 -2
  59. package/doc/gui/build.md +72 -16
  60. package/doc/gui/canvas.md +11 -3
  61. package/doc/gui/dom.md +33 -18
  62. package/doc/gui/known-issues.md +57 -24
  63. package/doc/gui/layout.md +18 -0
  64. package/doc/gui/media.md +17 -13
  65. package/doc/gui/rendering.md +50 -3
  66. package/doc/gui/webgl.md +256 -0
  67. package/doc/gui.md +108 -17
  68. package/doc/npm-package.md +6 -4
  69. package/doc/requirements.md +18 -9
  70. package/doc/zh-CN/gui/api.md +3 -2
  71. package/doc/zh-CN/gui/build.md +64 -15
  72. package/doc/zh-CN/gui/canvas.md +9 -2
  73. package/doc/zh-CN/gui/dom.md +20 -7
  74. package/doc/zh-CN/gui/known-issues.md +45 -19
  75. package/doc/zh-CN/gui/layout.md +14 -0
  76. package/doc/zh-CN/gui/media.md +15 -11
  77. package/doc/zh-CN/gui/rendering.md +41 -4
  78. package/doc/zh-CN/gui/webgl.md +115 -0
  79. package/doc/zh-CN/gui.md +89 -14
  80. package/doc/zh-CN/npm-package.md +9 -6
  81. package/doc/zh-CN/requirements.md +12 -6
  82. package/llms.txt +2 -0
  83. package/package.json +1 -1
  84. package/runtime/ext_gui/browser.cpp +39 -13
  85. package/runtime/ext_gui/canvas.cpp +3 -18
  86. package/runtime/ext_gui/canvas_internal.h +10 -0
  87. package/runtime/ext_gui/canvas_raster.cpp +60 -0
  88. package/runtime/ext_gui/clip_values.cpp +246 -0
  89. package/runtime/ext_gui/css.cpp +94 -0
  90. package/runtime/ext_gui/css.h +22 -1
  91. package/runtime/ext_gui/css_match.cpp +3 -0
  92. package/runtime/ext_gui/css_selector.cpp +9 -0
  93. package/runtime/ext_gui/css_sheet.cpp +89 -45
  94. package/runtime/ext_gui/document.cpp +4 -1
  95. package/runtime/ext_gui/document.h +2 -0
  96. package/runtime/ext_gui/dom.h +16 -0
  97. package/runtime/ext_gui/dom_api.cpp +1 -0
  98. package/runtime/ext_gui/dom_api_canvas.cpp +17 -80
  99. package/runtime/ext_gui/dom_api_canvas_extra.cpp +130 -1
  100. package/runtime/ext_gui/dom_api_canvas_pending.cpp +41 -0
  101. package/runtime/ext_gui/dom_api_internal.h +259 -0
  102. package/runtime/ext_gui/dom_api_webgl.cpp +376 -0
  103. package/runtime/ext_gui/dom_api_webgl2.cpp +434 -0
  104. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +269 -0
  105. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +212 -0
  106. package/runtime/ext_gui/dom_api_webgl_rt.h +30 -0
  107. package/runtime/ext_gui/dom_api_webgl_shader.cpp +443 -0
  108. package/runtime/ext_gui/dom_api_webgl_state.cpp +444 -0
  109. package/runtime/ext_gui/dom_api_webgl_texture.cpp +445 -0
  110. package/runtime/ext_gui/effect_values.cpp +312 -0
  111. package/runtime/ext_gui/gradient_values.cpp +476 -0
  112. package/runtime/ext_gui/grid_values.cpp +230 -0
  113. package/runtime/ext_gui/gui_canvas.cpp +50 -2
  114. package/runtime/ext_gui/gui_engine.h +27 -0
  115. package/runtime/ext_gui/gui_events.cpp +4 -1
  116. package/runtime/ext_gui/gui_media.cpp +1 -1
  117. package/runtime/ext_gui/gui_webgl.cpp +83 -0
  118. package/runtime/ext_gui/layout.cpp +21 -0
  119. package/runtime/ext_gui/layout_flow.cpp +81 -4
  120. package/runtime/ext_gui/layout_flow_internal.h +31 -0
  121. package/runtime/ext_gui/layout_grid.cpp +374 -0
  122. package/runtime/ext_gui/media.h +1 -1
  123. package/runtime/ext_gui/media_api.h +2 -2
  124. package/runtime/ext_gui/media_query.cpp +211 -0
  125. package/runtime/ext_gui/paint.cpp +111 -77
  126. package/runtime/ext_gui/paint.h +33 -3
  127. package/runtime/ext_gui/paint_background.cpp +229 -0
  128. package/runtime/ext_gui/paint_clip.cpp +268 -0
  129. package/runtime/ext_gui/paint_effects.cpp +324 -0
  130. package/runtime/ext_gui/paint_internal.h +85 -0
  131. package/runtime/ext_gui/renderer.cpp +8 -31
  132. package/runtime/ext_gui/renderer_clip.cpp +196 -0
  133. package/runtime/ext_gui/renderer_image.cpp +13 -8
  134. package/runtime/ext_gui/renderer_internal.h +15 -8
  135. package/runtime/ext_gui/renderer_text.cpp +6 -31
  136. package/runtime/ext_gui/style.cpp +343 -20
  137. package/runtime/ext_gui/style.h +144 -3
  138. package/runtime/ext_gui/style_print.cpp +134 -1
  139. package/runtime/ext_gui/style_values.cpp +152 -126
  140. package/runtime/ext_gui/style_values_internal.h +35 -0
  141. package/runtime/ext_gui/webgl.h +466 -0
  142. package/runtime/ext_gui/webgl2_constants.h +274 -0
  143. package/runtime/ext_gui/webgl_buffer.cpp +565 -0
  144. package/runtime/ext_gui/webgl_constants.h +317 -0
  145. package/runtime/ext_gui/webgl_context.cpp +466 -0
  146. package/runtime/ext_gui/webgl_draw.cpp +495 -0
  147. package/runtime/ext_gui/webgl_glsl.h +125 -0
  148. package/runtime/ext_gui/webgl_glsl_compile.cpp +431 -0
  149. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +349 -0
  150. package/runtime/ext_gui/webgl_glsl_scan.cpp +581 -0
  151. package/runtime/ext_gui/webgl_msl.cpp +62 -0
  152. package/runtime/ext_gui/webgl_msl.h +33 -0
  153. package/runtime/ext_gui/webgl_pipeline.cpp +274 -0
  154. package/runtime/ext_gui/webgl_program_internal.h +18 -0
  155. package/runtime/ext_gui/webgl_shader.cpp +582 -0
  156. package/runtime/ext_gui/webgl_texture.cpp +293 -0
  157. package/runtime/ext_gui/webgl_uniform.cpp +167 -0
  158. package/runtime/ext_gui/webgl_webgl2.cpp +224 -0
  159. package/runtime/ext_media/media.cpp +1 -1
  160. package/runtime/ext_media/media_bridge.c +9 -3
  161. package/runtime/rt_internal.h +1 -0
  162. package/runtime/xt_containers/access.inc +8 -0
  163. package/runtime/xt_regex/api.inc +2 -0
  164. package/runtime/xt_regex/matcher.inc +16 -3
  165. package/runtime/xt_regex/parser.inc +8 -2
  166. package/runtime/xt_regex/types.inc +3 -0
  167. package/runtime/xt_stdlib2/regexp-match.inc +60 -21
  168. package/runtime/xt_stdlib2/regexp.inc +1 -0
  169. package/runtime/xt_typed_array.c +1 -1
  170. package/scripts/build-gui.ts +90 -3
  171. package/scripts/build-media.ts +323 -58
  172. package/scripts/setup-vs-toolchain.ps1 +152 -0
  173. package/src/binder/references.ts +13 -1
  174. package/src/codegen/generator/module.ts +50 -0
  175. package/src/codegen/generator/statements/variables.ts +6 -3
  176. package/src/driver/version.ts +1 -1
  177. package/src/extensions/gui/css.ts +113 -0
  178. package/src/extensions/gui/html.ts +9 -2
  179. package/src/extensions/gui/index.ts +16 -2
  180. package/src/extensions/gui/three.ts +25 -0
  181. package/src/extensions/media/index.ts +13 -16
package/doc/gui/build.md CHANGED
@@ -8,8 +8,14 @@
8
8
  `runtime/ext_media/` and is built separately.
9
9
  - `npm run gui` (`scripts/build-gui.ts`) fetches a pinned SDL3 (`SDL3_TAG`,
10
10
  default `release-3.2.10`), builds a static SDL3, fetches and builds static
11
- FreeType (`FREETYPE_VERSION`) and HarfBuzz (`HARFBUZZ_VERSION`), compiles the
12
- engine and merges everything into `runtime/lib/<os>-<arch>/gui.a` (or
11
+ FreeType (`FREETYPE_VERSION`) and HarfBuzz (`HARFBUZZ_VERSION`), and fetches
12
+ and builds static **glslang** (`GLSLANG_VERSION`, default `16.6.0`) against
13
+ the pinned SPIR-V headers (`SPIRV_HEADERS_VERSION`), and **SPIRV-Cross**
14
+ (`SPIRV_CROSS_VERSION`, default `vulkan-sdk-1.4.313.0`). glslang is the
15
+ runtime GLSL ES compiler behind WebGL and SPIRV-Cross turns its SPIR-V into
16
+ MSL for the Metal backend (see [WebGL](./webgl.md)); both are merged into the
17
+ archive like the other libraries. The script then compiles the engine and
18
+ merges everything into `runtime/lib/<os>-<arch>/gui.a` (or
13
19
  `gui.lib` with the MSVC ABI on Windows), same convention as `core.a`,
14
20
  statically bundling all three. The merge uses `libtool` on macOS and
15
21
  `ar -M` (GNU/LLVM) elsewhere; CMake archives are discovered under the build
@@ -18,12 +24,13 @@
18
24
  backend. It fetches FFmpeg (`FFMPEG_VERSION`, default `7.1`), configures a
19
25
  minimal static build with only the demuxers/parsers/decoders the media
20
26
  elements need, compiles `runtime/ext_media/media.cpp` against it and merges
21
- everything into `runtime/lib/<os>-<arch>/media.a`. `gui.a` does **not** link
22
- FFmpeg: a program enables playback explicitly with `--ext media`.
27
+ everything into `runtime/lib/<os>-<arch>/media.a` (`media.lib` with the MSVC
28
+ ABI on Windows). `gui.a` does **not** link FFmpeg: a program enables playback
29
+ explicitly with `--ext media`.
23
30
  - `src/extensions/gui/index.ts` exposes the engine archive through
24
31
  `nativeObjects()` (via `findRuntimeLibrary`, so `.a`/`.lib` both work) and the
25
32
  OS frameworks through `linkerFlags()`. `src/extensions/media/index.ts` does
26
- the same for `media.a` and compiles the registration bridge
33
+ the same for `media.a`/`media.lib` and compiles the registration bridge
27
34
  (`runtime/ext_media/media_bridge.c`) as a normal runtime source.
28
35
  - `scripts/build-gui.ts` compiles with the *same* clang the driver resolves
29
36
  (`src/driver/toolchain-provider.ts`); on Windows that is the MSVC-targeting
@@ -32,14 +39,39 @@
32
39
  fallbacks, and Windows always uses `llvm-ar`, so the archive cannot mix object
33
40
  formats or C++ runtimes. The Visual Studio toolchain is found by absolute
34
41
  path, so no developer prompt is required. On Windows SDL_GPU selects the
35
- Vulkan backend; no DXIL/`dxc` toolchain is needed. `scripts/build-media.ts`
36
- uses the same resolved toolchain for FFmpeg and the player.
37
- - CI builds `gui.a` and `media.a` before assembling the release archive so they
38
- ship inside `runtime/lib/<slug>/` (the release tarball copies the whole
39
- `runtime/` tree). The archives are built and the examples are run on Linux
40
- (under Xvfb, with the lavapipe software Vulkan driver) and macOS; Windows is
41
- provisional because the hosted runners have no GPU/Vulkan driver, and the
42
- media archive is POSIX-only.
42
+ Vulkan backend; no DXIL/`dxc` toolchain is needed.
43
+ - `scripts/build-media.ts` uses the same resolved toolchain. On Windows it
44
+ drives FFmpeg's own MSVC mode: `configure --toolchain=msvc --cc=clang-cl`
45
+ compiles static FFmpeg against the static MSVC CRT, `clang++` compiles the
46
+ player, and `llvm-ar` merges the COFF archives into `media.lib`. The compiler
47
+ chain is entirely Visual Studio (clang-cl, `cl.exe`, `lib.exe`, `llvm-ar`).
48
+ FFmpeg's configure is a POSIX shell script and its
49
+ Makefiles need GNU make, so those two build drivers remain: the shell is Git
50
+ for Windows' bundled bash (Git is already required to clone SDL3), and make is
51
+ a native GNU make such as Chocolatey's `make` (or `xbintsc_MAKE`). The script
52
+ rewrites the generated source path to a drive-letter form and skips dependency
53
+ extraction, which native make cannot pass through the MSVC `/showIncludes` awk
54
+ pipeline. The MSVC tools are only needed by configure's toolchain probe; they
55
+ are resolved from the Visual Studio installation, not from a developer prompt.
56
+ `xbintsc_SHELL` and `xbintsc_MAKE` override the discovered shell/make. Headers
57
+ and libraries are copied into `vendor/ffmpeg-build-msvc/dist` by the script
58
+ rather than through `make install`.
59
+ - CI builds `gui.a`/`gui.lib` and `media.a`/`media.lib` before assembling the
60
+ release archive so they ship inside `runtime/lib/<slug>/` (the release tarball
61
+ copies the whole `runtime/` tree). The archives are built and the examples are
62
+ run on Linux (under Xvfb, with the lavapipe software Vulkan driver) and macOS;
63
+ the Windows x64 job builds both archives and links a `gui,media` program, so
64
+ the MSVC build is validated even though *running* the examples there stays
65
+ provisional (the hosted runner has no GPU/Vulkan driver). Windows arm64 still
66
+ skips both archives: FFmpeg's MSVC arm64 build is not validated yet.
67
+ - The Windows CI steps call `.github/actions/vs-toolchain` →
68
+ `scripts/setup-vs-toolchain.ps1`, which imports the same Visual Studio
69
+ `vcvarsall` environment and bundled LLVM a local build uses (no separate
70
+ Chocolatey LLVM). For the media build the action also installs GNU make
71
+ through Chocolatey and points `xbintsc_MAKE` at it; the FFmpeg source and the
72
+ installed `vendor/ffmpeg-build-msvc/dist` prefix are cached by script hash.
73
+ The configure/make tree itself is not cached (absolute paths and compiler
74
+ options from another machine).
43
75
  - The existing cache fingerprint already hashes `nativeObjects()` contents and
44
76
  `runtimeSources()` content, so rebuilding either archive invalidates cached
45
77
  binaries automatically.
@@ -65,6 +97,28 @@ npm run media # optional: build media.a (fetches and builds FFmpeg once)
65
97
  xbintsc run examples/gui/hello.ts --ext gui
66
98
  ```
67
99
 
100
+ On Windows the same commands produce `gui.lib` and `media.lib` from a normal
101
+ PowerShell — no developer command prompt. The Visual Studio installation
102
+ supplies the MSVC-targeting `clang++`, `clang-cl`, `cl.exe` and `lib.exe`, and
103
+ `npm run media` additionally needs a POSIX shell and GNU make for FFmpeg's
104
+ `configure`: Git for Windows supplies the shell (and is already needed to clone
105
+ SDL3), while GNU make is a native binary — install it with `choco install make`,
106
+ or point `xbintsc_MAKE` at one. The script reports whichever tool is missing and
107
+ accepts `xbintsc_SHELL` / `xbintsc_MAKE` overrides.
108
+
109
+ To select that toolchain explicitly (useful in a fresh shell or to pin the same
110
+ compiler CI uses), run:
111
+
112
+ ```powershell
113
+ pwsh -File scripts/setup-vs-toolchain.ps1
114
+ ```
115
+
116
+ It imports `vcvarsall` (MSVC, Windows SDK, MSBuild) and points `xbintsc_CLANG`
117
+ at the LLVM bundled with that Visual Studio installation. GitHub Actions uses
118
+ the same script through `.github/actions/vs-toolchain`, so no separate
119
+ Chocolatey/standalone LLVM is installed there and CI, local builds and the
120
+ prebuilt archives share one compiler chain.
121
+
68
122
  Two examples live here: `hello.ts` (HTML/CSS + a reactive counter) and
69
123
  [`pelican-bike/`](../../examples/gui/pelican-bike), a playable 2D game that uses
70
124
  positioned layers, CSS sprites, `requestAnimationFrame` and keyboard input — see
@@ -82,11 +136,13 @@ which the e2e test (`tests/e2e/gui-*.test.ts`) uses to run headlessly.
82
136
 
83
137
  Those suites are skipped on Windows by default (the archive is built and runs
84
138
  locally through Vulkan, but CI runners have no GPU driver to run it). With a
85
- `gui.lib` and a working GPU, opt in with `xbintsc_GUI_TESTS=1`:
139
+ `gui.lib` and a working GPU, opt in with `xbintsc_GUI_TESTS=1`; if you also ran
140
+ `npm run media`, the media suite then exercises the real decoder on Windows too:
86
141
 
87
142
  ```bat
88
143
  set xbintsc_GUI_TESTS=1
89
144
  npx vitest run tests/e2e/gui-layout.test.ts tests/e2e/gui-example.test.ts
145
+ npx vitest run tests/e2e/gui-media.test.ts
90
146
  ```
91
147
 
92
148
  On a headless Linux box, install the SDL3 build headers and run under Xvfb with a
@@ -101,8 +157,8 @@ xvfb-run -a --server-args="-screen 0 1280x720x24" \
101
157
  ```
102
158
 
103
159
  The media e2e suite (`tests/e2e/gui-media.test.ts`) skips itself unless both
104
- `gui.a` and `media.a` exist; `tests/e2e/gui-media-disabled.test.ts` covers the
105
- GUI-only build and runs with `gui.a` alone.
160
+ `gui.a`/`gui.lib` and `media.a`/`media.lib` exist; `tests/e2e/gui-media-disabled.test.ts`
161
+ covers the GUI-only build and runs with the GUI archive alone.
106
162
 
107
163
  `scripts/install-gui-deps.sh` holds the package list (`apt-get install -y clang
108
164
  cmake libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev
package/doc/gui/canvas.md CHANGED
@@ -23,9 +23,14 @@ state parsing in `canvas_style.cpp`; the DOM bindings live in
23
23
 
24
24
  ```ts
25
25
  const canvas: any = document.getElementById("c");
26
- const ctx = canvas.getContext("2d"); // or null for any other context kind
26
+ const ctx = canvas.getContext("2d"); // or "webgl" for a GPU context
27
27
  ```
28
28
 
29
+ A canvas has one context kind. `getContext("webgl")` or `getContext("webgl2")`
30
+ returns a WebGL context (see [WebGL](./webgl.md)); once a canvas has a 2D context,
31
+ asking for either WebGL kind answers `null`, and vice versa. A canvas that never
32
+ asked for a context stays transparent.
33
+
29
34
  `getContext("2d")` returns a context handle whose methods and accessors live on
30
35
  a shared prototype. The handle is cached on the element, so every call returns
31
36
  the **same** object and the drawing state (transform, styles, path) is per
@@ -101,8 +106,11 @@ rotate the glyph bitmaps. `strokeText` currently fills with the stroke style
101
106
  rather than producing an outline.
102
107
 
103
108
  Images: `drawImage(source, …)` in the 3-argument (`dx, dy`), 5-argument
104
- (`dx, dy, dw, dh`) and 9-argument (`dx, dy, dw, dh, sx, sy, sw, sh`) forms.
105
- `source` may be an `<img>` element (decoded through the image module) or another
109
+ (`dx, dy, dw, dh`) and 9-argument (`dx, dy, dw, dh, sx, sy, sw, sh`; this
110
+ engine's 9-argument form is destination-first) forms. A full affine transform
111
+ (rotation/skew/scale) is honoured: the destination rectangle becomes a
112
+ parallelogram and every device pixel is inverse-mapped to the source rectangle
113
+ (nearest-neighbour). `source` may be an `<img>` element (decoded through the image module) or another
106
114
  `<canvas>` element (its current bitmap). Sampling is nearest-neighbour, so a
107
115
  large upscale of a small source is blocky — fine for icons and pixel art, not a
108
116
  photo pipeline.
package/doc/gui/dom.md CHANGED
@@ -11,28 +11,43 @@ comments and doctype skipped, entity decoding (named + numeric/hex), void
11
11
  elements, raw-text elements (`<style>`/`<script>`), and the common implicit
12
12
  close rules (`li`, `dt`/`dd`, `option`, `p`, headings, table cells/rows).
13
13
 
14
- **CSS parser** (`runtime/ext_gui/css.{h,cpp}`): comments and at-rules skipped
15
- (for now), rules with multiple selectors, and declarations. Selectors: type,
16
- `.class`, `#id`, attribute (`=`, `~=`, `|=`, `^=`, `$=`, `*=`), the four
17
- combinators (descendant, child, adjacent and general sibling) and the
18
- pseudo-classes `:first-child`, `:last-child`, `:only-child`, `:empty`,
19
- `:root`, `:not(...)`, `:nth-child(an+b)`, `:disabled`, `:checked`, plus the
20
- stateful `:hover` and `:focus` (see [Input](./input.md)). Values:
21
- lengths (`px`, `%`, `em`, `rem`, `vw`, `vh`, `pt`, `pc`, `in`, `cm`, `mm`, `q`),
22
- colors (hex, `rgb()`/`rgba()`, a named subset), numbers, keywords and shorthands
23
- (`margin`/`padding`/`border`/`flex`). `transform` (`translate`/`translate3d`/
24
- `translateX`/`translateY`, `scale`/`scale3d`/`scaleX`/`scaleY`, `rotate`/
25
- `rotateZ`, `matrix`; the `3d` spellings' `z` argument is dropped) and
26
- `transform-origin` are parsed into the computed style as a list of operations
27
- plus two (unresolved) origin lengths; an unsupported function invalidates the
28
- whole declaration ([Layout](./layout.md#transforms-m15)).
14
+ **CSS parser** (`runtime/ext_gui/css.{h,cpp}`): comments are stripped; `@media`
15
+ blocks are parsed recursively (nested queries combine with `and`, and a comma
16
+ list distributes over the inner query) while other at-rules are skipped. Rules
17
+ support multiple selectors and declarations. Selectors: type, `.class`, `#id`,
18
+ attribute (`=`, `~=`, `|=`, `^=`, `$=`, `*=`), the four combinators
19
+ (descendant, child, adjacent and general sibling), the pseudo-classes
20
+ `:first-child`, `:last-child`, `:only-child`, `:empty`, `:root`, `:not(...)`,
21
+ `:nth-child(an+b)`, `:disabled`, `:checked`, plus the stateful `:hover` and
22
+ `:focus` (see [Input](./input.md)), and the pseudo-elements `::before`/`::after`
23
+ (legacy `:before`/`:after` too). Values: lengths (`px`, `%`, `em`, `rem`, `vw`,
24
+ `vh`, `pt`, `pc`, `in`, `cm`, `mm`, `q`), colors (hex, `rgb()`/`rgba()`, a named
25
+ subset), numbers, keywords and shorthands (`margin`/`padding`/`border`/`flex`).
26
+ `transform` (`translate`/`translate3d`/`translateX`/`translateY`, `scale`/
27
+ `scale3d`/`scaleX`/`scaleY`, `rotate`/`rotateZ`, `matrix`; the `3d` spellings'
28
+ `z` argument is dropped) and `transform-origin` are parsed into the computed
29
+ style as a list of operations plus two (unresolved) origin lengths; an
30
+ unsupported function invalidates the whole declaration
31
+ ([Layout](./layout.md#transforms-m15)). M19 adds `background-image` layer lists
32
+ (linear/radial), `clip-path` (`inset`/`circle`/`ellipse`/`polygon`), the
33
+ `filter`/`backdrop-filter` function lists, `text-shadow` and `content`.
29
34
 
30
35
  **Cascade & computed style** (`runtime/ext_gui/style.{h,cpp}`): a small built-in
31
36
  UA stylesheet, author rules sorted by `(!important, specificity, source order)`,
32
37
  then inline `style=""` (highest specificity, but non-`!important` inline loses to
33
- `!important`), plus CSS inheritance of the text properties. Relative lengths are
34
- kept unresolved until layout, except `font-size` (resolved against the *parent*
35
- font size) and `line-height`.
38
+ `!important`), plus CSS inheritance of the text properties. `@media` rules are
39
+ filtered against the current viewport on every restyle, so a resize switches
40
+ them. Relative lengths are kept unresolved until layout, except `font-size`
41
+ (resolved against the *parent* font size) and `line-height`.
42
+
43
+ `::before`/`::after` styles are computed after their originating element: the
44
+ selector's last compound must name the pseudo-element, and the rest of the
45
+ selector is matched against the origin (so `.card.info::before` works). The
46
+ pseudo style inherits the origin's computed style, `content` is resolved
47
+ (strings, escapes, `attr()`, quotes), and a synthetic node is placed in a
48
+ per-restyle arena. Layout inserts its box before/after the real children;
49
+ traversal, queries, `innerHTML` and `elementIndex()` never see it, and hit
50
+ testing targets the origin element.
36
51
 
37
52
  ## Interactive DOM (M8)
38
53
 
@@ -5,15 +5,24 @@
5
5
 
6
6
  ## Known issues
7
7
 
8
- - **No WebGL.** `getContext("webgl")` / `getContext("webgl2")` answer `null` and
9
- the `WebGL*` / `WebGL2RenderingContext` / `XRWebGL*` globals are `undefined`,
10
- so a WebGL page such as a Three.js scene fails with the library's own
11
- "Error creating WebGL context" and is expected to show its fallback panel.
12
- Only `getContext("2d")` is backed by a rasteriser (see
13
- [Canvas](./canvas.md)). Everything else about a ported page — module graph,
14
- DOM handles, events, the browser globals in [API](./api.md#browser-globals),
15
- the frame pump — works, so a Three.js program *compiles and starts*; it just
16
- cannot draw.
8
+ - **WebGL 1.0 and 2.0 run on the GPU, with a subset of each.**
9
+ `canvas.getContext("webgl")` / `"webgl2"` return real GPU contexts (see
10
+ [WebGL](./webgl.md)): GLSL ES shaders are translated to SPIR-V by the bundled
11
+ glslang — consumed directly by the Vulkan backend, or cross-compiled to MSL by
12
+ the bundled SPIRV-Cross for Metal — and the drawing buffer is an offscreen
13
+ colour texture that the page composites without a readback. WebGL 2 adds VAOs,
14
+ instancing, integer attributes, sampler objects, `texStorage2D` and buffer
15
+ copies. Uniform blocks, transform feedback, cube maps, 3D textures,
16
+ float/depth textures, compressed textures, multiple render targets,
17
+ `blitFramebuffer`, MSAA and stencil operations are not implemented; `clear`
18
+ ignores the scissor rectangle. On an SDL_GPU backend that accepts neither
19
+ SPIR-V nor MSL (Direct3D 12, which would need DXIL) both context kinds are
20
+ `null`. The `.js` asset loader now patches only three.js's shader unroller;
21
+ `WebGLRenderer` runs on the real backend and there is no Canvas2D fallback.
22
+ Shadow maps (depth textures), cube textures/environment maps and HDR render
23
+ targets remain unsupported.
24
+
25
+
17
26
 
18
27
  - **Intermittent crash at auto-close.** `XT_GUI_AUTOCLOSE_MS` can end a run
19
28
  with a native access violation in roughly two runs out of five; a clean exit
@@ -44,6 +53,23 @@
44
53
  *class change* on a flex container has also been observed to crash the same
45
54
  way; `examples/gui/pelican-bike` reveals its game-over overlay with `opacity`
46
55
  instead.
56
+ - **CSS coverage is a deliberate subset.** Grid, `clamp()`/`min()`/`max()`/`env()`,
57
+ `var()` custom properties, the `inset` shorthand, `box-sizing`, `visibility` and
58
+ `pointer-events`, `@media` queries, generated content (`::before`/`::after`),
59
+ multi-layer linear/radial backgrounds, `text-shadow`, `clip-path`
60
+ (`inset`/`circle`/`ellipse`/`polygon`) and `filter` colour functions are
61
+ implemented. Known limits: `mix-blend-mode`, `background-clip`,
62
+ `background-position`/`size`/`repeat`, repeating gradients and `backdrop-filter`
63
+ compositing are not implemented (`backdrop-filter` parses and cascades, but
64
+ sampling the backdrop needs an offscreen layer, so it paints nothing);
65
+ `filter: blur()` is a nine-tap per-shape spread rather than a true group blur,
66
+ so overlapping descendants can darken; `drop-shadow` is approximated from box
67
+ silhouettes; at most two nested `clip-path` shapes are intersected (deeper
68
+ nesting keeps the outer two); and images/canvases are not colour-filtered
69
+ except for the brightness/opacity tint the image shader can carry. Canvas
70
+ textures are sampled nearest-neighbour. Background gradients remain solid
71
+ bands (18 for linear, 32 concentric ellipses for radial), so a soft gradient
72
+ may show slight banding.
47
73
  - **SVG is NanoSVG's subset.** SVG *files* (`<img src="logo.svg">`) and inline
48
74
  `<svg>` trees share the vendored NanoSVG parser/rasteriser
49
75
  (`runtime/ext_gui/svg.cpp`, `inline_svg.cpp`), which renders
@@ -72,23 +98,30 @@
72
98
  MSVC-targeting clang as the driver (the LLVM bundled with Visual Studio when
73
99
  no newer one is on `PATH`), so a developer prompt is not required.
74
100
  - Windows: an MSVC-compatible `gui.lib` (COFF objects + `ar -M`/`llvm-ar`) is
75
- produced by `scripts/build-gui.ts` but is not validated in CI, so the Windows
76
- step is provisional (the job stays green) and `package` skips it. Building and
77
- running `examples/gui/pelican-svg` and `examples/gui/pelican-bike` on a
78
- developer machine works. SDL_GPU selects the **Vulkan** backend there
79
- (`win.driver()` reports `"vulkan"`), so the archive needs no DXIL/`dxc`
80
- toolchain; the hosted Windows runners have no GPU/Vulkan driver, which is why
81
- the CI run stays provisional.
82
- - **Media on Windows:** the `media` extension is POSIX-only for now. The GUI
83
- engine itself builds on Windows, but without the extension `<video>`/`<audio>`
84
- are inert there (an open fails and fires `error`), and `--ext media` reports an
85
- actionable error instead of linking. Enabling it needs an MSVC-compatible
86
- FFmpeg build and is deferred until the Windows GUI path itself is validated.
87
- `<video controls>` is likewise deferred — the intended pattern is TS + a CSS
88
- overlay, as in `examples/gui/media.ts`.
101
+ produced by `scripts/build-gui.ts`. The Windows x64 CI job builds it with the
102
+ Visual Studio toolchain (`.github/actions/vs-toolchain` imports `vcvarsall`
103
+ and uses VS's bundled LLVM), links a `gui,media` program, and ships it in the
104
+ x64 release. *Running* the examples on the hosted runner stays provisional
105
+ because it has no GPU/Vulkan driver (SDL_GPU selects the **Vulkan** backend
106
+ there, so no DXIL/`dxc` toolchain is needed). Windows arm64 still skips both
107
+ archives. Building and running `examples/gui/pelican-svg` and
108
+ `examples/gui/pelican-bike` on a developer machine works.
109
+ - **Windows: x64 builds in CI, arm64 and runtime validation stay provisional.**
110
+ `media.lib` is produced by `scripts/build-media.ts` with FFmpeg's MSVC
111
+ toolchain (`--toolchain=msvc --cc=clang-cl`) so its COFF objects and static CRT
112
+ match `gui.lib`, and `--ext media` links and plays there. Building it needs a
113
+ POSIX shell to run FFmpeg's `configure` and GNU make; the shell comes from Git
114
+ for Windows (already needed to clone SDL3), and make is a native GNU make
115
+ (Chocolatey's `make`, or `xbintsc_MAKE`). CI installs make explicitly,
116
+ builds `gui.lib` + `media.lib` on Windows x64 (using the same Visual Studio
117
+ `vcvarsall` + bundled LLVM as a local build), links a `gui,media` program, and
118
+ ships both archives in the Windows x64 release; the example *run* remains
119
+ provisional because hosted runners have no GPU/Vulkan driver. Windows arm64
120
+ still skips both archives, and `<video controls>` is likewise deferred — the
121
+ intended pattern is TS + a CSS overlay, as in `examples/gui/media.ts`.
89
122
  - **D3D12 shaders:** the SPIR-V (Vulkan) and DXIL (Direct3D 12) blobs are built
90
123
  and embedded, so the non-Metal path no longer skips geometry. DXIL remains
91
124
  unusable on **SDL 3.2.10**: its D3D12 backend rejects any graphics pipeline
92
125
  whose shaders declare a uniform buffer, and the engine passes the viewport that
93
126
  way. This does not affect Windows in practice because SDL_GPU selects Vulkan
94
- there; re-check after an SDL upgrade if the D3D12 path is ever forced.
127
+ there; re-check after an SDL upgrade if the D3D12 path is ever forced.
package/doc/gui/layout.md CHANGED
@@ -32,6 +32,24 @@ with absolute (viewport-relative) geometry:
32
32
  Item margins are part of the item's outer size on both axes, so a negative
33
33
  `margin-top` pulls an item up instead of being ignored. Whitespace-only text
34
34
  between items (source indentation) does not become a flex item.
35
+ - **Grid** — `display: grid` with `grid-template-columns` / `grid-template-rows`
36
+ (fixed lengths, `auto`, `<n>fr`, `minmax(...)`, `repeat(...)`), `gap` /
37
+ `row-gap` / `column-gap`, row-major auto-placement, simple `grid-column` /
38
+ `grid-row` line placement, and `align/justify-items` plus
39
+ `align/justify-self` (including `place-items` / `place-self`). Implicit rows
40
+ and columns grow from the items; a single-cell grid with `place-items: center` centres a card in the whole overlay. Item measurement is a two-pass
41
+ layout (intrinsic size, then final placement), so text fragments are cleared
42
+ between passes.
43
+ - **CSS value functions and custom properties** — `clamp(min, value, max)`,
44
+ `min(...)` and `max(...)` keep their arguments unresolved in the computed style
45
+ and are evaluated at layout time against the element's font size and the
46
+ viewport (`vw`/`vh`); `env(safe-area-inset-*)` resolves to zero in a desktop
47
+ window. `var(--name[, fallback])` is substituted from inherited custom
48
+ properties during the cascade. `inset` is the four-value
49
+ `top/right/bottom/left` shorthand, `box-sizing: border-box` subtracts padding
50
+ and borders from an explicit width/height, `visibility: hidden` skips painting
51
+ and hit testing, and `pointer-events: none` makes a box transparent to input
52
+ (its descendants can opt back in with `auto`).
35
53
  - **Positioning** — `position: relative` offsets a box (and its subtree) by
36
54
  `top`/`right`/`bottom`/`left` without changing the space it reserved;
37
55
  `position: absolute` (and `fixed`, which resolves against the viewport) takes
package/doc/gui/media.md CHANGED
@@ -68,21 +68,24 @@ clip paths are not rendered (see [Known issues](./known-issues.md)).
68
68
  Media playback is split across two extensions so a GUI program does not pay for
69
69
  a decoder it never uses:
70
70
 
71
- - **`gui`** (`gui.a`) owns the `<video>`/`<audio>` elements, the DOM API, the
72
- renderer integration and the SDL audio sink, but contains **no FFmpeg**.
73
- `runtime/ext_gui/media.{h,cpp}` declares the player interface and dispatches
74
- through a table the media backend installs at startup
71
+ - **`gui`** (`gui.a`/`gui.lib`) owns the `<video>`/`<audio>` elements, the DOM
72
+ API, the renderer integration and the SDL audio sink, but contains **no
73
+ FFmpeg**. `runtime/ext_gui/media.{h,cpp}` declares the player interface and
74
+ dispatches through a table the media backend installs at startup
75
75
  (`runtime/ext_gui/media_api.h`).
76
- - **`media`** (`media.a`, `runtime/ext_media/`) is the optional decoder
77
- backend: `runtime/ext_media/media.cpp` wraps a statically vendored **FFmpeg**
78
- build, and `runtime/ext_media/media_bridge.c` registers it with the engine.
79
- Enable it with `--ext media` (or `--ext gui,media`). `media` requires `gui`;
80
- the registry rejects the combination in the wrong order.
76
+ - **`media`** (`media.a`, or `media.lib` with the MSVC ABI on Windows;
77
+ `runtime/ext_media/`) is the optional decoder backend:
78
+ `runtime/ext_media/media.cpp` wraps a statically vendored **FFmpeg** build,
79
+ and `runtime/ext_media/media_bridge.c` registers it with the engine. Enable it
80
+ with `--ext media` (or `--ext gui,media`). `media` requires `gui`; the
81
+ registry rejects the combination in the wrong order.
81
82
 
82
83
  Without the `media` extension the element still exists and its DOM API is
83
- callable, but opening a source fails and fires `error`. That is also how Windows
84
- behaves today: the archive is POSIX-only. There is no WebView and no platform
85
- media framework in the path either way.
84
+ callable, but opening a source fails and fires `error`. The same happens on any
85
+ host where the archive has not been built (run `npm run media`); Windows is no
86
+ longer special: `media.lib` is FFmpeg compiled with clang-cl for the MSVC ABI,
87
+ so it links against `gui.lib` and plays normally. There is no WebView and no
88
+ platform media framework in the path either way.
86
89
 
87
90
  - **Decoding.** `avformat_open_input` opens the `src` (a path — `file://` is
88
91
  accepted, but media paths are *not* percent-decoded, unlike images), the
@@ -104,7 +107,8 @@ media framework in the path either way.
104
107
  push mode (`NULL` callback), buffering about 0.25 s ahead at 48 kHz stereo
105
108
  `SDL_AUDIO_F32`. `volume` maps to `SDL_SetAudioStreamGain`; audio is
106
109
  best-effort and is skipped when no device is available (headless CI). Keeping
107
- the sink in `gui.a` means `media.a` depends only on FFmpeg.
110
+ the sink in the GUI archive means `media.a`/`media.lib` depends only on
111
+ FFmpeg.
108
112
  - **Threading.** The engine is single-threaded and not thread-aware, so all
109
113
  decoding happens on the main thread inside `xt_gui_media_tick` — no decode
110
114
  thread, no cross-thread GC exposure.
@@ -10,8 +10,9 @@ emits a backend-agnostic `DisplayList`: a painter-order `ops` list of
10
10
  **rectangles** (backgrounds and four solid border edges, with `border-radius`),
11
11
  **text runs** (each carrying its text, colour and resolved `FontSpec`) and
12
12
  **images**, backed by parallel payload arrays. Each op also carries the active
13
- clip rectangle (the padding box of the nearest `overflow: hidden` ancestor), so
14
- `overflow` clipping and `z-index` ordering survive into the renderer.
13
+ clip rectangle (the padding box of the nearest `overflow: hidden` ancestor) and
14
+ up to two indices into the display list's triangulated `clip-path` polygons, so
15
+ `overflow`/`clip-path` clipping and `z-index` ordering survive into the renderer.
15
16
  `DisplayList::dump()` feeds `paintList()`/`paintCount()`.
16
17
 
17
18
  `opacity` is honoured only at its extremes: a box whose computed `opacity` is
@@ -73,7 +74,53 @@ divided back into logical units, exactly the way HiDPI scaling already worked.
73
74
 
74
75
  The window background (`setBackground`) is the render-pass clear colour.
75
76
 
76
- Still to do in M4: gradients.
77
+ ### Backgrounds, generated content and effects (M19)
78
+
79
+ `background-color` and every `background-image` layer are painted. A
80
+ comma-separated layer list is painted bottom-up (the first declaration layer is
81
+ topmost), and both gradient kinds are supported:
82
+
83
+ - a **linear** gradient is sampled at 18 points and emitted as solid rectangles;
84
+ `to <side>`, `deg`/`grad`/`rad`/`turn`, percentage and multi-stop colours work
85
+ (the band walk is axis-aligned, so an oblique angle uses the nearest axis);
86
+ - a **radial** gradient is 32 concentric ellipses. Each is a unit-radius rounded
87
+ rectangle mapped through the shape pipeline's transform slot, so a circle or
88
+ ellipse keeps the distance-field antialiasing. Shape/size keywords
89
+ (`circle`/`ellipse`, `closest-side`/`farthest-side`/`closest-corner`/
90
+ `farthest-corner`, explicit radii) and `at <position>` are parsed.
91
+
92
+ The background colour sits under every layer. Gradients are partially
93
+ transparent if their stops are, and the band approximation can show banding and
94
+ alpha stacking on a very translucent multi-stop gradient.
95
+
96
+ **Generated content** needs no new display-list kind: `::before`/`::after` are
97
+ ordinary layout boxes owned by the per-restyle arena in `style.cpp`, so their
98
+ background, border, text and transforms flow through the same paint walk.
99
+ `content` is resolved from strings, `attr()` and open/close quotes.
100
+
101
+ **`text-shadow`** is emitted as shadow `PaintText` runs *before* the real run, so
102
+ the text pipeline draws them without a new shader. A blurred shadow is nine
103
+ translucent copies spread around the offset (the same approximation `filter:
104
+ blur()` uses).
105
+
106
+ **`clip-path`** is resolved in `paint_clip.cpp` to a device-space polygon (the
107
+ box transform is applied to every point), triangulated with ear clipping, and
108
+ stored on `DisplayList::clip_paths`. Ops carry up to two path indices on their
109
+ `PaintClip`; `renderer_clip.cpp` intersects each convex shape/glyph/image quad
110
+ with those triangles at upload time. The result is exact even for concave
111
+ `polygon()` shapes, needs no stencil or shader change, and the edges are hard
112
+ (no analytic antialiasing). Intersecting two nested paths is supported; deeper
113
+ nesting keeps the outermost two.
114
+
115
+ **`filter`** colour functions (`brightness`, `contrast`, `grayscale`, `sepia`,
116
+ `invert`, `saturate`, `hue-rotate`, `opacity`) are folded into each painted
117
+ colour, so a box and its descendants see the filter. `blur()` and
118
+ `drop-shadow()` are approximations at the display-list level: blur spreads nine
119
+ translucent copies per shape, drop-shadow is a box/text silhouette. This keeps
120
+ the single render pass — there is no offscreen render target — at the cost of
121
+ group semantics: overlapping descendants of a blurred/filtered group can darken,
122
+ and `backdrop-filter` cannot sample what is behind the element, so it parses and
123
+ cascades but paints nothing.
77
124
 
78
125
  ## Text stack (M4b)
79
126