Skip to content

Commit 16636f6

Browse files
committed
📝 Update agent rules to use decorator-based control system
Replace HTML template references with TypeScript decorators This change updates all agent rules to reflect the decorator-based control system: - Remove references to template.html files across all agent rules - Update code examples to use @effect and control decorators - Add documentation for decorator implementation patterns - Update project structure descriptions to match core module system - Revise file paths to reflect current architecture (effects/, core/)
1 parent f59ab8e commit 16636f6

File tree

4 files changed

+419
-192
lines changed

4 files changed

+419
-192
lines changed

.cursor/rules/global-rules/lightscript-effect-creation-agent.mdc

+177-88
Original file line numberDiff line numberDiff line change
@@ -9,70 +9,107 @@ alwaysApply: false
99
## Critical Rules
1010

1111
- New effects must follow one of two base patterns: `CanvasEffect<T>` (Canvas 2D) or `WebGLEffect<T>` (WebGL/Three.js)
12-
- Each effect requires at minimum three files:
13-
- `main.ts` - Entry point with effect registration
14-
- `template.html` - HTML template with control definitions using `<meta>` tags
15-
- Effect implementation file (e.g., `effect-name-effect.ts`)
16-
- All controls must be defined in the template.html file with proper metadata
12+
- Each effect requires at minimum two files:
13+
- `main.ts` - Implementation with decorated effect class and control properties
14+
- For WebGL effects: shader files like `fragment.glsl`
15+
- All controls must be defined using decorator syntax: `@NumberControl`, `@ComboboxControl`, etc.
16+
- Use the `@Effect` decorator to define effect metadata (name, description, author)
1717
- WebGL effects must include shader code as separate `.glsl` files and import them
1818
- Shadertoy conversions must adapt uniforms to match the LightScript naming convention
19-
- Control interfaces must extend `BaseControls` and define all parameters with proper types
19+
- Control interfaces must define all parameters with proper types
2020
- Implement all required abstract methods from the base class
2121
- Canvas effects must implement the `draw(time, deltaTime)` method
2222
- WebGL effects must implement `createUniforms()` and `updateUniforms()` methods
23-
- New effects must be registered in `src/index.ts` to be discoverable
23+
- New effects must be registered in the effects registry to be discoverable
2424
- Follow the pattern: initialize → loadResources → render → update → cleanup
2525
- Always use strong typing with proper generics: `CanvasEffect<MyControlsInterface>`
26-
- Control values must be normalized using helper functions from `controls.ts`
26+
- Control values must be normalized using helper functions
2727
- Effect implementation must handle window resizing and device pixel ratio
2828
- Use provided debug utilities with consistent log levels and namespaces
2929
- Clean up all resources in the `stop()` method to prevent memory leaks
3030
- Include comprehensive JSDoc comments for public methods
3131

3232
## Canvas Effect Creation Workflow
3333

34-
1. Create directory structure: `src/effects/{effect-id}/`
35-
2. Define control interface extending `BaseControls`
36-
3. Create template.html with control metadata
37-
4. Implement effect class extending `CanvasEffect<T>`
38-
5. Implement `draw()` method with Canvas 2D rendering logic
39-
6. Register the effect in src/index.ts
34+
1. Create directory structure: `effects/{effect-id}/`
35+
2. Define control interface
36+
3. Implement effect class extending `CanvasEffect<T>` with appropriate decorators
37+
4. Implement `draw()` method with Canvas 2D rendering logic
38+
5. Register the effect in the effects registry
4039

4140
## WebGL Effect Creation Workflow
4241

43-
1. Create directory structure: `src/effects/{effect-id}/`
42+
1. Create directory structure: `effects/{effect-id}/`
4443
2. Define fragment shader (and optionally vertex shader) in .glsl files
45-
3. Define control interface extending `BaseControls`
46-
4. Create template.html with control metadata
47-
5. Implement effect class extending `WebGLEffect<T>`
48-
6. Implement shader uniform creation and updating
49-
7. Register the effect in src/index.ts
44+
3. Define control interface
45+
4. Implement effect class extending `WebGLEffect<T>` with appropriate decorators
46+
5. Implement shader uniform creation and updating
47+
6. Register the effect in the effects registry
5048

5149
## Examples
5250

5351
<example>
5452
// Canvas Effect Example (basic structure)
5553

56-
// src/effects/my-canvas-effect/main.ts
57-
import { initializeEffect } from "../../common";
58-
import { MyCanvasEffect } from "./my-canvas-effect";
54+
// effects/my-canvas-effect/main.ts
55+
import { CanvasEffect } from "../../core/effects/canvas-effect";
56+
import {
57+
Effect,
58+
NumberControl,
59+
BooleanControl
60+
} from "../../core/controls/decorators";
61+
import { normalizeSpeed } from "../../core/controls/helpers";
62+
import { initializeEffect } from "../../core";
5963

