@frapx/core 0.2.0
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/CHANGELOG.md +22 -0
- package/README.md +11 -0
- package/dist/chunk-P6ANS7XN.js +37 -0
- package/dist/chunk-P6ANS7XN.js.map +1 -0
- package/dist/chunk-PD6KUFXI.js +35 -0
- package/dist/chunk-PD6KUFXI.js.map +1 -0
- package/dist/color.d.ts +6 -0
- package/dist/color.js +9 -0
- package/dist/color.js.map +1 -0
- package/dist/glsl.d.ts +8 -0
- package/dist/glsl.js +9 -0
- package/dist/glsl.js.map +1 -0
- package/dist/index.d.ts +153 -0
- package/dist/index.js +695 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 - 2026-07-12
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Breaking: package renamed from `@frapx/runtime` to `@frapx/core`.** No API changes — only the npm package name and import path change. `@frapx/runtime` is deprecated on npm and will not receive further updates.
|
|
8
|
+
|
|
9
|
+
## 0.1.0 - 2026-06-27
|
|
10
|
+
|
|
11
|
+
Initial release. Shared WebGL runtime extracted from `@frapx/fx-canvas`.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `createRuntimeHost` — WebGL lifecycle host with canvas setup, animation loop, visibility gating, reduced-motion support, and context-loss recovery.
|
|
16
|
+
- Shared error hierarchy: `ShaderError`, `UnsupportedError`, `TargetNotFoundError`, `ShaderCompileError`, `TextureLoadError`, `DestroyedError`.
|
|
17
|
+
- Texture loading utilities with CORS support and dual uniform naming.
|
|
18
|
+
- WebGL shader compilation with GLSL ES 1.00 and 3.00 support, fullscreen quad geometry.
|
|
19
|
+
- Uniform naming convention utilities (`toUniformName`, `fromUniformName`).
|
|
20
|
+
- DOM canvas setup helpers for HiDPI rendering.
|
|
21
|
+
- `hexToRgb` / `hexToRgba` color utilities.
|
|
22
|
+
- `glsl` template tag and `glslUtils` GLSL snippet helpers.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @frapx/core
|
|
2
|
+
|
|
3
|
+
Shared WebGL runtime host for `@frapx` packages.
|
|
4
|
+
|
|
5
|
+
This package provides the core WebGL lifecycle engine (canvas setup, context management, animation loop, a11y gates) shared between `@frapx/fx-canvas` and future frapx packages. It is a low-level building block and its API may change between minor versions while `@frapx/core` remains at `0.x`.
|
|
6
|
+
|
|
7
|
+
If you are building a WebGL experience, you most likely want [`@frapx/fx-canvas`](https://www.npmjs.com/package/@frapx/fx-canvas) instead.
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
MIT
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/color.ts
|
|
2
|
+
var hexToRgb = (hex) => {
|
|
3
|
+
const value = normalizeHex(hex);
|
|
4
|
+
return [
|
|
5
|
+
parseChannel(value.slice(0, 2)),
|
|
6
|
+
parseChannel(value.slice(2, 4)),
|
|
7
|
+
parseChannel(value.slice(4, 6))
|
|
8
|
+
];
|
|
9
|
+
};
|
|
10
|
+
var hexToRgba = (hex) => {
|
|
11
|
+
const value = normalizeHex(hex);
|
|
12
|
+
return [
|
|
13
|
+
parseChannel(value.slice(0, 2)),
|
|
14
|
+
parseChannel(value.slice(2, 4)),
|
|
15
|
+
parseChannel(value.slice(4, 6)),
|
|
16
|
+
value.length === 8 ? parseChannel(value.slice(6, 8)) : 1
|
|
17
|
+
];
|
|
18
|
+
};
|
|
19
|
+
var normalizeHex = (hex) => {
|
|
20
|
+
const value = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
21
|
+
if (!/^(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(
|
|
22
|
+
value
|
|
23
|
+
)) {
|
|
24
|
+
throw new Error(`Invalid hex color: ${hex}`);
|
|
25
|
+
}
|
|
26
|
+
if (value.length === 3 || value.length === 4) {
|
|
27
|
+
return Array.from(value, (char) => `${char}${char}`).join("");
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
};
|
|
31
|
+
var parseChannel = (hex) => Number.parseInt(hex, 16) / 255;
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
hexToRgb,
|
|
35
|
+
hexToRgba
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=chunk-P6ANS7XN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/color.ts"],"sourcesContent":["export type Rgb = [number, number, number];\nexport type Rgba = [number, number, number, number];\n\nexport const hexToRgb = (hex: string): Rgb => {\n const value = normalizeHex(hex);\n return [\n parseChannel(value.slice(0, 2)),\n parseChannel(value.slice(2, 4)),\n parseChannel(value.slice(4, 6))\n ];\n};\n\nexport const hexToRgba = (hex: string): Rgba => {\n const value = normalizeHex(hex);\n return [\n parseChannel(value.slice(0, 2)),\n parseChannel(value.slice(2, 4)),\n parseChannel(value.slice(4, 6)),\n value.length === 8 ? parseChannel(value.slice(6, 8)) : 1\n ];\n};\n\nconst normalizeHex = (hex: string): string => {\n const value = hex.startsWith(\"#\") ? hex.slice(1) : hex;\n\n if (\n !/^(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(\n value\n )\n ) {\n throw new Error(`Invalid hex color: ${hex}`);\n }\n\n if (value.length === 3 || value.length === 4) {\n return Array.from(value, (char) => `${char}${char}`).join(\"\");\n }\n\n return value;\n};\n\nconst parseChannel = (hex: string): number => Number.parseInt(hex, 16) / 255;\n"],"mappings":";AAGO,IAAM,WAAW,CAAC,QAAqB;AAC5C,QAAM,QAAQ,aAAa,GAAG;AAC9B,SAAO;AAAA,IACL,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,IAC9B,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,IAC9B,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,EAChC;AACF;AAEO,IAAM,YAAY,CAAC,QAAsB;AAC9C,QAAM,QAAQ,aAAa,GAAG;AAC9B,SAAO;AAAA,IACL,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,IAC9B,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,IAC9B,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,IAC9B,MAAM,WAAW,IAAI,aAAa,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI;AAAA,EACzD;AACF;AAEA,IAAM,eAAe,CAAC,QAAwB;AAC5C,QAAM,QAAQ,IAAI,WAAW,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI;AAEnD,MACE,CAAC,oEAAoE;AAAA,IACnE;AAAA,EACF,GACA;AACA,UAAM,IAAI,MAAM,sBAAsB,GAAG,EAAE;AAAA,EAC7C;AAEA,MAAI,MAAM,WAAW,KAAK,MAAM,WAAW,GAAG;AAC5C,WAAO,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,KAAK,EAAE;AAAA,EAC9D;AAEA,SAAO;AACT;AAEA,IAAM,eAAe,CAAC,QAAwB,OAAO,SAAS,KAAK,EAAE,IAAI;","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/glsl.ts
|
|
2
|
+
var glsl = (strings, ...values) => String.raw({ raw: strings }, ...values);
|
|
3
|
+
var glslUtils = {
|
|
4
|
+
coverUv: glsl`
|
|
5
|
+
vec2 coverUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {
|
|
6
|
+
vec2 ratio = vec2(
|
|
7
|
+
min((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),
|
|
8
|
+
min((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)
|
|
9
|
+
);
|
|
10
|
+
return uv * ratio + (1.0 - ratio) * 0.5;
|
|
11
|
+
}
|
|
12
|
+
`,
|
|
13
|
+
containUv: glsl`
|
|
14
|
+
vec2 containUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {
|
|
15
|
+
vec2 ratio = vec2(
|
|
16
|
+
max((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),
|
|
17
|
+
max((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)
|
|
18
|
+
);
|
|
19
|
+
return uv * ratio + (1.0 - ratio) * 0.5;
|
|
20
|
+
}
|
|
21
|
+
`,
|
|
22
|
+
rotate2d: glsl`
|
|
23
|
+
mat2 rotate2d(float angle) {
|
|
24
|
+
float s = sin(angle);
|
|
25
|
+
float c = cos(angle);
|
|
26
|
+
return mat2(c, -s, s, c);
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
glsl,
|
|
33
|
+
glslUtils
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-PD6KUFXI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/glsl.ts"],"sourcesContent":["export const glsl = (\n strings: TemplateStringsArray,\n ...values: unknown[]\n): string => String.raw({ raw: strings }, ...values);\n\nexport const glslUtils = {\n coverUv: glsl`\nvec2 coverUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {\n vec2 ratio = vec2(\n min((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),\n min((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)\n );\n return uv * ratio + (1.0 - ratio) * 0.5;\n}\n`,\n containUv: glsl`\nvec2 containUv(vec2 uv, vec2 viewportSize, vec2 textureSize) {\n vec2 ratio = vec2(\n max((viewportSize.x / viewportSize.y) / (textureSize.x / textureSize.y), 1.0),\n max((viewportSize.y / viewportSize.x) / (textureSize.y / textureSize.x), 1.0)\n );\n return uv * ratio + (1.0 - ratio) * 0.5;\n}\n`,\n rotate2d: glsl`\nmat2 rotate2d(float angle) {\n float s = sin(angle);\n float c = cos(angle);\n return mat2(c, -s, s, c);\n}\n`\n} as const;\n"],"mappings":";AAAO,IAAM,OAAO,CAClB,YACG,WACQ,OAAO,IAAI,EAAE,KAAK,QAAQ,GAAG,GAAG,MAAM;AAE5C,IAAM,YAAY;AAAA,EACvB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASX,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOZ;","names":[]}
|
package/dist/color.d.ts
ADDED
package/dist/color.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/glsl.d.ts
ADDED
package/dist/glsl.js
ADDED
package/dist/glsl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export { Rgb, Rgba, hexToRgb, hexToRgba } from './color.js';
|
|
2
|
+
export { glsl, glslUtils } from './glsl.js';
|
|
3
|
+
|
|
4
|
+
type GLContext = WebGLRenderingContext | WebGL2RenderingContext;
|
|
5
|
+
type ShaderLayer = "background" | "overlay";
|
|
6
|
+
type RenderMode = "always" | "demand";
|
|
7
|
+
type DprOption = "auto" | number | (() => number);
|
|
8
|
+
type ShaderStatus = "idle" | "loading" | "ready" | "running" | "paused" | "error" | "unsupported" | "context-lost" | "destroyed";
|
|
9
|
+
type TextureSource = string | HTMLImageElement | HTMLCanvasElement;
|
|
10
|
+
type TextureOptions = {
|
|
11
|
+
source: TextureSource;
|
|
12
|
+
wrap?: "clamp" | "repeat" | "mirror";
|
|
13
|
+
filter?: "nearest" | "linear";
|
|
14
|
+
flipY?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type TextureInput = TextureSource | TextureOptions;
|
|
17
|
+
type TextureMap = Record<string, TextureInput>;
|
|
18
|
+
type HostPointer = {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
uvX: number;
|
|
22
|
+
uvY: number;
|
|
23
|
+
active: number;
|
|
24
|
+
};
|
|
25
|
+
type HostFrameState = {
|
|
26
|
+
gl: GLContext;
|
|
27
|
+
canvas: HTMLCanvasElement;
|
|
28
|
+
time: number;
|
|
29
|
+
delta: number;
|
|
30
|
+
frame: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
viewportWidth: number;
|
|
34
|
+
viewportHeight: number;
|
|
35
|
+
pixelRatio: number;
|
|
36
|
+
reducedMotion: boolean;
|
|
37
|
+
pointer: HostPointer;
|
|
38
|
+
};
|
|
39
|
+
type SetupCanvasOptions = {
|
|
40
|
+
target?: string | Element;
|
|
41
|
+
canvas?: HTMLCanvasElement;
|
|
42
|
+
layer?: ShaderLayer;
|
|
43
|
+
canvasClass?: string;
|
|
44
|
+
canvasStyle?: Partial<CSSStyleDeclaration>;
|
|
45
|
+
};
|
|
46
|
+
type RuntimeHostOptions = {
|
|
47
|
+
target?: string | Element;
|
|
48
|
+
canvas?: HTMLCanvasElement;
|
|
49
|
+
layer?: ShaderLayer;
|
|
50
|
+
dpr?: DprOption;
|
|
51
|
+
maxDpr?: number;
|
|
52
|
+
autoResize?: boolean;
|
|
53
|
+
canvasClass?: string;
|
|
54
|
+
canvasStyle?: Partial<CSSStyleDeclaration>;
|
|
55
|
+
autoStart?: boolean;
|
|
56
|
+
pauseWhenOffscreen?: boolean;
|
|
57
|
+
pauseWhenHidden?: boolean;
|
|
58
|
+
respectReducedMotion?: boolean;
|
|
59
|
+
renderMode?: RenderMode;
|
|
60
|
+
debug?: boolean;
|
|
61
|
+
createContext: (canvas: HTMLCanvasElement) => GLContext | null;
|
|
62
|
+
onInit: (gl: GLContext, width: number, height: number) => Promise<void> | void;
|
|
63
|
+
onDraw: (state: HostFrameState) => void;
|
|
64
|
+
/** Called whenever the observed size actually changes (not every frame). */
|
|
65
|
+
onResize?: (state: HostFrameState) => void;
|
|
66
|
+
onContextLost?: () => void;
|
|
67
|
+
onContextRestored?: (gl: GLContext, width: number, height: number) => Promise<void> | void;
|
|
68
|
+
onReady?: () => void;
|
|
69
|
+
onError?: (error: Error) => void;
|
|
70
|
+
};
|
|
71
|
+
type RuntimeHostInstance = {
|
|
72
|
+
readonly canvas: HTMLCanvasElement | null;
|
|
73
|
+
readonly gl: GLContext | null;
|
|
74
|
+
readonly status: ShaderStatus;
|
|
75
|
+
readonly supported: boolean;
|
|
76
|
+
readonly ready: Promise<void>;
|
|
77
|
+
start(): void;
|
|
78
|
+
stop(): void;
|
|
79
|
+
render(): void;
|
|
80
|
+
resize(): void;
|
|
81
|
+
destroy(): void;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
declare const createRuntimeHost: (options: RuntimeHostOptions) => RuntimeHostInstance;
|
|
85
|
+
|
|
86
|
+
declare class ShaderError extends Error {
|
|
87
|
+
constructor(message: string);
|
|
88
|
+
}
|
|
89
|
+
declare class UnsupportedError extends ShaderError {
|
|
90
|
+
}
|
|
91
|
+
declare class TargetNotFoundError extends ShaderError {
|
|
92
|
+
}
|
|
93
|
+
declare class ShaderCompileError extends ShaderError {
|
|
94
|
+
}
|
|
95
|
+
declare class TextureLoadError extends ShaderError {
|
|
96
|
+
}
|
|
97
|
+
declare class DestroyedError extends ShaderError {
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
declare const toUniformName: (name: string) => string;
|
|
101
|
+
declare const fromUniformName: (name: string) => string;
|
|
102
|
+
|
|
103
|
+
type DomSetup = {
|
|
104
|
+
target: Element;
|
|
105
|
+
canvas: HTMLCanvasElement;
|
|
106
|
+
createdCanvas: boolean;
|
|
107
|
+
restoredPosition: string | null;
|
|
108
|
+
};
|
|
109
|
+
declare const resolveElement: (target: string | Element | undefined) => Element | null;
|
|
110
|
+
declare const setupCanvas: (options: SetupCanvasOptions) => DomSetup | null;
|
|
111
|
+
declare const resolveDpr: (dpr: DprOption | undefined, maxDpr: number | undefined) => number;
|
|
112
|
+
declare const resizeCanvasToTarget: (canvas: HTMLCanvasElement, target: Element, dpr: number) => {
|
|
113
|
+
width: number;
|
|
114
|
+
height: number;
|
|
115
|
+
viewportWidth: number;
|
|
116
|
+
viewportHeight: number;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
type LoadedTexture = {
|
|
120
|
+
name: string;
|
|
121
|
+
uniformName: string;
|
|
122
|
+
sizeUniformName: string;
|
|
123
|
+
texture: WebGLTexture;
|
|
124
|
+
width: number;
|
|
125
|
+
height: number;
|
|
126
|
+
};
|
|
127
|
+
declare const loadTexture: (gl: GLContext, name: string, input: TextureInput, unit: number) => Promise<LoadedTexture>;
|
|
128
|
+
|
|
129
|
+
declare const defaultVertexShader = "\nattribute vec2 a_position;\nvarying vec2 v_uv;\n\nvoid main() {\n v_uv = a_position * 0.5 + 0.5;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n";
|
|
130
|
+
declare const defaultVertexShader300 = "#version 300 es\nin vec2 a_position;\nout vec2 v_uv;\n\nvoid main() {\n v_uv = a_position * 0.5 + 0.5;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n";
|
|
131
|
+
declare const isGLSL300: (src: string) => boolean;
|
|
132
|
+
declare const createProgram: (gl: GLContext, vertexSource: string, fragmentSource: string) => WebGLProgram;
|
|
133
|
+
declare const createFullscreenBuffer: (gl: GLContext, program: WebGLProgram) => WebGLBuffer;
|
|
134
|
+
|
|
135
|
+
type Blitter = {
|
|
136
|
+
/**
|
|
137
|
+
* Draws `texture` as a fullscreen quad into `target` (or the default
|
|
138
|
+
* framebuffer when `target` is `null`), setting the viewport to
|
|
139
|
+
* `width`/`height` first. Does not save or restore any other GL state.
|
|
140
|
+
*/
|
|
141
|
+
blit(texture: WebGLTexture, target: WebGLFramebuffer | null, width: number, height: number): void;
|
|
142
|
+
/** Releases the GL resources owned by this blitter. */
|
|
143
|
+
dispose(): void;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Creates a reusable fullscreen-quad "blit" utility: draws a texture into a
|
|
147
|
+
* framebuffer (or the canvas) using a minimal passthrough shader. Intended
|
|
148
|
+
* for hand-rolled multi-pass/FBO pipelines; frapx's own multi-pass features
|
|
149
|
+
* are built on the same primitive.
|
|
150
|
+
*/
|
|
151
|
+
declare const createBlitter: (gl: GLContext) => Blitter;
|
|
152
|
+
|
|
153
|
+
export { type Blitter, DestroyedError, type DomSetup, type DprOption, type GLContext, type HostFrameState, type HostPointer, type LoadedTexture, type RenderMode, type RuntimeHostInstance, type RuntimeHostOptions, type SetupCanvasOptions, ShaderCompileError, ShaderError, type ShaderLayer, type ShaderStatus, TargetNotFoundError, type TextureInput, TextureLoadError, type TextureMap, type TextureOptions, type TextureSource, UnsupportedError, createBlitter, createFullscreenBuffer, createProgram, createRuntimeHost, defaultVertexShader, defaultVertexShader300, fromUniformName, isGLSL300, loadTexture, resizeCanvasToTarget, resolveDpr, resolveElement, setupCanvas, toUniformName };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hexToRgb,
|
|
3
|
+
hexToRgba
|
|
4
|
+
} from "./chunk-P6ANS7XN.js";
|
|
5
|
+
import {
|
|
6
|
+
glsl,
|
|
7
|
+
glslUtils
|
|
8
|
+
} from "./chunk-PD6KUFXI.js";
|
|
9
|
+
|
|
10
|
+
// src/internal/errors.ts
|
|
11
|
+
var ShaderError = class extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = new.target.name;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var UnsupportedError = class extends ShaderError {
|
|
18
|
+
};
|
|
19
|
+
var TargetNotFoundError = class extends ShaderError {
|
|
20
|
+
};
|
|
21
|
+
var ShaderCompileError = class extends ShaderError {
|
|
22
|
+
};
|
|
23
|
+
var TextureLoadError = class extends ShaderError {
|
|
24
|
+
};
|
|
25
|
+
var DestroyedError = class extends ShaderError {
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/internal/dom.ts
|
|
29
|
+
var resolveElement = (target) => {
|
|
30
|
+
if (!target) return null;
|
|
31
|
+
if (typeof target === "string") return document.querySelector(target);
|
|
32
|
+
return target;
|
|
33
|
+
};
|
|
34
|
+
var setupCanvas = (options) => {
|
|
35
|
+
const target = resolveElement(options.target);
|
|
36
|
+
const canvas = options.canvas ?? document.createElement("canvas");
|
|
37
|
+
const measurementTarget = target ?? options.canvas;
|
|
38
|
+
if (!measurementTarget) return null;
|
|
39
|
+
const createdCanvas = !options.canvas;
|
|
40
|
+
const restoredPosition = target && getComputedStyle(target).position === "static" ? target.style.position : null;
|
|
41
|
+
if (target && restoredPosition !== null) {
|
|
42
|
+
target.style.position = "relative";
|
|
43
|
+
}
|
|
44
|
+
applyCanvasStyle(canvas, options, createdCanvas);
|
|
45
|
+
if (options.canvasClass) {
|
|
46
|
+
canvas.classList.add(options.canvasClass);
|
|
47
|
+
}
|
|
48
|
+
if (options.canvasStyle) {
|
|
49
|
+
Object.assign(canvas.style, options.canvasStyle);
|
|
50
|
+
}
|
|
51
|
+
if (createdCanvas && target) {
|
|
52
|
+
if ((options.layer ?? "background") === "overlay") {
|
|
53
|
+
target.append(canvas);
|
|
54
|
+
} else {
|
|
55
|
+
target.prepend(canvas);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
target: measurementTarget,
|
|
60
|
+
canvas,
|
|
61
|
+
createdCanvas,
|
|
62
|
+
restoredPosition
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
var resolveDpr = (dpr, maxDpr) => {
|
|
66
|
+
const raw = typeof dpr === "function" ? dpr() : typeof dpr === "number" ? dpr : typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
|
|
67
|
+
return Math.max(1, Math.min(raw, maxDpr ?? 2));
|
|
68
|
+
};
|
|
69
|
+
var resizeCanvasToTarget = (canvas, target, dpr) => {
|
|
70
|
+
const rect = target.getBoundingClientRect();
|
|
71
|
+
const viewportWidth = Math.max(1, rect.width || canvas.clientWidth || 1);
|
|
72
|
+
const viewportHeight = Math.max(1, rect.height || canvas.clientHeight || 1);
|
|
73
|
+
const width = Math.max(1, Math.round(viewportWidth * dpr));
|
|
74
|
+
const height = Math.max(1, Math.round(viewportHeight * dpr));
|
|
75
|
+
if (canvas.width !== width) canvas.width = width;
|
|
76
|
+
if (canvas.height !== height) canvas.height = height;
|
|
77
|
+
return { width, height, viewportWidth, viewportHeight };
|
|
78
|
+
};
|
|
79
|
+
var applyCanvasStyle = (canvas, options, shouldPosition) => {
|
|
80
|
+
canvas.dataset.frapxCanvas = "";
|
|
81
|
+
if (!shouldPosition) return;
|
|
82
|
+
const layer = options.layer ?? "background";
|
|
83
|
+
Object.assign(canvas.style, {
|
|
84
|
+
position: "absolute",
|
|
85
|
+
inset: "0",
|
|
86
|
+
width: "100%",
|
|
87
|
+
height: "100%",
|
|
88
|
+
display: "block",
|
|
89
|
+
pointerEvents: "none",
|
|
90
|
+
zIndex: layer === "overlay" ? "1" : "0"
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/host.ts
|
|
95
|
+
var MAX_DELTA_SECONDS = 0.1;
|
|
96
|
+
var createRuntimeHost = (options) => {
|
|
97
|
+
return new RuntimeHost(options);
|
|
98
|
+
};
|
|
99
|
+
var RuntimeHost = class {
|
|
100
|
+
constructor(options) {
|
|
101
|
+
this.readyResolve = null;
|
|
102
|
+
this.readyReject = null;
|
|
103
|
+
this.dom = null;
|
|
104
|
+
this.context = null;
|
|
105
|
+
this.resizeObserver = null;
|
|
106
|
+
this.intersectionObserver = null;
|
|
107
|
+
this.rafId = 0;
|
|
108
|
+
this.running = false;
|
|
109
|
+
this.inRender = false;
|
|
110
|
+
this.destroyed = false;
|
|
111
|
+
this.gates = /* @__PURE__ */ new Set();
|
|
112
|
+
this.reducedMotionActive = false;
|
|
113
|
+
this.frame = 0;
|
|
114
|
+
this.elapsed = 0;
|
|
115
|
+
this.lastTimestamp = 0;
|
|
116
|
+
this.pixelRatio = 1;
|
|
117
|
+
this.width = 1;
|
|
118
|
+
this.height = 1;
|
|
119
|
+
this.viewportWidth = 1;
|
|
120
|
+
this.viewportHeight = 1;
|
|
121
|
+
this.pointer = { x: 0, y: 0, uvX: 0, uvY: 0, active: 0 };
|
|
122
|
+
this.removeListeners = [];
|
|
123
|
+
this.currentStatus = "idle";
|
|
124
|
+
this.contextRestoredShouldRun = false;
|
|
125
|
+
this.options = options;
|
|
126
|
+
this.ready = new Promise((resolve, reject) => {
|
|
127
|
+
this.readyResolve = resolve;
|
|
128
|
+
this.readyReject = reject;
|
|
129
|
+
});
|
|
130
|
+
queueMicrotask(() => {
|
|
131
|
+
void this.init();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
get canvas() {
|
|
135
|
+
return this.dom?.canvas ?? null;
|
|
136
|
+
}
|
|
137
|
+
get gl() {
|
|
138
|
+
return this.context;
|
|
139
|
+
}
|
|
140
|
+
get status() {
|
|
141
|
+
return this.currentStatus;
|
|
142
|
+
}
|
|
143
|
+
get supported() {
|
|
144
|
+
return !["unsupported", "destroyed"].includes(this.currentStatus);
|
|
145
|
+
}
|
|
146
|
+
start() {
|
|
147
|
+
if (this.destroyed || this.currentStatus === "unsupported") return;
|
|
148
|
+
this.running = true;
|
|
149
|
+
this.reconcile();
|
|
150
|
+
}
|
|
151
|
+
stop() {
|
|
152
|
+
this.running = false;
|
|
153
|
+
this.cancelFrame();
|
|
154
|
+
if (!this.destroyed && this.currentStatus !== "unsupported") {
|
|
155
|
+
this.currentStatus = "paused";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
render() {
|
|
159
|
+
if (this.destroyed || !this.dom || !this.context || this.currentStatus === "unsupported" || this.hasVisibilityBlock || this.inRender)
|
|
160
|
+
return;
|
|
161
|
+
this.renderInternal(this.elapsed, 0);
|
|
162
|
+
}
|
|
163
|
+
resize() {
|
|
164
|
+
if (this.destroyed || !this.dom) return;
|
|
165
|
+
const prevWidth = this.width;
|
|
166
|
+
const prevHeight = this.height;
|
|
167
|
+
const prevViewportWidth = this.viewportWidth;
|
|
168
|
+
const prevViewportHeight = this.viewportHeight;
|
|
169
|
+
this.measureSize();
|
|
170
|
+
const sizeChanged = this.width !== prevWidth || this.height !== prevHeight || this.viewportWidth !== prevViewportWidth || this.viewportHeight !== prevViewportHeight;
|
|
171
|
+
if (sizeChanged && this.context) {
|
|
172
|
+
this.options.onResize?.({
|
|
173
|
+
gl: this.context,
|
|
174
|
+
canvas: this.dom.canvas,
|
|
175
|
+
time: this.elapsed,
|
|
176
|
+
delta: 0,
|
|
177
|
+
frame: this.frame,
|
|
178
|
+
width: this.width,
|
|
179
|
+
height: this.height,
|
|
180
|
+
viewportWidth: this.viewportWidth,
|
|
181
|
+
viewportHeight: this.viewportHeight,
|
|
182
|
+
pixelRatio: this.pixelRatio,
|
|
183
|
+
reducedMotion: this.reducedMotionActive,
|
|
184
|
+
pointer: { ...this.pointer }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (this.options.renderMode === "demand" && !this.inRender) {
|
|
188
|
+
this.render();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
destroy() {
|
|
192
|
+
if (this.destroyed) return;
|
|
193
|
+
this.destroyed = true;
|
|
194
|
+
this.running = false;
|
|
195
|
+
this.cancelFrame();
|
|
196
|
+
this.resizeObserver?.disconnect();
|
|
197
|
+
this.intersectionObserver?.disconnect();
|
|
198
|
+
for (const remove of this.removeListeners) remove();
|
|
199
|
+
this.removeListeners = [];
|
|
200
|
+
this.context = null;
|
|
201
|
+
if (this.dom?.createdCanvas) {
|
|
202
|
+
this.dom.canvas.remove();
|
|
203
|
+
}
|
|
204
|
+
const dom = this.dom;
|
|
205
|
+
if (dom?.restoredPosition !== null && dom && this.options.target) {
|
|
206
|
+
dom.target.style.position = dom.restoredPosition;
|
|
207
|
+
}
|
|
208
|
+
this.currentStatus = "destroyed";
|
|
209
|
+
this.readyReject?.(new DestroyedError("Runtime host was destroyed."));
|
|
210
|
+
}
|
|
211
|
+
get hasVisibilityBlock() {
|
|
212
|
+
return this.gates.has("offscreen") || this.gates.has("hidden");
|
|
213
|
+
}
|
|
214
|
+
get hasMotionBlock() {
|
|
215
|
+
return this.gates.has("reducedMotion") && this.options.renderMode !== "demand";
|
|
216
|
+
}
|
|
217
|
+
setGate(gate, closed) {
|
|
218
|
+
if (closed === this.gates.has(gate)) return;
|
|
219
|
+
if (closed) this.gates.add(gate);
|
|
220
|
+
else this.gates.delete(gate);
|
|
221
|
+
this.reconcile();
|
|
222
|
+
}
|
|
223
|
+
reconcile() {
|
|
224
|
+
if (this.destroyed || this.currentStatus === "unsupported" || this.currentStatus === "error" || this.currentStatus === "context-lost") {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (this.running) {
|
|
228
|
+
if (this.hasVisibilityBlock) {
|
|
229
|
+
this.cancelFrame();
|
|
230
|
+
this.currentStatus = "paused";
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (this.hasMotionBlock) {
|
|
234
|
+
this.cancelFrame();
|
|
235
|
+
this.currentStatus = "paused";
|
|
236
|
+
this.renderStaticFrame();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
this.currentStatus = "running";
|
|
240
|
+
if (this.options.renderMode === "demand") this.render();
|
|
241
|
+
else this.scheduleFrame();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (this.options.renderMode === "demand") this.render();
|
|
245
|
+
}
|
|
246
|
+
renderStaticFrame() {
|
|
247
|
+
if (this.hasVisibilityBlock) return;
|
|
248
|
+
this.renderInternal(this.elapsed, 0);
|
|
249
|
+
}
|
|
250
|
+
renderInternal(time, delta) {
|
|
251
|
+
if (!this.dom || !this.context) return;
|
|
252
|
+
const gl = this.context;
|
|
253
|
+
this.inRender = true;
|
|
254
|
+
this.measureSize();
|
|
255
|
+
const state = {
|
|
256
|
+
gl,
|
|
257
|
+
canvas: this.dom.canvas,
|
|
258
|
+
time,
|
|
259
|
+
delta,
|
|
260
|
+
frame: this.frame,
|
|
261
|
+
width: this.width,
|
|
262
|
+
height: this.height,
|
|
263
|
+
viewportWidth: this.viewportWidth,
|
|
264
|
+
viewportHeight: this.viewportHeight,
|
|
265
|
+
pixelRatio: this.pixelRatio,
|
|
266
|
+
reducedMotion: this.reducedMotionActive,
|
|
267
|
+
pointer: { ...this.pointer }
|
|
268
|
+
};
|
|
269
|
+
this.options.onDraw(state);
|
|
270
|
+
this.frame++;
|
|
271
|
+
this.inRender = false;
|
|
272
|
+
}
|
|
273
|
+
measureSize() {
|
|
274
|
+
if (!this.dom) return;
|
|
275
|
+
this.pixelRatio = resolveDpr(this.options.dpr, this.options.maxDpr);
|
|
276
|
+
const size = resizeCanvasToTarget(
|
|
277
|
+
this.dom.canvas,
|
|
278
|
+
this.dom.target,
|
|
279
|
+
this.pixelRatio
|
|
280
|
+
);
|
|
281
|
+
this.width = size.width;
|
|
282
|
+
this.height = size.height;
|
|
283
|
+
this.viewportWidth = size.viewportWidth;
|
|
284
|
+
this.viewportHeight = size.viewportHeight;
|
|
285
|
+
}
|
|
286
|
+
scheduleFrame() {
|
|
287
|
+
if (this.rafId || this.options.renderMode === "demand") return;
|
|
288
|
+
this.rafId = requestAnimationFrame((timestamp) => {
|
|
289
|
+
this.rafId = 0;
|
|
290
|
+
if (!this.running || this.destroyed) return;
|
|
291
|
+
const seconds = timestamp / 1e3;
|
|
292
|
+
const rawDelta = this.lastTimestamp ? seconds - this.lastTimestamp : 0;
|
|
293
|
+
const delta = Math.min(Math.max(rawDelta, 0), MAX_DELTA_SECONDS);
|
|
294
|
+
this.lastTimestamp = seconds;
|
|
295
|
+
this.elapsed += delta;
|
|
296
|
+
this.renderInternal(this.elapsed, delta);
|
|
297
|
+
this.scheduleFrame();
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
cancelFrame() {
|
|
301
|
+
if (this.rafId) {
|
|
302
|
+
cancelAnimationFrame(this.rafId);
|
|
303
|
+
this.rafId = 0;
|
|
304
|
+
}
|
|
305
|
+
this.lastTimestamp = 0;
|
|
306
|
+
}
|
|
307
|
+
async init() {
|
|
308
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
309
|
+
this.failUnsupported(new UnsupportedError("WebGL requires a browser."));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.dom = setupCanvas({
|
|
313
|
+
...this.options.target !== void 0 && { target: this.options.target },
|
|
314
|
+
...this.options.canvas !== void 0 && { canvas: this.options.canvas },
|
|
315
|
+
...this.options.layer !== void 0 && { layer: this.options.layer },
|
|
316
|
+
...this.options.canvasClass !== void 0 && { canvasClass: this.options.canvasClass },
|
|
317
|
+
...this.options.canvasStyle !== void 0 && { canvasStyle: this.options.canvasStyle }
|
|
318
|
+
});
|
|
319
|
+
if (!this.dom) {
|
|
320
|
+
this.failUnsupported(new TargetNotFoundError("Target or canvas not found."));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
let gl;
|
|
324
|
+
try {
|
|
325
|
+
gl = this.options.createContext(this.dom.canvas);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
this.failUnsupported(
|
|
328
|
+
error instanceof UnsupportedError ? error : new UnsupportedError(String(error))
|
|
329
|
+
);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (!gl) {
|
|
333
|
+
this.failUnsupported(new UnsupportedError("WebGL is not supported."));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this.context = gl;
|
|
337
|
+
this.currentStatus = "loading";
|
|
338
|
+
try {
|
|
339
|
+
this.setupDomObservers();
|
|
340
|
+
this.setupContextEvents();
|
|
341
|
+
this.measureSize();
|
|
342
|
+
await this.options.onInit(gl, this.width, this.height);
|
|
343
|
+
if (this.destroyed) return;
|
|
344
|
+
this.currentStatus = "ready";
|
|
345
|
+
this.readyResolve?.();
|
|
346
|
+
this.options.onReady?.();
|
|
347
|
+
if (this.running || (this.options.autoStart ?? true)) {
|
|
348
|
+
this.start();
|
|
349
|
+
} else if (this.options.renderMode === "demand") {
|
|
350
|
+
this.render();
|
|
351
|
+
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
setupDomObservers() {
|
|
357
|
+
if (!this.dom) return;
|
|
358
|
+
if (this.options.autoResize ?? true) {
|
|
359
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
360
|
+
this.resize();
|
|
361
|
+
});
|
|
362
|
+
this.resizeObserver.observe(this.dom.target);
|
|
363
|
+
}
|
|
364
|
+
if (this.options.pauseWhenOffscreen ?? true) {
|
|
365
|
+
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
366
|
+
const entry = entries[0];
|
|
367
|
+
this.setGate("offscreen", !entry?.isIntersecting);
|
|
368
|
+
});
|
|
369
|
+
this.intersectionObserver.observe(this.dom.target);
|
|
370
|
+
}
|
|
371
|
+
if (this.options.pauseWhenHidden ?? true) {
|
|
372
|
+
if (document.hidden) this.gates.add("hidden");
|
|
373
|
+
const onVisibility = () => {
|
|
374
|
+
this.setGate("hidden", document.hidden);
|
|
375
|
+
};
|
|
376
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
377
|
+
this.removeListeners.push(() => {
|
|
378
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
this.setupMotionObserver();
|
|
382
|
+
const onPointerMove = (event) => {
|
|
383
|
+
if (!this.dom) return;
|
|
384
|
+
const pointerEvent = event;
|
|
385
|
+
const rect = this.dom.target.getBoundingClientRect();
|
|
386
|
+
const x = pointerEvent.clientX - rect.left;
|
|
387
|
+
const y = pointerEvent.clientY - rect.top;
|
|
388
|
+
this.pointer.x = x * this.pixelRatio;
|
|
389
|
+
this.pointer.y = (rect.height - y) * this.pixelRatio;
|
|
390
|
+
this.pointer.uvX = rect.width > 0 ? x / rect.width : 0;
|
|
391
|
+
this.pointer.uvY = rect.height > 0 ? 1 - y / rect.height : 0;
|
|
392
|
+
this.pointer.active = 1;
|
|
393
|
+
if (this.options.renderMode === "demand") this.render();
|
|
394
|
+
};
|
|
395
|
+
const onPointerLeave = () => {
|
|
396
|
+
this.pointer.active = 0;
|
|
397
|
+
if (this.options.renderMode === "demand") this.render();
|
|
398
|
+
};
|
|
399
|
+
this.dom.target.addEventListener("pointermove", onPointerMove);
|
|
400
|
+
this.dom.target.addEventListener("pointerenter", onPointerMove);
|
|
401
|
+
this.dom.target.addEventListener("pointerleave", onPointerLeave);
|
|
402
|
+
this.removeListeners.push(() => {
|
|
403
|
+
this.dom?.target.removeEventListener("pointermove", onPointerMove);
|
|
404
|
+
this.dom?.target.removeEventListener("pointerenter", onPointerMove);
|
|
405
|
+
this.dom?.target.removeEventListener("pointerleave", onPointerLeave);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
setupMotionObserver() {
|
|
409
|
+
if (typeof window.matchMedia !== "function") return;
|
|
410
|
+
const query = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
411
|
+
this.reducedMotionActive = query.matches;
|
|
412
|
+
if (this.options.respectReducedMotion && query.matches) {
|
|
413
|
+
this.gates.add("reducedMotion");
|
|
414
|
+
}
|
|
415
|
+
const onChange = (event) => {
|
|
416
|
+
this.reducedMotionActive = event.matches;
|
|
417
|
+
if (this.options.respectReducedMotion) {
|
|
418
|
+
this.setGate("reducedMotion", event.matches);
|
|
419
|
+
} else if (this.options.renderMode === "demand") {
|
|
420
|
+
this.render();
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
query.addEventListener("change", onChange);
|
|
424
|
+
this.removeListeners.push(() => {
|
|
425
|
+
query.removeEventListener("change", onChange);
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
setupContextEvents() {
|
|
429
|
+
if (!this.dom) return;
|
|
430
|
+
const onLost = (event) => {
|
|
431
|
+
event.preventDefault();
|
|
432
|
+
this.contextRestoredShouldRun = this.running;
|
|
433
|
+
this.cancelFrame();
|
|
434
|
+
this.context = null;
|
|
435
|
+
this.currentStatus = "context-lost";
|
|
436
|
+
this.options.onContextLost?.();
|
|
437
|
+
this.options.onError?.(new UnsupportedError("WebGL context lost."));
|
|
438
|
+
};
|
|
439
|
+
const onRestored = () => {
|
|
440
|
+
if (!this.dom || this.destroyed) return;
|
|
441
|
+
const gl = this.options.createContext(this.dom.canvas);
|
|
442
|
+
if (!gl) return;
|
|
443
|
+
this.context = gl;
|
|
444
|
+
const afterRestore = async () => {
|
|
445
|
+
try {
|
|
446
|
+
await this.options.onContextRestored?.(gl, this.width, this.height);
|
|
447
|
+
if (this.contextRestoredShouldRun) this.start();
|
|
448
|
+
else this.render();
|
|
449
|
+
} catch (error) {
|
|
450
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
void afterRestore();
|
|
454
|
+
};
|
|
455
|
+
this.dom.canvas.addEventListener("webglcontextlost", onLost);
|
|
456
|
+
this.dom.canvas.addEventListener("webglcontextrestored", onRestored);
|
|
457
|
+
this.removeListeners.push(() => {
|
|
458
|
+
this.dom?.canvas.removeEventListener("webglcontextlost", onLost);
|
|
459
|
+
this.dom?.canvas.removeEventListener("webglcontextrestored", onRestored);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
failUnsupported(error) {
|
|
463
|
+
this.currentStatus = "unsupported";
|
|
464
|
+
this.debugWarn(error.message);
|
|
465
|
+
this.options.onError?.(error);
|
|
466
|
+
this.readyReject?.(error);
|
|
467
|
+
}
|
|
468
|
+
fail(error) {
|
|
469
|
+
this.currentStatus = "error";
|
|
470
|
+
this.debugWarn(error.message);
|
|
471
|
+
this.options.onError?.(error);
|
|
472
|
+
this.readyReject?.(error);
|
|
473
|
+
}
|
|
474
|
+
debugWarn(message) {
|
|
475
|
+
if (this.options.debug) {
|
|
476
|
+
console.warn(`[frapx/runtime] ${message}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/internal/names.ts
|
|
482
|
+
var toUniformName = (name) => name.startsWith("u_") ? name : `u_${name}`;
|
|
483
|
+
var fromUniformName = (name) => name.startsWith("u_") ? name.slice(2) : name;
|
|
484
|
+
|
|
485
|
+
// src/internal/textures.ts
|
|
486
|
+
var loadTexture = async (gl, name, input, unit) => {
|
|
487
|
+
const options = normalizeTextureInput(input);
|
|
488
|
+
const source = await resolveSource(options.source);
|
|
489
|
+
const texture = gl.createTexture();
|
|
490
|
+
if (!texture) {
|
|
491
|
+
throw new TextureLoadError(`Failed to create texture: ${name}`);
|
|
492
|
+
}
|
|
493
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
494
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
495
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, options.flipY ?? true);
|
|
496
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
497
|
+
const wrap = getWrap(gl, options.wrap ?? "clamp");
|
|
498
|
+
const filter = getFilter(gl, options.filter ?? "linear");
|
|
499
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
|
|
500
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
|
|
501
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
|
|
502
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
|
|
503
|
+
return {
|
|
504
|
+
name,
|
|
505
|
+
uniformName: toUniformName(name),
|
|
506
|
+
sizeUniformName: toUniformName(`${name}Size`),
|
|
507
|
+
texture,
|
|
508
|
+
width: getSourceWidth(source),
|
|
509
|
+
height: getSourceHeight(source)
|
|
510
|
+
};
|
|
511
|
+
};
|
|
512
|
+
var normalizeTextureInput = (input) => {
|
|
513
|
+
if (typeof input === "string" || isTextureSource(input)) {
|
|
514
|
+
return { source: input };
|
|
515
|
+
}
|
|
516
|
+
return input;
|
|
517
|
+
};
|
|
518
|
+
var resolveSource = async (source) => {
|
|
519
|
+
if (typeof source !== "string") {
|
|
520
|
+
return source;
|
|
521
|
+
}
|
|
522
|
+
const image = new Image();
|
|
523
|
+
image.crossOrigin = "anonymous";
|
|
524
|
+
image.src = source;
|
|
525
|
+
try {
|
|
526
|
+
await image.decode();
|
|
527
|
+
} catch {
|
|
528
|
+
throw new TextureLoadError(`Failed to load texture: ${source}`);
|
|
529
|
+
}
|
|
530
|
+
return image;
|
|
531
|
+
};
|
|
532
|
+
var getWrap = (gl, wrap) => {
|
|
533
|
+
if (wrap === "repeat") return gl.REPEAT;
|
|
534
|
+
if (wrap === "mirror") return gl.MIRRORED_REPEAT;
|
|
535
|
+
return gl.CLAMP_TO_EDGE;
|
|
536
|
+
};
|
|
537
|
+
var getFilter = (gl, filter) => filter === "nearest" ? gl.NEAREST : gl.LINEAR;
|
|
538
|
+
var isTextureSource = (input) => typeof HTMLImageElement !== "undefined" && (input instanceof HTMLImageElement || input instanceof HTMLCanvasElement);
|
|
539
|
+
var getSourceWidth = (source) => "naturalWidth" in source ? source.naturalWidth : source.width;
|
|
540
|
+
var getSourceHeight = (source) => "naturalHeight" in source ? source.naturalHeight : source.height;
|
|
541
|
+
|
|
542
|
+
// src/internal/webgl.ts
|
|
543
|
+
var defaultVertexShader = `
|
|
544
|
+
attribute vec2 a_position;
|
|
545
|
+
varying vec2 v_uv;
|
|
546
|
+
|
|
547
|
+
void main() {
|
|
548
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
549
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
550
|
+
}
|
|
551
|
+
`;
|
|
552
|
+
var defaultVertexShader300 = `#version 300 es
|
|
553
|
+
in vec2 a_position;
|
|
554
|
+
out vec2 v_uv;
|
|
555
|
+
|
|
556
|
+
void main() {
|
|
557
|
+
v_uv = a_position * 0.5 + 0.5;
|
|
558
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
559
|
+
}
|
|
560
|
+
`;
|
|
561
|
+
var isGLSL300 = (src) => /^\s*#version\s+300\s+es\b/.test(src);
|
|
562
|
+
var createProgram = (gl, vertexSource, fragmentSource) => {
|
|
563
|
+
const vertex = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
564
|
+
const fragment = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
565
|
+
const program = gl.createProgram();
|
|
566
|
+
if (!program) {
|
|
567
|
+
throw new ShaderCompileError("Failed to create WebGL program.");
|
|
568
|
+
}
|
|
569
|
+
gl.attachShader(program, vertex);
|
|
570
|
+
gl.attachShader(program, fragment);
|
|
571
|
+
gl.linkProgram(program);
|
|
572
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
573
|
+
const log = gl.getProgramInfoLog(program) ?? "Unknown program link error.";
|
|
574
|
+
gl.deleteProgram(program);
|
|
575
|
+
gl.deleteShader(vertex);
|
|
576
|
+
gl.deleteShader(fragment);
|
|
577
|
+
throw new ShaderCompileError(log);
|
|
578
|
+
}
|
|
579
|
+
gl.deleteShader(vertex);
|
|
580
|
+
gl.deleteShader(fragment);
|
|
581
|
+
return program;
|
|
582
|
+
};
|
|
583
|
+
var createFullscreenBuffer = (gl, program) => {
|
|
584
|
+
const buffer = gl.createBuffer();
|
|
585
|
+
if (!buffer) {
|
|
586
|
+
throw new ShaderCompileError("Failed to create fullscreen buffer.");
|
|
587
|
+
}
|
|
588
|
+
const location = gl.getAttribLocation(program, "a_position");
|
|
589
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
590
|
+
gl.bufferData(
|
|
591
|
+
gl.ARRAY_BUFFER,
|
|
592
|
+
new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
|
|
593
|
+
gl.STATIC_DRAW
|
|
594
|
+
);
|
|
595
|
+
if (location >= 0) {
|
|
596
|
+
gl.enableVertexAttribArray(location);
|
|
597
|
+
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
|
|
598
|
+
}
|
|
599
|
+
return buffer;
|
|
600
|
+
};
|
|
601
|
+
var compileShader = (gl, type, source) => {
|
|
602
|
+
const shader = gl.createShader(type);
|
|
603
|
+
if (!shader) {
|
|
604
|
+
throw new ShaderCompileError("Failed to create WebGL shader.");
|
|
605
|
+
}
|
|
606
|
+
gl.shaderSource(shader, source);
|
|
607
|
+
gl.compileShader(shader);
|
|
608
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
609
|
+
const log = gl.getShaderInfoLog(shader) ?? "Unknown shader compile error.";
|
|
610
|
+
gl.deleteShader(shader);
|
|
611
|
+
throw new ShaderCompileError(log);
|
|
612
|
+
}
|
|
613
|
+
return shader;
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// src/internal/blit.ts
|
|
617
|
+
var blitFragmentShader = `
|
|
618
|
+
precision mediump float;
|
|
619
|
+
varying vec2 v_uv;
|
|
620
|
+
uniform sampler2D u_texture;
|
|
621
|
+
|
|
622
|
+
void main() {
|
|
623
|
+
gl_FragColor = texture2D(u_texture, v_uv);
|
|
624
|
+
}
|
|
625
|
+
`;
|
|
626
|
+
var blitFragmentShader300 = `#version 300 es
|
|
627
|
+
precision mediump float;
|
|
628
|
+
in vec2 v_uv;
|
|
629
|
+
uniform sampler2D u_texture;
|
|
630
|
+
out vec4 fragColor;
|
|
631
|
+
|
|
632
|
+
void main() {
|
|
633
|
+
fragColor = texture(u_texture, v_uv);
|
|
634
|
+
}
|
|
635
|
+
`;
|
|
636
|
+
var isWebGL2Context = (gl) => typeof gl.createVertexArray === "function";
|
|
637
|
+
var createBlitter = (gl) => {
|
|
638
|
+
const program = createProgram(
|
|
639
|
+
gl,
|
|
640
|
+
isWebGL2Context(gl) ? defaultVertexShader300 : defaultVertexShader,
|
|
641
|
+
isWebGL2Context(gl) ? blitFragmentShader300 : blitFragmentShader
|
|
642
|
+
);
|
|
643
|
+
const buffer = createFullscreenBuffer(gl, program);
|
|
644
|
+
const textureUniform = gl.getUniformLocation(program, "u_texture");
|
|
645
|
+
return {
|
|
646
|
+
blit(texture, target, width, height) {
|
|
647
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
|
|
648
|
+
gl.viewport(0, 0, width, height);
|
|
649
|
+
gl.useProgram(program);
|
|
650
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
651
|
+
const location = gl.getAttribLocation(program, "a_position");
|
|
652
|
+
if (location >= 0) {
|
|
653
|
+
gl.enableVertexAttribArray(location);
|
|
654
|
+
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
|
|
655
|
+
}
|
|
656
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
657
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
658
|
+
if (textureUniform) {
|
|
659
|
+
gl.uniform1i(textureUniform, 0);
|
|
660
|
+
}
|
|
661
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
662
|
+
},
|
|
663
|
+
dispose() {
|
|
664
|
+
gl.deleteProgram(program);
|
|
665
|
+
gl.deleteBuffer(buffer);
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
export {
|
|
670
|
+
DestroyedError,
|
|
671
|
+
ShaderCompileError,
|
|
672
|
+
ShaderError,
|
|
673
|
+
TargetNotFoundError,
|
|
674
|
+
TextureLoadError,
|
|
675
|
+
UnsupportedError,
|
|
676
|
+
createBlitter,
|
|
677
|
+
createFullscreenBuffer,
|
|
678
|
+
createProgram,
|
|
679
|
+
createRuntimeHost,
|
|
680
|
+
defaultVertexShader,
|
|
681
|
+
defaultVertexShader300,
|
|
682
|
+
fromUniformName,
|
|
683
|
+
glsl,
|
|
684
|
+
glslUtils,
|
|
685
|
+
hexToRgb,
|
|
686
|
+
hexToRgba,
|
|
687
|
+
isGLSL300,
|
|
688
|
+
loadTexture,
|
|
689
|
+
resizeCanvasToTarget,
|
|
690
|
+
resolveDpr,
|
|
691
|
+
resolveElement,
|
|
692
|
+
setupCanvas,
|
|
693
|
+
toUniformName
|
|
694
|
+
};
|
|
695
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/errors.ts","../src/internal/dom.ts","../src/host.ts","../src/internal/names.ts","../src/internal/textures.ts","../src/internal/webgl.ts","../src/internal/blit.ts"],"sourcesContent":["export class ShaderError extends Error {\n constructor(message: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\nexport class UnsupportedError extends ShaderError {}\nexport class TargetNotFoundError extends ShaderError {}\nexport class ShaderCompileError extends ShaderError {}\nexport class TextureLoadError extends ShaderError {}\nexport class DestroyedError extends ShaderError {}\n","import type { DprOption, SetupCanvasOptions } from \"../types\";\n\nexport type DomSetup = {\n target: Element;\n canvas: HTMLCanvasElement;\n createdCanvas: boolean;\n restoredPosition: string | null;\n};\n\nexport const resolveElement = (\n target: string | Element | undefined\n): Element | null => {\n if (!target) return null;\n if (typeof target === \"string\") return document.querySelector(target);\n return target;\n};\n\nexport const setupCanvas = (options: SetupCanvasOptions): DomSetup | null => {\n const target = resolveElement(options.target);\n const canvas = options.canvas ?? document.createElement(\"canvas\");\n const measurementTarget = target ?? options.canvas;\n\n if (!measurementTarget) return null;\n\n const createdCanvas = !options.canvas;\n const restoredPosition =\n target && getComputedStyle(target).position === \"static\"\n ? (target as HTMLElement).style.position\n : null;\n\n if (target && restoredPosition !== null) {\n (target as HTMLElement).style.position = \"relative\";\n }\n\n applyCanvasStyle(canvas, options, createdCanvas);\n\n if (options.canvasClass) {\n canvas.classList.add(options.canvasClass);\n }\n\n if (options.canvasStyle) {\n Object.assign(canvas.style, options.canvasStyle);\n }\n\n if (createdCanvas && target) {\n if ((options.layer ?? \"background\") === \"overlay\") {\n target.append(canvas);\n } else {\n target.prepend(canvas);\n }\n }\n\n return {\n target: measurementTarget,\n canvas,\n createdCanvas,\n restoredPosition\n };\n};\n\nexport const resolveDpr = (\n dpr: DprOption | undefined,\n maxDpr: number | undefined\n): number => {\n const raw =\n typeof dpr === \"function\"\n ? dpr()\n : typeof dpr === \"number\"\n ? dpr\n : typeof window === \"undefined\"\n ? 1\n : window.devicePixelRatio || 1;\n\n return Math.max(1, Math.min(raw, maxDpr ?? 2));\n};\n\nexport const resizeCanvasToTarget = (\n canvas: HTMLCanvasElement,\n target: Element,\n dpr: number\n): {\n width: number;\n height: number;\n viewportWidth: number;\n viewportHeight: number;\n} => {\n const rect = target.getBoundingClientRect();\n const viewportWidth = Math.max(1, rect.width || canvas.clientWidth || 1);\n const viewportHeight = Math.max(1, rect.height || canvas.clientHeight || 1);\n const width = Math.max(1, Math.round(viewportWidth * dpr));\n const height = Math.max(1, Math.round(viewportHeight * dpr));\n\n if (canvas.width !== width) canvas.width = width;\n if (canvas.height !== height) canvas.height = height;\n\n return { width, height, viewportWidth, viewportHeight };\n};\n\nconst applyCanvasStyle = (\n canvas: HTMLCanvasElement,\n options: SetupCanvasOptions,\n shouldPosition: boolean\n): void => {\n canvas.dataset.frapxCanvas = \"\";\n\n if (!shouldPosition) return;\n\n const layer = options.layer ?? \"background\";\n Object.assign(canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n display: \"block\",\n pointerEvents: \"none\",\n zIndex: layer === \"overlay\" ? \"1\" : \"0\"\n });\n};\n","import type {\n GLContext,\n HostFrameState,\n RuntimeHostInstance,\n RuntimeHostOptions,\n ShaderStatus\n} from \"./types\";\nimport { DestroyedError, TargetNotFoundError, UnsupportedError } from \"./internal/errors\";\nimport {\n resolveDpr,\n resizeCanvasToTarget,\n setupCanvas,\n type DomSetup\n} from \"./internal/dom\";\n\nconst MAX_DELTA_SECONDS = 0.1;\n\ntype Gate = \"offscreen\" | \"hidden\" | \"reducedMotion\";\n\nexport const createRuntimeHost = (\n options: RuntimeHostOptions\n): RuntimeHostInstance => {\n return new RuntimeHost(options);\n};\n\nclass RuntimeHost implements RuntimeHostInstance {\n readonly ready: Promise<void>;\n\n private readonly options: RuntimeHostOptions;\n private readyResolve: (() => void) | null = null;\n private readyReject: ((error: Error) => void) | null = null;\n private dom: DomSetup | null = null;\n private context: GLContext | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private intersectionObserver: IntersectionObserver | null = null;\n private rafId = 0;\n private running = false;\n private inRender = false;\n private destroyed = false;\n private gates = new Set<Gate>();\n private reducedMotionActive = false;\n private frame = 0;\n private elapsed = 0;\n private lastTimestamp = 0;\n private pixelRatio = 1;\n private width = 1;\n private height = 1;\n private viewportWidth = 1;\n private viewportHeight = 1;\n private pointer = { x: 0, y: 0, uvX: 0, uvY: 0, active: 0 };\n private removeListeners: Array<() => void> = [];\n private currentStatus: ShaderStatus = \"idle\";\n private contextRestoredShouldRun = false;\n\n constructor(options: RuntimeHostOptions) {\n this.options = options;\n this.ready = new Promise<void>((resolve, reject) => {\n this.readyResolve = resolve;\n this.readyReject = reject;\n });\n queueMicrotask(() => {\n void this.init();\n });\n }\n\n get canvas(): HTMLCanvasElement | null {\n return this.dom?.canvas ?? null;\n }\n\n get gl(): GLContext | null {\n return this.context;\n }\n\n get status(): ShaderStatus {\n return this.currentStatus;\n }\n\n get supported(): boolean {\n return ![\"unsupported\", \"destroyed\"].includes(this.currentStatus);\n }\n\n start(): void {\n if (this.destroyed || this.currentStatus === \"unsupported\") return;\n this.running = true;\n this.reconcile();\n }\n\n stop(): void {\n this.running = false;\n this.cancelFrame();\n if (!this.destroyed && this.currentStatus !== \"unsupported\") {\n this.currentStatus = \"paused\";\n }\n }\n\n render(): void {\n if (\n this.destroyed ||\n !this.dom ||\n !this.context ||\n this.currentStatus === \"unsupported\" ||\n this.hasVisibilityBlock ||\n this.inRender\n )\n return;\n this.renderInternal(this.elapsed, 0);\n }\n\n resize(): void {\n if (this.destroyed || !this.dom) return;\n\n const prevWidth = this.width;\n const prevHeight = this.height;\n const prevViewportWidth = this.viewportWidth;\n const prevViewportHeight = this.viewportHeight;\n\n this.measureSize();\n\n const sizeChanged =\n this.width !== prevWidth ||\n this.height !== prevHeight ||\n this.viewportWidth !== prevViewportWidth ||\n this.viewportHeight !== prevViewportHeight;\n\n if (sizeChanged && this.context) {\n this.options.onResize?.({\n gl: this.context,\n canvas: this.dom.canvas,\n time: this.elapsed,\n delta: 0,\n frame: this.frame,\n width: this.width,\n height: this.height,\n viewportWidth: this.viewportWidth,\n viewportHeight: this.viewportHeight,\n pixelRatio: this.pixelRatio,\n reducedMotion: this.reducedMotionActive,\n pointer: { ...this.pointer }\n });\n }\n\n if (this.options.renderMode === \"demand\" && !this.inRender) {\n this.render();\n }\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.running = false;\n this.cancelFrame();\n this.resizeObserver?.disconnect();\n this.intersectionObserver?.disconnect();\n\n for (const remove of this.removeListeners) remove();\n this.removeListeners = [];\n\n this.context = null;\n\n if (this.dom?.createdCanvas) {\n this.dom.canvas.remove();\n }\n\n const dom = this.dom;\n if (dom?.restoredPosition !== null && dom && this.options.target) {\n (dom.target as HTMLElement).style.position = dom.restoredPosition!;\n }\n\n this.currentStatus = \"destroyed\";\n this.readyReject?.(new DestroyedError(\"Runtime host was destroyed.\"));\n }\n\n private get hasVisibilityBlock(): boolean {\n return this.gates.has(\"offscreen\") || this.gates.has(\"hidden\");\n }\n\n private get hasMotionBlock(): boolean {\n return (\n this.gates.has(\"reducedMotion\") && this.options.renderMode !== \"demand\"\n );\n }\n\n private setGate(gate: Gate, closed: boolean): void {\n if (closed === this.gates.has(gate)) return;\n if (closed) this.gates.add(gate);\n else this.gates.delete(gate);\n this.reconcile();\n }\n\n private reconcile(): void {\n if (\n this.destroyed ||\n this.currentStatus === \"unsupported\" ||\n this.currentStatus === \"error\" ||\n this.currentStatus === \"context-lost\"\n ) {\n return;\n }\n\n if (this.running) {\n if (this.hasVisibilityBlock) {\n this.cancelFrame();\n this.currentStatus = \"paused\";\n return;\n }\n\n if (this.hasMotionBlock) {\n this.cancelFrame();\n this.currentStatus = \"paused\";\n this.renderStaticFrame();\n return;\n }\n\n this.currentStatus = \"running\";\n if (this.options.renderMode === \"demand\") this.render();\n else this.scheduleFrame();\n return;\n }\n\n if (this.options.renderMode === \"demand\") this.render();\n }\n\n private renderStaticFrame(): void {\n if (this.hasVisibilityBlock) return;\n this.renderInternal(this.elapsed, 0);\n }\n\n private renderInternal(time: number, delta: number): void {\n if (!this.dom || !this.context) return;\n const gl = this.context;\n\n this.inRender = true;\n this.measureSize();\n\n const state: HostFrameState = {\n gl,\n canvas: this.dom.canvas,\n time,\n delta,\n frame: this.frame,\n width: this.width,\n height: this.height,\n viewportWidth: this.viewportWidth,\n viewportHeight: this.viewportHeight,\n pixelRatio: this.pixelRatio,\n reducedMotion: this.reducedMotionActive,\n pointer: { ...this.pointer }\n };\n\n this.options.onDraw(state);\n this.frame++;\n this.inRender = false;\n }\n\n private measureSize(): void {\n if (!this.dom) return;\n this.pixelRatio = resolveDpr(this.options.dpr, this.options.maxDpr);\n const size = resizeCanvasToTarget(\n this.dom.canvas,\n this.dom.target,\n this.pixelRatio\n );\n this.width = size.width;\n this.height = size.height;\n this.viewportWidth = size.viewportWidth;\n this.viewportHeight = size.viewportHeight;\n }\n\n private scheduleFrame(): void {\n if (this.rafId || this.options.renderMode === \"demand\") return;\n\n this.rafId = requestAnimationFrame((timestamp) => {\n this.rafId = 0;\n if (!this.running || this.destroyed) return;\n\n const seconds = timestamp / 1000;\n const rawDelta = this.lastTimestamp ? seconds - this.lastTimestamp : 0;\n const delta = Math.min(Math.max(rawDelta, 0), MAX_DELTA_SECONDS);\n this.lastTimestamp = seconds;\n this.elapsed += delta;\n this.renderInternal(this.elapsed, delta);\n this.scheduleFrame();\n });\n }\n\n private cancelFrame(): void {\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = 0;\n }\n this.lastTimestamp = 0;\n }\n\n private async init(): Promise<void> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n this.failUnsupported(new UnsupportedError(\"WebGL requires a browser.\"));\n return;\n }\n\n this.dom = setupCanvas({\n ...(this.options.target !== undefined && { target: this.options.target }),\n ...(this.options.canvas !== undefined && { canvas: this.options.canvas }),\n ...(this.options.layer !== undefined && { layer: this.options.layer }),\n ...(this.options.canvasClass !== undefined && { canvasClass: this.options.canvasClass }),\n ...(this.options.canvasStyle !== undefined && { canvasStyle: this.options.canvasStyle })\n });\n\n if (!this.dom) {\n this.failUnsupported(new TargetNotFoundError(\"Target or canvas not found.\"));\n return;\n }\n\n let gl: GLContext | null;\n try {\n gl = this.options.createContext(this.dom.canvas);\n } catch (error) {\n this.failUnsupported(\n error instanceof UnsupportedError\n ? error\n : new UnsupportedError(String(error))\n );\n return;\n }\n if (!gl) {\n this.failUnsupported(new UnsupportedError(\"WebGL is not supported.\"));\n return;\n }\n\n this.context = gl;\n this.currentStatus = \"loading\";\n\n try {\n this.setupDomObservers();\n this.setupContextEvents();\n this.measureSize();\n await this.options.onInit(gl, this.width, this.height);\n if (this.destroyed) return;\n this.currentStatus = \"ready\";\n this.readyResolve?.();\n this.options.onReady?.();\n\n if (this.running || (this.options.autoStart ?? true)) {\n this.start();\n } else if (this.options.renderMode === \"demand\") {\n this.render();\n }\n } catch (error) {\n this.fail(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n private setupDomObservers(): void {\n if (!this.dom) return;\n\n if (this.options.autoResize ?? true) {\n this.resizeObserver = new ResizeObserver(() => {\n this.resize();\n });\n this.resizeObserver.observe(this.dom.target);\n }\n\n if (this.options.pauseWhenOffscreen ?? true) {\n this.intersectionObserver = new IntersectionObserver((entries) => {\n const entry = entries[0];\n this.setGate(\"offscreen\", !entry?.isIntersecting);\n });\n this.intersectionObserver.observe(this.dom.target);\n }\n\n if (this.options.pauseWhenHidden ?? true) {\n if (document.hidden) this.gates.add(\"hidden\");\n const onVisibility = () => {\n this.setGate(\"hidden\", document.hidden);\n };\n document.addEventListener(\"visibilitychange\", onVisibility);\n this.removeListeners.push(() => {\n document.removeEventListener(\"visibilitychange\", onVisibility);\n });\n }\n\n this.setupMotionObserver();\n\n const onPointerMove = (event: Event) => {\n if (!this.dom) return;\n const pointerEvent = event as PointerEvent;\n const rect = this.dom.target.getBoundingClientRect();\n const x = pointerEvent.clientX - rect.left;\n const y = pointerEvent.clientY - rect.top;\n this.pointer.x = x * this.pixelRatio;\n this.pointer.y = (rect.height - y) * this.pixelRatio;\n this.pointer.uvX = rect.width > 0 ? x / rect.width : 0;\n this.pointer.uvY = rect.height > 0 ? 1 - y / rect.height : 0;\n this.pointer.active = 1;\n\n if (this.options.renderMode === \"demand\") this.render();\n };\n const onPointerLeave = () => {\n this.pointer.active = 0;\n if (this.options.renderMode === \"demand\") this.render();\n };\n\n this.dom.target.addEventListener(\"pointermove\", onPointerMove);\n this.dom.target.addEventListener(\"pointerenter\", onPointerMove);\n this.dom.target.addEventListener(\"pointerleave\", onPointerLeave);\n this.removeListeners.push(() => {\n this.dom?.target.removeEventListener(\"pointermove\", onPointerMove);\n this.dom?.target.removeEventListener(\"pointerenter\", onPointerMove);\n this.dom?.target.removeEventListener(\"pointerleave\", onPointerLeave);\n });\n }\n\n private setupMotionObserver(): void {\n if (typeof window.matchMedia !== \"function\") return;\n\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n this.reducedMotionActive = query.matches;\n if (this.options.respectReducedMotion && query.matches) {\n this.gates.add(\"reducedMotion\");\n }\n\n const onChange = (event: MediaQueryListEvent) => {\n this.reducedMotionActive = event.matches;\n if (this.options.respectReducedMotion) {\n this.setGate(\"reducedMotion\", event.matches);\n } else if (this.options.renderMode === \"demand\") {\n this.render();\n }\n };\n\n query.addEventListener(\"change\", onChange);\n this.removeListeners.push(() => {\n query.removeEventListener(\"change\", onChange);\n });\n }\n\n private setupContextEvents(): void {\n if (!this.dom) return;\n\n const onLost = (event: Event) => {\n event.preventDefault();\n this.contextRestoredShouldRun = this.running;\n this.cancelFrame();\n this.context = null;\n this.currentStatus = \"context-lost\";\n this.options.onContextLost?.();\n this.options.onError?.(new UnsupportedError(\"WebGL context lost.\"));\n };\n\n const onRestored = () => {\n if (!this.dom || this.destroyed) return;\n const gl = this.options.createContext(this.dom.canvas);\n if (!gl) return;\n this.context = gl;\n\n const afterRestore = async () => {\n try {\n await this.options.onContextRestored?.(gl, this.width, this.height);\n if (this.contextRestoredShouldRun) this.start();\n else this.render();\n } catch (error) {\n this.fail(error instanceof Error ? error : new Error(String(error)));\n }\n };\n void afterRestore();\n };\n\n this.dom.canvas.addEventListener(\"webglcontextlost\", onLost);\n this.dom.canvas.addEventListener(\"webglcontextrestored\", onRestored);\n this.removeListeners.push(() => {\n this.dom?.canvas.removeEventListener(\"webglcontextlost\", onLost);\n this.dom?.canvas.removeEventListener(\"webglcontextrestored\", onRestored);\n });\n }\n\n private failUnsupported(error: Error): void {\n this.currentStatus = \"unsupported\";\n this.debugWarn(error.message);\n this.options.onError?.(error);\n this.readyReject?.(error);\n }\n\n private fail(error: Error): void {\n this.currentStatus = \"error\";\n this.debugWarn(error.message);\n this.options.onError?.(error);\n this.readyReject?.(error);\n }\n\n private debugWarn(message: string): void {\n if (this.options.debug) {\n console.warn(`[frapx/runtime] ${message}`);\n }\n }\n}\n","export const toUniformName = (name: string): string =>\n name.startsWith(\"u_\") ? name : `u_${name}`;\n\nexport const fromUniformName = (name: string): string =>\n name.startsWith(\"u_\") ? name.slice(2) : name;\n","import type { GLContext, TextureInput, TextureOptions, TextureSource } from \"../types\";\nimport { TextureLoadError } from \"./errors\";\nimport { toUniformName } from \"./names\";\n\nexport type LoadedTexture = {\n name: string;\n uniformName: string;\n sizeUniformName: string;\n texture: WebGLTexture;\n width: number;\n height: number;\n};\n\nexport const loadTexture = async (\n gl: GLContext,\n name: string,\n input: TextureInput,\n unit: number\n): Promise<LoadedTexture> => {\n const options = normalizeTextureInput(input);\n const source = await resolveSource(options.source);\n const texture = gl.createTexture();\n\n if (!texture) {\n throw new TextureLoadError(`Failed to create texture: ${name}`);\n }\n\n gl.activeTexture(gl.TEXTURE0 + unit);\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, options.flipY ?? true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);\n\n const wrap = getWrap(gl, options.wrap ?? \"clamp\");\n const filter = getFilter(gl, options.filter ?? \"linear\");\n\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);\n\n return {\n name,\n uniformName: toUniformName(name),\n sizeUniformName: toUniformName(`${name}Size`),\n texture,\n width: getSourceWidth(source),\n height: getSourceHeight(source)\n };\n};\n\nconst normalizeTextureInput = (input: TextureInput): TextureOptions => {\n if (typeof input === \"string\" || isTextureSource(input)) {\n return { source: input };\n }\n\n return input;\n};\n\nconst resolveSource = async (\n source: TextureSource\n): Promise<HTMLImageElement | HTMLCanvasElement> => {\n if (typeof source !== \"string\") {\n return source;\n }\n\n const image = new Image();\n image.crossOrigin = \"anonymous\";\n image.src = source;\n\n try {\n await image.decode();\n } catch {\n throw new TextureLoadError(`Failed to load texture: ${source}`);\n }\n\n return image;\n};\n\nconst getWrap = (\n gl: GLContext,\n wrap: NonNullable<TextureOptions[\"wrap\"]>\n): number => {\n if (wrap === \"repeat\") return gl.REPEAT;\n if (wrap === \"mirror\") return gl.MIRRORED_REPEAT;\n return gl.CLAMP_TO_EDGE;\n};\n\nconst getFilter = (\n gl: GLContext,\n filter: NonNullable<TextureOptions[\"filter\"]>\n): number => (filter === \"nearest\" ? gl.NEAREST : gl.LINEAR);\n\nconst isTextureSource = (\n input: unknown\n): input is HTMLImageElement | HTMLCanvasElement =>\n typeof HTMLImageElement !== \"undefined\" &&\n (input instanceof HTMLImageElement || input instanceof HTMLCanvasElement);\n\nconst getSourceWidth = (\n source: HTMLImageElement | HTMLCanvasElement\n): number =>\n \"naturalWidth\" in source ? source.naturalWidth : source.width;\n\nconst getSourceHeight = (\n source: HTMLImageElement | HTMLCanvasElement\n): number =>\n \"naturalHeight\" in source ? source.naturalHeight : source.height;\n","import type { GLContext } from \"../types\";\nimport { ShaderCompileError } from \"./errors\";\n\nexport const defaultVertexShader = `\nattribute vec2 a_position;\nvarying vec2 v_uv;\n\nvoid main() {\n v_uv = a_position * 0.5 + 0.5;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n`;\n\nexport const defaultVertexShader300 = `#version 300 es\nin vec2 a_position;\nout vec2 v_uv;\n\nvoid main() {\n v_uv = a_position * 0.5 + 0.5;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n`;\n\nexport const isGLSL300 = (src: string): boolean =>\n /^\\s*#version\\s+300\\s+es\\b/.test(src);\n\nexport const createProgram = (\n gl: GLContext,\n vertexSource: string,\n fragmentSource: string\n): WebGLProgram => {\n const vertex = compileShader(gl, gl.VERTEX_SHADER, vertexSource);\n const fragment = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);\n const program = gl.createProgram();\n\n if (!program) {\n throw new ShaderCompileError(\"Failed to create WebGL program.\");\n }\n\n gl.attachShader(program, vertex);\n gl.attachShader(program, fragment);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const log = gl.getProgramInfoLog(program) ?? \"Unknown program link error.\";\n gl.deleteProgram(program);\n gl.deleteShader(vertex);\n gl.deleteShader(fragment);\n throw new ShaderCompileError(log);\n }\n\n gl.deleteShader(vertex);\n gl.deleteShader(fragment);\n return program;\n};\n\nexport const createFullscreenBuffer = (\n gl: GLContext,\n program: WebGLProgram\n): WebGLBuffer => {\n const buffer = gl.createBuffer();\n if (!buffer) {\n throw new ShaderCompileError(\"Failed to create fullscreen buffer.\");\n }\n\n const location = gl.getAttribLocation(program, \"a_position\");\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n gl.STATIC_DRAW\n );\n\n if (location >= 0) {\n gl.enableVertexAttribArray(location);\n gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);\n }\n\n return buffer;\n};\n\nconst compileShader = (\n gl: GLContext,\n type: number,\n source: string\n): WebGLShader => {\n const shader = gl.createShader(type);\n if (!shader) {\n throw new ShaderCompileError(\"Failed to create WebGL shader.\");\n }\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const log = gl.getShaderInfoLog(shader) ?? \"Unknown shader compile error.\";\n gl.deleteShader(shader);\n throw new ShaderCompileError(log);\n }\n\n return shader;\n};\n","import type { GLContext } from \"../types\";\nimport {\n createFullscreenBuffer,\n createProgram,\n defaultVertexShader,\n defaultVertexShader300\n} from \"./webgl\";\n\nconst blitFragmentShader = `\nprecision mediump float;\nvarying vec2 v_uv;\nuniform sampler2D u_texture;\n\nvoid main() {\n gl_FragColor = texture2D(u_texture, v_uv);\n}\n`;\n\nconst blitFragmentShader300 = `#version 300 es\nprecision mediump float;\nin vec2 v_uv;\nuniform sampler2D u_texture;\nout vec4 fragColor;\n\nvoid main() {\n fragColor = texture(u_texture, v_uv);\n}\n`;\n\n// Duck-typed instead of `instanceof WebGL2RenderingContext` so this also works\n// against test mocks and other environments where the global constructor may\n// be unavailable.\nconst isWebGL2Context = (gl: GLContext): boolean =>\n typeof (gl as WebGL2RenderingContext).createVertexArray === \"function\";\n\nexport type Blitter = {\n /**\n * Draws `texture` as a fullscreen quad into `target` (or the default\n * framebuffer when `target` is `null`), setting the viewport to\n * `width`/`height` first. Does not save or restore any other GL state.\n */\n blit(\n texture: WebGLTexture,\n target: WebGLFramebuffer | null,\n width: number,\n height: number\n ): void;\n /** Releases the GL resources owned by this blitter. */\n dispose(): void;\n};\n\n/**\n * Creates a reusable fullscreen-quad \"blit\" utility: draws a texture into a\n * framebuffer (or the canvas) using a minimal passthrough shader. Intended\n * for hand-rolled multi-pass/FBO pipelines; frapx's own multi-pass features\n * are built on the same primitive.\n */\nexport const createBlitter = (gl: GLContext): Blitter => {\n const program = createProgram(\n gl,\n isWebGL2Context(gl) ? defaultVertexShader300 : defaultVertexShader,\n isWebGL2Context(gl) ? blitFragmentShader300 : blitFragmentShader\n );\n const buffer = createFullscreenBuffer(gl, program);\n const textureUniform = gl.getUniformLocation(program, \"u_texture\");\n\n return {\n blit(\n texture: WebGLTexture,\n target: WebGLFramebuffer | null,\n width: number,\n height: number\n ): void {\n gl.bindFramebuffer(gl.FRAMEBUFFER, target);\n gl.viewport(0, 0, width, height);\n gl.useProgram(program);\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n const location = gl.getAttribLocation(program, \"a_position\");\n if (location >= 0) {\n gl.enableVertexAttribArray(location);\n gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);\n }\n\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, texture);\n if (textureUniform) {\n gl.uniform1i(textureUniform, 0);\n }\n\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n },\n dispose(): void {\n gl.deleteProgram(program);\n gl.deleteBuffer(buffer);\n }\n };\n};\n"],"mappings":";;;;;;;;;;AAAO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;AAEO,IAAM,mBAAN,cAA+B,YAAY;AAAC;AAC5C,IAAM,sBAAN,cAAkC,YAAY;AAAC;AAC/C,IAAM,qBAAN,cAAiC,YAAY;AAAC;AAC9C,IAAM,mBAAN,cAA+B,YAAY;AAAC;AAC5C,IAAM,iBAAN,cAA6B,YAAY;AAAC;;;ACF1C,IAAM,iBAAiB,CAC5B,WACmB;AACnB,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,WAAW,SAAU,QAAO,SAAS,cAAc,MAAM;AACpE,SAAO;AACT;AAEO,IAAM,cAAc,CAAC,YAAiD;AAC3E,QAAM,SAAS,eAAe,QAAQ,MAAM;AAC5C,QAAM,SAAS,QAAQ,UAAU,SAAS,cAAc,QAAQ;AAChE,QAAM,oBAAoB,UAAU,QAAQ;AAE5C,MAAI,CAAC,kBAAmB,QAAO;AAE/B,QAAM,gBAAgB,CAAC,QAAQ;AAC/B,QAAM,mBACJ,UAAU,iBAAiB,MAAM,EAAE,aAAa,WAC3C,OAAuB,MAAM,WAC9B;AAEN,MAAI,UAAU,qBAAqB,MAAM;AACvC,IAAC,OAAuB,MAAM,WAAW;AAAA,EAC3C;AAEA,mBAAiB,QAAQ,SAAS,aAAa;AAE/C,MAAI,QAAQ,aAAa;AACvB,WAAO,UAAU,IAAI,QAAQ,WAAW;AAAA,EAC1C;AAEA,MAAI,QAAQ,aAAa;AACvB,WAAO,OAAO,OAAO,OAAO,QAAQ,WAAW;AAAA,EACjD;AAEA,MAAI,iBAAiB,QAAQ;AAC3B,SAAK,QAAQ,SAAS,kBAAkB,WAAW;AACjD,aAAO,OAAO,MAAM;AAAA,IACtB,OAAO;AACL,aAAO,QAAQ,MAAM;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,aAAa,CACxB,KACA,WACW;AACX,QAAM,MACJ,OAAO,QAAQ,aACX,IAAI,IACJ,OAAO,QAAQ,WACb,MACA,OAAO,WAAW,cAChB,IACA,OAAO,oBAAoB;AAErC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,UAAU,CAAC,CAAC;AAC/C;AAEO,IAAM,uBAAuB,CAClC,QACA,QACA,QAMG;AACH,QAAM,OAAO,OAAO,sBAAsB;AAC1C,QAAM,gBAAgB,KAAK,IAAI,GAAG,KAAK,SAAS,OAAO,eAAe,CAAC;AACvE,QAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,UAAU,OAAO,gBAAgB,CAAC;AAC1E,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,gBAAgB,GAAG,CAAC;AACzD,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,iBAAiB,GAAG,CAAC;AAE3D,MAAI,OAAO,UAAU,MAAO,QAAO,QAAQ;AAC3C,MAAI,OAAO,WAAW,OAAQ,QAAO,SAAS;AAE9C,SAAO,EAAE,OAAO,QAAQ,eAAe,eAAe;AACxD;AAEA,IAAM,mBAAmB,CACvB,QACA,SACA,mBACS;AACT,SAAO,QAAQ,cAAc;AAE7B,MAAI,CAAC,eAAgB;AAErB,QAAM,QAAQ,QAAQ,SAAS;AAC/B,SAAO,OAAO,OAAO,OAAO;AAAA,IAC1B,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,QAAQ,UAAU,YAAY,MAAM;AAAA,EACtC,CAAC;AACH;;;ACtGA,IAAM,oBAAoB;AAInB,IAAM,oBAAoB,CAC/B,YACwB;AACxB,SAAO,IAAI,YAAY,OAAO;AAChC;AAEA,IAAM,cAAN,MAAiD;AAAA,EA6B/C,YAAY,SAA6B;AAzBzC,SAAQ,eAAoC;AAC5C,SAAQ,cAA+C;AACvD,SAAQ,MAAuB;AAC/B,SAAQ,UAA4B;AACpC,SAAQ,iBAAwC;AAChD,SAAQ,uBAAoD;AAC5D,SAAQ,QAAQ;AAChB,SAAQ,UAAU;AAClB,SAAQ,WAAW;AACnB,SAAQ,YAAY;AACpB,SAAQ,QAAQ,oBAAI,IAAU;AAC9B,SAAQ,sBAAsB;AAC9B,SAAQ,QAAQ;AAChB,SAAQ,UAAU;AAClB,SAAQ,gBAAgB;AACxB,SAAQ,aAAa;AACrB,SAAQ,QAAQ;AAChB,SAAQ,SAAS;AACjB,SAAQ,gBAAgB;AACxB,SAAQ,iBAAiB;AACzB,SAAQ,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,EAAE;AAC1D,SAAQ,kBAAqC,CAAC;AAC9C,SAAQ,gBAA8B;AACtC,SAAQ,2BAA2B;AAGjC,SAAK,UAAU;AACf,SAAK,QAAQ,IAAI,QAAc,CAAC,SAAS,WAAW;AAClD,WAAK,eAAe;AACpB,WAAK,cAAc;AAAA,IACrB,CAAC;AACD,mBAAe,MAAM;AACnB,WAAK,KAAK,KAAK;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,SAAmC;AACrC,WAAO,KAAK,KAAK,UAAU;AAAA,EAC7B;AAAA,EAEA,IAAI,KAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,CAAC,CAAC,eAAe,WAAW,EAAE,SAAS,KAAK,aAAa;AAAA,EAClE;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,aAAa,KAAK,kBAAkB,cAAe;AAC5D,SAAK,UAAU;AACf,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,OAAa;AACX,SAAK,UAAU;AACf,SAAK,YAAY;AACjB,QAAI,CAAC,KAAK,aAAa,KAAK,kBAAkB,eAAe;AAC3D,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,SAAe;AACb,QACE,KAAK,aACL,CAAC,KAAK,OACN,CAAC,KAAK,WACN,KAAK,kBAAkB,iBACvB,KAAK,sBACL,KAAK;AAEL;AACF,SAAK,eAAe,KAAK,SAAS,CAAC;AAAA,EACrC;AAAA,EAEA,SAAe;AACb,QAAI,KAAK,aAAa,CAAC,KAAK,IAAK;AAEjC,UAAM,YAAY,KAAK;AACvB,UAAM,aAAa,KAAK;AACxB,UAAM,oBAAoB,KAAK;AAC/B,UAAM,qBAAqB,KAAK;AAEhC,SAAK,YAAY;AAEjB,UAAM,cACJ,KAAK,UAAU,aACf,KAAK,WAAW,cAChB,KAAK,kBAAkB,qBACvB,KAAK,mBAAmB;AAE1B,QAAI,eAAe,KAAK,SAAS;AAC/B,WAAK,QAAQ,WAAW;AAAA,QACtB,IAAI,KAAK;AAAA,QACT,QAAQ,KAAK,IAAI;AAAA,QACjB,MAAM,KAAK;AAAA,QACX,OAAO;AAAA,QACP,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,eAAe,KAAK;AAAA,QACpB,gBAAgB,KAAK;AAAA,QACrB,YAAY,KAAK;AAAA,QACjB,eAAe,KAAK;AAAA,QACpB,SAAS,EAAE,GAAG,KAAK,QAAQ;AAAA,MAC7B,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,QAAQ,eAAe,YAAY,CAAC,KAAK,UAAU;AAC1D,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,YAAY;AACjB,SAAK,gBAAgB,WAAW;AAChC,SAAK,sBAAsB,WAAW;AAEtC,eAAW,UAAU,KAAK,gBAAiB,QAAO;AAClD,SAAK,kBAAkB,CAAC;AAExB,SAAK,UAAU;AAEf,QAAI,KAAK,KAAK,eAAe;AAC3B,WAAK,IAAI,OAAO,OAAO;AAAA,IACzB;AAEA,UAAM,MAAM,KAAK;AACjB,QAAI,KAAK,qBAAqB,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAChE,MAAC,IAAI,OAAuB,MAAM,WAAW,IAAI;AAAA,IACnD;AAEA,SAAK,gBAAgB;AACrB,SAAK,cAAc,IAAI,eAAe,6BAA6B,CAAC;AAAA,EACtE;AAAA,EAEA,IAAY,qBAA8B;AACxC,WAAO,KAAK,MAAM,IAAI,WAAW,KAAK,KAAK,MAAM,IAAI,QAAQ;AAAA,EAC/D;AAAA,EAEA,IAAY,iBAA0B;AACpC,WACE,KAAK,MAAM,IAAI,eAAe,KAAK,KAAK,QAAQ,eAAe;AAAA,EAEnE;AAAA,EAEQ,QAAQ,MAAY,QAAuB;AACjD,QAAI,WAAW,KAAK,MAAM,IAAI,IAAI,EAAG;AACrC,QAAI,OAAQ,MAAK,MAAM,IAAI,IAAI;AAAA,QAC1B,MAAK,MAAM,OAAO,IAAI;AAC3B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,YAAkB;AACxB,QACE,KAAK,aACL,KAAK,kBAAkB,iBACvB,KAAK,kBAAkB,WACvB,KAAK,kBAAkB,gBACvB;AACA;AAAA,IACF;AAEA,QAAI,KAAK,SAAS;AAChB,UAAI,KAAK,oBAAoB;AAC3B,aAAK,YAAY;AACjB,aAAK,gBAAgB;AACrB;AAAA,MACF;AAEA,UAAI,KAAK,gBAAgB;AACvB,aAAK,YAAY;AACjB,aAAK,gBAAgB;AACrB,aAAK,kBAAkB;AACvB;AAAA,MACF;AAEA,WAAK,gBAAgB;AACrB,UAAI,KAAK,QAAQ,eAAe,SAAU,MAAK,OAAO;AAAA,UACjD,MAAK,cAAc;AACxB;AAAA,IACF;AAEA,QAAI,KAAK,QAAQ,eAAe,SAAU,MAAK,OAAO;AAAA,EACxD;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK,mBAAoB;AAC7B,SAAK,eAAe,KAAK,SAAS,CAAC;AAAA,EACrC;AAAA,EAEQ,eAAe,MAAc,OAAqB;AACxD,QAAI,CAAC,KAAK,OAAO,CAAC,KAAK,QAAS;AAChC,UAAM,KAAK,KAAK;AAEhB,SAAK,WAAW;AAChB,SAAK,YAAY;AAEjB,UAAM,QAAwB;AAAA,MAC5B;AAAA,MACA,QAAQ,KAAK,IAAI;AAAA,MACjB;AAAA,MACA;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,eAAe,KAAK;AAAA,MACpB,gBAAgB,KAAK;AAAA,MACrB,YAAY,KAAK;AAAA,MACjB,eAAe,KAAK;AAAA,MACpB,SAAS,EAAE,GAAG,KAAK,QAAQ;AAAA,IAC7B;AAEA,SAAK,QAAQ,OAAO,KAAK;AACzB,SAAK;AACL,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,IAAK;AACf,SAAK,aAAa,WAAW,KAAK,QAAQ,KAAK,KAAK,QAAQ,MAAM;AAClE,UAAM,OAAO;AAAA,MACX,KAAK,IAAI;AAAA,MACT,KAAK,IAAI;AAAA,MACT,KAAK;AAAA,IACP;AACA,SAAK,QAAQ,KAAK;AAClB,SAAK,SAAS,KAAK;AACnB,SAAK,gBAAgB,KAAK;AAC1B,SAAK,iBAAiB,KAAK;AAAA,EAC7B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,SAAS,KAAK,QAAQ,eAAe,SAAU;AAExD,SAAK,QAAQ,sBAAsB,CAAC,cAAc;AAChD,WAAK,QAAQ;AACb,UAAI,CAAC,KAAK,WAAW,KAAK,UAAW;AAErC,YAAM,UAAU,YAAY;AAC5B,YAAM,WAAW,KAAK,gBAAgB,UAAU,KAAK,gBAAgB;AACrE,YAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,iBAAiB;AAC/D,WAAK,gBAAgB;AACrB,WAAK,WAAW;AAChB,WAAK,eAAe,KAAK,SAAS,KAAK;AACvC,WAAK,cAAc;AAAA,IACrB,CAAC;AAAA,EACH;AAAA,EAEQ,cAAoB;AAC1B,QAAI,KAAK,OAAO;AACd,2BAAqB,KAAK,KAAK;AAC/B,WAAK,QAAQ;AAAA,IACf;AACA,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAc,OAAsB;AAClC,QAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAK,gBAAgB,IAAI,iBAAiB,2BAA2B,CAAC;AACtE;AAAA,IACF;AAEA,SAAK,MAAM,YAAY;AAAA,MACrB,GAAI,KAAK,QAAQ,WAAW,UAAa,EAAE,QAAQ,KAAK,QAAQ,OAAO;AAAA,MACvE,GAAI,KAAK,QAAQ,WAAW,UAAa,EAAE,QAAQ,KAAK,QAAQ,OAAO;AAAA,MACvE,GAAI,KAAK,QAAQ,UAAU,UAAa,EAAE,OAAO,KAAK,QAAQ,MAAM;AAAA,MACpE,GAAI,KAAK,QAAQ,gBAAgB,UAAa,EAAE,aAAa,KAAK,QAAQ,YAAY;AAAA,MACtF,GAAI,KAAK,QAAQ,gBAAgB,UAAa,EAAE,aAAa,KAAK,QAAQ,YAAY;AAAA,IACxF,CAAC;AAED,QAAI,CAAC,KAAK,KAAK;AACb,WAAK,gBAAgB,IAAI,oBAAoB,6BAA6B,CAAC;AAC3E;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,WAAK,KAAK,QAAQ,cAAc,KAAK,IAAI,MAAM;AAAA,IACjD,SAAS,OAAO;AACd,WAAK;AAAA,QACH,iBAAiB,mBACb,QACA,IAAI,iBAAiB,OAAO,KAAK,CAAC;AAAA,MACxC;AACA;AAAA,IACF;AACA,QAAI,CAAC,IAAI;AACP,WAAK,gBAAgB,IAAI,iBAAiB,yBAAyB,CAAC;AACpE;AAAA,IACF;AAEA,SAAK,UAAU;AACf,SAAK,gBAAgB;AAErB,QAAI;AACF,WAAK,kBAAkB;AACvB,WAAK,mBAAmB;AACxB,WAAK,YAAY;AACjB,YAAM,KAAK,QAAQ,OAAO,IAAI,KAAK,OAAO,KAAK,MAAM;AACrD,UAAI,KAAK,UAAW;AACpB,WAAK,gBAAgB;AACrB,WAAK,eAAe;AACpB,WAAK,QAAQ,UAAU;AAEvB,UAAI,KAAK,YAAY,KAAK,QAAQ,aAAa,OAAO;AACpD,aAAK,MAAM;AAAA,MACb,WAAW,KAAK,QAAQ,eAAe,UAAU;AAC/C,aAAK,OAAO;AAAA,MACd;AAAA,IACF,SAAS,OAAO;AACd,WAAK,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACrE;AAAA,EACF;AAAA,EAEQ,oBAA0B;AAChC,QAAI,CAAC,KAAK,IAAK;AAEf,QAAI,KAAK,QAAQ,cAAc,MAAM;AACnC,WAAK,iBAAiB,IAAI,eAAe,MAAM;AAC7C,aAAK,OAAO;AAAA,MACd,CAAC;AACD,WAAK,eAAe,QAAQ,KAAK,IAAI,MAAM;AAAA,IAC7C;AAEA,QAAI,KAAK,QAAQ,sBAAsB,MAAM;AAC3C,WAAK,uBAAuB,IAAI,qBAAqB,CAAC,YAAY;AAChE,cAAM,QAAQ,QAAQ,CAAC;AACvB,aAAK,QAAQ,aAAa,CAAC,OAAO,cAAc;AAAA,MAClD,CAAC;AACD,WAAK,qBAAqB,QAAQ,KAAK,IAAI,MAAM;AAAA,IACnD;AAEA,QAAI,KAAK,QAAQ,mBAAmB,MAAM;AACxC,UAAI,SAAS,OAAQ,MAAK,MAAM,IAAI,QAAQ;AAC5C,YAAM,eAAe,MAAM;AACzB,aAAK,QAAQ,UAAU,SAAS,MAAM;AAAA,MACxC;AACA,eAAS,iBAAiB,oBAAoB,YAAY;AAC1D,WAAK,gBAAgB,KAAK,MAAM;AAC9B,iBAAS,oBAAoB,oBAAoB,YAAY;AAAA,MAC/D,CAAC;AAAA,IACH;AAEA,SAAK,oBAAoB;AAEzB,UAAM,gBAAgB,CAAC,UAAiB;AACtC,UAAI,CAAC,KAAK,IAAK;AACf,YAAM,eAAe;AACrB,YAAM,OAAO,KAAK,IAAI,OAAO,sBAAsB;AACnD,YAAM,IAAI,aAAa,UAAU,KAAK;AACtC,YAAM,IAAI,aAAa,UAAU,KAAK;AACtC,WAAK,QAAQ,IAAI,IAAI,KAAK;AAC1B,WAAK,QAAQ,KAAK,KAAK,SAAS,KAAK,KAAK;AAC1C,WAAK,QAAQ,MAAM,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ;AACrD,WAAK,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI,IAAI,KAAK,SAAS;AAC3D,WAAK,QAAQ,SAAS;AAEtB,UAAI,KAAK,QAAQ,eAAe,SAAU,MAAK,OAAO;AAAA,IACxD;AACA,UAAM,iBAAiB,MAAM;AAC3B,WAAK,QAAQ,SAAS;AACtB,UAAI,KAAK,QAAQ,eAAe,SAAU,MAAK,OAAO;AAAA,IACxD;AAEA,SAAK,IAAI,OAAO,iBAAiB,eAAe,aAAa;AAC7D,SAAK,IAAI,OAAO,iBAAiB,gBAAgB,aAAa;AAC9D,SAAK,IAAI,OAAO,iBAAiB,gBAAgB,cAAc;AAC/D,SAAK,gBAAgB,KAAK,MAAM;AAC9B,WAAK,KAAK,OAAO,oBAAoB,eAAe,aAAa;AACjE,WAAK,KAAK,OAAO,oBAAoB,gBAAgB,aAAa;AAClE,WAAK,KAAK,OAAO,oBAAoB,gBAAgB,cAAc;AAAA,IACrE,CAAC;AAAA,EACH;AAAA,EAEQ,sBAA4B;AAClC,QAAI,OAAO,OAAO,eAAe,WAAY;AAE7C,UAAM,QAAQ,OAAO,WAAW,kCAAkC;AAClE,SAAK,sBAAsB,MAAM;AACjC,QAAI,KAAK,QAAQ,wBAAwB,MAAM,SAAS;AACtD,WAAK,MAAM,IAAI,eAAe;AAAA,IAChC;AAEA,UAAM,WAAW,CAAC,UAA+B;AAC/C,WAAK,sBAAsB,MAAM;AACjC,UAAI,KAAK,QAAQ,sBAAsB;AACrC,aAAK,QAAQ,iBAAiB,MAAM,OAAO;AAAA,MAC7C,WAAW,KAAK,QAAQ,eAAe,UAAU;AAC/C,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAEA,UAAM,iBAAiB,UAAU,QAAQ;AACzC,SAAK,gBAAgB,KAAK,MAAM;AAC9B,YAAM,oBAAoB,UAAU,QAAQ;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EAEQ,qBAA2B;AACjC,QAAI,CAAC,KAAK,IAAK;AAEf,UAAM,SAAS,CAAC,UAAiB;AAC/B,YAAM,eAAe;AACrB,WAAK,2BAA2B,KAAK;AACrC,WAAK,YAAY;AACjB,WAAK,UAAU;AACf,WAAK,gBAAgB;AACrB,WAAK,QAAQ,gBAAgB;AAC7B,WAAK,QAAQ,UAAU,IAAI,iBAAiB,qBAAqB,CAAC;AAAA,IACpE;AAEA,UAAM,aAAa,MAAM;AACvB,UAAI,CAAC,KAAK,OAAO,KAAK,UAAW;AACjC,YAAM,KAAK,KAAK,QAAQ,cAAc,KAAK,IAAI,MAAM;AACrD,UAAI,CAAC,GAAI;AACT,WAAK,UAAU;AAEf,YAAM,eAAe,YAAY;AAC/B,YAAI;AACF,gBAAM,KAAK,QAAQ,oBAAoB,IAAI,KAAK,OAAO,KAAK,MAAM;AAClE,cAAI,KAAK,yBAA0B,MAAK,MAAM;AAAA,cACzC,MAAK,OAAO;AAAA,QACnB,SAAS,OAAO;AACd,eAAK,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,QACrE;AAAA,MACF;AACA,WAAK,aAAa;AAAA,IACpB;AAEA,SAAK,IAAI,OAAO,iBAAiB,oBAAoB,MAAM;AAC3D,SAAK,IAAI,OAAO,iBAAiB,wBAAwB,UAAU;AACnE,SAAK,gBAAgB,KAAK,MAAM;AAC9B,WAAK,KAAK,OAAO,oBAAoB,oBAAoB,MAAM;AAC/D,WAAK,KAAK,OAAO,oBAAoB,wBAAwB,UAAU;AAAA,IACzE,CAAC;AAAA,EACH;AAAA,EAEQ,gBAAgB,OAAoB;AAC1C,SAAK,gBAAgB;AACrB,SAAK,UAAU,MAAM,OAAO;AAC5B,SAAK,QAAQ,UAAU,KAAK;AAC5B,SAAK,cAAc,KAAK;AAAA,EAC1B;AAAA,EAEQ,KAAK,OAAoB;AAC/B,SAAK,gBAAgB;AACrB,SAAK,UAAU,MAAM,OAAO;AAC5B,SAAK,QAAQ,UAAU,KAAK;AAC5B,SAAK,cAAc,KAAK;AAAA,EAC1B;AAAA,EAEQ,UAAU,SAAuB;AACvC,QAAI,KAAK,QAAQ,OAAO;AACtB,cAAQ,KAAK,mBAAmB,OAAO,EAAE;AAAA,IAC3C;AAAA,EACF;AACF;;;AC7eO,IAAM,gBAAgB,CAAC,SAC5B,KAAK,WAAW,IAAI,IAAI,OAAO,KAAK,IAAI;AAEnC,IAAM,kBAAkB,CAAC,SAC9B,KAAK,WAAW,IAAI,IAAI,KAAK,MAAM,CAAC,IAAI;;;ACSnC,IAAM,cAAc,OACzB,IACA,MACA,OACA,SAC2B;AAC3B,QAAM,UAAU,sBAAsB,KAAK;AAC3C,QAAM,SAAS,MAAM,cAAc,QAAQ,MAAM;AACjD,QAAM,UAAU,GAAG,cAAc;AAEjC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,iBAAiB,6BAA6B,IAAI,EAAE;AAAA,EAChE;AAEA,KAAG,cAAc,GAAG,WAAW,IAAI;AACnC,KAAG,YAAY,GAAG,YAAY,OAAO;AACrC,KAAG,YAAY,GAAG,qBAAqB,QAAQ,SAAS,IAAI;AAC5D,KAAG,WAAW,GAAG,YAAY,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,eAAe,MAAM;AAE1E,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,OAAO;AAChD,QAAM,SAAS,UAAU,IAAI,QAAQ,UAAU,QAAQ;AAEvD,KAAG,cAAc,GAAG,YAAY,GAAG,gBAAgB,IAAI;AACvD,KAAG,cAAc,GAAG,YAAY,GAAG,gBAAgB,IAAI;AACvD,KAAG,cAAc,GAAG,YAAY,GAAG,oBAAoB,MAAM;AAC7D,KAAG,cAAc,GAAG,YAAY,GAAG,oBAAoB,MAAM;AAE7D,SAAO;AAAA,IACL;AAAA,IACA,aAAa,cAAc,IAAI;AAAA,IAC/B,iBAAiB,cAAc,GAAG,IAAI,MAAM;AAAA,IAC5C;AAAA,IACA,OAAO,eAAe,MAAM;AAAA,IAC5B,QAAQ,gBAAgB,MAAM;AAAA,EAChC;AACF;AAEA,IAAM,wBAAwB,CAAC,UAAwC;AACrE,MAAI,OAAO,UAAU,YAAY,gBAAgB,KAAK,GAAG;AACvD,WAAO,EAAE,QAAQ,MAAM;AAAA,EACzB;AAEA,SAAO;AACT;AAEA,IAAM,gBAAgB,OACpB,WACkD;AAClD,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,IAAI,MAAM;AACxB,QAAM,cAAc;AACpB,QAAM,MAAM;AAEZ,MAAI;AACF,UAAM,MAAM,OAAO;AAAA,EACrB,QAAQ;AACN,UAAM,IAAI,iBAAiB,2BAA2B,MAAM,EAAE;AAAA,EAChE;AAEA,SAAO;AACT;AAEA,IAAM,UAAU,CACd,IACA,SACW;AACX,MAAI,SAAS,SAAU,QAAO,GAAG;AACjC,MAAI,SAAS,SAAU,QAAO,GAAG;AACjC,SAAO,GAAG;AACZ;AAEA,IAAM,YAAY,CAChB,IACA,WACY,WAAW,YAAY,GAAG,UAAU,GAAG;AAErD,IAAM,kBAAkB,CACtB,UAEA,OAAO,qBAAqB,gBAC3B,iBAAiB,oBAAoB,iBAAiB;AAEzD,IAAM,iBAAiB,CACrB,WAEA,kBAAkB,SAAS,OAAO,eAAe,OAAO;AAE1D,IAAM,kBAAkB,CACtB,WAEA,mBAAmB,SAAS,OAAO,gBAAgB,OAAO;;;ACvGrD,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAU5B,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAU/B,IAAM,YAAY,CAAC,QACxB,4BAA4B,KAAK,GAAG;AAE/B,IAAM,gBAAgB,CAC3B,IACA,cACA,mBACiB;AACjB,QAAM,SAAS,cAAc,IAAI,GAAG,eAAe,YAAY;AAC/D,QAAM,WAAW,cAAc,IAAI,GAAG,iBAAiB,cAAc;AACrE,QAAM,UAAU,GAAG,cAAc;AAEjC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,mBAAmB,iCAAiC;AAAA,EAChE;AAEA,KAAG,aAAa,SAAS,MAAM;AAC/B,KAAG,aAAa,SAAS,QAAQ;AACjC,KAAG,YAAY,OAAO;AAEtB,MAAI,CAAC,GAAG,oBAAoB,SAAS,GAAG,WAAW,GAAG;AACpD,UAAM,MAAM,GAAG,kBAAkB,OAAO,KAAK;AAC7C,OAAG,cAAc,OAAO;AACxB,OAAG,aAAa,MAAM;AACtB,OAAG,aAAa,QAAQ;AACxB,UAAM,IAAI,mBAAmB,GAAG;AAAA,EAClC;AAEA,KAAG,aAAa,MAAM;AACtB,KAAG,aAAa,QAAQ;AACxB,SAAO;AACT;AAEO,IAAM,yBAAyB,CACpC,IACA,YACgB;AAChB,QAAM,SAAS,GAAG,aAAa;AAC/B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,mBAAmB,qCAAqC;AAAA,EACpE;AAEA,QAAM,WAAW,GAAG,kBAAkB,SAAS,YAAY;AAC3D,KAAG,WAAW,GAAG,cAAc,MAAM;AACrC,KAAG;AAAA,IACD,GAAG;AAAA,IACH,IAAI,aAAa,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC;AAAA,IAC3D,GAAG;AAAA,EACL;AAEA,MAAI,YAAY,GAAG;AACjB,OAAG,wBAAwB,QAAQ;AACnC,OAAG,oBAAoB,UAAU,GAAG,GAAG,OAAO,OAAO,GAAG,CAAC;AAAA,EAC3D;AAEA,SAAO;AACT;AAEA,IAAM,gBAAgB,CACpB,IACA,MACA,WACgB;AAChB,QAAM,SAAS,GAAG,aAAa,IAAI;AACnC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,mBAAmB,gCAAgC;AAAA,EAC/D;AAEA,KAAG,aAAa,QAAQ,MAAM;AAC9B,KAAG,cAAc,MAAM;AAEvB,MAAI,CAAC,GAAG,mBAAmB,QAAQ,GAAG,cAAc,GAAG;AACrD,UAAM,MAAM,GAAG,iBAAiB,MAAM,KAAK;AAC3C,OAAG,aAAa,MAAM;AACtB,UAAM,IAAI,mBAAmB,GAAG;AAAA,EAClC;AAEA,SAAO;AACT;;;AC7FA,IAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAU3B,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAc9B,IAAM,kBAAkB,CAAC,OACvB,OAAQ,GAA8B,sBAAsB;AAwBvD,IAAM,gBAAgB,CAAC,OAA2B;AACvD,QAAM,UAAU;AAAA,IACd;AAAA,IACA,gBAAgB,EAAE,IAAI,yBAAyB;AAAA,IAC/C,gBAAgB,EAAE,IAAI,wBAAwB;AAAA,EAChD;AACA,QAAM,SAAS,uBAAuB,IAAI,OAAO;AACjD,QAAM,iBAAiB,GAAG,mBAAmB,SAAS,WAAW;AAEjE,SAAO;AAAA,IACL,KACE,SACA,QACA,OACA,QACM;AACN,SAAG,gBAAgB,GAAG,aAAa,MAAM;AACzC,SAAG,SAAS,GAAG,GAAG,OAAO,MAAM;AAC/B,SAAG,WAAW,OAAO;AAErB,SAAG,WAAW,GAAG,cAAc,MAAM;AACrC,YAAM,WAAW,GAAG,kBAAkB,SAAS,YAAY;AAC3D,UAAI,YAAY,GAAG;AACjB,WAAG,wBAAwB,QAAQ;AACnC,WAAG,oBAAoB,UAAU,GAAG,GAAG,OAAO,OAAO,GAAG,CAAC;AAAA,MAC3D;AAEA,SAAG,cAAc,GAAG,QAAQ;AAC5B,SAAG,YAAY,GAAG,YAAY,OAAO;AACrC,UAAI,gBAAgB;AAClB,WAAG,UAAU,gBAAgB,CAAC;AAAA,MAChC;AAEA,SAAG,WAAW,GAAG,WAAW,GAAG,CAAC;AAAA,IAClC;AAAA,IACA,UAAgB;AACd,SAAG,cAAc,OAAO;AACxB,SAAG,aAAa,MAAM;AAAA,IACxB;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@frapx/core",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Shared WebGL runtime host for frapx packages.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./glsl": {
|
|
13
|
+
"types": "./dist/glsl.d.ts",
|
|
14
|
+
"import": "./dist/glsl.js"
|
|
15
|
+
},
|
|
16
|
+
"./color": {
|
|
17
|
+
"types": "./dist/color.d.ts",
|
|
18
|
+
"import": "./dist/color.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"CHANGELOG.md"
|
|
25
|
+
],
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"vitest": "^3.2.4"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/kramhash/frapx.git",
|
|
33
|
+
"directory": "packages/core"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run"
|
|
42
|
+
}
|
|
43
|
+
}
|