xbintsc 0.5.36 → 0.5.37

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 (66) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/driver/version.d.ts +1 -1
  3. package/dist/src/driver/version.js +1 -1
  4. package/dist/src/extensions/gui/index.js +12 -0
  5. package/dist/src/extensions/gui/index.js.map +1 -1
  6. package/dist/tests/e2e/gui-browser.test.js +65 -0
  7. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  8. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  9. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  10. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  11. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  12. package/dist/tests/e2e/gui-dom.test.js +215 -0
  13. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  14. package/dist/tests/e2e/gui-events.test.js +112 -0
  15. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  16. package/doc/gui/api.md +38 -9
  17. package/doc/gui/canvas.md +49 -18
  18. package/doc/gui/dom.md +51 -4
  19. package/doc/zh-CN/gui/api.md +33 -4
  20. package/doc/zh-CN/gui/canvas.md +44 -19
  21. package/doc/zh-CN/gui/dom.md +45 -3
  22. package/package.json +7 -7
  23. package/runtime/ext_gui/browser.cpp +34 -49
  24. package/runtime/ext_gui/browser_abort.cpp +249 -0
  25. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  26. package/runtime/ext_gui/browser_media.cpp +176 -0
  27. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  28. package/runtime/ext_gui/canvas.cpp +88 -30
  29. package/runtime/ext_gui/canvas.h +74 -9
  30. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  31. package/runtime/ext_gui/canvas_internal.h +8 -5
  32. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  33. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  34. package/runtime/ext_gui/canvas_style.cpp +4 -2
  35. package/runtime/ext_gui/dom.h +4 -0
  36. package/runtime/ext_gui/dom_api.cpp +1 -0
  37. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  38. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  39. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  40. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  41. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  42. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  43. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  44. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  45. package/runtime/ext_gui/dom_api_internal.h +55 -2
  46. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  47. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  48. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  49. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  50. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  51. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  52. package/runtime/ext_gui/gui.cpp +59 -5
  53. package/runtime/ext_gui/gui_engine.h +47 -0
  54. package/runtime/ext_gui/gui_events.cpp +80 -9
  55. package/runtime/ext_gui/listener_options.h +43 -0
  56. package/runtime/ext_gui/paint.cpp +28 -0
  57. package/runtime/ext_gui/paint.h +4 -0
  58. package/runtime/ext_gui/window.cpp +14 -65
  59. package/runtime/ext_gui/window_events.cpp +125 -0
  60. package/runtime/rt.h +26 -0
  61. package/runtime/rt_internal.h +4 -0
  62. package/runtime/xt_builtins.c +0 -21
  63. package/runtime/xt_containers/access.inc +94 -2
  64. package/runtime/xt_containers/objects.inc +29 -31
  65. package/src/driver/version.ts +1 -1
  66. package/src/extensions/gui/index.ts +12 -0
@@ -98,5 +98,220 @@ describe.skipIf(!guiAvailable)("gui extension — dom", () => {
98
98
  expect(value("winCompat")).toBe("win:div#inner.warm");
99
99
  expect(value("stopped")).toBe("inner");
100
100
  });