60-
// Create and export the effect instance
61-
const effect = new MyCanvasEffect();
62-
63-
// Initialize the effect
64-
initializeEffect(() => {
65-
effect.initialize();
66-
});
67-
68-
export default effect;
69-
70-
// src/effects/my-canvas-effect/my-canvas-effect.ts
71-
import { CanvasEffect } from "../../common/canvas-effect";
72-
import { MyEffectControls } from "./types";
73-
import { getControlValue, normalizeSpeed } from "../../common/controls";
64+
// Define control interface
65+
interface MyEffectControls {
66+
speed: number;
67+
particleCount: number;
68+
useGlow: boolean;
69+
colorIntensity: number;
70+
}
7471

72+
@Effect({
73+
name: "My Canvas Effect",
74+
description: "A beautiful canvas-based effect",
75+
author: "YourName"
76+
})
7577
export class MyCanvasEffect extends CanvasEffect<MyEffectControls> {
78+
// Define controls with decorators
79+
@NumberControl({
80+
label: "Animation Speed",
81+
min: 1,
82+
max: 10,
83+
default: 5,
84+
tooltip: "Controls how fast the animation runs"
85+
})
86+
speed!: number;
87+
88+
@NumberControl({
89+
label: "Particle Count",
90+
min: 10,
91+
max: 500,
92+
default: 100,
93+
tooltip: "Number of particles in the effect"
94+
})
95+
particleCount!: number;
96+
97+
@BooleanControl({
98+
label: "Enable Glow",
99+
default: true,
100+
tooltip: "Enables glowing effect on particles"
101+
})
102+
useGlow!: boolean;
103+
104+
@NumberControl({
105+
label: "Color Intensity",
106+
min: 10,
107+
max: 200,
108+
default: 100,
109+
tooltip: "Controls color brightness"
110+
})
111+
colorIntensity!: number;
112+
76113
// Effect properties
77114
private particles = [];
78115
private lastTime = 0;
@@ -86,19 +123,19 @@ export class MyCanvasEffect extends CanvasEffect<MyEffectControls> {
86123
}
87124

88125
protected initializeControls(): void {
89-
// Set default values
126+
// Set default values (now matches decorator defaults)
90127
window.speed = 5;
91128
window.particleCount = 100;
92-
window.useGlow = 1;
129+
window.useGlow = true;
130+
window.colorIntensity = 100;
93131
}
94132

95133
protected getControlValues(): MyEffectControls {
96134
return {
97-
speed: normalizeSpeed(getControlValue("speed", 5)),
98-
particleCount: getControlValue("particleCount", 100),
99-
useGlow: Boolean(getControlValue("useGlow", 1)),
100-
colorIntensity: getControlValue("colorIntensity", 100),
101-
colorSaturation: getControlValue("colorSaturation", 100)
135+
speed: normalizeSpeed(window.speed ?? 5),
136+
particleCount: window.particleCount ?? 100,
137+
useGlow: Boolean(window.useGlow ?? true),
138+
colorIntensity: window.colorIntensity ?? 100
102139
};
103140
}
104141

@@ -119,19 +156,17 @@ export class MyCanvasEffect extends CanvasEffect<MyEffectControls> {
119156
}
120157
}
121158

122-
// src/effects/my-canvas-effect/types.ts
123-
import { BaseControls } from "../../common/controls";
159+
// Create and initialize the effect
160+
const effect = new MyCanvasEffect();
161+
initializeEffect(() => effect.initialize());
124162

125-
export interface MyEffectControls extends BaseControls {
126-
particleCount: number;
127-
useGlow: boolean;
128-
}
163+
export default effect;
129164
</example>
130165

131166
<example>
132167
// WebGL/Shadertoy Conversion Example
133168

134-
// src/effects/shadertoy-effect/fragment.glsl
169+
// effects/shadertoy-effect/fragment.glsl
135170
// Modified Shadertoy shader with LightScript uniforms
136171
uniform float iTime;
137172
uniform vec2 iResolution;
@@ -166,13 +201,19 @@ void main() {
166201
mainImage(gl_FragColor, gl_FragCoord.xy);
167202
}
168203

169-
// src/effects/shadertoy-effect/main.ts
170-
import { WebGLEffect } from "../../common/webgl-effect";
171-
import { initializeEffect } from "../../common";
204+
// effects/shadertoy-effect/main.ts
205+
import { WebGLEffect } from "../../core/effects/webgl-effect";
206+
import {
207+
Effect,
208+
NumberControl,
209+
ComboboxControl
210+
} from "../../core/controls/decorators";
211+
import { normalizeSpeed } from "../../core/controls/helpers";
212+
import { initializeEffect } from "../../core";
172213
import * as THREE from "three";
173214
import fragmentShader from "./fragment.glsl";
174-
import { getControlValue, normalizeSpeed } from "../../common/controls";
175215

