@rosalana/sandbox 0.0.5 → 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.
@@ -0,0 +1,79 @@
1
+ import { ShaderFunction, ShaderUniform, WebGLVersion } from "../types";
2
+ import Parser from "./parser";
3
+ export default class Compilable {
4
+ /** Flag to track if the shader has been compiled */
5
+ protected isCompiled: boolean;
6
+ /** Original and compiled shader parsers */
7
+ protected original: Parser;
8
+ /** Compiled parser will be updated with rewritten source after processing imports */
9
+ protected compiled: Parser;
10
+ /** Collected requirements from imports */
11
+ protected requirements: {
12
+ uniforms: Map<string, ShaderUniform>;
13
+ functions: Map<string, ShaderFunction>;
14
+ };
15
+ constructor(source: string);
16
+ /**
17
+ * Detect WebGL version from shader source
18
+ */
19
+ version(): WebGLVersion;
20
+ /**
21
+ * Get the original source code of the shader
22
+ */
23
+ source(): string;
24
+ /**
25
+ * Force recompilation of the shader, reprocessing all imports and rewrites
26
+ * It's not necessary to call this manually because the state gets lost whenever the shader is switched out and back in.
27
+ */
28
+ recompile(): string;
29
+ /**
30
+ * Compile the shader source, resolving all imports
31
+ */
32
+ compile(): string;
33
+ /**
34
+ * Process all #import directives
35
+ */
36
+ private processImports;
37
+ /**
38
+ * Process an extraction: rewrite names and collect as requirements
39
+ */
40
+ private processExtraction;
41
+ /**
42
+ * Rewrite a function: namespace all uniform and helper function references
43
+ */
44
+ private rewriteFunction;
45
+ /**
46
+ * Apply rewrite operations to a string, processing from end to start
47
+ */
48
+ private applyRewrites;
49
+ /**
50
+ * Build the final compiled shader
51
+ */
52
+ private build;
53
+ private replaceMentions;
54
+ /**
55
+ * Remove #import lines from shader source
56
+ */
57
+ private removeImportLines;
58
+ /**
59
+ * Find insertion point for uniforms (after existing uniforms)
60
+ */
61
+ private findInsertionPointForUniforms;
62
+ private findInsertionPointForFunctions;
63
+ /**
64
+ * Generate GLSL code for uniforms
65
+ */
66
+ private generateUniformsCode;
67
+ /**
68
+ * Generate GLSL code for functions
69
+ */
70
+ private generateFunctionCode;
71
+ /**
72
+ * Check which required uniforms are missing from the original shader
73
+ */
74
+ private checkUniformsPresence;
75
+ /**
76
+ * Check which required functions are missing from the original shader
77
+ */
78
+ private checkFunctionsPresence;
79
+ }
@@ -0,0 +1,46 @@
1
+ import { ModuleDefinition, ModuleFunctionExtraction } from "../types";
2
+ import Compilable from "./compilable";
3
+ export default class Module extends Compilable {
4
+ name: ModuleDefinition["name"];
5
+ options: ModuleDefinition["options"];
6
+ constructor(name: ModuleDefinition["name"], source: ModuleDefinition["source"], options?: ModuleDefinition["options"]);
7
+ private resolveOptions;
8
+ /**
9
+ * Define a module with given name and source, and register it
10
+ */
11
+ static define(definition: ModuleDefinition): Module;
12
+ /**
13
+ * Resolve a module by name from the registry, throwing an error if not found
14
+ */
15
+ static resolve(name: string): Module;
16
+ /**
17
+ * Create a copy of the module. To unplug references to the original object.
18
+ * Used when copying module to the runtime registry to allow independent runtime changes to options without affecting the original module definition.
19
+ */
20
+ copy(state?: "original" | "compiled"): Module;
21
+ /**
22
+ * Merge options from another module into this one, without affecting the original module definition.
23
+ * Used when merging imported modules into the runtime registry.
24
+ */
25
+ merge(module: Module): void;
26
+ /**
27
+ * Get the module definition
28
+ */
29
+ getDefinition(): {
30
+ name: string;
31
+ methods: string[];
32
+ uniforms: {
33
+ name: string;
34
+ type: import("..").GLSLType;
35
+ }[];
36
+ options: Record<string, Record<string, import("..").ModuleMethodOption>> | undefined;
37
+ };
38
+ /**
39
+ * Extract a method with all its dependencies
40
+ */
41
+ extract(name: string): ModuleFunctionExtraction;
42
+ /**
43
+ * Recursively collect all function and uniform dependencies
44
+ */
45
+ private collectDependencies;
46
+ }
@@ -0,0 +1,63 @@
1
+ import { ModuleMethodOption, UniformSchema } from "../types";
2
+ import type Module from "./module";
3
+ export default class ModuleRegistry {
4
+ private modules;
5
+ constructor(initialModules?: Module[]);
6
+ /**
7
+ * Compile all registered modules.
8
+ */
9
+ compile(): void;
10
+ /**
11
+ * Get the list of available shader modules.
12
+ */
13
+ available(): {
14
+ name: string;
15
+ methods: string[];
16
+ uniforms: {
17
+ name: string;
18
+ type: import("..").GLSLType;
19
+ }[];
20
+ options: Record<string, Record<string, ModuleMethodOption>> | undefined;
21
+ }[];
22
+ /**
23
+ * Get the list of uniforms required by the currently registered modules.
24
+ * This is used to automatically set up the uniforms in the shader based on the modules in use.
25
+ */
26
+ defaults(): UniformSchema;
27
+ /**
28
+ * Resolve the options from the module definitions for a given function name.
29
+ */
30
+ resolveOptions(func: string): Record<string, ModuleMethodOption> | null;
31
+ /**
32
+ * Register a new module in the registry.
33
+ */
34
+ register(name: string, module: Module): void;
35
+ /**
36
+ * Merge a module into the registry. If a module with the same name already exists, options will be merged together.
37
+ */
38
+ merge(name: string, module: Module): void;
39
+ /**
40
+ * Resolve a module by name. Throws an error if the module is not found.
41
+ */
42
+ resolve(name: string): Module;
43
+ /**
44
+ * Check if a module exists in the registry by name.
45
+ */
46
+ has(name: string): boolean;
47
+ /**
48
+ * Check if the registry is empty (no modules registered).
49
+ */
50
+ isEmpty(): boolean;
51
+ /**
52
+ * Remove a module from the registry by name.
53
+ */
54
+ remove(name: string): void;
55
+ /**
56
+ * Load multiple modules into the registry at once.
57
+ */
58
+ load(modules: Module[]): void;
59
+ /**
60
+ * Clear all registered modules from the registry.
61
+ */
62
+ clear(): void;
63
+ }
@@ -0,0 +1,32 @@
1
+ import { ShaderParseResult, WebGLVersion } from "../types";
2
+ export default class Parser {
3
+ source: string;
4
+ parsed: ShaderParseResult | null;
5
+ constructor(source: string);
6
+ /**
7
+ * Parse the shader source to extract imports, uniforms, functions, and version.
8
+ */
9
+ parse(): ShaderParseResult;
10
+ /**
11
+ * Check if the shader source has already been parsed
12
+ */
13
+ isParsed(): boolean;
14
+ /**
15
+ * Change the shader source and reset the parsed result
16
+ */
17
+ setSource(newSource: string): void;
18
+ /**
19
+ * Get the detected GLSL version from the shader source without parsing the entire shader
20
+ */
21
+ version(): WebGLVersion;
22
+ private detectVersion;
23
+ private detectImports;
24
+ private diagnoseImport;
25
+ private detectUniforms;
26
+ private detectFunctions;
27
+ private parseParams;
28
+ private findClosingBrace;
29
+ private findFunctionCalls;
30
+ private findUniformCalls;
31
+ private findMentionCalls;
32
+ }
@@ -1,20 +1,13 @@
1
- import type { WebGLContext, WebGLVersion } from "../types";
1
+ import type { WebGLContext } from "../types";
2
2
  /**
3
3
  * Manages shader compilation and program linking.
4
- * Automatically detects WebGL version from shader source.
5
4
  */