101
+ it("mutates the tree with append/prepend/replaceChildren/insertAdjacentHTML and namespaces", () => {
102
+ const { value } = harness.compileAndRun("dom-mutation", `
103
+ import { createWindow, run } from "gui";
104
+
105
+ const HTML = \`
106
+ <html><body style="margin:0">
107
+ <div id="host"></div>
108
+ </body></html>\`;
109
+
110
+ const SVG_NS = "http://www.w3.org/2000/svg";
111
+ const win = createWindow({ title: "dom-mutation", width: 400, height: 300 });
112
+ win.on("ready", () => {
113
+ const doc = win.document;
114
+ const host: any = doc.getElementById("host");
115
+
116
+ // append/prepend mix nodes and strings, like the DOM.
117
+ host.append("a", doc.createElement("b"));
118
+ console.log("append=" + host.textContent + ":" + host.children.length + ":" +
119
+ host.lastElementChild.tagName);
120
+ host.prepend("z", doc.createElement("i"));
121
+ console.log("prepend=" + host.textContent + ":" + host.firstElementChild.tagName);
122
+
123
+ // insertAdjacentHTML at all four positions.
124
+ host.insertAdjacentHTML("beforebegin", "<p id='pre'>pre</p>");
125
+ host.insertAdjacentHTML("afterbegin", "<b>head</b>");
126
+ host.insertAdjacentHTML("beforeend", "<u>tail</u>");
127
+ host.insertAdjacentHTML("afterend", "<p id='post'>post</p>");
128
+ console.log("adjacent=" + doc.getElementById("pre").textContent + ":" +
129
+ doc.getElementById("post").textContent + ":" +
130
+ host.firstElementChild.tagName + ":" + host.lastElementChild.tagName);
131
+
132
+ // replaceChildren keeps a node argument that used to be a child.
133
+ const keep: any = doc.createElement("em");
134
+ keep.textContent = "keep";
135
+ host.append(keep);
136
+ host.replaceChildren("t", keep);
137
+ console.log("replace=" + host.textContent + ":" + host.children.length + ":" +
138
+ host.firstElementChild.tagName);
139
+
140
+ // createElementNS records the namespace and preserves SVG's case.
141
+ const gradient: any = doc.createElementNS(SVG_NS, "svg:linearGradient");
142
+ console.log("ns=" + gradient.namespaceURI + ":" + gradient.localName + ":" +
143
+ gradient.prefix + ":" + gradient.tagName);
144
+ const rect: any = doc.createElementNS(SVG_NS, "rect");
145
+ console.log("svg-case=" + rect.tagName + ":" + rect.localName + ":" + rect.prefix);
146
+ const html: any = doc.createElementNS("http://www.w3.org/1999/xhtml", "DIV");
147
+ console.log("html-ns=" + html.localName + ":" + html.namespaceURI + ":" + html.tagName);
148
+ win.close();
149
+ });
150
+ win.loadHTML(HTML);
151
+ run();
152
+ `);
153
+ expect(value("append")).toBe("a:1:B");
154
+ expect(value("prepend")).toBe("za:I");
155
+ expect(value("adjacent")).toBe("pre:post:B:U");
156
+ expect(value("replace")).toBe("tkeep:1:EM");
157
+ expect(value("ns")).toBe("http://www.w3.org/2000/svg:linearGradient:svg:linearGradient");
158
+ expect(value("svg-case")).toBe("rect:rect:null");
159
+ expect(value("html-ns")).toBe("div:http://www.w3.org/1999/xhtml:DIV");
160
+ });
161
+ it("honours the style object, computed style and geometry arguments", () => {
162
+ const { value } = harness.compileAndRun("dom-style-args", `
163
+ import { createWindow, run } from "gui";
164
+
165
+ const HTML = \`
166
+ <html><head><style>
167
+ body { margin: 0; }
168
+ #box { width: 100px; height: 40px; margin-top: 10px; }
169
+ #box::before { content: "hi"; color: #123456; }
170
+ </style></head><body>
171
+ <div id="box">x</div>
172
+ </body></html>\`;
173
+
174
+ const win = createWindow({ title: "dom-style-args", width: 400, height: 300 });
175
+ win.on("ready", () => {
176
+ const doc = win.document;
177
+ const box: any = doc.getElementById("box");
178
+ const cl = box.classList;
179
+
180
+ // toggle(name, force) is idempotent; toggle(name) still flips.
181
+ console.log("t1=" + cl.toggle("on", true) + ":" + box.className);
182
+ console.log("t2=" + cl.toggle("on", true) + ":" + box.className);
183
+ console.log("t3=" + cl.toggle("on", false) + ":" + box.className);
184
+ console.log("t4=" + cl.toggle("on", false) + ":" + box.className);
185
+ console.log("t5=" + cl.toggle("on") + ":" + box.className);
186
+
187
+ box.style.setProperty("color", "#ff0000", "important");
188
+ console.log("p1=" + box.style.getPropertyValue("color") + ":" + box.style.getPropertyPriority("color"));
189
+ box.style.setProperty("color", "#0000ff");
190
+ console.log("p2=" + box.style.getPropertyPriority("color") + ":" + getComputedStyle(box).getPropertyValue("color"));
191
+ // An invalid priority makes the call a no-op, as in the CSSOM.
192
+ box.style.setProperty("color", "#00ff00", "urgent");
193
+ console.log("p3=" + box.style.getPropertyValue("color"));
194
+ console.log("rm=" + box.style.removeProperty("color") + ":" + box.style.getPropertyValue("color"));
195
+
196
+ box.style.cssText = "background-color: #010203; width: 55px";
197
+ console.log("ct=" + box.style.getPropertyValue("width") + ":" + box.style.getPropertyValue("background-color"));
198
+
199
+ const cs: any = getComputedStyle(box);
200
+ console.log("cs=" + cs.getPropertyValue("display") + ":" + cs.getPropertyValue("width") +
201
+ ":" + cs.getPropertyPriority("width") + ":" + cs.getPropertyValue("margin-top"));
202
+ const before: any = getComputedStyle(box, "::before");
203
+ console.log("before=" + before.getPropertyValue("content") + ":" + before.getPropertyValue("color"));
204
+ console.log("unsupported-pseudo=" + getComputedStyle(box, "::marker").getPropertyValue("color"));
205
+
206
+ doc.title = "changed";
207
+ console.log("title=" + doc.title);
208
+
209
+ const rect: any = box.getBoundingClientRect();
210
+ console.log("rect=" + rect.x + "," + rect.y + "," + rect.top + "," + rect.right + "," +
211
+ rect.bottom + "," + rect.left + "," + rect.width);
212
+ win.close();
213
+ });
214
+ win.loadHTML(HTML);
215
+ run();
216
+ `);
217
+ expect(value("t1")).toBe("true:on");
218
+ expect(value("t2")).toBe("true:on");
219
+ expect(value("t3")).toBe("false:");
220
+ expect(value("t4")).toBe("false:");
221
+ expect(value("t5")).toBe("true:on");
222
+ expect(value("p1")).toBe("#ff0000:important");
223
+ expect(value("p2")).toBe(":rgb(0, 0, 255)");
224
+ expect(value("p3")).toBe("#0000ff");
225
+ expect(value("rm")).toBe("#0000ff:");
226
+ expect(value("ct")).toBe("55px:#010203");
227
+ expect(value("cs")).toBe("block:55px::10px");
228
+ expect(value("before")).toBe('"hi":rgb(18, 52, 86)');
229
+ expect(value("unsupported-pseudo")).toBe("");
230
+ expect(value("title")).toBe("changed");
231
+ expect(value("rect")).toBe("0,10,10,55,50,0,55");
232
+ });
233
+ it("writes style and dataset properties through named access", () => {
234
+ const { value } = harness.compileAndRun("dom-properties", `
235
+ import { createWindow, run } from "gui";
236
+
237
+ const HTML = \`
238
+ <html><head><style>
239
+ body { margin: 0; }
240
+ #box { width: 100px; height: 40px; margin-top: 10px; }
241
+ #box::before { content: "hi"; color: #123456; }
242
+ </style></head><body>
243
+ <div id="box" data-role="lead" data-user-id="7">x</div>
244
+ </body></html>\`;
245
+
246
+ const win = createWindow({ title: "dom-properties", width: 400, height: 300 });
247
+ win.on("ready", () => {
248
+ const doc = win.document;
249
+ const box: any = doc.getElementById("box");
250
+
251
+ // Property assignment used to park a dead field on the JS object.
252
+ box.style.width = "50%";
253
+ box.style.height = "30px";
254
+ console.log("width=" + box.style.width + ":" + win.computedStyle("#box", "width"));
255
+ console.log("rect=" + box.getBoundingClientRect().width + "x" + box.getBoundingClientRect().height);
256
+ box.style.backgroundColor = "#ff0000";
257
+ console.log("bg=" + box.style.backgroundColor + ":" + win.computedStyle("#box", "background-color"));
258
+ box.style.display = "none";
259
+ console.log("hidden=" + win.computedStyle("#box", "display"));
260
+ box.style.display = "";
261
+ console.log("shown=" + win.computedStyle("#box", "display"));
262
+ box.style.zIndex = "5";
263
+ console.log("z=" + win.computedStyle("#box", "z-index"));
264
+
265
+ // setProperty and named access address the same declaration block.
266
+ box.style.setProperty("color", "#00ff00");
267
+ console.log("color=" + box.style.color + ":" + box.style.getPropertyValue("color") +
268
+ ":" + win.computedStyle("#box", "color"));
269
+ box.style.setProperty("--accent", "#abcdef");
270
+ console.log("custom=" + box.style.getPropertyValue("--accent") + ":" + box.style["--accent"]);
271
+ console.log("decl=" + box.style.length + ":" + box.style.item(0) + ":" + box.style.item(5));
272
+ box.style.removeProperty("z-index");
273
+ console.log("removed=" + ("zIndex" in box.style) + ":" + box.style.zIndex);
274
+
275
+ // dataset maps camelCase keys to data-* attributes, both ways.
276
+ console.log("dataset=" + box.dataset.role + ":" + box.dataset.userId);
277
+ box.dataset.count = "3";
278
+ console.log("data-set=" + box.getAttribute("data-count") + ":" + ("count" in box.dataset) +
279
+ ":" + box.dataset.hasOwnProperty("count"));
280
+ box.dataset.role = "hero";
281
+ console.log("data-write=" + box.getAttribute("data-role"));
282
+ delete box.dataset.count;
283
+ console.log("data-delete=" + box.hasAttribute("data-count") + ":" + (box.dataset.count === undefined));
284
+ console.log("data-keys=" + Object.keys(box.dataset).join(","));
285
+
286
+ // getComputedStyle answers named properties from the cascade too.
287
+ const cs: any = getComputedStyle(box);
288
+ console.log("cs=" + cs.width + ":" + cs.height + ":" + cs.backgroundColor + ":" +
289
+ cs.marginTop + ":" + cs.getPropertyValue("background-color") +
290
+ ":" + cs.getPropertyValue("--accent"));
291
+ const before: any = getComputedStyle(box, "::before");
292
+ console.log("before=" + before.content + ":" + before.color);
293
+ win.close();
294
+ });
295
+ win.loadHTML(HTML);
296
+ run();
297
+ `);
298
+ expect(value("width")).toBe("50%:50%");
299
+ expect(value("rect")).toBe("200x30");
300
+ expect(value("bg")).toBe("#ff0000:rgb(255, 0, 0)");
301
+ expect(value("hidden")).toBe("none");
302
+ expect(value("shown")).toBe("block");
303
+ expect(value("z")).toBe("5");
304
+ expect(value("color")).toBe("#00ff00:#00ff00:rgb(0, 255, 0)");
305
+ expect(value("custom")).toBe("#abcdef:#abcdef");
306
+ expect(value("decl")).toBe("6:width:--accent");
307
+ expect(value("removed")).toBe("false:");
308
+ expect(value("dataset")).toBe("lead:7");
309
+ expect(value("data-set")).toBe("3:true:true");
310
+ expect(value("data-write")).toBe("hero");
311
+ expect(value("data-delete")).toBe("false:true");
312
+ expect(value("data-keys")).toBe("role,userId");
313
+ expect(value("cs")).toBe("50%:30px:rgb(255, 0, 0):10px:rgb(255, 0, 0):#abcdef");
314
+ expect(value("before")).toBe('"hi":rgb(18, 52, 86)');
315
+ });
101
316
  });
