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.
- package/README.md +16 -10
- package/dist/src/binder/references.js +14 -1
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/module.d.ts +1 -0
- package/dist/src/codegen/generator/module.js +54 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/statements/variables.js +6 -4
- package/dist/src/codegen/generator/statements/variables.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/css.d.ts +26 -0
- package/dist/src/extensions/gui/css.js +100 -0
- package/dist/src/extensions/gui/css.js.map +1 -0
- package/dist/src/extensions/gui/html.js +9 -2
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +16 -2
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +16 -0
- package/dist/src/extensions/gui/three.js +22 -0
- package/dist/src/extensions/gui/three.js.map +1 -0
- package/dist/src/extensions/media/index.d.ts +6 -5
- package/dist/src/extensions/media/index.js +12 -14
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/tests/e2e/compile-builtins.test.js +24 -0
- package/dist/tests/e2e/compile-builtins.test.js.map +1 -1
- package/dist/tests/e2e/compile-objects-classes.test.js +23 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/gui-browser.test.js +1 -1
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-effects.test.d.ts +6 -0
- package/dist/tests/e2e/gui-effects.test.js +262 -0
- package/dist/tests/e2e/gui-effects.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-webgl.test.d.ts +10 -0
- package/dist/tests/e2e/gui-example-webgl.test.js +75 -0
- package/dist/tests/e2e/gui-example-webgl.test.js.map +1 -0
- package/dist/tests/e2e/gui-example-webgl2.test.d.ts +8 -0
- package/dist/tests/e2e/gui-example-webgl2.test.js +72 -0
- package/dist/tests/e2e/gui-example-webgl2.test.js.map +1 -0
- package/dist/tests/e2e/gui-layout.test.js +48 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +5 -4
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.d.ts +11 -0
- package/dist/tests/e2e/gui-webgl.test.js +179 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2.test.js +238 -0
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +41 -0
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +7 -3
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/doc/ai/extensions.md +29 -9
- package/doc/ai/troubleshooting.md +12 -8
- package/doc/ai/zh-CN/extensions.md +26 -8
- package/doc/ai/zh-CN/troubleshooting.md +10 -6
- package/doc/gui/api.md +3 -2
- package/doc/gui/build.md +72 -16
- package/doc/gui/canvas.md +11 -3
- package/doc/gui/dom.md +33 -18
- package/doc/gui/known-issues.md +57 -24
- package/doc/gui/layout.md +18 -0
- package/doc/gui/media.md +17 -13
- package/doc/gui/rendering.md +50 -3
- package/doc/gui/webgl.md +256 -0
- package/doc/gui.md +108 -17
- package/doc/npm-package.md +6 -4
- package/doc/requirements.md +18 -9
- package/doc/zh-CN/gui/api.md +3 -2
- package/doc/zh-CN/gui/build.md +64 -15
- package/doc/zh-CN/gui/canvas.md +9 -2
- package/doc/zh-CN/gui/dom.md +20 -7
- package/doc/zh-CN/gui/known-issues.md +45 -19
- package/doc/zh-CN/gui/layout.md +14 -0
- package/doc/zh-CN/gui/media.md +15 -11
- package/doc/zh-CN/gui/rendering.md +41 -4
- package/doc/zh-CN/gui/webgl.md +115 -0
- package/doc/zh-CN/gui.md +89 -14
- package/doc/zh-CN/npm-package.md +9 -6
- package/doc/zh-CN/requirements.md +12 -6
- package/llms.txt +2 -0
- package/package.json +1 -1
- package/runtime/ext_gui/browser.cpp +39 -13
- package/runtime/ext_gui/canvas.cpp +3 -18
- package/runtime/ext_gui/canvas_internal.h +10 -0
- package/runtime/ext_gui/canvas_raster.cpp +60 -0
- package/runtime/ext_gui/clip_values.cpp +246 -0
- package/runtime/ext_gui/css.cpp +94 -0
- package/runtime/ext_gui/css.h +22 -1
- package/runtime/ext_gui/css_match.cpp +3 -0
- package/runtime/ext_gui/css_selector.cpp +9 -0
- package/runtime/ext_gui/css_sheet.cpp +89 -45
- package/runtime/ext_gui/document.cpp +4 -1
- package/runtime/ext_gui/document.h +2 -0
- package/runtime/ext_gui/dom.h +16 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +17 -80
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +130 -1
- package/runtime/ext_gui/dom_api_canvas_pending.cpp +41 -0
- package/runtime/ext_gui/dom_api_internal.h +259 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +376 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +434 -0
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +269 -0
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +212 -0
- package/runtime/ext_gui/dom_api_webgl_rt.h +30 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +443 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +444 -0
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +445 -0
- package/runtime/ext_gui/effect_values.cpp +312 -0
- package/runtime/ext_gui/gradient_values.cpp +476 -0
- package/runtime/ext_gui/grid_values.cpp +230 -0
- package/runtime/ext_gui/gui_canvas.cpp +50 -2
- package/runtime/ext_gui/gui_engine.h +27 -0
- package/runtime/ext_gui/gui_events.cpp +4 -1
- package/runtime/ext_gui/gui_media.cpp +1 -1
- package/runtime/ext_gui/gui_webgl.cpp +83 -0
- package/runtime/ext_gui/layout.cpp +21 -0
- package/runtime/ext_gui/layout_flow.cpp +81 -4
- package/runtime/ext_gui/layout_flow_internal.h +31 -0
- package/runtime/ext_gui/layout_grid.cpp +374 -0
- package/runtime/ext_gui/media.h +1 -1
- package/runtime/ext_gui/media_api.h +2 -2
- package/runtime/ext_gui/media_query.cpp +211 -0
- package/runtime/ext_gui/paint.cpp +111 -77
- package/runtime/ext_gui/paint.h +33 -3
- package/runtime/ext_gui/paint_background.cpp +229 -0
- package/runtime/ext_gui/paint_clip.cpp +268 -0
- package/runtime/ext_gui/paint_effects.cpp +324 -0
- package/runtime/ext_gui/paint_internal.h +85 -0
- package/runtime/ext_gui/renderer.cpp +8 -31
- package/runtime/ext_gui/renderer_clip.cpp +196 -0
- package/runtime/ext_gui/renderer_image.cpp +13 -8
- package/runtime/ext_gui/renderer_internal.h +15 -8
- package/runtime/ext_gui/renderer_text.cpp +6 -31
- package/runtime/ext_gui/style.cpp +343 -20
- package/runtime/ext_gui/style.h +144 -3
- package/runtime/ext_gui/style_print.cpp +134 -1
- package/runtime/ext_gui/style_values.cpp +152 -126
- package/runtime/ext_gui/style_values_internal.h +35 -0
- package/runtime/ext_gui/webgl.h +466 -0
- package/runtime/ext_gui/webgl2_constants.h +274 -0
- package/runtime/ext_gui/webgl_buffer.cpp +565 -0
- package/runtime/ext_gui/webgl_constants.h +317 -0
- package/runtime/ext_gui/webgl_context.cpp +466 -0
- package/runtime/ext_gui/webgl_draw.cpp +495 -0
- package/runtime/ext_gui/webgl_glsl.h +125 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +431 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +349 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +581 -0
- package/runtime/ext_gui/webgl_msl.cpp +62 -0
- package/runtime/ext_gui/webgl_msl.h +33 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +274 -0
- package/runtime/ext_gui/webgl_program_internal.h +18 -0
- package/runtime/ext_gui/webgl_shader.cpp +582 -0
- package/runtime/ext_gui/webgl_texture.cpp +293 -0
- package/runtime/ext_gui/webgl_uniform.cpp +167 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +224 -0
- package/runtime/ext_media/media.cpp +1 -1
- package/runtime/ext_media/media_bridge.c +9 -3
- package/runtime/rt_internal.h +1 -0
- package/runtime/xt_containers/access.inc +8 -0
- package/runtime/xt_regex/api.inc +2 -0
- package/runtime/xt_regex/matcher.inc +16 -3
- package/runtime/xt_regex/parser.inc +8 -2
- package/runtime/xt_regex/types.inc +3 -0
- package/runtime/xt_stdlib2/regexp-match.inc +60 -21
- package/runtime/xt_stdlib2/regexp.inc +1 -0
- package/runtime/xt_typed_array.c +1 -1
- package/scripts/build-gui.ts +90 -3
- package/scripts/build-media.ts +323 -58
- package/scripts/setup-vs-toolchain.ps1 +152 -0
- package/src/binder/references.ts +13 -1
- package/src/codegen/generator/module.ts +50 -0
- package/src/codegen/generator/statements/variables.ts +6 -3
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/css.ts +113 -0
- package/src/extensions/gui/html.ts +9 -2
- package/src/extensions/gui/index.ts +16 -2
- package/src/extensions/gui/three.ts +25 -0
- package/src/extensions/media/index.ts +13 -16
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type ArrowFunction,
|
|
10
10
|
type Block,
|
|
11
11
|
type Expression,
|
|
12
|
+
type FunctionDeclaration,
|
|
12
13
|
type Parameter,
|
|
13
14
|
} from "../../ast/nodes.js";
|
|
14
15
|
import type { ClassInfo, FunctionInfo, SymbolInfo } from "../../binder/binder.js";
|
|
@@ -39,6 +40,7 @@ function symbolSuffix(name: string): string {
|
|
|
39
40
|
export interface ModuleMethods {
|
|
40
41
|
run(this: Generator): string;
|
|
41
42
|
emitFunction(this: Generator, fn: FunctionInfo): void;
|
|
43
|
+
emitHoistedFunctions(this: Generator, fn: FunctionInfo): void;
|
|
42
44
|
emitFunctionReturn(this: Generator, value: string): void;
|
|
43
45
|
emitMain(this: Generator): void;
|
|
44
46
|
functionName(this: Generator, fn: FunctionInfo): string;
|
|
@@ -192,6 +194,14 @@ export const moduleMethods: ModuleMethods = {
|
|
|
192
194
|
this.defineCaptureSlot(symbol, value);
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
// Hoisted function declarations: create their closure values in this
|
|
198
|
+
// frame (before the body runs) so a nested function that references one
|
|
199
|
+
// gets the closure built against the declaring frame, not against the
|
|
200
|
+
// caller's slots. Captured function symbols are boxed by the binder, so
|
|
201
|
+
// the second pass publishes the closures into those boxes and mutual
|
|
202
|
+
// recursion sees the same values.
|
|
203
|
+
this.emitHoistedFunctions(fn);
|
|
204
|
+
|
|
195
205
|
// Arrow functions inherit `this` lexically; it travels as an extra capture.
|
|
196
206
|
if (fn.isArrow && fn.capturesThis) {
|
|
197
207
|
const inherited = this.reg();
|
|
@@ -337,6 +347,46 @@ export const moduleMethods: ModuleMethods = {
|
|
|
337
347
|
return name;
|
|
338
348
|
},
|
|
339
349
|
|
|
350
|
+
emitHoistedFunctions(fn: FunctionInfo): void {
|
|
351
|
+
if (fn.isModule) return;
|
|
352
|
+
const body = (fn.node as { body?: Block }).body;
|
|
353
|
+
if (!body || body.kind !== SyntaxKind.Block) return;
|
|
354
|
+
const declarations: FunctionDeclaration[] = [];
|
|
355
|
+
for (const statement of body.statements) {
|
|
356
|
+
if (statement.kind === SyntaxKind.FunctionDeclaration) {
|
|
357
|
+
declarations.push(statement as FunctionDeclaration);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (declarations.length === 0) return;
|
|
361
|
+
/* Captured locals of this frame may be declared textually after the
|
|
362
|
+
* function that uses them. Function declarations are hoisted, so create
|
|
363
|
+
* their boxes now; the `let`/`const`/`var` statement fills the box when it
|
|
364
|
+
* runs (emitVariableStatement writes into an existing slot). */
|
|
365
|
+
for (const declaration of declarations) {
|
|
366
|
+
const child = this.binding.functionOfNode.get(declaration);
|
|
367
|
+
if (!child) continue;
|
|
368
|
+
for (const captured of child.captures) {
|
|
369
|
+
if (captured.fn !== fn) continue;
|
|
370
|
+
if (!this.current.slots.has(captured.id)) this.declareSlot(captured, i64(XT_UNDEFINED));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/* First pass: a slot (and a box, when captured) exists for every name
|
|
374
|
+
* before any closure is built, so sibling references cannot observe an
|
|
375
|
+
* uninitialised slot. */
|
|
376
|
+
const ready: Array<{ symbol: SymbolInfo; child: FunctionInfo }> = [];
|
|
377
|
+
for (const declaration of declarations) {
|
|
378
|
+
const symbol = this.binding.symbolOfDeclaration.get(declaration);
|
|
379
|
+
const child = this.binding.functionOfNode.get(declaration);
|
|
380
|
+
if (!symbol || !child) continue;
|
|
381
|
+
if (!this.current.slots.has(symbol.id)) this.declareSlot(symbol, i64(XT_UNDEFINED));
|
|
382
|
+
ready.push({ symbol, child });
|
|
383
|
+
}
|
|
384
|
+
/* Second pass: build and store the closures. */
|
|
385
|
+
for (const entry of ready) {
|
|
386
|
+
this.writeSlot(entry.symbol, this.emitClosureValue(entry.child));
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
|
|
340
390
|
emitClassReference(classInfo: ClassInfo): string {
|
|
341
391
|
const globalName = this.classGlobal(classInfo);
|
|
342
392
|
const value = this.reg();
|
|
@@ -33,9 +33,12 @@ export const variableStatementMethods: VariableStatementMethods = {
|
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
35
|
if (this.current.slots.has(symbol.id)) {
|
|
36
|
-
// Hoisted `var
|
|
37
|
-
//
|
|
38
|
-
|
|
36
|
+
// Hoisted `var`, or a box created early for a captured binding
|
|
37
|
+
// declared textually after a hoisted function: do not redeclare the
|
|
38
|
+
// slot, but assigning an initializer is still part of the statement.
|
|
39
|
+
if (declaration.initializer) {
|
|
40
|
+
this.writeSlot(symbol, this.emitExpression(declaration.initializer));
|
|
41
|
+
}
|
|
39
42
|
continue;
|
|
40
43
|
}
|
|
41
44
|
if (symbol.boxed) {
|
package/src/driver/version.ts
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GUI extension: inline external stylesheets into the HTML asset.
|
|
3
|
+
*
|
|
4
|
+
* The engine's CSS cascade only reads `<style>` elements (`document.cpp` skips
|
|
5
|
+
* `<link rel="stylesheet">` because resolving it would need I/O at runtime).
|
|
6
|
+
* A page that ships its CSS as a separate file therefore loses *all* styling
|
|
7
|
+
* once it is compiled. The bundler, unlike the runtime, can read files, so this
|
|
8
|
+
* loader rewrites every local stylesheet link (and every local `@import` inside
|
|
9
|
+
* it) into an inline `<style>` block. The page keeps its author-time structure
|
|
10
|
+
* and the compiled binary has no runtime file dependency.
|
|
11
|
+
*
|
|
12
|
+
* Remote URLs are left untouched: there is no network-backed style loader, so
|
|
13
|
+
* the engine simply ignores them, exactly as before.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { dirname, resolve } from "node:path";
|
|
18
|
+
|
|
19
|
+
/** Read a stylesheet as a byte string (one code unit per UTF-8 byte).
|
|
20
|
+
*
|
|
21
|
+
* The bundler hands asset loaders the same shape (`sourceTextBytes`), so CSS
|
|
22
|
+
* must join the HTML *as bytes*. Decoding it to UTF-16 and putting that into
|
|
23
|
+
* the byte-string document would make codegen encode the HTML's own bytes a
|
|
24
|
+
* second time. `toString("latin1")` is exactly the byte-string mapping; the
|
|
25
|
+
* self-hosted runtime's `readFileSync` already returns bytes. */
|
|
26
|
+
function readCssText(file: string): string {
|
|
27
|
+
const data: string | Buffer = readFileSync(file);
|
|
28
|
+
return typeof data === "string" ? data : data.toString("latin1");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A resolved local stylesheet: its inlined text is returned to the caller. */
|
|
32
|
+
export interface InlinedStylesheets {
|
|
33
|
+
readonly html: string;
|
|
34
|
+
/** Every stylesheet file that was read (for bundler cache invalidation). */
|
|
35
|
+
readonly dependencies: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** True for a URL the compiler cannot read (http:, data:, //host, …). */
|
|
39
|
+
const URL_SPEC = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
40
|
+
|
|
41
|
+
/** Read one attribute from an element's attribute text. */
|
|
42
|
+
function attribute(attributes: string, name: string): string | undefined {
|
|
43
|
+
const pattern = new RegExp(
|
|
44
|
+
`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'=<>\`]+))`,
|
|
45
|
+
"i",
|
|
46
|
+
);
|
|
47
|
+
const match = pattern.exec(attributes);
|
|
48
|
+
if (!match) return undefined;
|
|
49
|
+
return match[1] ?? match[2] ?? match[3];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Strip a `?query` / `#fragment`, then resolve against a base file. */
|
|
53
|
+
function localPath(baseFile: string, specifier: string): string | undefined {
|
|
54
|
+
if (specifier === "" || URL_SPEC.test(specifier)) return undefined;
|
|
55
|
+
const clean = specifier.split(/[?#]/, 1)[0] ?? specifier;
|
|
56
|
+
return resolve(dirname(baseFile), clean);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** True when the `rel` attribute lists `stylesheet` among its tokens. */
|
|
60
|
+
function isStylesheet(rel: string | undefined): boolean {
|
|
61
|
+
if (rel === undefined) return false;
|
|
62
|
+
return rel.toLowerCase().split(/\s+/).includes("stylesheet");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Inline every local `@import` in `css`. Imports are resolved relative to
|
|
67
|
+
* `cssFile`, which is the file the rules came from (not the HTML page), so a
|
|
68
|
+
* partial that imports a sibling keeps working. A cycle is cut at the repeated
|
|
69
|
+
* file rather than recursing forever.
|
|
70
|
+
*/
|
|
71
|
+
function inlineImports(
|
|
72
|
+
css: string,
|
|
73
|
+
cssFile: string,
|
|
74
|
+
dependencies: Set<string>,
|
|
75
|
+
stack: readonly string[],
|
|
76
|
+
): string {
|
|
77
|
+
/* `@import url("./x.css") screen;` and `@import "./x.css";` are the two
|
|
78
|
+
* spellings the CSS parser accepts. Match one at a time so a single bad
|
|
79
|
+
* import cannot swallow the rest of the sheet. */
|
|
80
|
+
const pattern =
|
|
81
|
+
/@import\s+(?:url\(\s*(?:"([^"]*)"|'([^']*)'|([^'")\s]+))\s*\)|"([^"]*)"|'([^']*)')\s*[^;]*;/gi;
|
|
82
|
+
return css.replace(pattern, (match, a, b, c, d, e) => {
|
|
83
|
+
const specifier = a ?? b ?? c ?? d ?? e;
|
|
84
|
+
if (specifier === undefined) return "";
|
|
85
|
+
const file = localPath(cssFile, specifier);
|
|
86
|
+
if (file === undefined) return match;
|
|
87
|
+
if (stack.includes(file)) return "";
|
|
88
|
+
if (!existsSync(file)) return match;
|
|
89
|
+
dependencies.add(file);
|
|
90
|
+
const nested = readCssText(file);
|
|
91
|
+
return inlineImports(nested, file, dependencies, [...stack, file]);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Replace `<link rel="stylesheet">` elements with inline `<style>` blocks,
|
|
97
|
+
* recursively resolving local `@import` rules. Returns the rewritten HTML and
|
|
98
|
+
* the list of stylesheet files it read.
|
|
99
|
+
*/
|
|
100
|
+
export function inlineStylesheets(htmlFile: string, html: string): InlinedStylesheets {
|
|
101
|
+
const dependencies = new Set<string>();
|
|
102
|
+
const rewritten = html.replace(/<link\b([^>]*)>/gi, (match, attributes: string) => {
|
|
103
|
+
if (!isStylesheet(attribute(attributes, "rel"))) return match;
|
|
104
|
+
const href = attribute(attributes, "href");
|
|
105
|
+
if (href === undefined) return match;
|
|
106
|
+
const file = localPath(htmlFile, href);
|
|
107
|
+
if (file === undefined || !existsSync(file)) return match;
|
|
108
|
+
dependencies.add(file);
|
|
109
|
+
const css = inlineImports(readCssText(file), file, dependencies, [file]);
|
|
110
|
+
return `<style data-xt-href="${href.replace(/"/g, """)}">\n${css}\n</style>`;
|
|
111
|
+
});
|
|
112
|
+
return { html: rewritten, dependencies: [...dependencies] };
|
|
113
|
+
}
|
|
@@ -38,6 +38,7 @@ import { Parser } from "../../parser/parser.js";
|
|
|
38
38
|
import { artifactPath } from "../../canonical.js";
|
|
39
39
|
import { hashString } from "../../driver/cache.js";
|
|
40
40
|
import type { AssetLoadResult, AssetSourceRange } from "../registry.js";
|
|
41
|
+
import { inlineStylesheets } from "./css.js";
|
|
41
42
|
|
|
42
43
|
/** Matches an inline `<script …>body</script>` element, attributes included. */
|
|
43
44
|
const SCRIPT_RE = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
|
|
@@ -191,7 +192,10 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
|
|
|
191
192
|
const importedText = new Set<string>();
|
|
192
193
|
const bound = new Set<string>();
|
|
193
194
|
|
|
194
|
-
|
|
195
|
+
/* The engine's cascade only reads `<style>` elements, so a linked stylesheet
|
|
196
|
+
* would be dropped at runtime. Inline it (and its `@import`s) now. */
|
|
197
|
+
const styles = inlineStylesheets(path, source);
|
|
198
|
+
const html = styles.html.replace(SCRIPT_RE, (match, attributes: string, inlineBody: string) => {
|
|
195
199
|
if (!isExecutable(attribute(attributes, "type"), attribute(attributes, "lang"))) return match;
|
|
196
200
|
const src = attribute(attributes, "src");
|
|
197
201
|
|
|
@@ -244,6 +248,9 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
|
|
|
244
248
|
offset += part.length + 1;
|
|
245
249
|
}
|
|
246
250
|
parts.push(`export default ${JSON.stringify(html)};`);
|
|
247
|
-
const result: AssetLoadResult = {
|
|
251
|
+
const result: AssetLoadResult = {
|
|
252
|
+
moduleSource: parts.join("\n"),
|
|
253
|
+
...(styles.dependencies.length > 0 ? { dependencies: styles.dependencies } : {}),
|
|
254
|
+
};
|
|
248
255
|
return sourceRanges.length > 0 ? { ...result, sourceRanges } : result;
|
|
249
256
|
}
|
|
@@ -26,6 +26,7 @@ import type { Extension, ExtensionModule } from "../registry.js";
|
|
|
26
26
|
import { findRuntimeDir, platformSlug } from "../../driver/paths.js";
|
|
27
27
|
import { findRuntimeLibrary } from "../../driver/runtime-lib.js";
|
|
28
28
|
import { loadHtmlAsset } from "./html.js";
|
|
29
|
+
import { loadScriptAsset } from "./three.js";
|
|
29
30
|
|
|
30
31
|
/** Top-level `gui` module bindings. */
|
|
31
32
|
const guiModule: ExtensionModule = {
|
|
@@ -77,6 +78,19 @@ const hostTypes = [
|
|
|
77
78
|
"ReadableStream",
|
|
78
79
|
"WebGLRenderingContext",
|
|
79
80
|
"WebGL2RenderingContext",
|
|
81
|
+
"WebGLBuffer",
|
|
82
|
+
"WebGLTexture",
|
|
83
|
+
"WebGLShader",
|
|
84
|
+
"WebGLProgram",
|
|
85
|
+
"WebGLFramebuffer",
|
|
86
|
+
"WebGLRenderbuffer",
|
|
87
|
+
"WebGLUniformLocation",
|
|
88
|
+
"WebGLActiveInfo",
|
|
89
|
+
"WebGLVertexArrayObject",
|
|
90
|
+
"WebGLSampler",
|
|
91
|
+
"WebGLQuery",
|
|
92
|
+
"WebGLSync",
|
|
93
|
+
"WebGLTransformFeedback",
|
|
80
94
|
"XRWebGLLayer",
|
|
81
95
|
"XRWebGLBinding",
|
|
82
96
|
] as const;
|
|
@@ -186,6 +200,6 @@ export const guiExtension: Extension = {
|
|
|
186
200
|
}),
|
|
187
201
|
/** Browser globals (`window`, `document`, the DOM type names). */
|
|
188
202
|
globalValues: () => browserGlobals,
|
|
189
|
-
/** Rewrite imported `.html` assets
|
|
190
|
-
assetLoaders: () => ({ ".html": loadHtmlAsset }),
|
|
203
|
+
/** Rewrite imported `.html` assets and patch three.js's shader unroller. */
|
|
204
|
+
assetLoaders: () => ({ ".html": loadHtmlAsset, ".js": loadScriptAsset, ".mjs": loadScriptAsset }),
|
|
191
205
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GUI extension: three.js shader-unroller compatibility.
|
|
3
|
+
*
|
|
4
|
+
* three.js's `WebGLRenderer` runs unchanged on this engine's real WebGL 2
|
|
5
|
+
* backend; no Canvas2D fallback (and no renderer wrapper) is needed. The one
|
|
6
|
+
* shader-side workaround is its `unrollLoops` pass: its lazy global regex does
|
|
7
|
+
* not behave reliably on this runtime, and the shader rewriter already defines
|
|
8
|
+
* `UNROLLED_LOOP_INDEX` and keeps the numeric `for` loop. Disabling the pattern
|
|
9
|
+
* therefore leaves valid GLSL instead of a half-expanded loop.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { AssetLoadResult } from "../registry.js";
|
|
13
|
+
|
|
14
|
+
/** three.js's unroll-loop regex declaration (r150–r16x naming). */
|
|
15
|
+
const LOOP_PATTERN = /const unrollLoopPattern = [^\n]*;/;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Patch a three.js ES module so its shaders keep their light loops. Any other
|
|
19
|
+
* `.js` asset is returned verbatim.
|
|
20
|
+
*/
|
|
21
|
+
export function loadScriptAsset(path: string, source: string): AssetLoadResult {
|
|
22
|
+
return {
|
|
23
|
+
moduleSource: source.replace(LOOP_PATTERN, "const unrollLoopPattern = /__xt_never_match__/;"),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* so a program that only needs windows and HTML/CSS does not pay for a media
|
|
7
7
|
* stack it never uses. This extension supplies the decoding half:
|
|
8
8
|
*
|
|
9
|
-
* - `media.a` (
|
|
10
|
-
*
|
|
9
|
+
* - `media.a` (`media.lib` with the MSVC ABI on Windows, built by
|
|
10
|
+
* `npm run media`) contains the FFmpeg-linked player from
|
|
11
|
+
* `runtime/ext_media/media.cpp` and the static FFmpeg libraries;
|
|
11
12
|
* - `runtime/ext_media/media_bridge.c` is compiled and linked directly. Its
|
|
12
13
|
* constructor installs the player table into the GUI engine and references
|
|
13
14
|
* the backend entry points, which makes the linker extract the archive
|
|
@@ -19,9 +20,9 @@
|
|
|
19
20
|
*
|
|
20
21
|
* xbintsc run app.ts --ext gui,media
|
|
21
22
|
*
|
|
22
|
-
* The archive is
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* The archive is built for every host platform. On Windows FFmpeg is compiled
|
|
24
|
+
* with the MSVC-targeting clang-cl so its COFF objects and static CRT match
|
|
25
|
+
* `gui.lib`; see `doc/gui/build.md`.
|
|
25
26
|
*/
|
|
26
27
|
|
|
27
28
|
import { join } from "node:path";
|
|
@@ -31,7 +32,7 @@ import { findRuntimeLibrary } from "../../driver/runtime-lib.js";
|
|
|
31
32
|
|
|
32
33
|
/** Absolute path to the per-platform prebuilt media archive. */
|
|
33
34
|
function mediaArchive(): string {
|
|
34
|
-
return join(findRuntimeDir(), "lib", platformSlug(), "media.a");
|
|
35
|
+
return join(findRuntimeDir(), "lib", platformSlug(), process.platform === "win32" ? "media.lib" : "media.a");
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export const mediaExtension: Extension = {
|
|
@@ -45,23 +46,19 @@ export const mediaExtension: Extension = {
|
|
|
45
46
|
*/
|
|
46
47
|
runtimeSources: () => [join(findRuntimeDir(), "ext_media", "media_bridge.c")],
|
|
47
48
|
nativeObjects: () => {
|
|
48
|
-
if (process.platform === "win32") {
|
|
49
|
-
throw new Error(
|
|
50
|
-
"the media extension is not available on Windows yet: it needs an MSVC-compatible " +
|
|
51
|
-
"FFmpeg build (see doc/gui/known-issues.md). Build without --ext media; media " +
|
|
52
|
-
"elements then report an 'error' event.",
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
49
|
const archive = findRuntimeLibrary("media");
|
|
56
50
|
if (archive === undefined) {
|
|
57
51
|
throw new Error(
|
|
58
|
-
`media native library not found at ${mediaArchive()} (or media.lib).\n` +
|
|
52
|
+
`media native library not found at ${mediaArchive()} (media.a or media.lib).\n` +
|
|
59
53
|
`Build it for ${platformSlug()} first with \`npm run media\` (see doc/gui/build.md).`,
|
|
60
54
|
);
|
|
61
55
|
}
|
|
62
56
|
return [archive];
|
|
63
57
|
},
|
|
64
58
|
/** `media.a` is C++ and FFmpeg itself needs libm; the GUI extension already
|
|
65
|
-
* contributes the C++ runtime and the OS frameworks.
|
|
66
|
-
|
|
59
|
+
* contributes the C++ runtime and the OS frameworks. On Windows the FFmpeg
|
|
60
|
+
* objects reference the Win32 crypto/COM services (`bcrypt`, `ole32`) and
|
|
61
|
+
* `user32`. */
|
|
62
|
+
linkerFlags: () =>
|
|
63
|
+
process.platform === "win32" ? ["-lbcrypt", "-lole32", "-luser32"] : ["-lm"],
|
|
67
64
|
};
|