6
5
  export default class Program {
7
6
  private gl;
8
7
  private program;
9
8
  private vertexShader;
10
9
  private fragmentShader;
11
- private version;
12
10
  constructor(gl: WebGLContext);
13
- /**
14
- * Detect WebGL version from shader source.
15
- * Looks for "#version 300 es" directive.
16
- */
17
- static detectVersion(source: string): WebGLVersion;
18
11
  /**
19
12
  * Compile shaders and link program.
20
13
  * @throws ShaderCompilationError if compilation fails
@@ -29,10 +22,6 @@ export default class Program {
29
22
  * Get the compiled WebGL program.
30
23
  */
31
24
  getProgram(): WebGLProgram | null;
32
- /**
33
- * Get detected WebGL version.
34
- */
35
- getVersion(): WebGLVersion;
36
25
  /**
37
26
  * Get attribute location.
38
27
  */
@@ -0,0 +1,4 @@
1
+ import Compilable from "./compilable";
2
+ export default class Shader extends Compilable {
3
+ constructor(source: string);
4
+ }
@@ -0,0 +1,64 @@
1
+ import type { TextureOptions, TextureSource, WebGLContext } from "../types";
2
+ /**
3
+ * Handles a single WebGL texture.
4
+ * Manages texture creation, pixel upload, binding, and location caching.
5
+ * Mirrors the Uniform class pattern for consistency.
6
+ */
7
+ export default class Texture {
8
+ readonly name: string;
9
+ private gl;
10
+ private texture;
11
+ private location;
12
+ private locationResolved;
13
+ private source;
14
+ private options;
15
+ private dynamicOverride;
16
+ private needsUpload;
17
+ private needsReupload;
18
+ constructor(gl: WebGLContext, name: string, source: TextureSource, options?: TextureOptions);
19
+ /**
20
+ * Update the texture source.
21
+ * Marks texture for re-upload on next bind.
22
+ * Respects the original `dynamic` option if explicitly set, otherwise re-infers from source type.
23
+ */
24
+ setSource(source: TextureSource): void;
25
+ /**
26
+ * Resolve and cache uniform location from program.
27
+ * Returns null if the sampler uniform doesn't exist in the shader.
28
+ */
29
+ resolveLocation(gl: WebGLContext, program: WebGLProgram): WebGLUniformLocation | null;
30
+ /**
31
+ * Invalidate cached location (call when program changes).
32
+ */
33
+ invalidateLocation(): void;
34
+ /**
35
+ * Bind texture to a texture unit and set the sampler uniform.
36
+ * @param program - Current WebGL program (for location resolution)
37
+ * @param unit - Texture unit index (0, 1, 2, ...)
38
+ */
39
+ upload(program: WebGLProgram, unit: number): void;
40
+ /**
41
+ * Cleanup WebGL texture resource.
42
+ */
43
+ destroy(): void;
44
+ /**
45
+ * Upload pixel data from source and apply texture parameters.
46
+ */
47
+ private uploadPixels;
48
+ /**
49
+ * Apply texture wrap and filter parameters.
50
+ */
51
+ private applyParameters;
52
+ /**
53
+ * Resolve wrap mode string to WebGL constant.
54
+ */
55
+ private static resolveWrap;
56
+ /**
57
+ * Resolve texture options with defaults.
58
+ */
59
+ private static resolveOptions;
60
+ /**
61
+ * Check if a source is dynamic (needs re-upload every frame).
62
+ */
63
+ private static isDynamicSource;
64
+ }
@@ -0,0 +1,48 @@
1
+ import type { TextureOptions, TextureSource, WebGLContext } from "../types";
2
+ import Texture from "./texture";
3
+ /**
4
+ * Manages a collection of textures with fluent API.
5
+ * Assigns texture units sequentially and handles program attachment.
6
+ * Mirrors the Uniforms class pattern for consistency.
7
+ */
8
+ export default class Textures {
9
+ private gl;
10
+ private program;
11
+ private textures;
12
+ constructor(gl: WebGLContext);
13
+ /**
14
+ * Attach to a WebGL program.
15
+ * Invalidates all cached locations since they're program-specific.
16
+ */
17
+ attachProgram(program: WebGLProgram): this;
18
+ /**
19
+ * Set a texture.
20
+ * Creates the texture entry if it doesn't exist, updates source if it does.
21
+ */
22
+ set(name: string, source: TextureSource, options?: TextureOptions): this;
23
+ /**
24
+ * Get a texture by name.
25
+ */
26
+ get(name: string): Texture | undefined;
27
+ /**
28
+ * Check if a texture exists.
29
+ */
30
+ has(name: string): boolean;
31
+ /**
32
+ * Remove a texture and free its GPU resources.
33
+ */
34
+ delete(name: string): boolean;
35
+ /**
36
+ * Upload all textures to their respective texture units.
37
+ * Units are assigned sequentially (0, 1, 2, ...).
38
+ */
39
+ uploadAll(): this;
40
+ /**
41
+ * Get texture count.
42
+ */
43
+ get size(): number;
44
+ /**
45
+ * Cleanup all textures.
46
+ */
47
+ destroy(): void;
48
+ }
@@ -7,8 +7,6 @@ export default class Uniforms {
7
7
  private gl;
8
8
  private program;
9
9
  private uniforms;
10
- /** Built-in uniform names that are handled automatically */
11
- private static readonly BUILT_INS;
12
10
  constructor(gl: WebGLContext);
13
11
  /**
14
12
  * Attach to a WebGL program.
@@ -1,6 +1,7 @@
1
- import type { AnyUniformValue, ResolvedSandboxOptions, UniformSchema, WebGLContext, WebGLVersion } from "../types";
1
+ import type { AnyUniformValue, ResolvedSandboxOptions, TextureOptions, TextureSchema, TextureSource, UniformSchema, WebGLContext, WebGLVersion } from "../types";
2
2
  import Clock from "./clock";
3
3
  import Hooks from "./hooks";
4
+ import Shader from "./shader";
4
5
  /**
5
6
  * Main WebGL orchestrator.
6
7
  * Manages context, program, geometry, uniforms, and rendering loop.
@@ -14,6 +15,7 @@ export default class WebGL {
14
15
  private _program;
15
16
  private _geometry;
16
17
  private _uniforms;
18
+ private _textures;
17
19
  private _clock;
18
20
  private _resolution;
19
21
  private _mouse;
@@ -58,11 +60,23 @@ export default class WebGL {
58
60
  * Get current uniform value.
59
61
  */
60
62
  getUniform<T extends AnyUniformValue>(name: string): T | undefined;
63
+ /**
64
+ * Set a texture.
65
+ */
66
+ texture(name: string, source: TextureSource, options?: TextureOptions): this;
67
+ /**
68
+ * Set multiple textures from a schema.
69
+ */
70
+ texturesFromSchema(schema: TextureSchema): this;
71
+ /**
72
+ * Remove a texture.
73
+ */
74
+ removeTexture(name: string): this;
61
75
  /**
62
76
  * Compile and link shaders.
63
77
  * Errors are handled via onError callback, never thrown.
64
78
  */
65
- shader(vertex: string, fragment: string): this;
79
+ shader(vertex: Shader, fragment: Shader): this;
66
80
  /**
67
81
  * Start animation loop.
68
82
  */
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { SandboxError } from "./errors";
2
+ import Shader from "./tools/shader";
2
3
  /** Sandbox configuration options */
3
4
  export interface SandboxOptions {
4
5
  /** Vertex shader source code */
@@ -27,9 +28,16 @@ export interface SandboxOptions {
27
28
  onAfterRender?: HookCallback | null;
28
29
  /** Initial uniforms to set */
29
30
  uniforms?: UniformSchema;
31
+ /** Configure used modules behavior */
32
+ modules?: Record<string, Record<string, AnyUniformValue>>;
33
+ /** Initial textures to bind */
34
+ textures?: TextureSchema;
30
35
  }
31
36
  /** Resolved sandbox options with all defaults applied */
32
- export type ResolvedSandboxOptions = Required<SandboxOptions>;
37
+ export type ResolvedSandboxOptions = Omit<Required<SandboxOptions>, "vertex" | "fragment"> & {
38
+ vertex: Shader;
39
+ fragment: Shader;
40
+ };
33
41
  /** WebGL version (1 = WebGL, 2 = WebGL2) */
34
42
  export type WebGLVersion = 1 | 2;
35
43
  /** Union of WebGL context types */
@@ -115,3 +123,117 @@ export interface UniformEntry {
115
123
  export type DrawMode = "TRIANGLES" | "TRIANGLE_STRIP" | "TRIANGLE_FAN";
116
124
  /** Render callback signature */
117
125
  export type HookCallback = (clock: ClockState) => void | false;
126
+ /** Valid texture source types */
127
+ export type TextureSource = HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap | ImageData | OffscreenCanvas;
128
+ /** Texture wrapping mode */
129
+ export type TextureWrap = "clamp" | "repeat" | "mirror";
130
+ /** Texture filter mode */
131
+ export type TextureFilter = "nearest" | "linear";
132
+ /** Per-texture configuration */
133
+ export interface TextureOptions {
134
+ /** Wrapping mode for both axes (shorthand for wrapS + wrapT) */
135
+ wrap?: TextureWrap;
136
+ /** Wrapping mode for S (horizontal) axis */
137
+ wrapS?: TextureWrap;
138
+ /** Wrapping mode for T (vertical) axis */
139
+ wrapT?: TextureWrap;
140
+ /** Minification filter */
141
+ minFilter?: TextureFilter;
142
+ /** Magnification filter */
143
+ magFilter?: TextureFilter;
144
+ /** Flip texture vertically on upload (default: true) */
145
+ flipY?: boolean;
146
+ /** Re-upload pixels every frame for animated sources (default: true for video, false otherwise) */
147
+ dynamic?: boolean;
148
+ }
149
+ /** Resolved texture options with all defaults applied */
150
+ export type ResolvedTextureOptions = Required<TextureOptions>;
151
+ /** Schema for defining multiple textures at once */
152
+ export interface TextureSchema {
153
+ [name: string]: TextureSource | ({
154
+ source: TextureSource;
155
+ } & TextureOptions);
156
+ }
157
+ /** GLSL uniform types */
158
+ export type GLSLType = "float" | "int" | "bool" | "vec2" | "vec3" | "vec4" | "ivec2" | "ivec3" | "ivec4" | "bvec2" | "bvec3" | "bvec4" | "mat2" | "mat3" | "mat4" | "sampler2D" | "samplerCube";
159
+ export type GLSLVariable = {
160
+ /** Variable name */
161
+ name: string;
162
+ /** GLSL type */
163
+ type: GLSLType;
164
+ };
165
+ /** Import statement parsed from shader */
166
+ export type ShaderImport = {
167
+ /** Module identifier (e.g., "sandbox/math") */
168
+ module: string;
169
+ /** Original function name in module */
170
+ name: string;
171
+ /** Alias to use in shader (defaults to name) */
172
+ alias: string;
173
+ /** Line number where import appears */
174
+ line?: number;
175
+ };
176
+ export type ShaderUniform = GLSLVariable & {
177
+ /** Line number where import appears */
178
+ name: "u_time" | "u_resolution" | "u_delta" | "u_mouse" | "u_frame" | string;
179
+ arrayNum?: number;
180
+ line?: number;
181
+ };
182
+ type ShaderFunctionDependency = {
183
+ /** Name of the dependent */
184
+ name: string;
185
+ /** Type of dependency */
186
+ type: "function" | "uniform" | "mention";
187
+ /** Character index from start of the function body for rewriting purposes */
188
+ index?: number;
189
+ };
190
+ export type ShaderFunction = {
191
+ /** Function name */
192
+ name: string;
193
+ /** Return type */
194
+ type: GLSLType;
195
+ /** Function parameters */
196
+ params: GLSLVariable[];
197
+ /** Function body (including braces) */
198
+ body: string;
199
+ /** List of dependencies (functions or uniforms) this function has */
200
+ dependencies: ShaderFunctionDependency[];
201
+ /** Line number where function is declared */
202
+ line?: number;
203
+ };
204
+ export type ShaderParseResult = {
205
+ /** All imports found in shader */
206
+ imports: ShaderImport[];
207
+ /** All uniforms declared in shader */
208
+ uniforms: ShaderUniform[];
209
+ /** All functions declared in shader */
210
+ functions: ShaderFunction[];
211
+ /** GLSL version */
212
+ version: WebGLVersion;
213
+ };
214
+ export interface ModuleDefinition {
215
+ /** Module name */
216
+ name: string;
217
+ /** GLSL source code containing functions */
218
+ source: string;
219
+ /**
220
+ * Options per-function if the module has custom uniforms that can be used to configure methods behavior.
221
+ * The key is the function name.
222
+ * For each function set the option name and link it to a uniform with optional default value.
223
+ */
224
+ options?: Record<string, Record<string, ModuleMethodOption>>;
225
+ }
226
+ export type ModuleMethodOption = {
227
+ uniform: string;
228
+ default?: AnyUniformValue;
229
+ };
230
+ export type ModuleFunctionExtraction = {
231
+ /** Extracted function definition */
232
+ function: ShaderFunction;
233
+ /** All dependencies (functions and uniforms) required by this function */
234
+ dependencies: {
235
+ functions: ShaderFunction[];
236
+ uniforms: ShaderUniform[];
237
+ };
238
+ };
239
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosalana/sandbox",
3
- "version": "0.0.5",
3
+ "version": "0.2.0",
4
4
  "description": "Lightweight WebGL wrapper for simple, beautiful shader effects",
5
5
  "keywords": [
6
6
  "webgl",
@@ -21,9 +21,9 @@
21
21
  "types": "./dist/index.d.ts",
22
22
  "exports": {
23
23
  ".": {
24
+ "types": "./dist/index.d.ts",
24
25
  "import": "./dist/index.es.js",
25
- "require": "./dist/index.cjs.js",
26
- "types": "./dist/index.d.ts"
26
+ "require": "./dist/index.cjs.js"
27
27
  }
28
28
  },
29
29
  "files": [
@@ -35,7 +35,9 @@
35
35
  "typecheck": "tsc --noEmit",
36
36
  "dev": "npm run build -- --watch",
37
37
  "build": "vite build && tsc --declaration --emitDeclarationOnly",
38
- "clean": "rm -rf dist"
38
+ "clean": "rm -rf dist",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest"
39
41
  },
40
42
  "repository": {
41
43
  "type": "git",
@@ -49,6 +51,7 @@
49
51
  "license": "MIT",
50
52
  "devDependencies": {
51
53
  "typescript": "^5.4.0",
52
- "vite": "^5.2.0"
54
+ "vite": "^5.2.0",
55
+ "vitest": "^4.0.18"
53
56
  }
54
57
  }
package/dist/errors.d.ts DELETED
@@ -1,32 +0,0 @@
1
- /** Error codes for Sandbox errors */
2
- export type SandboxErrorCode = "WEBGL_NOT_SUPPORTED" | "CONTEXT_CREATION_FAILED" | "SHADER_COMPILATION_FAILED" | "PROGRAM_LINK_FAILED" | "SHADER_VERSION_MISMATCH" | "UNKNOWN_ERROR";
3
- /** Base error class for all Sandbox errors */
4
- export declare class SandboxError extends Error {
5
- readonly code: SandboxErrorCode;
6
- constructor(message: string, code: SandboxErrorCode);
7
- }
8
- /** WebGL context creation failure */
9
- export declare class SandboxContextError extends SandboxError {
10
- constructor(reason: "not_supported" | "creation_failed");
11
- }
12
- export declare class SandboxShaderVersionMismatchError extends SandboxError {
13
- readonly vertexVersion: number;
14
- readonly fragmentVersion: number;
15
- constructor(vertexVersion: number, fragmentVersion: number);
16
- }
17
- /** Shader compilation failure with line info extraction */
18
- export declare class SandboxShaderCompilationError extends SandboxError {
19
- readonly shaderType: "vertex" | "fragment";
20
- readonly source: string;
21
- readonly infoLog: string;
22
- /** Line numbers where errors occurred */
23
- readonly lines: number[];
24
- constructor(shaderType: "vertex" | "fragment", source: string, infoLog: string);
25
- /** Parse error log to extract line numbers */
26
- private static parseErrorLines;
27
- }
28
- /** Shader program linking failure */
29
- export declare class SandboxProgramError extends SandboxError {
30
- readonly infoLog: string;
31
- constructor(infoLog: string);
32
- }