102
317
  //# sourceMappingURL=gui-dom.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gui-dom.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-dom.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gui-dom.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-dom.test.ts"],"names":[],"mappings":"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"}
@@ -88,5 +88,117 @@ describe.skipIf(!guiAvailable)("gui extension — events", () => {
88
88
  expect(stdout).not.toContain("web-event=click:");
89
89
  expect(stdout).toContain("web-event=key:a");
90
90
  });
91
+ it("honours listener options, event init fields and dispatch identity", () => {
92
+ const { value } = harness.compileAndRun("event-options", `
93
+ import { createWindow, run } from "gui";
94
+
95
+ const HTML = \`
96
+ <html><body style="margin:0">
97
+ <div id="outer"><div id="inner">x</div></div>
98
+ </body></html>\`;
99
+
100
+ const win = createWindow({ title: "event-options", width: 300, height: 200 });
101
+ win.on("ready", () => {
102
+ const doc = win.document;
103
+ const outer: any = doc.getElementById("outer");
104
+ const inner: any = doc.getElementById("inner");
105
+
106
+ // once
107
+ let once = 0;
108
+ inner.addEventListener("click", () => { once++; }, { once: true });
109
+ inner.click();
110
+ inner.click();
111
+ console.log("once=" + once);
112
+
113
+ // passive: preventDefault is ignored, a normal listener still cancels
114
+ let passive = false;
115
+ inner.addEventListener("click", (e: any) => {
116
+ e.preventDefault();
117
+ passive = e.defaultPrevented;
118
+ }, { passive: true });
119
+ inner.click();
120
+ console.log("passive=" + passive);
121
+ let active = false;
122
+ inner.addEventListener("click", (e: any) => {
123
+ e.preventDefault();
124
+ active = e.defaultPrevented;
125
+ });
126
+ inner.click();
127
+ console.log("active=" + active);
128
+
129
+ // signal removes the listener, including an already-aborted one
130
+ const controller = new AbortController();
131
+ let signaled = 0;
132
+ inner.addEventListener("click", () => { signaled++; }, { signal: controller.signal });
133
+ inner.click();
134
+ controller.abort();
135
+ inner.click();
136
+ console.log("signal=" + signaled + ":" + controller.signal.aborted);
137
+ const abortEvent = new AbortController();
138
+ let onAbort = 0;
139
+ abortEvent.signal.addEventListener("abort", () => { onAbort++; });
140
+ abortEvent.abort();
141
+ console.log("abort-event=" + onAbort + ":" + abortEvent.signal.aborted +
142
+ ":" + (abortEvent.signal.reason instanceof Error));
143
+ const pre = new AbortController();
144
+ pre.abort();
145
+ let never = 0;
146
+ inner.addEventListener("click", () => { never++; }, { signal: pre.signal });
147
+ inner.click();
148
+ console.log("pre-aborted=" + never);
149
+
150
+ // window listeners take the same options
151
+ let winOnce = 0;
152
+ win.addEventListener("custom", () => { winOnce++; }, { once: true });
153
+ win.sendEvent("custom", {});
154
+ win.sendEvent("custom", {});
155
+ console.log("win-once=" + winOnce);
156
+ const winAbort = new AbortController();
157
+ let winSignaled = 0;
158
+ win.addEventListener("custom2", () => { winSignaled++; }, { signal: winAbort.signal });
159
+ win.sendEvent("custom2", {});
160
+ winAbort.abort();
161
+ win.sendEvent("custom2", {});
162
+ console.log("win-signal=" + winSignaled);
163
+
164
+ // EventInit fields survive construction
165
+ const custom: any = new CustomEvent("ping", { detail: 42, bubbles: true, composed: true });
166
+ console.log("event=" + custom.type + ":" + custom.detail + ":" + custom.bubbles +
167
+ ":" + custom.composed + ":" + custom.isTrusted + ":" + custom.eventPhase);
168
+ const mouse: any = new MouseEvent("click", { clientX: 7, clientY: 8, button: 2, bubbles: true });
169
+ console.log("mouse=" + mouse.clientX + "," + mouse.clientY + "," + mouse.button);
170
+
171
+ // dispatchEvent keeps the event identity and exposes the path/phases
172
+ const phases: string[] = [];
173
+ outer.addEventListener("click", (e: any) => {
174
+ phases.push("capture" + e.eventPhase + ":" + (e.composedPath()[0] === inner));
175
+ }, true);
176
+ inner.addEventListener("click", (e: any) => {
177
+ phases.push("target" + e.eventPhase + ":" + (e.composedPath().length > 1));
178
+ });
179
+ const dispatched: any = new CustomEvent("click", { bubbles: true });
180
+ let seen = false;
181
+ inner.addEventListener("click", (e: any) => { seen = e === dispatched; });
182
+ inner.dispatchEvent(dispatched);
183
+ console.log("path=" + phases.join("|") + ":identity=" + seen + ":target=" + (dispatched.target === inner));
184
+ win.close();
185
+ });
186
+ win.loadHTML(HTML);
187
+ run();
188
+ `);
189
+ expect(value("once")).toBe("1");
190
+ expect(value("passive")).toBe("false");
191
+ expect(value("active")).toBe("true");
192
+ expect(value("signal")).toBe("1:true");
193
+ expect(value("abort-event")).toBe("1:true:true");
194
+ expect(value("pre-aborted")).toBe("0");
195
+ expect(value("win-once")).toBe("1");
196
+ expect(value("win-signal")).toBe("1");
197
+ expect(value("event")).toBe("ping:42:true:true:false:0");
198
+ expect(value("mouse")).toBe("7,8,2");
199
+ // Capture runs ancestor-first (phase 1), the target runs at phase 2, the
200
+ // dispatched object keeps its identity and gains its target.
201
+ expect(value("path")).toBe("capture1:true|target2:true:identity=true:target=true");
202
+ });
91
203
  });
