xbintsc 0.3.46 → 0.3.49
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.
- package/AGENTS.md +95 -0
- package/README.md +25 -0
- package/README.zh-CN.md +23 -0
- package/dist/src/cli/hints.d.ts +54 -0
- package/dist/src/cli/hints.js +165 -0
- package/dist/src/cli/hints.js.map +1 -0
- package/dist/src/cli/main.js +73 -9
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +26 -0
- package/dist/src/codegen/generator/tables.js +64 -12
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/diagnostics/source-text.d.ts +22 -0
- package/dist/src/diagnostics/source-text.js +76 -0
- package/dist/src/diagnostics/source-text.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +2 -1
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/compiler.js +3 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/lexer/scanner/strings.js +16 -3
- package/dist/src/lexer/scanner/strings.js.map +1 -1
- package/dist/tests/cli/hints.test.d.ts +9 -0
- package/dist/tests/cli/hints.test.js +143 -0
- package/dist/tests/cli/hints.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +6 -4
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +17 -2
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/helpers.js +3 -2
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +14 -2
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/DESIGN.md +117 -0
- package/doc/ai/README.md +63 -0
- package/doc/ai/build-recipe.md +137 -0
- package/doc/ai/cli.md +142 -0
- package/doc/ai/contributing.md +196 -0
- package/doc/ai/extensions.md +148 -0
- package/doc/ai/language-support.md +152 -0
- package/doc/ai/troubleshooting.md +163 -0
- package/doc/ai/zh-CN/README.md +56 -0
- package/doc/ai/zh-CN/build-recipe.md +132 -0
- package/doc/ai/zh-CN/cli.md +127 -0
- package/doc/ai/zh-CN/contributing.md +173 -0
- package/doc/ai/zh-CN/extensions.md +139 -0
- package/doc/ai/zh-CN/language-support.md +147 -0
- package/doc/ai/zh-CN/troubleshooting.md +150 -0
- package/doc/gui-scripts.md +350 -0
- package/doc/gui.md +646 -0
- package/doc/icon.md +265 -0
- package/doc/implemented.md +373 -0
- package/doc/node-implemented.md +588 -0
- package/doc/node-unimplemented.md +167 -0
- package/doc/post/announce.md +43 -0
- package/doc/requirements.md +145 -0
- package/doc/unimplemented.md +286 -0
- package/doc/xbintsc.config.schema.json +67 -0
- package/doc/zh-CN/DESIGN.md +104 -0
- package/doc/zh-CN/gui-scripts.md +329 -0
- package/doc/zh-CN/gui.md +588 -0
- package/doc/zh-CN/icon.md +241 -0
- package/doc/zh-CN/implemented.md +365 -0
- package/doc/zh-CN/node-implemented.md +533 -0
- package/doc/zh-CN/node-unimplemented.md +141 -0
- package/doc/zh-CN/plan-require-node-modules.md +284 -0
- package/doc/zh-CN/post/announce.md +47 -0
- package/doc/zh-CN/requirements.md +134 -0
- package/doc/zh-CN/unimplemented.md +247 -0
- package/llms.txt +45 -0
- package/package.json +4 -1
- package/runtime/ext_gui/gui.cpp +3 -1
- package/runtime/ext_gui/renderer.cpp +13 -11
- package/runtime/ext_gui/renderer_image.cpp +12 -8
- package/runtime/ext_gui/renderer_shaders.h +131 -4
- package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
- package/runtime/ext_gui/renderer_text.cpp +12 -8
- package/runtime/ext_gui/shaders.hlsl +98 -0
- package/runtime/ext_gui/spirv/fill.frag +19 -0
- package/runtime/ext_gui/spirv/fill.vert +42 -0
- package/runtime/ext_gui/spirv/image.frag +16 -0
- package/runtime/ext_gui/spirv/quad.vert +30 -0
- package/runtime/ext_gui/spirv/text.frag +16 -0
- package/scripts/build-gui-shaders.mjs +204 -0
- package/scripts/build-gui.ts +35 -0
- package/scripts/check-file-length.ts +5 -1
- package/src/cli/hints.ts +194 -0
- package/src/cli/main.ts +82 -9
- package/src/codegen/generator/tables.ts +60 -14
- package/src/diagnostics/source-text.ts +78 -0
- package/src/driver/bundler/graph.ts +2 -1
- package/src/driver/compiler.ts +3 -2
- package/src/lexer/scanner/strings.ts +16 -3
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# GUI extension — DOM handles, events and AOT `<script>`
|
|
2
|
+
|
|
3
|
+
Status: **design** — this document specifies how the `gui` extension grows from
|
|
4
|
+
read-only HTML/CSS rendering (M7) into a small, *interactive* DOM with
|
|
5
|
+
**compile-time (AOT) scripts**, without adding a JavaScript engine and without
|
|
6
|
+
letting GUI concerns leak into the core compiler.
|
|
7
|
+
|
|
8
|
+
It is the plan behind milestones **M8** (DOM object model + mutation + element
|
|
9
|
+
events) and **M9** (AOT `<script>` pipeline). The locked decisions in
|
|
10
|
+
`doc/gui.md` still hold; this document only refines what "no page JS" means.
|
|
11
|
+
|
|
12
|
+
## Motivation
|
|
13
|
+
|
|
14
|
+
Today the engine renders an HTML/CSS tree and exposes only *window-level*
|
|
15
|
+
events (`win.on("click", …)`). The handler receives `e.target` as a CSS
|
|
16
|
+
descriptor string (`div#main.card`), the DOM is read-only (diagnostics only),
|
|
17
|
+
and the only way to change the UI is `win.loadHTML(...)`, which rebuilds
|
|
18
|
+
everything.
|
|
19
|
+
|
|
20
|
+
That is enough for static demos, but the obvious next question — "can I write
|
|
21
|
+
`<script>` and react to clicks on a specific element?" — has no answer yet.
|
|
22
|
+
xbintsc is a **pure AOT compiler** and has **no runtime interpreter/JIT**, so
|
|
23
|
+
`eval`-style script execution is impossible. The only self-consistent way to
|
|
24
|
+
run scripts is to **compile them ahead of time**.
|
|
25
|
+
|
|
26
|
+
## Goals
|
|
27
|
+
|
|
28
|
+
1. **Element handles** — `document.querySelector(...)` returns an object with
|
|
29
|
+
stable identity (`a === b` for the same element) and readable/writable DOM
|
|
30
|
+
properties (`id`, `className`, `textContent`, `innerHTML`, `style`,
|
|
31
|
+
`classList`, attributes, traversal, geometry).
|
|
32
|
+
2. **Mutation** — `appendChild` / `removeChild` / `insertBefore` /
|
|
33
|
+
`replaceChild` / `textContent=` / `innerHTML=` / `classList.*` /
|
|
34
|
+
`style.setProperty` update the tree; the engine restyles + relayouts lazily
|
|
35
|
+
and repaints.
|
|
36
|
+
3. **Element events** — `el.addEventListener(type, fn, options?)` with capture
|
|
37
|
+
and bubble phases, `removeEventListener`, `dispatchEvent`, `el.click()`,
|
|
38
|
+
event objects with `target` / `currentTarget` / `preventDefault` /
|
|
39
|
+
`stopPropagation`.
|
|
40
|
+
4. **AOT `<script>`** — a `<script>` body written in TypeScript is extracted at
|
|
41
|
+
**compile time**, compiled by the *existing* xbintsc front-end as a normal
|
|
42
|
+
module, and invoked by the engine after the document is parsed.
|
|
43
|
+
5. **Zero core-compiler coupling** — the lexer/parser/binder/codegen learn
|
|
44
|
+
nothing about HTML or the GUI. Extensions contribute only modules/objects
|
|
45
|
+
(and, new in M9, an **asset-loader hook**).
|
|
46
|
+
6. **Backward compatibility** — existing programs and tests keep working:
|
|
47
|
+
`win.on(...)` handlers keep receiving a *string* `e.target`.
|
|
48
|
+
|
|
49
|
+
## Non-goals
|
|
50
|
+
|
|
51
|
+
- **A JS engine** (QuickJS/V8/…). Still explicitly out of scope. There is no
|
|
52
|
+
runtime `eval`, no interpreter, no JIT.
|
|
53
|
+
- **Dynamic scripts at runtime.** Scripts are compile-time assets. HTML fetched
|
|
54
|
+
over the network or produced at runtime does **not** execute its scripts.
|
|
55
|
+
- **A security sandbox / browser semantics.** There is no origin model; a
|
|
56
|
+
script is ordinary native TS and can `import` anything the compiler allows.
|
|
57
|
+
This is documented as "not a browser".
|
|
58
|
+
- **Full DOM/BOM.** We implement the practical subset a UI needs.
|
|
59
|
+
|
|
60
|
+
## Locked decisions (refined)
|
|
61
|
+
|
|
62
|
+
| # | Decision |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| 1 | **No page JS engine.** Behaviour is native TS. `<script>` bodies are *AOT-compiled* to native code by xbintsc and invoked through `xt_call_with_this` — there is still no interpreter. |
|
|
65
|
+
| 8 | **Scripts are compile-time assets.** The HTML asset loader transforms inline `<script>` bodies into compiled modules and replaces them with `data-xt-id` markers; the runtime only *invokes* already-compiled functions. |
|
|
66
|
+
| 9 | **The DOM is owned by the engine; handles are engine objects.** Handles use the same shared-prototype object model as window handles; hidden fields carry a window reference, a document generation and a node index. |
|
|
67
|
+
| 10 | **`window`/`document` are injected as parameters**, not globals, so no global-object machinery is added to the compiler. |
|
|
68
|
+
|
|
69
|
+
## Architecture
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
<script>…</script> compile time (bundler)
|
|
73
|
+
│ gui .html asset loader
|
|
74
|
+
▼
|
|
75
|
+
┌──────────────────────┐ ┌───────────────────────────┐
|
|
76
|
+
│ transform the HTML │ │ emit __xt_script_<hash> │ a normal
|
|
77
|
+
│ body → function │ │ (window, document) │ xbintsc
|
|
78
|
+
│ <script data-xt-id> │ │ + __registerScript(hash) │ module
|
|
79
|
+
└──────────────────────┘ └───────────────────────────┘
|
|
80
|
+
│ │
|
|
81
|
+
▼ ▼
|
|
82
|
+
runtime HTML string native code linked into the binary
|
|
83
|
+
│ win.loadHTML(html)
|
|
84
|
+
▼
|
|
85
|
+
engine parses HTML, finds `data-xt-id` markers, looks each hash up in the
|
|
86
|
+
script registry and calls `fn(windowHandle, documentHandle)`
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 1. `Extension.assetLoaders` (the only new core hook)
|
|
90
|
+
|
|
91
|
+
The core compiler currently has no way for an extension to influence *how a
|
|
92
|
+
file is loaded*. M9 adds one generic hook to `src/extensions/registry.ts`:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
interface AssetLoadResult {
|
|
96
|
+
/** Replaces the file contents as seen by the bundler. */
|
|
97
|
+
moduleSource: string;
|
|
98
|
+
/** Additional files this asset depends on (recompiled when they change). */
|
|
99
|
+
dependencies?: string[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface Extension {
|
|
103
|
+
// …existing fields…
|
|
104
|
+
/** Optional: rewrite an imported asset into a TS module. Keyed by extension. */
|
|
105
|
+
assetLoaders?(): Record<string, (path: string, source: string) => AssetLoadResult>;
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The bundler (`src/driver/bundler/graph.ts`) consults the registry immediately
|
|
110
|
+
after `readFileSync`, *before* parsing. The core stays platform-agnostic: it
|
|
111
|
+
only knows "an extension may transform the bytes of `*.foo` into TS".
|
|
112
|
+
The `gui` extension registers an `.html` loader. A plain `import html from
|
|
113
|
+
"./index.html"` therefore yields a **string constant** by default, and (when
|
|
114
|
+
the file contains `<script>`) a **side-effecting module** that registers the
|
|
115
|
+
compiled script bodies and default-exports the rewritten HTML.
|
|
116
|
+
|
|
117
|
+
### 2. The `.html` transform
|
|
118
|
+
|
|
119
|
+
Given:
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<button id="b">0</button>
|
|
123
|
+
<script lang="ts">
|
|
124
|
+
const b = document.getElementById("b");
|
|
125
|
+
let n = 0;
|
|
126
|
+
b.addEventListener("click", () => { b.textContent = String(++n); });
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
the loader emits a module shaped like:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// generated
|
|
134
|
+
const __html = "<button id=\"b\">0</button>\n<script data-xt-id=\"a1b2…\"></script>";
|
|
135
|
+
function __xt_script_a1b2(window: any, document: any): void {
|
|
136
|
+
const b = document.getElementById("b");
|
|
137
|
+
let n = 0;
|
|
138
|
+
b.addEventListener("click", () => { b.textContent = String(++n); });
|
|
139
|
+
}
|
|
140
|
+
__registerScript("a1b2…", __xt_script_a1b2);
|
|
141
|
+
export default __html;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- The id is a content hash of the body (sha256, first 32 hex chars); the body is
|
|
145
|
+
replaced by an empty marker element so the engine can find it in document
|
|
146
|
+
order.
|
|
147
|
+
- `window` and `document` are **function parameters**, so script globals resolve
|
|
148
|
+
as ordinary locals — **no compiler changes**, no global object.
|
|
149
|
+
- `__registerScript(id, fn)` is a new runtime builtin that stores the closure in
|
|
150
|
+
a global `unordered_map<string, xt_value>`.
|
|
151
|
+
- Deferred scripts (`<script defer>`, or all inline scripts, as decided in M9c)
|
|
152
|
+
run after the full document is parsed and the first layout is computed, in
|
|
153
|
+
document order. `DOMContentLoaded` then `load` are fired on `window`.
|
|
154
|
+
|
|
155
|
+
### 2b. External `<script src>`
|
|
156
|
+
|
|
157
|
+
`<script src="./app.ts">` is read relative to the HTML file, parsed, and split:
|
|
158
|
+
its top-level `import`/`export … from` statements are **hoisted** to the
|
|
159
|
+
generated module (with relative specifiers rewritten so they resolve from the
|
|
160
|
+
HTML file's directory), and the remaining statements are wrapped in the script
|
|
161
|
+
function. This is how an external script still runs at `loadHTML` time while
|
|
162
|
+
being able to `import` other modules.
|
|
163
|
+
|
|
164
|
+
Two limitations are enforced with clear errors rather than silent breakage:
|
|
165
|
+
|
|
166
|
+
- a missing `src` file throws (turned into a bundler diagnostic), and
|
|
167
|
+
- two `src` scripts that import the same local binding name collide; the user
|
|
168
|
+
must alias one of them. (Non-conflicting or identical imports are fine.)
|
|
169
|
+
|
|
170
|
+
`src` URLs (`https://…`, `data:…`, `//host`) are left untouched — they are not
|
|
171
|
+
compile-time assets and never run.
|
|
172
|
+
|
|
173
|
+
```html
|
|
174
|
+
<button id="b">0</button>
|
|
175
|
+
<script src="./counter.ts"></script>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// counter.ts
|
|
180
|
+
import { double } from "./helper";
|
|
181
|
+
const b = document.getElementById("b");
|
|
182
|
+
let n = 0;
|
|
183
|
+
b.addEventListener("click", () => { n = double(n) + 1; b.textContent = String(n); });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 3. Runtime execution
|
|
187
|
+
|
|
188
|
+
`win.loadHTML(html)`:
|
|
189
|
+
|
|
190
|
+
1. parses the document as today,
|
|
191
|
+
2. scans for `data-xt-id` markers,
|
|
192
|
+
3. for each, calls `registry[id](winHandle, docHandle)`,
|
|
193
|
+
4. triggers the first layout, then fires `DOMContentLoaded` and `load`.
|
|
194
|
+
|
|
195
|
+
Because the calls happen after parsing, `document.getElementById(...)` inside a
|
|
196
|
+
script always finds its element. `innerHTML = …` never executes embedded
|
|
197
|
+
scripts (markers are inert).
|
|
198
|
+
|
|
199
|
+
## Element handle object model (M8)
|
|
200
|
+
|
|
201
|
+
Handles are plain runtime objects (`xt_object_new_with_proto`) sharing a
|
|
202
|
+
prototype per kind, exactly like window handles:
|
|
203
|
+
|
|
204
|
+
| Hidden field | Meaning |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `__xt_gui_win` | owning window object (resolved by `xt_gui_window_from_this`) |
|
|
207
|
+
| `__xt_gui_gen` | document generation the handle was created for |
|
|
208
|
+
| `__xt_gui_node` | index into the window's node table (element/text handles) |
|
|
209
|
+
| `__xt_gui_doc` | `true` for the singleton `document` handle |
|
|
210
|
+
|
|
211
|
+
**Identity is stable**: the window keeps `node_order: vector<Node*>` plus
|
|
212
|
+
`node_index`/`node_handles: map<Node*, …>`, so querying the same element twice
|
|
213
|
+
returns the *same* object value (`a === b`).
|
|
214
|
+
|
|
215
|
+
**Detached nodes** stay alive in the document pool, so a handle held across a
|
|
216
|
+
`remove()` still resolves (tombstoned semantics); a generation mismatch makes a
|
|
217
|
+
stale handle a no-op instead of a dangling pointer.
|
|
218
|
+
|
|
219
|
+
### Accessors, not fields
|
|
220
|
+
|
|
221
|
+
Read/write DOM properties are defined with `xt_object_define_getter` /
|
|
222
|
+
`xt_object_define_setter`, which already support prototype inheritance with
|
|
223
|
+
`this` = receiver. So `el.textContent = "x"` and `el.id = "y"` work while the
|
|
224
|
+
values live in the C++ tree. `classList` and `style` are small objects that hold
|
|
225
|
+
a back-reference to the element handle (no closures/env needed; the arena never
|
|
226
|
+
frees, so cycles are harmless).
|
|
227
|
+
|
|
228
|
+
### Implemented surface (M8)
|
|
229
|
+
|
|
230
|
+
- `document`: `querySelector`, `querySelectorAll`, `getElementById`,
|
|
231
|
+
`createElement`, `createTextNode`, `body`, `documentElement`,
|
|
232
|
+
`addEventListener` / `removeEventListener` / `dispatchEvent`.
|
|
233
|
+
- node (element/text/document): `tagName` / `nodeName` / `nodeType`, `id`,
|
|
234
|
+
`className`, `textContent` / `innerText`, `innerHTML`, `parentNode` /
|
|
235
|
+
`parentElement`, `children` / `childNodes` / `childElementCount`,
|
|
236
|
+
`firstElementChild` / `lastElementChild`, `nextElementSibling` /
|
|
237
|
+
`previousElementSibling`, `isConnected`, `classList`, `style`.
|
|
238
|
+
- attributes: `getAttribute` / `setAttribute` / `hasAttribute` /
|
|
239
|
+
`removeAttribute`.
|
|
240
|
+
- queries: `querySelector` / `querySelectorAll` / `matches` (subtree-scoped).
|
|
241
|
+
- mutation: `appendChild` / `insertBefore` / `removeChild` / `replaceChild` /
|
|
242
|
+
`remove` / `cloneNode(deep?)`.
|
|
243
|
+
- geometry/focus: `getBoundingClientRect`, `focus`, `blur`, `click`.
|
|
244
|
+
- events: `addEventListener(type, fn, optionsOrCapture?)`,
|
|
245
|
+
`removeEventListener`, `dispatchEvent`.
|
|
246
|
+
|
|
247
|
+
### Lazy restyle / relayout
|
|
248
|
+
|
|
249
|
+
Every mutation calls `XtDocument::invalidate()` and sets `win->struct_dirty`.
|
|
250
|
+
Before any synchronous read (`computedStyle`, `getBoundingClientRect`,
|
|
251
|
+
`queryCount`, `layoutTree`, `paintList`, `hitTest`, …) and before each frame,
|
|
252
|
+
`xt_gui_flush_dom()` restyles + relayouts **once** if anything is pending. This
|
|
253
|
+
keeps mutations cheap and batched while making reads immediately consistent.
|
|
254
|
+
|
|
255
|
+
## Events and bubbling
|
|
256
|
+
|
|
257
|
+
Element listeners are stored on the window (`node_listeners`), keyed by node
|
|
258
|
+
(not on `Node` itself, so the tree stays layout-focused).
|
|
259
|
+
|
|
260
|
+
Dispatch:
|
|
261
|
+
|
|
262
|
+
1. build the propagation path `target → … → root`,
|
|
263
|
+
2. create an Event object (`type`, `target` = node handle, `currentTarget`,
|
|
264
|
+
`bubbles`, `defaultPrevented`, internal `__stop` / `__stopImmediate`),
|
|
265
|
+
3. **capture** phase: root → target (listeners registered with `capture: true`),
|
|
266
|
+
4. **bubble** phase: target → root,
|
|
267
|
+
5. legacy window-level `win.on(type, fn)` handlers run **last**.
|
|
268
|
+
|
|
269
|
+
`once` listeners are removed before being called; `stopPropagation` ends the
|
|
270
|
+
current phase chain and `stopImmediatePropagation` also skips later listeners on
|
|
271
|
+
the same node. `el.click()` synthesises `click` at that node (DOM only);
|
|
272
|
+
real pointer/wheel/key input dispatches **both** the DOM event and the legacy
|
|
273
|
+
window payload.
|
|
274
|
+
|
|
275
|
+
### Backward compatibility
|
|
276
|
+
|
|
277
|
+
The legacy window payload keeps its **string** `e.target`
|
|
278
|
+
(`tests/e2e/gui-*.test.ts` asserts `e.target === "div#inner"`). Only the *element*
|
|
279
|
+
Event's `target` is a handle, whose descriptor (`toString`) matches the same
|
|
280
|
+
`div#id.class` string. `make_dom_event` deliberately does **not** copy the
|
|
281
|
+
legacy `target` field over the handle.
|
|
282
|
+
|
|
283
|
+
## Milestones
|
|
284
|
+
|
|
285
|
+
- **M8 — DOM object model + mutation + element events** ✅
|
|
286
|
+
- `runtime/ext_gui/dom_api.{h,cpp}`, `document.{h,cpp}`, `dom.{h,cpp}`,
|
|
287
|
+
`gui_engine.h`, `gui.cpp`, `window.cpp`, e2e coverage.
|
|
288
|
+
- No compiler changes; independently testable.
|
|
289
|
+
- **M9 — AOT `<script>`** ✅
|
|
290
|
+
- **M9a** ✅ — `Extension.assetLoaders` in `src/extensions/registry.ts` +
|
|
291
|
+
bundler integration in `src/driver/bundler/{graph,merge}.ts`; unit tests.
|
|
292
|
+
- **M9b** ✅ — gui `.html` loader (`src/extensions/gui/html.ts`), the
|
|
293
|
+
`__registerScript` builtin, the script registry (`runtime/ext_gui/script.cpp`),
|
|
294
|
+
`loadHTML` execution and `DOMContentLoaded`/`load`; e2e coverage.
|
|
295
|
+
- **M9c** ✅ — `<script src>` files are read, their top-level imports are
|
|
296
|
+
hoisted (specifiers rewritten to resolve from the HTML file) and their body
|
|
297
|
+
is wrapped and registered; missing files and import-binding collisions
|
|
298
|
+
surface as diagnostics. All scripts run in document order (effectively
|
|
299
|
+
deferred). e2e coverage.
|
|
300
|
+
- **M9d** *(optional)* — detect inline HTML in template literals passed to
|
|
301
|
+
`win.loadHTML(...)` and transform them too (fragile; deferred).
|
|
302
|
+
- **M10 — polish** ✅ — `requestAnimationFrame`/`cancelAnimationFrame` on the
|
|
303
|
+
window, `offsetWidth`/`offsetHeight`/`contains` on elements, docs.
|
|
304
|
+
|
|
305
|
+
### Landing order
|
|
306
|
+
|
|
307
|
+
1. M8 (this branch) — pure engine work, no compiler impact.
|
|
308
|
+
2. M9a — the generic hook + bundler wiring (unit-testable with a fake loader).
|
|
309
|
+
3. M9b — the minimal end-to-end script path (inline `<script>` only).
|
|
310
|
+
4. M9c — external `<script src>`.
|
|
311
|
+
5. M10 — animation frames and helpers.
|
|
312
|
+
|
|
313
|
+
## Open risks
|
|
314
|
+
|
|
315
|
+
- **Handle lifetime.** Solved with a generation counter + detached pool; stale
|
|
316
|
+
handles no-op. `Node*` addresses are never exposed to TS.
|
|
317
|
+
- **No sandbox.** A `<script>` can `import fs`. Documented as "not a browser";
|
|
318
|
+
a real origin/sandbox model is out of scope.
|
|
319
|
+
- **Dynamic HTML scripts.** Scripts in runtime-created HTML (`innerHTML`,
|
|
320
|
+
network fetches) do not run. Documented.
|
|
321
|
+
- **Template-literal HTML** passed to `win.loadHTML(…)` is not transformed in
|
|
322
|
+
M9b (only imported assets are). M9d addresses this if needed.
|
|
323
|
+
- **Import-binding collisions** between external `<script src>` files. Detected
|
|
324
|
+
and reported (alias the import); a per-script rename with reference rewriting
|
|
325
|
+
is possible later.
|
|
326
|
+
- **Assets.** `<script src>` is resolved relative to the HTML asset; URL `src`
|
|
327
|
+
values are ignored. There is no fetch/network loading.
|
|
328
|
+
|
|
329
|
+
## Testing
|
|
330
|
+
|
|
331
|
+
- **M8**: a new e2e case in `tests/e2e/gui-*.test.ts` covering handle identity,
|
|
332
|
+
traversal, attributes, `classList`, inline `style`, `createElement` +
|
|
333
|
+
`appendChild` + `removeChild`, `getBoundingClientRect`, element bubbling,
|
|
334
|
+
`stopPropagation`, `el.click()` and **window-level `e.target` compatibility**.
|
|
335
|
+
- **M9a**: a unit test with a fake extension loader asserting the bundler
|
|
336
|
+
rewrites `*.foo` and records dependencies.
|
|
337
|
+
- **M9b**: an e2e case with an inline `<script>` incrementing a counter on
|
|
338
|
+
click, asserted through `console.log` from the handler.
|
|
339
|
+
- **M10**: e2e cases for `requestAnimationFrame` (runs each frame, re-queues,
|
|
340
|
+
receives a timestamp, honours `cancelAnimationFrame`) and for
|
|
341
|
+
`offsetWidth`/`offsetHeight`/`contains`.
|
|
342
|
+
|
|
343
|
+
## Docs to update
|
|
344
|
+
|
|
345
|
+
- `doc/gui.md` — Non-goals ("Executing page `<script>`" → "Executing *runtime*
|
|
346
|
+
page scripts; compile-time scripts are AOT-compiled"), locked decision #1,
|
|
347
|
+
TS-facing API, milestones, progress log.
|
|
348
|
+
- `doc/implemented.md` / `doc/unimplemented.md` — move the new DOM/script
|
|
349
|
+
capabilities across.
|
|
350
|
+
- `README.md` — mention the DOM API + AOT scripts.
|