@rosalana/sandbox 0.0.4 → 0.1.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.
@@ -12,10 +12,13 @@ export default class Clock {
12
12
  frame: number;
13
13
  /** Is clock running */
14
14
  running: boolean;
15
+ /** Smoothed frames per second */
16
+ fps: number;
15
17
  private startTime;
16
18
  private lastTime;
17
19
  private rafId;
18
20
  private callback;
21
+ private maxFps;
19
22
  constructor();
20
23
  /**
21
24
  * Start the animation loop with a render callback.
@@ -47,6 +50,10 @@ export default class Clock {
47
50
  * Cleanup.
48
51
  */
49
52
  destroy(): void;
53
+ /**
54
+ * Set maximum frames per second.
55
+ */
56
+ setMaxFps(fps: number): this;
50
57
  /**
51
58
  * Internal animation frame handler.
52
59
  */
@@ -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
+ }
@@ -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,5 +1,7 @@
1
1
  import type { AnyUniformValue, ResolvedSandboxOptions, UniformSchema, WebGLContext, WebGLVersion } from "../types";
2
+ import Clock from "./clock";
2
3
  import Hooks from "./hooks";
4
+ import Shader from "./shader";
3
5
  /**
4
6
  * Main WebGL orchestrator.
5
7
  * Manages context, program, geometry, uniforms, and rendering loop.
@@ -61,7 +63,7 @@ export default class WebGL {
61
63
  * Compile and link shaders.
62
64
  * Errors are handled via onError callback, never thrown.
63
65
  */
64
- shader(vertex: string, fragment: string): this;
66
+ shader(vertex: Shader, fragment: Shader): this;
65
67
  /**
66
68
  * Start animation loop.
67
69
  */
@@ -82,6 +84,10 @@ export default class WebGL {
82
84
  * Get detected WebGL version.
83
85
  */
84
86
  getVersion(): WebGLVersion;
87
+ /**
88
+ * Get the clock instance
89
+ */
90
+ getClock(): Clock;
85
91
  /**
86
92
  * Cleanup all resources.
87
93
  */
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 */
@@ -11,6 +12,8 @@ export interface SandboxOptions {
11
12
  pauseWhenHidden?: boolean;
12
13
  /** Device pixel ratio - "auto" uses window.devicePixelRatio (default: "auto") */
13
14
  dpr?: number | "auto";
15
+ /** Max Frame rate (default: 0 as unlimited) */
16
+ fps?: number;
14
17
  /** Preserve drawing buffer for screenshots (default: false) */
15
18
  preserveDrawingBuffer?: boolean;
16
19
  /** Enable antialiasing (default: true) */
@@ -25,9 +28,14 @@ export interface SandboxOptions {
25
28
  onAfterRender?: HookCallback | null;
26
29
  /** Initial uniforms to set */
27
30
  uniforms?: UniformSchema;
31
+ /** Configure used modules behavior */
32
+ modules?: Record<string, Record<string, AnyUniformValue>>;
28
33
  }
29
34
  /** Resolved sandbox options with all defaults applied */
30
- export type ResolvedSandboxOptions = Required<SandboxOptions>;
35
+ export type ResolvedSandboxOptions = Omit<Required<SandboxOptions>, "vertex" | "fragment"> & {
36
+ vertex: Shader;
37
+ fragment: Shader;
38
+ };
31
39
  /** WebGL version (1 = WebGL, 2 = WebGL2) */
32
40
  export type WebGLVersion = 1 | 2;
33
41
  /** Union of WebGL context types */
@@ -95,6 +103,10 @@ export interface ClockState {
95
103
  delta: number;
96
104
  /** Frame counter */
97
105
  frame: number;
106
+ /** Is the clock currently running */
107
+ running: boolean;
108
+ /** Frame rate in frames per second */
109
+ fps: number;
98
110
  }
99
111
  /** Internal uniform entry for caching */
100
112
  export interface UniformEntry {
@@ -109,3 +121,86 @@ export interface UniformEntry {
109
121
  export type DrawMode = "TRIANGLES" | "TRIANGLE_STRIP" | "TRIANGLE_FAN";
110
122
  /** Render callback signature */
111
123
  export type HookCallback = (clock: ClockState) => void | false;
124
+ /** GLSL uniform types */
125
+ export type GLSLType = "float" | "int" | "bool" | "vec2" | "vec3" | "vec4" | "ivec2" | "ivec3" | "ivec4" | "bvec2" | "bvec3" | "bvec4" | "mat2" | "mat3" | "mat4" | "sampler2D" | "samplerCube";
126
+ export type GLSLVariable = {
127
+ /** Variable name */
128
+ name: string;
129
+ /** GLSL type */
130
+ type: GLSLType;
131
+ };
132
+ /** Import statement parsed from shader */
133
+ export type ShaderImport = {
134
+ /** Module identifier (e.g., "sandbox/math") */
135
+ module: string;
136
+ /** Original function name in module */
137
+ name: string;
138
+ /** Alias to use in shader (defaults to name) */
139
+ alias: string;
140
+ /** Line number where import appears */
141
+ line?: number;
142
+ };
143
+ export type ShaderUniform = GLSLVariable & {
144
+ /** Line number where import appears */
145
+ name: "u_time" | "u_resolution" | "u_delta" | "u_mouse" | "u_frame" | string;
146
+ arrayNum?: number;
147
+ line?: number;
148
+ };
149
+ type ShaderFunctionDependency = {
150
+ /** Name of the dependent */
151
+ name: string;
152
+ /** Type of dependency */
153
+ type: "function" | "uniform" | "mention";
154
+ /** Character index from start of the function body for rewriting purposes */
155
+ index?: number;
156
+ };
157
+ export type ShaderFunction = {
158
+ /** Function name */
159
+ name: string;
160
+ /** Return type */
161
+ type: GLSLType;
162
+ /** Function parameters */
163
+ params: GLSLVariable[];
164
+ /** Function body (including braces) */
165
+ body: string;
166
+ /** List of dependencies (functions or uniforms) this function has */
167
+ dependencies: ShaderFunctionDependency[];
168
+ /** Line number where function is declared */
169
+ line?: number;
170
+ };
171
+ export type ShaderParseResult = {
172
+ /** All imports found in shader */
173
+ imports: ShaderImport[];
174
+ /** All uniforms declared in shader */
175
+ uniforms: ShaderUniform[];
176
+ /** All functions declared in shader */
177
+ functions: ShaderFunction[];
178
+ /** GLSL version */
179
+ version: WebGLVersion;
180
+ };
181
+ export interface ModuleDefinition {
182
+ /** Module name */
183
+ name: string;
184
+ /** GLSL source code containing functions */
185
+ source: string;
186
+ /**
187
+ * Options per-function if the module has custom uniforms that can be used to configure methods behavior.
188
+ * The key is the function name.
189
+ * For each function set the option name and link it to a uniform with optional default value.
190
+ */
191
+ options?: Record<string, Record<string, ModuleMethodOption>>;
192
+ }
193
+ export type ModuleMethodOption = {
194
+ uniform: string;
195
+ default?: AnyUniformValue;
196
+ };
197
+ export type ModuleFunctionExtraction = {
198
+ /** Extracted function definition */
199
+ function: ShaderFunction;
200
+ /** All dependencies (functions and uniforms) required by this function */
201
+ dependencies: {
202
+ functions: ShaderFunction[];
203
+ uniforms: ShaderUniform[];
204
+ };
205
+ };
206
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosalana/sandbox",
3
- "version": "0.0.4",
3
+ "version": "0.1.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
- }