92
204
  //# sourceMappingURL=gui-events.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gui-events.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-events.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEnC,QAAQ,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,wBAAwB,EAAE,GAAG,EAAE;IAC5D,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAElC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,OAAO,EACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCC,CACF,CAAC;QAEF,2DAA2D;QAC3D,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,2EAA2E;QAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAC7C,YAAY,EACZ;YACE,0CAA0C;YAC1C,EAAE;YACF,sEAAsE;YACtE,iBAAiB;YACjB,uDAAuD;YACvD,gDAAgD;YAChD,yCAAyC;YACzC,yCAAyC;YACzC,4CAA4C;YAC5C,EAAE;YACF,sFAAsF;YACtF,uCAAuC;YACvC,+CAA+C;YAC/C,sDAAsD;YACtD,yDAAyD;YACzD,6DAA6D;YAC7D,gBAAgB;YAChB,KAAK;YACL,0CAA0C;YAC1C,QAAQ;YACR,4CAA4C;SAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,8CAA8C;QAC9C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QACnD,6EAA6E;QAC7E,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"gui-events.test.js","sourceRoot":"","sources":["../../../tests/e2e/gui-events.test.ts"],"names":[],"mappings":"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"}
package/doc/gui/api.md CHANGED
@@ -21,13 +21,41 @@ Methods implemented on a window handle: `setTitle` / `setSize` / `loadHTML` /
21
21
  `getHTML` / `setBackground` / `close` / `isOpen` / `on` / `off`, plus `driver()`,