216+
// Define control interface
176217
interface ShadertoyEffectControls {
177218
speed: number;
178219
colorIntensity: number;
@@ -181,9 +222,58 @@ interface ShadertoyEffectControls {
181222
colorSaturation: number;
182223
}
183224

184-
class ShadertoyEffect extends WebGLEffect<ShadertoyEffectControls> {
225+
@Effect({
226+
name: "Shadertoy Effect",
227+
description: "Converted from Shadertoy",
228+
author: "YourName"
229+
})
230+
export class ShadertoyEffect extends WebGLEffect<ShadertoyEffectControls> {
185231
private readonly colorModes = ["Standard", "Neon", "Monochrome"];
186232

233+
@NumberControl({
234+
label: "Animation Speed",
235+
min: 1,
236+
max: 10,
237+
default: 5,
238+
tooltip: "Controls animation speed"
239+
})
240+
speed!: number;
241+
242+
@NumberControl({
243+
label: "Color Intensity",
244+
min: 10,
245+
max: 200,
246+
default: 100,
247+
tooltip: "Brightness of colors"
248+
})
249+
colorIntensity!: number;
250+
251+
@ComboboxControl({
252+
label: "Color Mode",
253+
values: ["Standard", "Neon", "Monochrome"],
254+
default: "Standard",
255+
tooltip: "Changes the color scheme"
256+
})
257+
colorMode!: string;
258+
259+
@NumberControl({
260+
label: "Pattern Density",
261+
min: 1,
262+
max: 10,
263+
default: 5,
264+
tooltip: "Controls pattern density"
265+
})
266+
pattern!: number;
267+
268+
@NumberControl({
269+
label: "Color Saturation",
270+
min: 0,
271+
max: 200,
272+
default: 100,
273+
tooltip: "Controls color saturation"
274+
})
275+
colorSaturation!: number;
276+
187277
constructor() {
188278
super({
189279
id: "shadertoy-effect",
@@ -202,7 +292,7 @@ class ShadertoyEffect extends WebGLEffect<ShadertoyEffectControls> {
202292

203293
protected getControlValues(): ShadertoyEffectControls {
204294
// Handle colorMode conversion
205-
const rawColorMode = getControlValue("colorMode", "Standard");
295+
const rawColorMode = window.colorMode ?? "Standard";
206296
let colorMode: number | string = rawColorMode;
207297

208298
if (typeof colorMode === "string") {
@@ -211,11 +301,11 @@ class ShadertoyEffect extends WebGLEffect<ShadertoyEffectControls> {
211301
}
212302

213303
return {
214-
speed: normalizeSpeed(getControlValue("speed", 5)),
215-
colorIntensity: getControlValue("colorIntensity", 100) / 100,
304+
speed: normalizeSpeed(window.speed ?? 5),
305+
colorIntensity: (window.colorIntensity ?? 100) / 100,
216306
colorMode,
217-
pattern: getControlValue("pattern", 5),
218-
colorSaturation: getControlValue("colorSaturation", 100)
307+
pattern: window.pattern ?? 5,
308+
colorSaturation: window.colorSaturation ?? 100
219309
};
220310
}
221311

@@ -245,44 +335,43 @@ export default effect;
245335
</example>
246336

247337
<example type="invalid">
248-
// Invalid WebGL effect implementation
249-
250-
// Missing separate shader file, incorrect directory structure
251-
// main.js in src/ root instead of proper structure
338+
// Invalid effect implementation
252339

253-
// Direct DOM manipulation instead of using framework
254-
const canvas = document.getElementById("canvas");
255-
const gl = canvas.getContext("webgl");
256-
257-
// Inline shaders instead of separate files
258-
const vertexShader = `
259-
attribute vec4 aPosition;
260-
void main() {
261-
gl_Position = aPosition;
262-
}
263-
`;
264-
265-
// Not using base classes
266-
class MyEffect {
340+
// Missing decorators for controls
341+
class MyEffect extends WebGLEffect<MyControls> {
342+
// Controls defined without decorators
343+
private speed = 5;
344+
private colorMode = "Standard";
345+
346+
// Missing @Effect decorator
267347
constructor() {
268-
this.speed = 1.0;
269-
this.init();
348+
super({
349+
id: "my-effect",
350+
name: "My Effect",
351+
fragmentShader
352+
});
270353
}
271354

272-
init() {
355+
// Trying to directly manipulate DOM instead of using framework
356+
protected initializeRenderer(): Promise<void> {
357+
const canvas = document.getElementById("canvas");
358+
const gl = canvas.getContext("webgl");
273359
// Direct WebGL calls instead of using Three.js
274-
const program = gl.createProgram();
275-
// ...more WebGL code
276360
}
277361

278-
update() {
279-
// Not using the lifecycle methods
362+
// Missing required methods
363+
// No createUniforms() or updateUniforms() implementation
364+
365+
// Missing proper control updates
366+
protected updateParameters(controls: MyControls): void {
367+
// Directly setting uniform values without checking if material exists
368+
this.material.uniforms.iSpeed.value = controls.speed;
280369
}
281370

282371
// Missing proper cleanup
283372
}
284373

285-
// Not registering in index.ts
286-
const myEffect = new MyEffect();
374+
// Not providing proper initialization
375+
new MyEffect().initialize();
287376
</example>
288377
</rewritten_file>

0 commit comments

Comments
 (0)