22
22
  which reports the SDL_GPU backend in use (`"vulkan"`, `"direct3d12"`, `"metal"`,
23
23
  … — SDL picks the best one the device supports). The Web spellings are available
24
- too: `addEventListener` / `removeEventListener` are aliases of `on` / `off` (their
25
- `options` argument is accepted and ignored) and the `closed` getter mirrors
26
- `isOpen()`, so a document written against the browser API runs unchanged.
27
- Events emitted:
28
- `ready` (after the first presented frame), `load`, `close`, `resize` and the
29
- input events `mousemove`, `mousedown`, `mouseup`, `click`, `wheel`, `keydown`,
30
- `keyup`.
24
+ too: `addEventListener` / `removeEventListener` take the standard options bag
25
+ (`capture`, `once`, `passive`, `signal`), and `closed` mirrors `isOpen()`, so a
26
+ document written against the browser API runs unchanged.
27
+
28
+ Element listeners accept the same options: `{ once }` drops the listener after
29
+ its first call, `{ passive: true }` makes `preventDefault()` a no-op, and
30
+ `{ signal }` (an `AbortSignal` from `new AbortController()`) removes it on
31
+ abort, including a signal that was already aborted when the listener was added.
32
+ `element.dispatchEvent(event)` dispatches the *same* event object (identity is
33
+ observable), fills in `target` / `currentTarget` / `eventPhase` and records the
34
+ path for `event.composedPath()`. `new Event(...)`, `new CustomEvent(...)`,
35
+ `new MouseEvent(...)`, `new KeyboardEvent(...)`, `new PointerEvent(...)`,
36
+ `new WheelEvent(...)`, `new FocusEvent(...)`, `new InputEvent(...)` and
37
+ `new SubmitEvent(...)` keep every field of their `init` object (`detail`,
38
+ `composed`, the mouse/keyboard members, …). Window listeners take the same bag
39
+ (capture is part of listener identity; window delivery has no capture phase).
40
+
41
+ Window navigation is real: `scrollTo(x, y)` / `scrollTo({ left, top })` /
42
+ `scrollBy(...)` move the page viewport (clamped to the content bounds; painting
43
+ and hit testing follow it), and `scrollX` / `scrollY` / `pageXOffset` /
44
+ `pageYOffset` report it. `focus()` raises the OS window and sets
45
+ `document.hasFocus()`; `blur()` clears it. Events emitted:
46
+ `ready` (after the first presented frame), `load`, `close`, `resize`, `scroll`,
47
+ `focus`, `blur` and the input events `mousemove`, `mousedown`, `mouseup`,
48
+ `click`, `wheel`, `keydown`, `keyup`.
49
+
50
+ `createWindow(options)` honours more than the title and size:
51
+ `{ title, width, height, highDensity, resizable, borderless, alwaysOnTop,
52
+ hidden, fullscreen, x, y, opacity, maximized, minimized }` map to the matching
53
+ SDL window flags/calls instead of being ignored.
54
+
55
+ `window.confirm(message)` / `window.prompt(message, defaultValue)` never drop
56
+ their arguments: with `XT_GUI_DIALOGS=1` and a live window they show a native
57
+ message box; otherwise the message goes to stderr and the documented default is
58
+ returned (`confirm` → `false`, `prompt` → `defaultValue` or `""`).
31
59
 
32
60
  On Windows a GUI program is linked as a **Windows-subsystem** executable, so
33
61
  launching it does not flash a console window. That also means stdout/stderr go
@@ -63,12 +91,13 @@ requestAnimationFrame(frame);
63
91
  | `navigator` | `userAgent` (a desktop Chrome string ending in `xbintsc`), `platform`, `vendor`, `language(s)`, `hardwareConcurrency`, `maxTouchPoints`, `onLine`, `vibrate()` |
64
92
  | `localStorage` | an in-memory `getItem` / `setItem` / `removeItem` / `clear` / `key` / `length` store |
65
93
  | `AudioContext`, `webkitAudioContext` | a Web Audio graph with a real mixer and decoder (see below) |
66
- | `Event`, `CustomEvent`, `ProgressEvent`, … | event constructors |
94
+ | `Event`, `CustomEvent`, `ProgressEvent`, `MouseEvent`, `KeyboardEvent`, `PointerEvent`, `WheelEvent`, `FocusEvent`, `InputEvent`, `SubmitEvent` | event constructors; the `init` argument is kept as given (`detail`, `composed`, mouse/keyboard fields, …) |
95
+ | `AbortController`, `AbortSignal` | a real controller/signal pair for `{ signal }` listener removal, with `abort()`, `aborted`, `reason`, `onabort` and `throwIfAborted()` |
67
96
  | `HTMLCanvasElement`, `HTMLImageElement`, `WebGLRenderingContext`, `WebGL2RenderingContext`, `WebGLBuffer`/`WebGLTexture`/`WebGLShader`/`WebGLProgram`/`WebGLFramebuffer`/`WebGLRenderbuffer`/`WebGLUniformLocation`/`WebGLActiveInfo`/`WebGLVertexArrayObject`/`WebGLSampler`/`WebGLQuery`/`WebGLSync`/`WebGLTransformFeedback` | real constructor values. `canvas.getContext("webgl")` and `getContext("webgl2")` return GPU contexts (see [WebGL](./webgl.md)). |
68
97
  | `ImageBitmap`, `createImageBitmap(source)` | immutable bitmaps backed by decoded pixels (`<img>`, `<canvas>`, `<video>` frames and `ImageData` shapes); the promise resolves immediately and `texImage2D` accepts the result |
69
98
  | `ImageData`, `OffscreenCanvas`, `XRWebGL*`, `Request`, `Headers`, `Response`, `ReadableStream`, `DOMParser` | `typeof` probes answer `"undefined"` (no XR/fetch/streaming backend), which lets a page take its "unsupported" branch instead of calling a no-op stub. Three.js pages use its real renderer on this backend (see [Known issues](./known-issues.md)). |
70
99
  | `requestAnimationFrame` / `cancelAnimationFrame` | queued on the window's frame list; see [Runtime integration](./runtime.md) |
71
- | `alert` / `confirm` / `prompt`, `matchMedia`, `getComputedStyle`, `escape` | host implementations (Annex B `escape` encodes per UTF-16 code unit, matching `decodeURIComponent(escape(bytes))`) |
100
+ | `alert` / `confirm` / `prompt`, `matchMedia`, `getComputedStyle`, `escape` | host implementations. `matchMedia` returns a **live** list (cached per query): `matches` is re-evaluated on resize and `change` listeners fire when it flips. `confirm`/`prompt` use a native dialog only with `XT_GUI_DIALOGS=1`; otherwise they report the message on stderr and return the documented default (Annex B `escape` encodes per UTF-16 code unit, matching `decodeURIComponent(escape(bytes))`) |
72
101
 
73
102
  `getElementById` / `querySelector` are answered even before `loadHTML` runs: the
74
103
  handle is *pending* and binds to the real node the first time it is used, so a
package/doc/gui/canvas.md CHANGED
@@ -36,12 +36,20 @@ a shared prototype. The handle is cached on the element, so every call returns
36
36
  the **same** object and the drawing state (transform, styles, path) is per
37
37
  canvas, not per call. `ctx.canvas` is the owning element handle.
38
38
 
39
+ `getContext("2d", attributes)` honours `alpha` on the first call: with
40
+ `{ alpha: false }` the bitmap starts (and clears) opaque black. The other
41
+ attributes are reported by `getContextAttributes()` at their specification
42
+ defaults (`antialias: true`, `depth: true`, `stencil: false`,
43
+ `premultipliedAlpha: true`, `preserveDrawingBuffer: false`, `colorSpace:
44
+ "srgb"`, …), so a feature test gets an honest answer.
45
+
39
46
  ## Drawing model
40
47
 
41
48
  The rasteriser is deliberately small and self-written (no Skia/Cairo). It
42
49
  anti-aliases with **4 vertical sub-scanlines** per pixel row and an **analytic
43
50
  horizontal coverage** pass, and fills paths with the **non-zero winding rule**
44
- (so `arc`-shaped holes work when wound the other way). An axis-aligned rectangle
51
+ (so `arc`-shaped holes work when wound the other way) unless
52
+ `fill("evenodd")` selects the even-odd rule. An axis-aligned rectangle
45
53
  (`b == 0 && c == 0` in the transform) takes a direct coverage fast path.
46
54
  Blending is non-premultiplied source-over, scaled by `globalAlpha`.
47
55
 
@@ -63,10 +71,11 @@ matching GPU texture.
63
71
 
64
72
  State and transforms: `save` / `restore` (which restore the transform, line
65
73
  width, alpha, font/text settings **and** the fill/stroke styles),
66
- `translate`, `rotate`, `scale`, `transform(a,b,c,d,e,f)`, `setTransform`,
67
- `resetTransform`, `getTransform` (returns an object with `a`…`f` and `is2D`).
68
- Transforms compose in canvas order: `translate(x, y); rotate(a)` rotates around
69
- the translated origin.
74
+ `translate`, `rotate`, `scale`, `transform(a,b,c,d,e,f)` / `transform(matrix)`,
75
+ `setTransform(a,b,c,d,e,f)` / `setTransform(matrix)`, `resetTransform`,
76
+ `getTransform` (returns an object with `a`…`f` and `is2D`). A matrix is a
77
+ `{ a, b, c, d, e, f }` object or a six-element array. Transforms compose in
78
+ canvas order: `translate(x, y); rotate(a)` rotates around the translated origin.
70
79
 
71
80
  Styles and settings: `fillStyle` / `strokeStyle` (CSS colors understood by the
72
81
  cascade's color parser, or a `CanvasGradient`), `lineWidth`, `globalAlpha`,
@@ -86,10 +95,18 @@ distance from the **outer** circle's centre (`x1, y1`), so `x0`/`y0` are
86
95
  currently ignored: use concentric circles (`x0 == x1`, `y0 == y1`).
87
96
 
88
97
  Paths: `beginPath`, `closePath`, `moveTo`, `lineTo`, `rect`, `roundRect`,
89
- `arc`, `ellipse`, `quadraticCurveTo`, `bezierCurveTo`, then `fill` (multi-loop,
90
- non-zero winding) or `stroke`. Bezier and quadratic segments are flattened into
91
- line segments; arcs and ellipses are flattened too. `roundRect` accepts one
92
- radius number (the array form is not implemented).
98
+ `arc`, `ellipse`, `quadraticCurveTo`, `bezierCurveTo`, then
99
+ `fill(fillRule?)` / `fill(path, fillRule?)` (multi-loop; `"nonzero"` or
100
+ `"evenodd"`) or `stroke(path?)`. Bezier and quadratic segments are flattened
101
+ into line segments; arcs and ellipses are flattened too. `roundRect` accepts a
102
+ single radius number or the CSS radius list (`[all]`, `[tl, br]`,
103
+ `[tl, tr, br]`, `[tl, tr, br, bl]`, numbers or `{ x, y }` pairs; corners are
104
+ drawn circular).
105
+
106
+ `Path2D` is implemented with the same path commands (`moveTo`, `lineTo`,
107
+ `closePath`, `rect`, `roundRect`, `arc`, `ellipse`, `quadraticCurveTo`,
108
+ `bezierCurveTo`, `addPath`) and can be passed to `fill`/`stroke`, so a path can
109
+ be built once and replayed under different transforms.
93
110
 
94
111
  Rectangles: `clearRect`, `fillRect`, `strokeRect`.
95
112
 
@@ -97,9 +114,12 @@ Dashes: `setLineDash(segments)` / `getLineDash()` / `lineDashOffset` implement
97
114
  the standard pattern rules (an odd-length list is duplicated, negative or
98
115
  non-finite entries are ignored, an all-zero list means a solid line).
99
116
 
100
- Text: `measureText(text)` (returns `{ width }`), `fillText(text, x, y)` and
101
- `strokeText(text, x, y)`. Text is shaped with HarfBuzz and rasterised with
102
- FreeType (the same stack as HTML text) at the transform's approximate scale.
117
+ Text: `measureText(text)` (returns `{ width }`), `fillText(text, x, y,
118
+ maxWidth?)` and `strokeText(text, x, y, maxWidth?)`. Text is shaped with
119
+ HarfBuzz and rasterised with FreeType (the same stack as HTML text) at the
120
+ transform's approximate scale. A positive `maxWidth` condenses advances and
121
+ glyph bitmaps horizontally so the run is at most that wide (as the canvas API
122
+ specifies) instead of being ignored.
103
123
  **Only translation and uniform scale are honoured** — a rotated or skewed
104
124
  transform still places each glyph at its transformed baseline point but does not
105
125
  rotate the glyph bitmaps. `strokeText` currently fills with the stroke style
@@ -119,14 +139,25 @@ Test hook: `getImageData(x, y, w, h)` copies a region to
119
139
  `{ width, height, data }` where `data` is a row-major RGBA8 byte array.
120
140
  Out-of-bounds pixels are transparent.
121
141
 
142
+ Patterns: `createPattern(source, repetition?)` accepts an `<img>` or another
143
+ `<canvas>` and tiles it in user space (`repeat`, `repeat-x`, `repeat-y`,
144
+ `no-repeat`), so the tiles follow the current transform. Assign the result to
145
+ `fillStyle`/`strokeStyle`; assigning a colour replaces it.
146
+
147
+ Image data and export: `createImageData(width, height)` allocates a blank
148
+ `{ width, height, data }`, and `putImageData(imageData, dx, dy[, dirtyX, dirtyY,
149
+ dirtyWidth, dirtyHeight])` copies bytes straight into the bitmap (no transform,
150
+ blending or `globalAlpha`). `canvas.toDataURL(type?)` exports a PNG data URL and
151
+ `canvas.toBlob(callback, type?)` delivers a Blob-shaped `{ type, size }` from a
152
+ microtask (the bytes are on `__xt_gui_blob_data`; there is no
153
+ `URL.createObjectURL`).
154
+
122
155
  ## Not implemented
123
156
 
124
- Patterns (`createPattern`), shadows, `clip()`, `globalCompositeOperation`,
125
- `putImageData`, `createImageData`, `isPointInPath`, `setTransform` with a
126
- `DOMMatrix`, `drawImage` with an image smoothing toggle, `toDataURL`/`toBlob`,
127
- `<canvas>` as a CSS `background-image`, and offscreen canvases
128
- (`OffscreenCanvas`, `transferControlToOffscreen`). `roundRect` takes a single
129
- radius, and radial gradients ignore the inner circle's offset.
157
+ Shadows, `clip()`, `globalCompositeOperation`, `isPointInPath`, `drawImage` with
158
+ an image smoothing toggle, `<canvas>` as a CSS `background-image`, and offscreen
159
+ canvases (`OffscreenCanvas`, `transferControlToOffscreen`).
160
+ Radial gradients ignore the inner circle's offset.
130
161
 
131
162
  See also [Media](./media.md) for the image/video/audio decode stack that
132
163
  `drawImage` and the texture upload share.
package/doc/gui/dom.md CHANGED
@@ -72,18 +72,65 @@ created.textContent = "added";
72
72
  box.appendChild(created);
73
73
  box.removeChild(created);
74
74
 
75
+ // Parent/child helpers accept nodes and strings in one call.
76
+ box.append("text", doc.createElement("span"));
77
+ box.prepend(doc.createElement("b"));
78
+ box.insertAdjacentHTML("beforeend", "<i>tail</i>"); // beforebegin/afterbegin/beforeend/afterend
79
+ box.replaceChildren("fresh", doc.createElement("em"));
80
+
81
+ // Namespaces are recorded; foreign (SVG) element names keep their case.
82
+ const gradient = doc.createElementNS("http://www.w3.org/2000/svg", "svg:linearGradient");
83
+ console.log(gradient.namespaceURI, gradient.localName, gradient.prefix);
84
+
75
85
  inner.addEventListener("click", (e) => console.log(e.target.id, e.currentTarget.id));
76
86
  inner.click(); // synthesise a click at the element
77
87
  console.log(box.offsetWidth, box.offsetHeight); // rounded border box
78
88
  console.log(box.contains(inner)); // descendant test
79
89
  ```
80
90
 
91
+ ### Style properties, dataset, computed style and geometry
92
+
93
+ The inline `style` object is a CSSOM declaration block. Named access reads and
94
+ writes the same block as `setProperty` (`el.style.display = "none"`,
95
+ `el.style.backgroundColor = "#f00"`, camelCase mapped to the CSS spelling, and
96
+ `el.style["--accent"]` for custom properties); `setProperty(name, value,
97
+ priority)` stores `!important` when `priority` is `"important"` (any other
98
+ priority makes the call a no-op, and an empty value removes the property).
99
+ `getPropertyValue` / `getPropertyPriority` read it back, `removeProperty`
100
+ returns the previous value, `cssText` gets or replaces the whole block,
101
+ `style.length` / `style.item(i)` enumerate it, and an unset property answers
102
+ `""`.
103
+
104
+ `el.dataset` is a live `DOMStringMap` over the element's `data-*` attributes:
105
+ `dataset.fooBar` reads `data-foo-bar`, assignment writes the attribute, `delete`
106
+ removes it, and `Object.keys(dataset)` / `for...in` list only the data keys.
107
+
108
+ `getComputedStyle(el, pseudoElt)` answers from the **cascade** — the UA sheet,
109
+ author rules, inheritance and the inline declarations — rather than echoing the
110
+ inline block, so a stylesheet rule is visible. It exposes `getPropertyValue`,
111
+ `getPropertyPriority` and named properties (`cs.width`, `cs.backgroundColor`,
112
+ `cs["--accent"]`). Pass `"::before"`/`"::after"` to read generated content; any
113
+ other pseudo-element answers empty values. Every read flushes a pending restyle,
114
+ so a handle created early stays live after later mutations.
115
+
116
+ `getBoundingClientRect()` carries the legacy `top` / `right` / `bottom` / `left`
117
+ members next to `x` / `y` / `width` / `height`, and `document.title = …` writes
118
+ the document's `<title>` (creating the element, and a `<head>`, when the page
119
+ has none).
120
+
81
121
  Mutations mark the document dirty; the engine restyles + relayouts lazily (before
82
122
  the next read or frame). `inner = …`/`innerHTML = …` do **not** run scripts.
83
- Element events support capture and bubble phases, `stopPropagation`, `once`, and
84
- bubble up to `document`/`window`. The legacy `win.on(type, fn)` payload keeps its
85
- **string** `e.target` (`div#id.class`); the element `Event.target` is a handle
86
- whose descriptor matches the same string.
123
+ Element events support capture and bubble phases, `stopPropagation`, `once`,
124
+ `passive` and `signal` (an `AbortController`'s signal removes the listener), and
125
+ bubble up to `document`/`window`. `dispatchEvent` dispatches the same event
126
+ object and fills in `target`/`currentTarget`/`eventPhase`/`composedPath()`. The
127
+ legacy `win.on(type, fn)` payload keeps its **string** `e.target`
128
+ (`div#id.class`); the element `Event.target` is a handle whose descriptor
129
+ matches the same string.
130
+
131
+ `element.focus()` scrolls the element into view (pass `{ preventScroll: true }`
132
+ to keep the page offset), and `document.hasFocus()` reports the window state
133
+ toggled by `window.focus()` / `window.blur()`.
87
134
 
88
135
  ### Node lifetime
89
136