@pixerate/flair 0.1.0 → 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 +17 -0
- package/README.md +21 -6
- package/dist/editor/index.d.ts +89 -41
- package/dist/editor/index.js +172 -83
- package/dist/editor/index.js.map +1 -1
- package/package.json +14 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @pixerate/flair
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6fb310a: Build the effect editor on `@pixerate/editor` 0.29's new subpaths (now the minimum version of the optional peer):
|
|
8
|
+
|
|
9
|
+
- Keyframes use `@pixerate/editor/animation`'s easing engine and track types. Flair's own copy is gone; its exports remain as aliases.
|
|
10
|
+
- Agent tools are typed by `@pixerate/editor/tools`. A new `createEffectToolRegistry(editor)` serves them through its `ToolRegistry`, with argument checking.
|
|
11
|
+
- New `parameterSchemaFor(controls)` and `bindEffectParameters(editor)` drive `@pixerate/editor/parameters`, including the React and Svelte `ParametersPanel`s, with every change and keyframe toggle going through the effect editor.
|
|
12
|
+
- New `EffectEditor.setTrack` and its `keyframe.setTrack` command replace a control's keyframes in one step.
|
|
13
|
+
|
|
14
|
+
## 0.1.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 921f9f4: Ship `CHANGELOG.md` in the package and normalise the `bin` and `repository` fields, so npm no longer rewrites them on publish.
|
|
19
|
+
|
|
3
20
|
## 0.1.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ Flair gives products a library of good-looking, configurable visual effects (her
|
|
|
43
43
|
| `@pixerate/flair` | Shader engine, vector motion, time and easing, colour, blending, transitions, scenes, interchange exporters | None (browser-safe) |
|
|
44
44
|
| `@pixerate/flair/catalog` | `EFFECTS`, `getEffect`, `findEffects`, `effectDefaults`, `renderVectorEffect`, GLSL helpers, sample images | None |
|
|
45
45
|
| `@pixerate/flair/vector` | `SvgMotion`, `svgFrame`, `svgView`, `SvgPlayer`, `svgMotionIssues` | None |
|
|
46
|
-
| `@pixerate/flair/editor` | `EffectEditor`, effect documents, keyframe tracks, renderer bindings, agent tools and instructions | `@pixerate/editor` (optional peer) |
|
|
46
|
+
| `@pixerate/flair/editor` | `EffectEditor`, effect documents, keyframe tracks, renderer bindings, parameter-panel binding, agent tools and instructions | `@pixerate/editor` ≥ 0.29 (optional peer; only its dependency-free `/history`, `/commands`, `/tools`, `/animation` and `/parameters` subpaths) |
|
|
47
47
|
| `@pixerate/flair/node` | `exportMotionGraphic`: renders a shader to GIF/MP4 through headless Chrome and ffmpeg | Node.js |
|
|
48
48
|
|
|
49
49
|
The `flair` CLI inspects shaders, writes standalone pages and directs scenes (see [CLI](#-cli)).
|
|
@@ -90,6 +90,8 @@ Flair stays free of LLM SDKs and UI frameworks. Your app owns the interface and
|
|
|
90
90
|
npm install @pixerate/flair
|
|
91
91
|
# For the editor:
|
|
92
92
|
npm install @pixerate/flair @pixerate/editor
|
|
93
|
+
# For the ready-made React (or Svelte) controls panel:
|
|
94
|
+
npm install @pixerate/editor-react
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
### 1. Drop in a catalog effect
|
|
@@ -163,12 +165,24 @@ editor.commands.execute("values.randomize");
|
|
|
163
165
|
window.addEventListener("keydown", (e) => editor.commands.handleKeyDown(e)); // Space plays/pauses
|
|
164
166
|
```
|
|
165
167
|
|
|
166
|
-
`getState()` returns the same object until something changes, so React's `useSyncExternalStore` works as is. Documents are plain JSON (`EffectDocument`)
|
|
168
|
+
`getState()` returns the same object until something changes, so React's `useSyncExternalStore` works as is. Documents are plain JSON (`EffectDocument`). Keyframe tracks use `@pixerate/editor/animation`'s track shape and easing engine.
|
|
169
|
+
|
|
170
|
+
For a ready-made controls panel, bind the effect to `@pixerate/editor`'s parameters editor. Every field, image pick and keyframe diamond goes through the effect editor and its undo history:
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { bindEffectParameters } from "@pixerate/flair/editor";
|
|
174
|
+
import { ParametersPanel } from "@pixerate/editor-react/parameters"; // or @pixerate/editor-svelte/parameters
|
|
175
|
+
|
|
176
|
+
const { controller, dispose } = bindEffectParameters(editor);
|
|
177
|
+
<ParametersPanel controller={controller} showToolbar={false} showPresets={false} showKeyframes />;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`parameterSchemaFor(controls)` gives you the schema alone, for a UI of your own.
|
|
167
181
|
|
|
168
182
|
### 4. Let an agent drive it
|
|
169
183
|
|
|
170
184
|
```ts
|
|
171
|
-
import { createEffectToolProvider, toolDefinitions, callTool, EFFECT_AGENT_INSTRUCTIONS } from "@pixerate/flair/editor";
|
|
185
|
+
import { createEffectToolProvider, createEffectToolRegistry, toolDefinitions, callTool, EFFECT_AGENT_INSTRUCTIONS } from "@pixerate/flair/editor";
|
|
172
186
|
|
|
173
187
|
const tools = createEffectToolProvider(editor, {
|
|
174
188
|
capture: () => renderer.capture({ type: "image/png" }), // adds a `view_effect` tool so the model can look
|
|
@@ -177,7 +191,8 @@ const tools = createEffectToolProvider(editor, {
|
|
|
177
191
|
// Give your model EFFECT_AGENT_INSTRUCTIONS and toolDefinitions(tools), then for each tool call:
|
|
178
192
|
const result = await callTool(tools, call.name, call.input); // MCP tools/call result
|
|
179
193
|
|
|
180
|
-
// Or
|
|
194
|
+
// Or serve them through @pixerate/editor/tools' ToolRegistry (MCP tools/list and tools/call):
|
|
195
|
+
const registry = createEffectToolRegistry(editor); // registry.listTools(), registry.callTool(name, args)
|
|
181
196
|
```
|
|
182
197
|
|
|
183
198
|
The tools are `list_commands`, `run_command` and `run_commands`, plus `view_effect` when you can capture the preview. The commands they run:
|
|
@@ -185,8 +200,8 @@ The tools are `list_commands`, `run_command` and `run_commands`, plus `view_effe
|
|
|
185
200
|
- **Inspect and start:** `effect.describe`, `catalog.search`, `effect.load`.
|
|
186
201
|
- **Edit the effect:** `effect.setSource`.
|
|
187
202
|
- **Values and presets:** `values.set`, `values.reset`, `values.randomize`, `preset.apply` and `preset.save`.
|
|
188
|
-
- **Keyframes:** `keyframe.set`, `keyframe.remove`, `keyframe.setEasing` and `keyframe.clear`.
|
|
189
|
-
- **Timeline:** `timeline.setTiming`, `timeline.play` and `timeline.seek`.
|
|
203
|
+
- **Keyframes:** `keyframe.set`, `keyframe.setTrack`, `keyframe.remove`, `keyframe.setEasing` and `keyframe.clear`.
|
|
204
|
+
- **Timeline:** `timeline.setTiming`, `timeline.play`, `timeline.pause` and `timeline.seek`.
|
|
190
205
|
- **History:** `history.undo` and `history.redo`.
|
|
191
206
|
|
|
192
207
|
Every change is one undo step, and refusals come back as messages the model can act on.
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -1,60 +1,47 @@
|
|
|
1
1
|
import { V as VectorControl, c as EffectPreset, E as EffectDefinition } from '../types-CkKkavvB.js';
|
|
2
2
|
import { C as ControlValue, a as ControlDef, b as ControlType } from '../controls-DdXMbovi.js';
|
|
3
3
|
import { S as SvgMotion } from '../motion-D0TVv2CF.js';
|
|
4
|
+
import { AnimationEasingPreset, AnimationEasing, AnimationTrack, AnimationKeyframe, cubicBezierAt as cubicBezierAt$1, evaluateEasing as evaluateEasing$1 } from '@pixerate/editor/animation';
|
|
5
|
+
export { CubicBezierEasing, SpringEasing } from '@pixerate/editor/animation';
|
|
4
6
|
import { CommandRegistry, CommandSpec } from '@pixerate/editor/commands';
|
|
5
7
|
import { HistoryManager, HistoryState } from '@pixerate/editor/history';
|
|
6
8
|
import { C as CompileError, a as CompileStatus } from '../renderer-Oyynz-fs.js';
|
|
7
9
|
import { C as Clock } from '../clock-BhdXWH-n.js';
|
|
8
|
-
import { AgentToolProvider, McpToolResult, McpToolDefinition } from '@pixerate/editor/
|
|
10
|
+
import { AgentToolProvider, McpToolResult, ToolRegistryOptions, ToolRegistry, McpToolDefinition } from '@pixerate/editor/tools';
|
|
11
|
+
import { ParameterImageOption, ParametersController, ParameterField, ParameterSchema, ParameterValue, ParameterValues } from '@pixerate/editor/parameters';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Keyframed control values. A track holds keyframes `{ time (ms), value,
|
|
12
15
|
* easing }` for one control; the easing governs the segment leaving its
|
|
13
|
-
* keyframe.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* keyframe. Easing comes from `@pixerate/editor/animation`, and tracks keep
|
|
17
|
+
* its `AnimationTrack` shape; Flair adds what control values need beyond
|
|
18
|
+
* numbers and colours (vectors, booleans, images, ints).
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
/** Named easings (CSS keywords plus back and spring curves). */
|
|
19
|
-
type EasingPreset =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
x1: number;
|
|
23
|
-
y1: number;
|
|
24
|
-
x2: number;
|
|
25
|
-
y2: number;
|
|
26
|
-
}
|
|
27
|
-
interface SpringEasing {
|
|
28
|
-
type: "spring";
|
|
29
|
-
/** Default 170. */
|
|
30
|
-
stiffness?: number;
|
|
31
|
-
/** Default 26. */
|
|
32
|
-
damping?: number;
|
|
33
|
-
/** Default 1. */
|
|
34
|
-
mass?: number;
|
|
35
|
-
}
|
|
36
|
-
type Easing = EasingPreset | CubicBezierEasing | SpringEasing;
|
|
22
|
+
type EasingPreset = AnimationEasingPreset;
|
|
23
|
+
type Easing = AnimationEasing;
|
|
24
|
+
|
|
37
25
|
/** Every easing preset, in picker order. */
|
|
38
26
|
declare const EASING_PRESETS: readonly EasingPreset[];
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
/** Eased progress at `u` (0–1) through a segment `durationMs` long (`@pixerate/editor`'s easing engine). */
|
|
28
|
+
declare const evaluateEasing: typeof evaluateEasing$1;
|
|
29
|
+
/** CSS `cubic-bezier(x1, y1, x2, y2)` solved for y at progress x. */
|
|
30
|
+
declare const cubicBezierAt: typeof cubicBezierAt$1;
|
|
31
|
+
/**
|
|
32
|
+
* Whether `value` is an easing (a preset name, a cubic bezier or a spring),
|
|
33
|
+
* with a spring's stiffness, damping and mass above 0 when given.
|
|
34
|
+
*/
|
|
35
|
+
declare function isEasing(value: unknown): value is Easing;
|
|
36
|
+
/** A keyframe of a control: like `AnimationKeyframe`, with any control value (vectors and booleans too). */
|
|
37
|
+
interface Keyframe extends Omit<AnimationKeyframe, "value"> {
|
|
42
38
|
value: ControlValue;
|
|
43
|
-
/** Easing of the segment from this keyframe to the next (default `linear`). */
|
|
44
|
-
easing?: Easing;
|
|
45
39
|
}
|
|
46
|
-
|
|
40
|
+
/** A control's keyframes: `AnimationTrack`'s shape, with control values. */
|
|
41
|
+
interface Track extends Omit<AnimationTrack, "keyframes"> {
|
|
47
42
|
/** Sorted by time, at most one per time. */
|
|
48
43
|
keyframes: Keyframe[];
|
|
49
|
-
/** Disabled tracks keep their keyframes but do not animate (default `true`). */
|
|
50
|
-
enabled?: boolean;
|
|
51
44
|
}
|
|
52
|
-
/** Whether `value` is an easing this module understands. */
|
|
53
|
-
declare function isEasing(value: unknown): value is Easing;
|
|
54
|
-
/** CSS `cubic-bezier(x1, y1, x2, y2)` solved for y at progress x. */
|
|
55
|
-
declare function cubicBezierAt(x1: number, y1: number, x2: number, y2: number, x: number): number;
|
|
56
|
-
/** Eased progress at `u` (0–1) through a segment `durationMs` long. */
|
|
57
|
-
declare function evaluateEasing(easing: Easing | undefined, u: number, durationMs?: number): number;
|
|
58
45
|
/**
|
|
59
46
|
* A control value `t` of the way from `a` to `b`: numbers and vectors blend
|
|
60
47
|
* (overshooting with back and spring easings), colours blend in Oklab, `int`s
|
|
@@ -249,6 +236,19 @@ declare class EffectEditor {
|
|
|
249
236
|
setKeyframeEasing(name: string, time: number, easing: Easing): EditResult;
|
|
250
237
|
/** Removes a control's track; it keeps the value it had at the playhead. */
|
|
251
238
|
clearTrack(name: string): EditResult;
|
|
239
|
+
/**
|
|
240
|
+
* Replaces a control's whole track (or removes it with `undefined`), one
|
|
241
|
+
* undo step. Every keyframe is checked like `setKeyframe`; values may be
|
|
242
|
+
* any form the control accepts (colours as "#RRGGBB" too).
|
|
243
|
+
*/
|
|
244
|
+
setTrack(name: string, track: {
|
|
245
|
+
keyframes: readonly {
|
|
246
|
+
time: number;
|
|
247
|
+
value: unknown;
|
|
248
|
+
easing?: unknown;
|
|
249
|
+
}[];
|
|
250
|
+
enabled?: boolean;
|
|
251
|
+
} | undefined): EditResult;
|
|
252
252
|
setTiming(timing: {
|
|
253
253
|
duration?: number;
|
|
254
254
|
loop?: boolean;
|
|
@@ -345,7 +345,8 @@ declare function effectCommands(): CommandSpec<EffectEditor>[];
|
|
|
345
345
|
/**
|
|
346
346
|
* Agents edit effects through the editor's commands. Flair ships the tools
|
|
347
347
|
* and the instructions; the model, its credentials and the agent loop are
|
|
348
|
-
* the host's
|
|
348
|
+
* the host's. Serve the tools with `@pixerate/editor/tools`' `ToolRegistry`
|
|
349
|
+
* (MCP `tools/list` and `tools/call`), or call them directly with `callTool`.
|
|
349
350
|
*/
|
|
350
351
|
|
|
351
352
|
interface EffectToolProviderOptions {
|
|
@@ -372,12 +373,21 @@ interface EffectToolProviderOptions {
|
|
|
372
373
|
* - `view_effect`, when `capture` is given: an image of the preview.
|
|
373
374
|
*/
|
|
374
375
|
declare function createEffectToolProvider(editor: EffectEditor, options?: EffectToolProviderOptions): AgentToolProvider;
|
|
376
|
+
/**
|
|
377
|
+
* A `ToolRegistry` (from `@pixerate/editor/tools`) serving the effect editor's
|
|
378
|
+
* tools: `listTools()` and `callTool(name, args)` are MCP's `tools/list` and
|
|
379
|
+
* `tools/call`, with arguments checked against each tool's schema. Register
|
|
380
|
+
* more providers on it to give the agent other tools too.
|
|
381
|
+
*/
|
|
382
|
+
declare function createEffectToolRegistry(editor: EffectEditor, options?: EffectToolProviderOptions & {
|
|
383
|
+
registry?: ToolRegistryOptions;
|
|
384
|
+
}): ToolRegistry;
|
|
375
385
|
/** A provider's tool definitions without their handlers, to send to a model. */
|
|
376
386
|
declare function toolDefinitions(provider: AgentToolProvider): McpToolDefinition[];
|
|
377
387
|
/**
|
|
378
|
-
* Runs one of a provider's tools by name
|
|
379
|
-
*
|
|
380
|
-
*
|
|
388
|
+
* Runs one of a provider's tools by name without a registry: the handler's
|
|
389
|
+
* result comes back as an MCP `tools/call` result, and a thrown error as an
|
|
390
|
+
* `isError` result. (A `ToolRegistry` also checks the arguments first.)
|
|
381
391
|
*/
|
|
382
392
|
declare function callTool(provider: AgentToolProvider, name: string, args?: Record<string, unknown>): Promise<McpToolResult>;
|
|
383
393
|
/** A system prompt for agents that author effects with these tools. Add your product's own guidance to it. */
|
|
@@ -410,4 +420,42 @@ declare function bindShaderRenderer(editor: EffectEditor, renderer: ShaderPrevie
|
|
|
410
420
|
*/
|
|
411
421
|
declare function bindSvgView(editor: EffectEditor, container: HTMLElement): () => void;
|
|
412
422
|
|
|
413
|
-
|
|
423
|
+
/**
|
|
424
|
+
* An effect's controls as a `@pixerate/editor/parameters` schema, and a
|
|
425
|
+
* parameters controller kept in step with an `EffectEditor`. With them, any
|
|
426
|
+
* parameters UI (the ready-made React and Svelte panels, or your own) edits
|
|
427
|
+
* an effect: values, image picks and keyframe diamonds go through the effect
|
|
428
|
+
* editor, so its undo history and agent tools see every change.
|
|
429
|
+
*/
|
|
430
|
+
|
|
431
|
+
interface EffectParameterOptions {
|
|
432
|
+
/** Pictures image controls offer to pick from (any URL still works). */
|
|
433
|
+
images?: readonly ParameterImageOption[];
|
|
434
|
+
/** Schema title (default: none). */
|
|
435
|
+
title?: string;
|
|
436
|
+
}
|
|
437
|
+
/** One control as a parameter field. */
|
|
438
|
+
declare function parameterFieldFor(control: ControlDef, options?: EffectParameterOptions): ParameterField;
|
|
439
|
+
/** An effect's controls as a parameters schema. */
|
|
440
|
+
declare function parameterSchemaFor(controls: readonly ControlDef[], options?: EffectParameterOptions): ParameterSchema;
|
|
441
|
+
/** A control value as a parameter value: colours become `#rrggbb` (`#rrggbbaa` for `vec4`). */
|
|
442
|
+
declare function toParameterValue(control: ControlDef, value: ControlValue): ParameterValue;
|
|
443
|
+
/** Control values as parameter values (see {@link toParameterValue}). */
|
|
444
|
+
declare function toParameterValues(controls: readonly ControlDef[], values: Record<string, ControlValue>): ParameterValues;
|
|
445
|
+
interface EffectParametersBinding {
|
|
446
|
+
/** Drive a parameters UI with it (e.g. `<ParametersPanel controller={…} />`). */
|
|
447
|
+
readonly controller: ParametersController;
|
|
448
|
+
/** Stops following the editor. */
|
|
449
|
+
dispose(): void;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* A `ParametersController` for the effect in `editor`, kept in step with it:
|
|
453
|
+
* the schema follows the effect's controls (a recompiled shader updates it),
|
|
454
|
+
* values and the playhead follow the editor, and changes made through the
|
|
455
|
+
* controller (fields, keyframe diamonds, reset, randomize) are applied to the
|
|
456
|
+
* editor, which records the undo steps. Use the editor's undo, not the
|
|
457
|
+
* controller's, and its presets (the controller's own presets are not saved).
|
|
458
|
+
*/
|
|
459
|
+
declare function bindEffectParameters(editor: EffectEditor, options?: EffectParameterOptions): EffectParametersBinding;
|
|
460
|
+
|
|
461
|
+
export { type CompileReport, EASING_PRESETS, EFFECT_AGENT_INSTRUCTIONS, type Easing, type EasingPreset, type EditResult, type EffectDocument, EffectEditor, type EffectEditorOptions, type EffectEditorState, type EffectParameterOptions, type EffectParametersBinding, type EffectSource, type EffectToolProviderOptions, type Keyframe, type ShaderPreview, type Track, bindEffectParameters, bindShaderRenderer, bindSvgView, callTool, createEffectDocument, createEffectEditor, createEffectToolProvider, createEffectToolRegistry, cubicBezierAt, describeEditor, documentControls, documentIssues, effectCommands, evaluateEasing, evaluateTrack, interpolateControlValue, isEasing, keyframeTimes, motionAt, normalizeKeyframes, parameterFieldFor, parameterSchemaFor, parseEffectDocument, toParameterValue, toParameterValues, toolDefinitions, valuesAt };
|
package/dist/editor/index.js
CHANGED
|
@@ -24,85 +24,19 @@ import {
|
|
|
24
24
|
} from "../chunk-J55UBNAT.js";
|
|
25
25
|
|
|
26
26
|
// src/editor/timeline.ts
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"hold"
|
|
37
|
-
];
|
|
38
|
-
var BEZIERS = {
|
|
39
|
-
ease: [0.25, 0.1, 0.25, 1],
|
|
40
|
-
"ease-in": [0.42, 0, 1, 1],
|
|
41
|
-
"ease-out": [0, 0, 0.58, 1],
|
|
42
|
-
"ease-in-out": [0.42, 0, 0.58, 1],
|
|
43
|
-
"ease-in-back": [0.36, 0, 0.66, -0.56],
|
|
44
|
-
"ease-out-back": [0.34, 1.56, 0.64, 1]
|
|
45
|
-
};
|
|
27
|
+
import {
|
|
28
|
+
ANIMATION_EASING_PRESETS,
|
|
29
|
+
cubicBezierAt as editorCubicBezierAt,
|
|
30
|
+
evaluateEasing as evaluateAnimationEasing,
|
|
31
|
+
isAnimationEasing
|
|
32
|
+
} from "@pixerate/editor/animation";
|
|
33
|
+
var EASING_PRESETS = ANIMATION_EASING_PRESETS;
|
|
34
|
+
var evaluateEasing = evaluateAnimationEasing;
|
|
35
|
+
var cubicBezierAt = editorCubicBezierAt;
|
|
46
36
|
function isEasing(value) {
|
|
47
|
-
if (
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
if (e.type === "cubic-bezier") return ["x1", "y1", "x2", "y2"].every((k) => Number.isFinite(e[k]));
|
|
51
|
-
if (e.type === "spring")
|
|
52
|
-
return ["stiffness", "damping", "mass"].every((k) => e[k] === void 0 || typeof e[k] === "number" && e[k] > 0);
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
function cubicBezierAt(x1, y1, x2, y2, x) {
|
|
56
|
-
if (x <= 0) return 0;
|
|
57
|
-
if (x >= 1) return 1;
|
|
58
|
-
x1 = Math.min(1, Math.max(0, x1));
|
|
59
|
-
x2 = Math.min(1, Math.max(0, x2));
|
|
60
|
-
const bx = (t2) => 3 * x1 * t2 * (1 - t2) ** 2 + 3 * x2 * t2 * t2 * (1 - t2) + t2 ** 3;
|
|
61
|
-
const by = (t2) => 3 * y1 * t2 * (1 - t2) ** 2 + 3 * y2 * t2 * t2 * (1 - t2) + t2 ** 3;
|
|
62
|
-
const dx = (t2) => 3 * x1 * (1 - t2) ** 2 + 6 * (x2 - x1) * t2 * (1 - t2) + 3 * (1 - x2) * t2 * t2;
|
|
63
|
-
let t = x;
|
|
64
|
-
for (let i = 0; i < 8; i++) {
|
|
65
|
-
const err = bx(t) - x;
|
|
66
|
-
if (Math.abs(err) < 1e-6) return by(t);
|
|
67
|
-
const d = dx(t);
|
|
68
|
-
if (Math.abs(d) < 1e-6) break;
|
|
69
|
-
t -= err / d;
|
|
70
|
-
}
|
|
71
|
-
let lo = 0;
|
|
72
|
-
let hi = 1;
|
|
73
|
-
t = x;
|
|
74
|
-
for (let i = 0; i < 40; i++) {
|
|
75
|
-
if (bx(t) < x) lo = t;
|
|
76
|
-
else hi = t;
|
|
77
|
-
t = (lo + hi) / 2;
|
|
78
|
-
}
|
|
79
|
-
return by(t);
|
|
80
|
-
}
|
|
81
|
-
function springAt(easing, u, durationMs) {
|
|
82
|
-
const k = easing.stiffness ?? 170;
|
|
83
|
-
const c = easing.damping ?? 26;
|
|
84
|
-
const m = easing.mass ?? 1;
|
|
85
|
-
const w0 = Math.sqrt(k / m);
|
|
86
|
-
const zeta = c / (2 * Math.sqrt(k * m));
|
|
87
|
-
const t = u * Math.max(durationMs / 1e3, 0.6);
|
|
88
|
-
if (zeta < 1) {
|
|
89
|
-
const wd = w0 * Math.sqrt(1 - zeta * zeta);
|
|
90
|
-
return 1 - Math.exp(-zeta * w0 * t) * (Math.cos(wd * t) + zeta * w0 / wd * Math.sin(wd * t));
|
|
91
|
-
}
|
|
92
|
-
return 1 - Math.exp(-w0 * t) * (1 + w0 * t);
|
|
93
|
-
}
|
|
94
|
-
function evaluateEasing(easing, u, durationMs = 1e3) {
|
|
95
|
-
if (u <= 0) return 0;
|
|
96
|
-
if (u >= 1) return 1;
|
|
97
|
-
if (!easing || easing === "linear") return u;
|
|
98
|
-
if (easing === "hold") return 0;
|
|
99
|
-
if (easing === "spring") return springAt({ type: "spring" }, u, durationMs);
|
|
100
|
-
if (typeof easing === "string") {
|
|
101
|
-
const b = BEZIERS[easing];
|
|
102
|
-
return b ? cubicBezierAt(b[0], b[1], b[2], b[3], u) : u;
|
|
103
|
-
}
|
|
104
|
-
if (easing.type === "spring") return springAt(easing, u, durationMs);
|
|
105
|
-
return cubicBezierAt(easing.x1, easing.y1, easing.x2, easing.y2, u);
|
|
37
|
+
if (!isAnimationEasing(value)) return false;
|
|
38
|
+
if (typeof value !== "object" || value.type !== "spring") return true;
|
|
39
|
+
return [value.stiffness, value.damping, value.mass].every((v) => v === void 0 || v > 0);
|
|
106
40
|
}
|
|
107
41
|
function interpolateControlValue(control, a, b, t) {
|
|
108
42
|
if (typeof a === "number" && typeof b === "number") {
|
|
@@ -456,6 +390,29 @@ function effectCommands() {
|
|
|
456
390
|
},
|
|
457
391
|
run: (editor, p) => done(editor.setKeyframeEasing(p.name, p.time, p.easing))
|
|
458
392
|
},
|
|
393
|
+
{
|
|
394
|
+
id: "keyframe.setTrack",
|
|
395
|
+
label: "Set keyframes",
|
|
396
|
+
group: "Keyframes",
|
|
397
|
+
description: "Replaces all of a control's keyframes at once (an empty list removes them). Times are ms; each easing shapes the segment leaving its keyframe.",
|
|
398
|
+
params: {
|
|
399
|
+
type: "object",
|
|
400
|
+
properties: {
|
|
401
|
+
name: { type: "string" },
|
|
402
|
+
keyframes: {
|
|
403
|
+
type: "array",
|
|
404
|
+
items: {
|
|
405
|
+
type: "object",
|
|
406
|
+
properties: { time: { type: "number", minimum: 0 }, value: valueSchema, easing: easingSchema },
|
|
407
|
+
required: ["time", "value"]
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
required: ["name", "keyframes"],
|
|
412
|
+
additionalProperties: false
|
|
413
|
+
},
|
|
414
|
+
run: (editor, p) => done(editor.setTrack(p.name, { keyframes: p.keyframes }))
|
|
415
|
+
},
|
|
459
416
|
{
|
|
460
417
|
id: "keyframe.clear",
|
|
461
418
|
label: "Clear keyframes",
|
|
@@ -757,6 +714,33 @@ var EffectEditor = class {
|
|
|
757
714
|
this.commit("Clear keyframes", { ...this.doc, tracks, values });
|
|
758
715
|
return ok;
|
|
759
716
|
}
|
|
717
|
+
/**
|
|
718
|
+
* Replaces a control's whole track (or removes it with `undefined`), one
|
|
719
|
+
* undo step. Every keyframe is checked like `setKeyframe`; values may be
|
|
720
|
+
* any form the control accepts (colours as "#RRGGBB" too).
|
|
721
|
+
*/
|
|
722
|
+
setTrack(name, track) {
|
|
723
|
+
const control = this.control(name);
|
|
724
|
+
if (!control) return this.unknownControl(name);
|
|
725
|
+
const tracks = { ...this.doc.tracks };
|
|
726
|
+
if (!track || track.keyframes.length === 0) {
|
|
727
|
+
if (!tracks[name]) return ok;
|
|
728
|
+
delete tracks[name];
|
|
729
|
+
this.commit("Clear keyframes", { ...this.doc, tracks });
|
|
730
|
+
return ok;
|
|
731
|
+
}
|
|
732
|
+
const keyframes = [];
|
|
733
|
+
for (const k of track.keyframes) {
|
|
734
|
+
if (!(k.time >= 0)) return fail("A keyframe's time is 0 ms or more.");
|
|
735
|
+
if (k.easing !== void 0 && !isEasing(k.easing)) return fail(`Unknown easing ${JSON.stringify(k.easing)}.`);
|
|
736
|
+
const check = checkControlValue(control, k.value);
|
|
737
|
+
if (!check.ok) return fail(`At ${k.time} ms: ${check.message}`);
|
|
738
|
+
keyframes.push({ time: k.time, value: check.value, ...k.easing !== void 0 && { easing: k.easing } });
|
|
739
|
+
}
|
|
740
|
+
tracks[name] = { keyframes: normalizeKeyframes(keyframes), ...track.enabled === false && { enabled: false } };
|
|
741
|
+
this.commit(`Keyframes ${control.label}`, { ...this.doc, tracks });
|
|
742
|
+
return ok;
|
|
743
|
+
}
|
|
760
744
|
setTiming(timing) {
|
|
761
745
|
if (timing.duration !== void 0 && !(timing.duration > 0)) return fail("The duration must be above 0 ms.");
|
|
762
746
|
this.commit("Set timing", { ...this.doc, duration: timing.duration ?? this.doc.duration, loop: timing.loop ?? this.doc.loop });
|
|
@@ -872,6 +856,10 @@ function createEffectEditor(options = {}) {
|
|
|
872
856
|
|
|
873
857
|
// src/editor/agent.ts
|
|
874
858
|
import { createCommandToolProvider } from "@pixerate/editor/commands";
|
|
859
|
+
import {
|
|
860
|
+
createToolRegistry,
|
|
861
|
+
toToolResult
|
|
862
|
+
} from "@pixerate/editor/tools";
|
|
875
863
|
function createEffectToolProvider(editor, options = {}) {
|
|
876
864
|
const base = createCommandToolProvider(editor.commands, {
|
|
877
865
|
id: options.id ?? "flair-effect",
|
|
@@ -898,6 +886,11 @@ function createEffectToolProvider(editor, options = {}) {
|
|
|
898
886
|
const baseTools = () => typeof base.tools === "function" ? base.tools() : base.tools;
|
|
899
887
|
return { id: base.id, namespace: base.namespace, subscribe: base.subscribe, tools: () => [...baseTools(), ...extra] };
|
|
900
888
|
}
|
|
889
|
+
function createEffectToolRegistry(editor, options = {}) {
|
|
890
|
+
const registry = createToolRegistry({ validateArguments: true, ...options.registry });
|
|
891
|
+
registry.register(createEffectToolProvider(editor, options));
|
|
892
|
+
return registry;
|
|
893
|
+
}
|
|
901
894
|
function toolDefinitions(provider) {
|
|
902
895
|
const tools = typeof provider.tools === "function" ? provider.tools() : provider.tools;
|
|
903
896
|
return tools.map(({ handler: _handler, ...definition }) => definition);
|
|
@@ -907,11 +900,7 @@ async function callTool(provider, name, args = {}) {
|
|
|
907
900
|
const tool = tools.find((t) => t.name === name);
|
|
908
901
|
if (!tool) return { content: [{ type: "text", text: `There is no tool "${name}".` }], isError: true };
|
|
909
902
|
try {
|
|
910
|
-
|
|
911
|
-
if (result && typeof result === "object" && Array.isArray(result.content)) return result;
|
|
912
|
-
if (result === void 0) return { content: [] };
|
|
913
|
-
if (typeof result === "string") return { content: [{ type: "text", text: result }] };
|
|
914
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }], structuredContent: result };
|
|
903
|
+
return toToolResult(await tool.handler(args, { name, providerId: provider.id }));
|
|
915
904
|
} catch (e) {
|
|
916
905
|
return { content: [{ type: "text", text: e.message }], isError: true };
|
|
917
906
|
}
|
|
@@ -980,16 +969,112 @@ function bindSvgView(editor, container) {
|
|
|
980
969
|
view?.element.remove();
|
|
981
970
|
};
|
|
982
971
|
}
|
|
972
|
+
|
|
973
|
+
// src/editor/parameters.ts
|
|
974
|
+
import {
|
|
975
|
+
ParametersController,
|
|
976
|
+
rgba01ToHex
|
|
977
|
+
} from "@pixerate/editor/parameters";
|
|
978
|
+
function parameterFieldFor(control, options = {}) {
|
|
979
|
+
const base = { id: control.name, label: control.label };
|
|
980
|
+
switch (control.type) {
|
|
981
|
+
case "bool":
|
|
982
|
+
return { ...base, type: "boolean", default: control.default === true };
|
|
983
|
+
case "int":
|
|
984
|
+
return { ...base, type: "int", min: control.min, max: control.max, step: Math.max(1, Math.round(control.step)), default: Number(control.default) };
|
|
985
|
+
case "float":
|
|
986
|
+
return { ...base, type: "number", min: control.min, max: control.max, step: control.step, default: Number(control.default) };
|
|
987
|
+
case "sampler2D":
|
|
988
|
+
return { ...base, type: "image", default: String(control.default), ...options.images && { options: options.images } };
|
|
989
|
+
default: {
|
|
990
|
+
const value = control.default;
|
|
991
|
+
if (control.color) {
|
|
992
|
+
const alpha = control.type === "vec4";
|
|
993
|
+
return { ...base, type: "color", alpha, default: rgba01ToHex(value, alpha) };
|
|
994
|
+
}
|
|
995
|
+
return { ...base, type: "vector", size: value.length, min: control.min, max: control.max, step: control.step, default: [...value] };
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function parameterSchemaFor(controls, options = {}) {
|
|
1000
|
+
return { ...options.title && { title: options.title }, fields: controls.map((c) => parameterFieldFor(c, options)) };
|
|
1001
|
+
}
|
|
1002
|
+
function toParameterValue(control, value) {
|
|
1003
|
+
if (control.color && Array.isArray(value)) return rgba01ToHex(value, control.type === "vec4");
|
|
1004
|
+
return Array.isArray(value) ? [...value] : value;
|
|
1005
|
+
}
|
|
1006
|
+
function toParameterValues(controls, values) {
|
|
1007
|
+
const out = {};
|
|
1008
|
+
for (const control of controls) if (values[control.name] !== void 0) out[control.name] = toParameterValue(control, values[control.name]);
|
|
1009
|
+
return out;
|
|
1010
|
+
}
|
|
1011
|
+
function toParameterTrack(control, track) {
|
|
1012
|
+
if (!track) return void 0;
|
|
1013
|
+
return {
|
|
1014
|
+
...track.enabled === false && { enabled: false },
|
|
1015
|
+
keyframes: track.keyframes.map((k) => ({ ...k, value: control ? toParameterValue(control, k.value) : k.value }))
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
function bindEffectParameters(editor, options = {}) {
|
|
1019
|
+
let state = editor.getState();
|
|
1020
|
+
let controls = state.controls;
|
|
1021
|
+
let signature = JSON.stringify(controls);
|
|
1022
|
+
let syncing = false;
|
|
1023
|
+
const byName = () => new Map(controls.map((c) => [c.name, c]));
|
|
1024
|
+
const keyframes = {
|
|
1025
|
+
getTrack: (id) => toParameterTrack(byName().get(id), editor.getState().document.tracks[id]),
|
|
1026
|
+
setTrack: (id, track) => {
|
|
1027
|
+
editor.setTrack(id, track);
|
|
1028
|
+
},
|
|
1029
|
+
subscribe: (listener) => editor.subscribe(() => listener())
|
|
1030
|
+
};
|
|
1031
|
+
const controller = new ParametersController({
|
|
1032
|
+
schema: parameterSchemaFor(controls, options),
|
|
1033
|
+
values: toParameterValues(controls, state.values),
|
|
1034
|
+
time: state.time,
|
|
1035
|
+
keyframes,
|
|
1036
|
+
onChange: (values, changed) => {
|
|
1037
|
+
if (syncing) return;
|
|
1038
|
+
const result = changed.length === 1 ? editor.setValue(changed[0], values[changed[0]]) : editor.setValues(Object.fromEntries(changed.map((id) => [id, values[id]])));
|
|
1039
|
+
if (!result.ok) sync(editor.getState());
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
const sync = (next) => {
|
|
1043
|
+
state = next;
|
|
1044
|
+
const nextSignature = JSON.stringify(next.controls);
|
|
1045
|
+
syncing = true;
|
|
1046
|
+
try {
|
|
1047
|
+
if (nextSignature !== signature) {
|
|
1048
|
+
signature = nextSignature;
|
|
1049
|
+
controls = next.controls;
|
|
1050
|
+
controller.setSchema(parameterSchemaFor(controls, options));
|
|
1051
|
+
}
|
|
1052
|
+
controller.setTime(next.time, toParameterValues(controls, next.values));
|
|
1053
|
+
} finally {
|
|
1054
|
+
syncing = false;
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
const unsubscribe = editor.subscribe(sync);
|
|
1058
|
+
return {
|
|
1059
|
+
controller,
|
|
1060
|
+
dispose: () => {
|
|
1061
|
+
unsubscribe();
|
|
1062
|
+
controller.destroy();
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
983
1066
|
export {
|
|
984
1067
|
EASING_PRESETS,
|
|
985
1068
|
EFFECT_AGENT_INSTRUCTIONS,
|
|
986
1069
|
EffectEditor,
|
|
1070
|
+
bindEffectParameters,
|
|
987
1071
|
bindShaderRenderer,
|
|
988
1072
|
bindSvgView,
|
|
989
1073
|
callTool,
|
|
990
1074
|
createEffectDocument,
|
|
991
1075
|
createEffectEditor,
|
|
992
1076
|
createEffectToolProvider,
|
|
1077
|
+
createEffectToolRegistry,
|
|
993
1078
|
cubicBezierAt,
|
|
994
1079
|
describeEditor,
|
|
995
1080
|
documentControls,
|
|
@@ -1002,7 +1087,11 @@ export {
|
|
|
1002
1087
|
keyframeTimes,
|
|
1003
1088
|
motionAt,
|
|
1004
1089
|
normalizeKeyframes,
|
|
1090
|
+
parameterFieldFor,
|
|
1091
|
+
parameterSchemaFor,
|
|
1005
1092
|
parseEffectDocument,
|
|
1093
|
+
toParameterValue,
|
|
1094
|
+
toParameterValues,
|
|
1006
1095
|
toolDefinitions,
|
|
1007
1096
|
valuesAt
|
|
1008
1097
|
};
|
package/dist/editor/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/editor/timeline.ts","../../src/editor/document.ts","../../src/editor/editor.ts","../../src/editor/commands.ts","../../src/editor/agent.ts","../../src/editor/bind.ts"],"sourcesContent":["/**\n * Keyframed control values. A track holds keyframes `{ time (ms), value,\n * easing }` for one control; the easing governs the segment leaving its\n * keyframe. The shapes and easing names match `@pixerate/editor`'s keyframe\n * animation (`AnimationTrack`, `AnimationEasing`), so timelines move between\n * the two unchanged.\n */\n\nimport type { ControlDef, ControlValue } from \"../shader/controls.js\";\nimport { interpolateOklab, type ColorRGB } from \"../shader/color.js\";\n\n/** Named easings (CSS keywords plus back and spring curves). */\nexport type EasingPreset =\n | \"linear\"\n | \"hold\"\n | \"ease\"\n | \"ease-in\"\n | \"ease-out\"\n | \"ease-in-out\"\n | \"ease-in-back\"\n | \"ease-out-back\"\n | \"spring\";\n\nexport interface CubicBezierEasing {\n type: \"cubic-bezier\";\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n}\n\nexport interface SpringEasing {\n type: \"spring\";\n /** Default 170. */\n stiffness?: number;\n /** Default 26. */\n damping?: number;\n /** Default 1. */\n mass?: number;\n}\n\nexport type Easing = EasingPreset | CubicBezierEasing | SpringEasing;\n\n/** Every easing preset, in picker order. */\nexport const EASING_PRESETS: readonly EasingPreset[] = [\n \"linear\",\n \"ease\",\n \"ease-in\",\n \"ease-out\",\n \"ease-in-out\",\n \"ease-in-back\",\n \"ease-out-back\",\n \"spring\",\n \"hold\",\n];\n\nconst BEZIERS: Record<string, readonly [number, number, number, number]> = {\n ease: [0.25, 0.1, 0.25, 1],\n \"ease-in\": [0.42, 0, 1, 1],\n \"ease-out\": [0, 0, 0.58, 1],\n \"ease-in-out\": [0.42, 0, 0.58, 1],\n \"ease-in-back\": [0.36, 0, 0.66, -0.56],\n \"ease-out-back\": [0.34, 1.56, 0.64, 1],\n};\n\nexport interface Keyframe {\n /** Ms from the start of the timeline. */\n time: number;\n value: ControlValue;\n /** Easing of the segment from this keyframe to the next (default `linear`). */\n easing?: Easing;\n}\n\nexport interface Track {\n /** Sorted by time, at most one per time. */\n keyframes: Keyframe[];\n /** Disabled tracks keep their keyframes but do not animate (default `true`). */\n enabled?: boolean;\n}\n\n/** Whether `value` is an easing this module understands. */\nexport function isEasing(value: unknown): value is Easing {\n if (typeof value === \"string\") return (EASING_PRESETS as readonly string[]).includes(value);\n if (!value || typeof value !== \"object\") return false;\n const e = value as Record<string, unknown>;\n if (e.type === \"cubic-bezier\") return [\"x1\", \"y1\", \"x2\", \"y2\"].every((k) => Number.isFinite(e[k]));\n if (e.type === \"spring\")\n return [\"stiffness\", \"damping\", \"mass\"].every((k) => e[k] === undefined || (typeof e[k] === \"number\" && e[k] > 0));\n return false;\n}\n\n/** CSS `cubic-bezier(x1, y1, x2, y2)` solved for y at progress x. */\nexport function cubicBezierAt(x1: number, y1: number, x2: number, y2: number, x: number): number {\n if (x <= 0) return 0;\n if (x >= 1) return 1;\n x1 = Math.min(1, Math.max(0, x1));\n x2 = Math.min(1, Math.max(0, x2));\n const bx = (t: number) => 3 * x1 * t * (1 - t) ** 2 + 3 * x2 * t * t * (1 - t) + t ** 3;\n const by = (t: number) => 3 * y1 * t * (1 - t) ** 2 + 3 * y2 * t * t * (1 - t) + t ** 3;\n const dx = (t: number) => 3 * x1 * (1 - t) ** 2 + 6 * (x2 - x1) * t * (1 - t) + 3 * (1 - x2) * t * t;\n let t = x;\n for (let i = 0; i < 8; i++) {\n const err = bx(t) - x;\n if (Math.abs(err) < 1e-6) return by(t);\n const d = dx(t);\n if (Math.abs(d) < 1e-6) break;\n t -= err / d;\n }\n let lo = 0;\n let hi = 1;\n t = x;\n for (let i = 0; i < 40; i++) {\n if (bx(t) < x) lo = t;\n else hi = t;\n t = (lo + hi) / 2;\n }\n return by(t);\n}\n\n/** A damped spring from 0 to 1 over `durationMs`, settling at 1. */\nfunction springAt(easing: SpringEasing, u: number, durationMs: number): number {\n const k = easing.stiffness ?? 170;\n const c = easing.damping ?? 26;\n const m = easing.mass ?? 1;\n const w0 = Math.sqrt(k / m);\n const zeta = c / (2 * Math.sqrt(k * m));\n // Run long enough to settle, scaled into the segment.\n const t = u * Math.max(durationMs / 1000, 0.6);\n if (zeta < 1) {\n const wd = w0 * Math.sqrt(1 - zeta * zeta);\n return 1 - Math.exp(-zeta * w0 * t) * (Math.cos(wd * t) + ((zeta * w0) / wd) * Math.sin(wd * t));\n }\n return 1 - Math.exp(-w0 * t) * (1 + w0 * t);\n}\n\n/** Eased progress at `u` (0–1) through a segment `durationMs` long. */\nexport function evaluateEasing(easing: Easing | undefined, u: number, durationMs = 1000): number {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n if (!easing || easing === \"linear\") return u;\n if (easing === \"hold\") return 0;\n if (easing === \"spring\") return springAt({ type: \"spring\" }, u, durationMs);\n if (typeof easing === \"string\") {\n const b = BEZIERS[easing];\n return b ? cubicBezierAt(b[0], b[1], b[2], b[3], u) : u;\n }\n if (easing.type === \"spring\") return springAt(easing, u, durationMs);\n return cubicBezierAt(easing.x1, easing.y1, easing.x2, easing.y2, u);\n}\n\n/**\n * A control value `t` of the way from `a` to `b`: numbers and vectors blend\n * (overshooting with back and spring easings), colours blend in Oklab, `int`s\n * round, and booleans and images switch halfway.\n */\nexport function interpolateControlValue(control: ControlDef | undefined, a: ControlValue, b: ControlValue, t: number): ControlValue {\n if (typeof a === \"number\" && typeof b === \"number\") {\n const v = a + (b - a) * t;\n return control?.type === \"int\" ? Math.round(v) : v;\n }\n if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {\n if (control?.color && a.length >= 3) {\n const rgb = interpolateOklab(a.slice(0, 3) as ColorRGB, b.slice(0, 3) as ColorRGB, t);\n const out = rgb.map((c) => Math.min(1, Math.max(0, c)));\n return a.length === 4 ? [...out, a[3]! + (b[3]! - a[3]!) * t] : out;\n }\n return a.map((v, i) => v + (b[i]! - v) * t);\n }\n return t < 0.5 ? a : b;\n}\n\n/** A track's value at `time` (ms), or undefined when it has no keyframes or is disabled. */\nexport function evaluateTrack(track: Track | undefined, time: number, control?: ControlDef): ControlValue | undefined {\n if (!track || track.enabled === false || track.keyframes.length === 0) return undefined;\n const keys = track.keyframes;\n if (time <= keys[0]!.time) return keys[0]!.value;\n const last = keys[keys.length - 1]!;\n if (time >= last.time) return last.value;\n let i = 0;\n while (i < keys.length - 2 && keys[i + 1]!.time <= time) i++;\n const from = keys[i]!;\n const to = keys[i + 1]!;\n const span = to.time - from.time;\n const u = span > 0 ? (time - from.time) / span : 1;\n return interpolateControlValue(control, from.value, to.value, evaluateEasing(from.easing, u, span));\n}\n\n/** Keyframes sorted by time, one per time (the later of two at the same time wins). */\nexport function normalizeKeyframes(keyframes: readonly Keyframe[]): Keyframe[] {\n const byTime = new Map<number, Keyframe>();\n for (const k of keyframes) byTime.set(Math.round(k.time * 1000) / 1000, { ...k, time: Math.round(k.time * 1000) / 1000 });\n return [...byTime.values()].sort((a, b) => a.time - b.time);\n}\n\n/** The keyframe times across tracks, sorted and without duplicates. */\nexport function keyframeTimes(tracks: Record<string, Track>): number[] {\n return [...new Set(Object.values(tracks).flatMap((t) => t.keyframes.map((k) => k.time)))].sort((a, b) => a - b);\n}\n","/**\n * The document an effect editor edits: an effect (a shader's source, or a\n * vector picture with its controls), the values picked for its controls,\n * keyframed tracks, presets and a timeline. Plain JSON, so it can be saved,\n * diffed, and written by an agent.\n */\n\nimport { getEffect, renderVectorEffect, vectorControlDef } from \"../catalog/index.js\";\nimport type { EffectDefinition, EffectPreset, VectorControl } from \"../catalog/types.js\";\nimport { checkControlValue, controlValue, parseControls, type ControlDef, type ControlValue } from \"../shader/controls.js\";\nimport { svgMotionIssues, type SvgMotion } from \"../vector/motion.js\";\nimport { evaluateTrack, isEasing, normalizeKeyframes, type Track } from \"./timeline.js\";\n\n/** What is being edited: a fragment shader, or an animated SVG with declared controls. */\nexport type EffectSource =\n | { kind: \"shader\"; source: string }\n | { kind: \"vector\"; motion: SvgMotion; controls: VectorControl[]; hold?: number };\n\nexport interface EffectDocument {\n /** Format version (1). */\n version: 1;\n name: string;\n /** The catalog effect it started from, if any. */\n basedOn?: string;\n effect: EffectSource;\n /** Picked control values; controls without one use their default. */\n values: Record<string, ControlValue>;\n /** Keyframed controls, by control name. A track overrides the control's value. */\n tracks: Record<string, Track>;\n /** Timeline length in ms. */\n duration: number;\n loop: boolean;\n presets: EffectPreset[];\n}\n\nconst DEFAULT_SHADER = `// A new effect: edit me.\nuniform vec3 uColorA; // @control color default=#6366F1 label=\"Colour A\"\nuniform vec3 uColorB; // @control color default=#EC4899 label=\"Colour B\"\nuniform float uSpeed; // @control min=0 max=4 default=1 label=\"Speed\"\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float t = 0.5 + 0.5 * sin(uv.x * 3.0 + iTime * uSpeed);\n fragColor = vec4(mix(uColorA, uColorB, t), 1.0);\n}\n`;\n\n/** Ms of timeline a new document gets (the effect's loop when it has one). */\nconst DEFAULT_DURATION = 5000;\n\n/**\n * A new document: blank (a small starter shader), from a catalog effect (by\n * id or definition), or from a shader's source.\n */\nexport function createEffectDocument(from?: string | EffectDefinition | { source: string; name?: string }): EffectDocument {\n if (from === undefined) return blankDocument(\"Untitled effect\", DEFAULT_SHADER);\n if (typeof from === \"object\" && !(\"kind\" in from)) return blankDocument(from.name ?? \"Untitled effect\", from.source);\n const effect = typeof from === \"string\" ? getEffect(from) : from;\n if (!effect) throw new Error(`There is no effect \"${String(from)}\" in the catalog.`);\n return {\n version: 1,\n name: effect.name,\n basedOn: effect.id,\n effect:\n effect.kind === \"shader\"\n ? { kind: \"shader\", source: effect.source }\n : { kind: \"vector\", motion: structuredClone(effect.motion), controls: effect.controls.map((c) => ({ ...c })), hold: effect.hold },\n values: {},\n tracks: {},\n duration: effect.kind === \"shader\" && effect.loop ? Math.round(effect.loop * 1000) : DEFAULT_DURATION,\n loop: true,\n presets: (effect.presets ?? []).map((p) => ({ name: p.name, values: { ...p.values } })),\n };\n}\n\nfunction blankDocument(name: string, source: string): EffectDocument {\n return { version: 1, name, effect: { kind: \"shader\", source }, values: {}, tracks: {}, duration: DEFAULT_DURATION, loop: true, presets: [] };\n}\n\n/** The document's controls. */\nexport function documentControls(doc: EffectDocument): ControlDef[] {\n if (doc.effect.kind === \"shader\") return parseControls(doc.effect.source);\n return doc.effect.controls.map((c, i) => vectorControlDef(c, i + 1));\n}\n\n/** Every control's value at `time` (ms): its track if it has one, else the picked value, else its default. */\nexport function valuesAt(doc: EffectDocument, time: number, controls = documentControls(doc)): Record<string, ControlValue> {\n const out: Record<string, ControlValue> = {};\n for (const control of controls) {\n const tracked = evaluateTrack(doc.tracks[control.name], time, control);\n out[control.name] = tracked ?? controlValue(control, doc.values);\n }\n return out;\n}\n\n/** A vector document's picture with the values at `time` filled in; undefined for shaders. */\nexport function motionAt(doc: EffectDocument, time: number): SvgMotion | undefined {\n if (doc.effect.kind !== \"vector\") return undefined;\n // Derived placeholders are code, so they come from the catalog effect the document started from.\n const base = doc.basedOn ? getEffect(doc.basedOn) : undefined;\n const derived = base?.kind === \"vector\" ? base.derived : undefined;\n return renderVectorEffect({ motion: doc.effect.motion, controls: doc.effect.controls, derived }, valuesAt(doc, time));\n}\n\n/**\n * Problems with a document, as sentences: unknown controls in values, tracks\n * or presets, values a control cannot take, bad keyframes, and (for vector\n * effects) problems with the picture. Shader compile errors come from the\n * GPU, not from here.\n */\nexport function documentIssues(doc: EffectDocument): string[] {\n const issues: string[] = [];\n if (!(doc.duration > 0)) issues.push(\"`duration` must be above 0 ms\");\n if (doc.effect.kind === \"shader\" && !/void\\s+mainImage\\s*\\(/.test(doc.effect.source))\n issues.push(\"the shader needs `void mainImage(out vec4 fragColor, in vec2 fragCoord)`\");\n const controls = new Map(documentControls(doc).map((c) => [c.name, c]));\n const check = (where: string, name: string, value: unknown) => {\n const control = controls.get(name);\n if (!control) return void issues.push(`${where}: there is no control \"${name}\"`);\n const result = checkControlValue(control, value);\n if (!result.ok) issues.push(`${where}: ${result.message}`);\n };\n for (const [name, value] of Object.entries(doc.values)) check(\"values\", name, value);\n for (const [name, track] of Object.entries(doc.tracks)) {\n track.keyframes.forEach((k, i) => {\n const where = `tracks.${name}[${i}]`;\n if (!(k.time >= 0)) issues.push(`${where}: \\`time\\` must be 0 or more`);\n if (k.easing !== undefined && !isEasing(k.easing)) issues.push(`${where}: unknown easing`);\n check(where, name, k.value);\n });\n }\n for (const preset of doc.presets) for (const [name, value] of Object.entries(preset.values)) check(`preset \"${preset.name}\"`, name, value);\n if (doc.effect.kind === \"vector\") {\n const motion = motionAt(doc, 0)!;\n issues.push(...svgMotionIssues(motion));\n if (/\\{\\{[^}]+\\}\\}/.test(JSON.stringify(motion))) issues.push(\"the picture has placeholders no control fills\");\n }\n return issues;\n}\n\n/** Reads a document from JSON (or a parsed object), checking its shape. */\nexport function parseEffectDocument(input: string | unknown): { ok: true; document: EffectDocument } | { ok: false; errors: string[] } {\n let raw: unknown = input;\n if (typeof input === \"string\") {\n try {\n raw = JSON.parse(input);\n } catch (e) {\n return { ok: false, errors: [`not JSON: ${(e as Error).message}`] };\n }\n }\n const errors: string[] = [];\n const o = (raw ?? {}) as Partial<EffectDocument> & Record<string, unknown>;\n if (o.version !== 1) errors.push(\"`version` must be 1\");\n if (typeof o.name !== \"string\") errors.push(\"`name` must be a string\");\n const effect = o.effect as Partial<EffectSource> | undefined;\n if (!effect || (effect.kind !== \"shader\" && effect.kind !== \"vector\")) errors.push('`effect.kind` must be \"shader\" or \"vector\"');\n else if (effect.kind === \"shader\" && typeof (effect as { source?: unknown }).source !== \"string\") errors.push(\"`effect.source` must be a string\");\n else if (effect.kind === \"vector\") {\n const v = effect as Partial<Extract<EffectSource, { kind: \"vector\" }>>;\n if (!v.motion || typeof v.motion.svg !== \"string\") errors.push(\"`effect.motion.svg` must be a string\");\n if (!Array.isArray(v.controls)) errors.push(\"`effect.controls` must be a list\");\n }\n const isRecord = (v: unknown) => !!v && typeof v === \"object\" && !Array.isArray(v);\n if (o.values !== undefined && !isRecord(o.values)) errors.push(\"`values` must be an object\");\n if (o.tracks !== undefined && !isRecord(o.tracks)) errors.push(\"`tracks` must be an object\");\n for (const [name, track] of Object.entries((isRecord(o.tracks) ? o.tracks : {}) as Record<string, Track>))\n if (!track || !Array.isArray(track.keyframes)) errors.push(`\\`tracks.${name}.keyframes\\` must be a list`);\n if (o.presets !== undefined && !Array.isArray(o.presets)) errors.push(\"`presets` must be a list\");\n if (errors.length) return { ok: false, errors };\n const tracks = Object.fromEntries(\n Object.entries((o.tracks ?? {}) as Record<string, Track>).map(([k, t]) => [k, { ...t, keyframes: normalizeKeyframes(t.keyframes) }]),\n );\n return {\n ok: true,\n document: {\n version: 1,\n name: o.name!,\n basedOn: typeof o.basedOn === \"string\" ? o.basedOn : undefined,\n effect: o.effect as EffectSource,\n values: (o.values ?? {}) as Record<string, ControlValue>,\n tracks,\n duration: typeof o.duration === \"number\" && o.duration > 0 ? o.duration : DEFAULT_DURATION,\n loop: o.loop !== false,\n presets: (o.presets ?? []) as EffectPreset[],\n },\n };\n}\n","/**\n * A UI-agnostic effect editor: the document, its undo history, a playhead,\n * and commands that menus, shortcuts, a palette and agents all run by id.\n * Bring your own UI (any framework, or none) and your own renderer binding\n * (see `bindShaderRenderer` and `bindSvgView`).\n */\n\nimport { createCommandRegistry, createHistoryCommands, type CommandRegistry } from \"@pixerate/editor/commands\";\nimport { createHistoryManager, HistoryManager, type HistoryState } from \"@pixerate/editor/history\";\nimport type { EffectDefinition } from \"../catalog/types.js\";\nimport type { CompileError } from \"../shader/program.js\";\nimport { checkControlValue, type ControlDef, type ControlValue } from \"../shader/controls.js\";\nimport { realClock, type Clock } from \"../time/clock.js\";\nimport type { SvgMotion } from \"../vector/motion.js\";\nimport { effectCommands } from \"./commands.js\";\nimport {\n createEffectDocument,\n documentControls,\n documentIssues,\n parseEffectDocument,\n valuesAt,\n type EffectDocument,\n} from \"./document.js\";\nimport { evaluateTrack, isEasing, normalizeKeyframes, type Easing, type Keyframe } from \"./timeline.js\";\n\n/** What an edit did: `ok`, or why it was refused (in a sentence an agent can act on). */\nexport type EditResult = { ok: true } | { ok: false; message: string };\n\n/** The result of compiling the shader, as reported by a renderer (see `reportCompile`). */\nexport interface CompileReport {\n ok: boolean;\n errors: CompileError[];\n}\n\nexport interface EffectEditorState {\n document: EffectDocument;\n controls: ControlDef[];\n /** Every control's value at the playhead. */\n values: Record<string, ControlValue>;\n /** Problems with the document (see `documentIssues`). */\n issues: string[];\n /** The latest compile report, or null before a renderer reported one (and for vector effects). */\n compile: CompileReport | null;\n /** Playhead, ms. */\n time: number;\n playing: boolean;\n /** Whether changing a value keyframes it at the playhead. */\n recording: boolean;\n history: HistoryState;\n}\n\nexport interface EffectEditorOptions {\n /** The document to edit, or what to start one from (see `createEffectDocument`). */\n document?: EffectDocument;\n from?: string | EffectDefinition | { source: string; name?: string };\n /** Undo history to record into (default: a new one, 100 steps deep). */\n history?: HistoryManager;\n /** Time source for playback (default: animation frames). */\n clock?: Clock;\n /** Ms within which value changes to one control merge into one undo step (default 400). */\n mergeWindowMs?: number;\n}\n\nconst ok: EditResult = { ok: true };\nconst fail = (message: string): EditResult => ({ ok: false, message });\n\n/**\n * Edits one effect. Every change to the document is an undo step (value\n * changes in quick succession, like a slider drag, merge into one), and\n * subscribers hear about every change to the state.\n */\nexport class EffectEditor {\n readonly history: HistoryManager;\n /** Every editing action, by id: for menus, shortcuts, a command palette and agents. */\n readonly commands: CommandRegistry<EffectEditor>;\n private doc: EffectDocument;\n private controlsCache: { doc: EffectDocument; controls: ControlDef[]; issues: string[] } | null = null;\n private compileReport: CompileReport | null = null;\n private time = 0;\n private playing = false;\n private recording = false;\n private readonly clock: Clock;\n private readonly mergeWindowMs: number;\n private readonly listeners = new Set<(state: EffectEditorState) => void>();\n private state: EffectEditorState | null = null;\n private readonly unsubscribeHistory: () => void;\n private disposed = false;\n\n constructor(options: EffectEditorOptions = {}) {\n this.doc = options.document ?? createEffectDocument(options.from);\n this.history = options.history ?? createHistoryManager({ maxDepth: 100 });\n this.clock = options.clock ?? realClock;\n this.mergeWindowMs = options.mergeWindowMs ?? 400;\n this.commands = createCommandRegistry<EffectEditor>({ context: this });\n this.commands.register([...createHistoryCommands(this.history), ...effectCommands()]);\n this.unsubscribeHistory = this.history.subscribe(() => this.changed());\n }\n\n // ── Reading ────────────────────────────────────────────────────────\n\n get document(): EffectDocument {\n return this.doc;\n }\n\n /** The current state; the same object until something changes (safe for `useSyncExternalStore`). */\n getState(): EffectEditorState {\n if (!this.state) {\n const { controls, issues } = this.analysis();\n this.state = {\n document: this.doc,\n controls,\n values: valuesAt(this.doc, this.time, controls),\n issues,\n compile: this.doc.effect.kind === \"shader\" ? this.compileReport : null,\n time: this.time,\n playing: this.playing,\n recording: this.recording,\n history: this.history.getState(),\n };\n }\n return this.state;\n }\n\n /** Calls `listener` with the new state after every change; returns an unsubscribe function. */\n subscribe(listener: (state: EffectEditorState) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** The document's controls. */\n get controls(): ControlDef[] {\n return this.analysis().controls;\n }\n\n /** The document as JSON. */\n toJSON(): EffectDocument {\n return this.doc;\n }\n\n // ── Whole documents ─────────────────────────────────────────────────\n\n /** Replaces the document (one undo step). */\n replaceDocument(next: EffectDocument, label = \"Replace effect\"): EditResult {\n const parsed = parseEffectDocument(next);\n if (!parsed.ok) return fail(parsed.errors.join(\"; \"));\n this.commit(label, parsed.document);\n return ok;\n }\n\n /** Starts over from a catalog effect, a shader source, or blank (one undo step). */\n load(from?: string | EffectDefinition | { source: string; name?: string }, preset?: string): EditResult {\n let next: EffectDocument;\n try {\n next = createEffectDocument(from);\n } catch (e) {\n return fail((e as Error).message);\n }\n if (preset) {\n const values = next.presets.find((p) => p.name === preset)?.values;\n if (!values) return fail(`\"${next.name}\" has no preset \"${preset}\"; it has ${next.presets.map((p) => `\"${p.name}\"`).join(\", \") || \"none\"}.`);\n next = { ...next, values: { ...values } };\n }\n this.compileReport = null;\n this.commit(`Load ${next.name}`, next);\n return ok;\n }\n\n rename(name: string): EditResult {\n if (!name.trim()) return fail(\"A name cannot be empty.\");\n this.commit(\"Rename\", { ...this.doc, name: name.trim() });\n return ok;\n }\n\n // ── The effect itself ───────────────────────────────────────────────\n\n /** Replaces a shader's source. Compile errors arrive later, from the renderer (see `reportCompile`). */\n setSource(source: string): EditResult {\n if (this.doc.effect.kind !== \"shader\") return fail(\"This is a vector effect; use setMotion.\");\n if (source === this.doc.effect.source) return ok;\n this.commit(\"Edit source\", { ...this.doc, effect: { kind: \"shader\", source } }, \"source\");\n return ok;\n }\n\n /** Replaces a vector effect's picture (and optionally its controls). */\n setMotion(motion: SvgMotion, controls?: Extract<EffectDocument[\"effect\"], { kind: \"vector\" }>[\"controls\"]): EditResult {\n if (this.doc.effect.kind !== \"vector\") return fail(\"This is a shader effect; use setSource.\");\n this.commit(\"Edit picture\", { ...this.doc, effect: { ...this.doc.effect, motion, controls: controls ?? this.doc.effect.controls } });\n return ok;\n }\n\n /** Called by a renderer binding with the result of compiling the current source. */\n reportCompile(report: CompileReport): void {\n this.compileReport = { ok: report.ok, errors: [...report.errors] };\n this.changed();\n }\n\n // ── Values ──────────────────────────────────────────────────────────\n\n /**\n * Sets a control's value. With a track on that control (or while\n * recording) it sets a keyframe at the playhead instead. Quick successive\n * changes to one control merge into one undo step.\n */\n setValue(name: string, value: unknown): EditResult {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n const check = checkControlValue(control, value);\n if (!check.ok) return fail(check.message);\n if (this.recording || this.doc.tracks[name]) return this.setKeyframe(name, this.time, check.value, undefined, `value:${name}`);\n this.commit(`Set ${control.label}`, { ...this.doc, values: { ...this.doc.values, [name]: check.value } }, `value:${name}`);\n return ok;\n }\n\n /** Sets several values in one undo step; refuses them all if any is wrong. */\n setValues(values: Record<string, unknown>): EditResult {\n const next = { ...this.doc.values };\n const tracks = { ...this.doc.tracks };\n for (const [name, value] of Object.entries(values)) {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n const check = checkControlValue(control, value);\n if (!check.ok) return fail(check.message);\n if (this.recording || tracks[name]) {\n const keyframes = [...(tracks[name]?.keyframes ?? []), { time: this.time, value: check.value }];\n tracks[name] = { ...tracks[name], keyframes: normalizeKeyframes(keyframes) };\n } else next[name] = check.value;\n }\n this.commit(\"Set values\", { ...this.doc, values: next, tracks });\n return ok;\n }\n\n /** Back to defaults: the named controls, or all of them (values only; tracks stay). */\n resetValues(names?: readonly string[]): EditResult {\n const unknown = names?.find((n) => !this.control(n));\n if (unknown) return this.unknownControl(unknown);\n const values = names ? Object.fromEntries(Object.entries(this.doc.values).filter(([k]) => !names.includes(k))) : {};\n this.commit(\"Reset values\", { ...this.doc, values });\n return ok;\n }\n\n /** Random values in each control's range (colours anywhere), for exploring. */\n randomize(names?: readonly string[], random: () => number = Math.random): EditResult {\n const values: Record<string, ControlValue> = {};\n for (const control of this.controls) {\n if (names ? !names.includes(control.name) : control.type === \"sampler2D\") continue;\n const pick = () => {\n const v = control.min + random() * (control.max - control.min);\n return control.type === \"int\" ? Math.round(v) : +v.toFixed(3);\n };\n if (control.type === \"bool\") values[control.name] = random() < 0.5;\n else if (control.type === \"sampler2D\") continue;\n else if (control.color) values[control.name] = Array.from({ length: control.type === \"vec4\" ? 4 : 3 }, () => +random().toFixed(3));\n else if (control.type === \"float\" || control.type === \"int\") values[control.name] = pick();\n else values[control.name] = Array.from({ length: Number(control.type.slice(3)) }, pick);\n }\n return this.setValues(values);\n }\n\n // ── Presets ─────────────────────────────────────────────────────────\n\n applyPreset(name: string): EditResult {\n const preset = this.doc.presets.find((p) => p.name === name);\n if (!preset) return fail(`There is no preset \"${name}\"; there are ${this.doc.presets.map((p) => `\"${p.name}\"`).join(\", \") || \"none\"}.`);\n this.commit(`Apply ${name}`, { ...this.doc, values: { ...this.doc.values, ...preset.values } });\n return ok;\n }\n\n /** Saves the current values (all, or the named ones) as a preset, replacing one of the same name. */\n savePreset(name: string, names?: readonly string[]): EditResult {\n if (!name.trim()) return fail(\"A preset needs a name.\");\n const all = valuesAt(this.doc, this.time, this.controls);\n const values = Object.fromEntries(Object.entries(all).filter(([k]) => (names ? names.includes(k) : true)));\n const presets = [...this.doc.presets.filter((p) => p.name !== name), { name: name.trim(), values }];\n this.commit(`Save preset ${name}`, { ...this.doc, presets });\n return ok;\n }\n\n deletePreset(name: string): EditResult {\n if (!this.doc.presets.some((p) => p.name === name)) return fail(`There is no preset \"${name}\".`);\n this.commit(`Delete preset ${name}`, { ...this.doc, presets: this.doc.presets.filter((p) => p.name !== name) });\n return ok;\n }\n\n // ── Keyframes ───────────────────────────────────────────────────────\n\n /** Sets (or replaces) a keyframe; `value` defaults to the control's value at that time. */\n setKeyframe(name: string, time: number, value?: unknown, easing?: Easing, mergeKey?: string): EditResult {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n if (!(time >= 0)) return fail(\"A keyframe's time is 0 ms or more.\");\n if (easing !== undefined && !isEasing(easing)) return fail(`Unknown easing ${JSON.stringify(easing)}.`);\n let v: ControlValue;\n if (value === undefined) v = valuesAt(this.doc, time, [control])[name]!;\n else {\n const check = checkControlValue(control, value);\n if (!check.ok) return fail(check.message);\n v = check.value;\n }\n const track = this.doc.tracks[name];\n const existing = track?.keyframes.find((k) => Math.abs(k.time - time) < 1e-3);\n const keyframe: Keyframe = { time, value: v, ...((easing ?? existing?.easing) !== undefined && { easing: easing ?? existing?.easing }) };\n const keyframes = normalizeKeyframes([...(track?.keyframes ?? []), keyframe]);\n this.commit(`Keyframe ${control.label}`, { ...this.doc, tracks: { ...this.doc.tracks, [name]: { ...track, keyframes } } }, mergeKey);\n return ok;\n }\n\n removeKeyframe(name: string, time: number): EditResult {\n const track = this.doc.tracks[name];\n const keyframes = track?.keyframes.filter((k) => Math.abs(k.time - time) >= 1e-3);\n if (!track || keyframes!.length === track.keyframes.length) return fail(`\"${name}\" has no keyframe at ${time} ms.`);\n const tracks = { ...this.doc.tracks };\n if (keyframes!.length) tracks[name] = { ...track, keyframes: keyframes! };\n else delete tracks[name];\n this.commit(\"Delete keyframe\", { ...this.doc, tracks });\n return ok;\n }\n\n setKeyframeEasing(name: string, time: number, easing: Easing): EditResult {\n if (!isEasing(easing)) return fail(`Unknown easing ${JSON.stringify(easing)}.`);\n const track = this.doc.tracks[name];\n if (!track?.keyframes.some((k) => Math.abs(k.time - time) < 1e-3)) return fail(`\"${name}\" has no keyframe at ${time} ms.`);\n const keyframes = track.keyframes.map((k) => (Math.abs(k.time - time) < 1e-3 ? { ...k, easing } : k));\n this.commit(\"Set easing\", { ...this.doc, tracks: { ...this.doc.tracks, [name]: { ...track, keyframes } } });\n return ok;\n }\n\n /** Removes a control's track; it keeps the value it had at the playhead. */\n clearTrack(name: string): EditResult {\n if (!this.doc.tracks[name]) return fail(`\"${name}\" has no keyframes.`);\n const control = this.control(name);\n const value = control ? evaluateTrack(this.doc.tracks[name], this.time, control) : undefined;\n const tracks = { ...this.doc.tracks };\n delete tracks[name];\n const values = value === undefined ? this.doc.values : { ...this.doc.values, [name]: value };\n this.commit(\"Clear keyframes\", { ...this.doc, tracks, values });\n return ok;\n }\n\n setTiming(timing: { duration?: number; loop?: boolean }): EditResult {\n if (timing.duration !== undefined && !(timing.duration > 0)) return fail(\"The duration must be above 0 ms.\");\n this.commit(\"Set timing\", { ...this.doc, duration: timing.duration ?? this.doc.duration, loop: timing.loop ?? this.doc.loop });\n return ok;\n }\n\n // ── Playhead ────────────────────────────────────────────────────────\n\n play(): void {\n if (this.playing || this.disposed) return;\n if (!this.doc.loop && this.time >= this.doc.duration) this.time = 0;\n this.playing = true;\n this.changed();\n void this.run();\n }\n\n pause(): void {\n if (!this.playing) return;\n this.playing = false;\n this.changed();\n }\n\n toggle(): void {\n if (this.playing) this.pause();\n else this.play();\n }\n\n /** Moves the playhead (clamped to the timeline). */\n seek(time: number): void {\n const t = Math.min(Math.max(0, time), this.doc.duration);\n if (t === this.time) return;\n this.time = t;\n this.changed();\n }\n\n stop(): void {\n this.playing = false;\n this.time = 0;\n this.changed();\n }\n\n /** While recording, changing a value keyframes it at the playhead. */\n setRecording(recording: boolean): void {\n if (recording === this.recording) return;\n this.recording = recording;\n this.changed();\n }\n\n undo(): boolean {\n return this.history.undo();\n }\n\n redo(): boolean {\n return this.history.redo();\n }\n\n dispose(): void {\n this.disposed = true;\n this.playing = false;\n this.unsubscribeHistory();\n this.listeners.clear();\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private async run(): Promise<void> {\n let last = this.clock.now();\n while (this.playing && !this.disposed) {\n await this.clock.frame();\n if (!this.playing || this.disposed) return;\n const now = this.clock.now();\n let next = this.time + (now - last);\n last = now;\n if (next >= this.doc.duration) {\n if (this.doc.loop) next %= this.doc.duration;\n else {\n this.time = this.doc.duration;\n this.playing = false;\n this.changed();\n return;\n }\n }\n this.time = next;\n this.changed();\n }\n }\n\n private analysis() {\n if (this.controlsCache?.doc !== this.doc) {\n this.controlsCache = { doc: this.doc, controls: documentControls(this.doc), issues: documentIssues(this.doc) };\n }\n return this.controlsCache;\n }\n\n private control(name: string): ControlDef | undefined {\n return this.controls.find((c) => c.name === name);\n }\n\n private unknownControl(name: string): EditResult {\n return fail(`There is no control \"${name}\"; the controls are ${this.controls.map((c) => c.name).join(\", \") || \"none\"}.`);\n }\n\n private commit(label: string, next: EffectDocument, mergeKey?: string): void {\n const before = this.doc;\n const command = HistoryManager.createSnapshotCommand<EffectDocument>({\n name: label,\n before,\n after: next,\n apply: (doc) => {\n this.doc = doc;\n if (this.time > doc.duration) this.time = doc.duration;\n this.changed();\n },\n tag: mergeKey,\n });\n if (mergeKey) this.history.executeMerged(command, { key: mergeKey, windowMs: this.mergeWindowMs });\n else this.history.execute(command);\n }\n\n private changed(): void {\n this.state = null;\n if (this.listeners.size === 0) return;\n const state = this.getState();\n for (const listener of [...this.listeners]) listener(state);\n }\n}\n\n/** A new {@link EffectEditor}. */\nexport function createEffectEditor(options: EffectEditorOptions = {}): EffectEditor {\n return new EffectEditor(options);\n}\n","/**\n * The effect editor's commands. Menus, shortcuts, a command palette and\n * agents (through `createCommandToolProvider`) all run these by id, so every\n * entry point shares the same checks and undo steps.\n */\n\nimport type { CommandSpec } from \"@pixerate/editor/commands\";\nimport { EFFECT_CATEGORIES } from \"../catalog/types.js\";\nimport { findEffects } from \"../catalog/index.js\";\nimport type { EditResult, EffectEditor } from \"./editor.js\";\nimport { EASING_PRESETS, type Easing } from \"./timeline.js\";\n\n/** Throws the refusal, so the command registry reports it as a failed command with its message. */\nfunction done(result: EditResult): { ok: true } {\n if (!result.ok) throw new Error(result.message);\n return result;\n}\n\nconst easingSchema = {\n description: `An easing preset (${EASING_PRESETS.join(\", \")}) or {\"type\":\"cubic-bezier\",\"x1\",\"y1\",\"x2\",\"y2\"} or {\"type\":\"spring\",\"stiffness\",\"damping\",\"mass\"}.`,\n} as const;\n\nconst valueSchema = {\n description:\n 'A control value: a number (float, int), true/false (bool), an array of numbers (vec2–vec4; colours 0–1), a \"#RRGGBB\" colour, or an image URL (sampler2D).',\n} as const;\n\n/** A summary of the editor's state for agents: the document, controls, values at the playhead, issues and compile errors. */\nexport function describeEditor(editor: EffectEditor) {\n const state = editor.getState();\n const doc = state.document;\n return {\n name: doc.name,\n basedOn: doc.basedOn,\n kind: doc.effect.kind,\n ...(doc.effect.kind === \"shader\" ? { source: doc.effect.source } : { motion: doc.effect.motion, vectorControls: doc.effect.controls }),\n controls: state.controls.map((c) => ({\n name: c.name,\n type: c.type,\n label: c.label,\n ...(c.color && { color: true }),\n ...(c.type !== \"bool\" && c.type !== \"sampler2D\" && { min: c.min, max: c.max, step: c.step }),\n default: c.default,\n })),\n values: state.values,\n tracks: doc.tracks,\n presets: doc.presets.map((p) => p.name),\n timeline: { duration: doc.duration, loop: doc.loop, time: state.time, playing: state.playing, recording: state.recording },\n issues: state.issues,\n compile: state.compile,\n history: { canUndo: state.history.canUndo, canRedo: state.history.canRedo },\n };\n}\n\n/** The effect editor's commands (the registry adds `history.undo` / `history.redo`). */\nexport function effectCommands(): CommandSpec<EffectEditor>[] {\n return [\n {\n id: \"effect.describe\",\n label: \"Describe effect\",\n group: \"Effect\",\n description: \"The effect being edited: its source (or picture), controls, values at the playhead, keyframes, presets, timeline, issues and compile errors.\",\n readOnly: true,\n run: (editor) => describeEditor(editor),\n },\n {\n id: \"catalog.search\",\n label: \"Search effects\",\n group: \"Catalog\",\n description: \"Searches the built-in effect catalog. Load one with effect.load to start from it.\",\n readOnly: true,\n params: {\n type: \"object\",\n properties: {\n text: { type: \"string\", description: \"Words to look for (all must match).\" },\n category: { type: \"string\", enum: EFFECT_CATEGORIES },\n kind: { type: \"string\", enum: [\"shader\", \"vector\"] },\n },\n additionalProperties: false,\n },\n run: (_, params: { text?: string; category?: (typeof EFFECT_CATEGORIES)[number]; kind?: \"shader\" | \"vector\" }) =>\n findEffects(params ?? {}).map((e) => ({ id: e.id, name: e.name, kind: e.kind, category: e.category, description: e.description, presets: e.presets?.map((p) => p.name) ?? [] })),\n },\n {\n id: \"effect.load\",\n label: \"Load effect\",\n group: \"Effect\",\n description: \"Starts over from a catalog effect (by id, optionally with one of its presets), or from a shader's source.\",\n params: {\n type: \"object\",\n properties: {\n id: { type: \"string\", description: \"A catalog effect id (see catalog.search).\" },\n preset: { type: \"string\" },\n source: { type: \"string\", description: \"A shader source to start from instead of a catalog effect.\" },\n name: { type: \"string\" },\n },\n additionalProperties: false,\n },\n run: (editor, p: { id?: string; preset?: string; source?: string; name?: string }) =>\n done(p?.source !== undefined ? editor.load({ source: p.source, name: p.name }) : editor.load(p?.id, p?.preset)),\n },\n {\n id: \"effect.setSource\",\n label: \"Set shader source\",\n group: \"Effect\",\n description:\n \"Replaces the shader's GLSL ES 3.00 source (Shadertoy-style mainImage; controls are uniforms annotated `// @control`). Compile errors show up in effect.describe once the preview has compiled it.\",\n params: { type: \"object\", properties: { source: { type: \"string\" } }, required: [\"source\"], additionalProperties: false },\n run: (editor, p: { source: string }) => done(editor.setSource(p.source)),\n },\n {\n id: \"effect.rename\",\n label: \"Rename effect\",\n group: \"Effect\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.rename(p.name)),\n },\n {\n id: \"values.set\",\n label: \"Set values\",\n group: \"Values\",\n description: \"Sets control values by name, all or nothing. A keyframed control (or any, while recording) gets a keyframe at the playhead instead.\",\n params: {\n type: \"object\",\n properties: { values: { type: \"object\", additionalProperties: valueSchema, description: \"Control name → value.\" } },\n required: [\"values\"],\n additionalProperties: false,\n },\n run: (editor, p: { values: Record<string, unknown> }) => done(editor.setValues(p.values)),\n },\n {\n id: \"values.reset\",\n label: \"Reset values\",\n group: \"Values\",\n description: \"Puts controls back to their defaults: the named ones, or all.\",\n params: { type: \"object\", properties: { names: { type: \"array\", items: { type: \"string\" } } }, additionalProperties: false },\n run: (editor, p: { names?: string[] }) => done(editor.resetValues(p?.names)),\n },\n {\n id: \"values.randomize\",\n label: \"Randomize values\",\n group: \"Values\",\n keywords: [\"shuffle\", \"surprise\", \"explore\"],\n params: { type: \"object\", properties: { names: { type: \"array\", items: { type: \"string\" } } }, additionalProperties: false },\n run: (editor, p: { names?: string[] }) => done(editor.randomize(p?.names)),\n },\n {\n id: \"preset.apply\",\n label: \"Apply preset\",\n group: \"Presets\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.applyPreset(p.name)),\n },\n {\n id: \"preset.save\",\n label: \"Save preset\",\n group: \"Presets\",\n description: \"Saves the values at the playhead (all, or the named controls) as a preset.\",\n params: {\n type: \"object\",\n properties: { name: { type: \"string\" }, names: { type: \"array\", items: { type: \"string\" } } },\n required: [\"name\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; names?: string[] }) => done(editor.savePreset(p.name, p.names)),\n },\n {\n id: \"preset.delete\",\n label: \"Delete preset\",\n group: \"Presets\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.deletePreset(p.name)),\n },\n {\n id: \"keyframe.set\",\n label: \"Set keyframe\",\n group: \"Keyframes\",\n description: \"Keyframes a control at a time (ms). Without a value, keyframes its current value there. The easing shapes the segment leaving this keyframe.\",\n params: {\n type: \"object\",\n properties: { name: { type: \"string\" }, time: { type: \"number\", minimum: 0 }, value: valueSchema, easing: easingSchema },\n required: [\"name\", \"time\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; time: number; value?: unknown; easing?: Easing }) => done(editor.setKeyframe(p.name, p.time, p.value, p.easing)),\n },\n {\n id: \"keyframe.remove\",\n label: \"Delete keyframe\",\n group: \"Keyframes\",\n params: { type: \"object\", properties: { name: { type: \"string\" }, time: { type: \"number\" } }, required: [\"name\", \"time\"], additionalProperties: false },\n run: (editor, p: { name: string; time: number }) => done(editor.removeKeyframe(p.name, p.time)),\n },\n {\n id: \"keyframe.setEasing\",\n label: \"Set keyframe easing\",\n group: \"Keyframes\",\n params: {\n type: \"object\",\n properties: { name: { type: \"string\" }, time: { type: \"number\" }, easing: easingSchema },\n required: [\"name\", \"time\", \"easing\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; time: number; easing: Easing }) => done(editor.setKeyframeEasing(p.name, p.time, p.easing)),\n },\n {\n id: \"keyframe.clear\",\n label: \"Clear keyframes\",\n group: \"Keyframes\",\n description: \"Removes a control's keyframes; it keeps the value it has at the playhead.\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.clearTrack(p.name)),\n },\n {\n id: \"timeline.setTiming\",\n label: \"Set timing\",\n group: \"Timeline\",\n params: {\n type: \"object\",\n properties: { duration: { type: \"number\", exclusiveMinimum: 0, description: \"Ms.\" }, loop: { type: \"boolean\" } },\n additionalProperties: false,\n },\n run: (editor, p: { duration?: number; loop?: boolean }) => done(editor.setTiming(p ?? {})),\n },\n {\n id: \"timeline.play\",\n label: \"Play\",\n group: \"Timeline\",\n run: (editor) => editor.play(),\n },\n {\n id: \"timeline.pause\",\n label: \"Pause\",\n group: \"Timeline\",\n run: (editor) => editor.pause(),\n },\n {\n id: \"timeline.toggle\",\n label: \"Play / pause\",\n group: \"Timeline\",\n shortcut: \"Space\",\n run: (editor) => editor.toggle(),\n },\n {\n id: \"timeline.seek\",\n label: \"Go to time\",\n group: \"Timeline\",\n params: { type: \"object\", properties: { time: { type: \"number\", minimum: 0, description: \"Ms.\" } }, required: [\"time\"], additionalProperties: false },\n run: (editor, p: { time: number }) => editor.seek(p.time),\n },\n {\n id: \"timeline.stop\",\n label: \"Stop\",\n group: \"Timeline\",\n run: (editor) => editor.stop(),\n },\n {\n id: \"timeline.toggleRecording\",\n label: \"Record keyframes\",\n group: \"Timeline\",\n description: \"While recording, changing a value keyframes it at the playhead.\",\n run: (editor) => editor.setRecording(!editor.getState().recording),\n },\n ];\n}\n","/**\n * Agents edit effects through the editor's commands. Flair ships the tools\n * and the instructions; the model, its credentials and the agent loop are\n * the host's (bridge the provider into an MCP server or any tool-calling LLM).\n */\n\nimport { createCommandToolProvider } from \"@pixerate/editor/commands\";\nimport type { AgentTool, AgentToolProvider, McpToolDefinition, McpToolResult } from \"@pixerate/editor/generation\";\nimport { BUILTIN_UNIFORMS } from \"../shader/program.js\";\nimport type { EffectEditor } from \"./editor.js\";\n\nexport interface EffectToolProviderOptions {\n /** Provider id (default `flair-effect`). */\n id?: string;\n /** Prefix every tool name with `<namespace>__`. */\n namespace?: string;\n /**\n * Captures the preview as a data URL (PNG or JPEG), adding a `view_effect`\n * tool so the agent can look at what it made. With `ShaderRenderer`:\n * `() => renderer.capture({ type: \"image/png\" })`.\n */\n capture?: () => string | null | undefined | Promise<string | null | undefined>;\n /** Commands agents may not see or run. */\n exclude?: (id: string) => boolean;\n}\n\n/**\n * The effect editor as agent tools (MCP-shaped, for `@pixerate/editor`'s\n * `ToolRegistry` or any tool loop):\n *\n * - `list_commands`, `run_command`, `run_commands`: every editor command\n * (describe, search the catalog, load, set source, values, presets,\n * keyframes, timing, undo and redo).\n * - `view_effect`, when `capture` is given: an image of the preview.\n */\nexport function createEffectToolProvider(editor: EffectEditor, options: EffectToolProviderOptions = {}): AgentToolProvider {\n const base = createCommandToolProvider(editor.commands, {\n id: options.id ?? \"flair-effect\",\n namespace: options.namespace,\n exclude: options.exclude,\n });\n const extra: AgentTool[] = [];\n if (options.capture) {\n const capture = options.capture;\n extra.push({\n name: options.namespace ? `${options.namespace}__view_effect` : \"view_effect\",\n title: \"View effect\",\n description: \"An image of the effect's preview as it is drawn now, to check what your changes look like.\",\n inputSchema: { type: \"object\", properties: {}, additionalProperties: false },\n annotations: { readOnlyHint: true },\n handler: async () => {\n const url = await capture();\n const match = url ? /^data:([^;,]+);base64,(.*)$/.exec(url) : null;\n if (!match) return { content: [{ type: \"text\", text: \"Nothing to show yet: the preview has not drawn a frame.\" }], isError: true };\n return { content: [{ type: \"image\", data: match[2]!, mimeType: match[1]! }] };\n },\n });\n }\n const baseTools = () => (typeof base.tools === \"function\" ? base.tools() : base.tools);\n return { id: base.id, namespace: base.namespace, subscribe: base.subscribe, tools: () => [...baseTools(), ...extra] };\n}\n\n/** A provider's tool definitions without their handlers, to send to a model. */\nexport function toolDefinitions(provider: AgentToolProvider): McpToolDefinition[] {\n const tools = typeof provider.tools === \"function\" ? provider.tools() : provider.tools;\n return tools.map(({ handler: _handler, ...definition }) => definition);\n}\n\n/**\n * Runs one of a provider's tools by name, for hosts without a `ToolRegistry`:\n * the handler's result comes back as an MCP `tools/call` result, and a\n * thrown error as an `isError` result.\n */\nexport async function callTool(provider: AgentToolProvider, name: string, args: Record<string, unknown> = {}): Promise<McpToolResult> {\n const tools = typeof provider.tools === \"function\" ? provider.tools() : provider.tools;\n const tool = tools.find((t) => t.name === name);\n if (!tool) return { content: [{ type: \"text\", text: `There is no tool \"${name}\".` }], isError: true };\n try {\n const result = await tool.handler(args, { name, providerId: provider.id });\n if (result && typeof result === \"object\" && Array.isArray((result as McpToolResult).content)) return result as McpToolResult;\n if (result === undefined) return { content: [] };\n if (typeof result === \"string\") return { content: [{ type: \"text\", text: result }] };\n return { content: [{ type: \"text\", text: JSON.stringify(result) }], structuredContent: result as Record<string, unknown> };\n } catch (e) {\n return { content: [{ type: \"text\", text: (e as Error).message }], isError: true };\n }\n}\n\n/** A system prompt for agents that author effects with these tools. Add your product's own guidance to it. */\nexport const EFFECT_AGENT_INSTRUCTIONS = `You design web visual effects in an effect editor. Work through its tools: \\`list_commands\\` shows every command with its parameters; \\`run_command\\` runs one; \\`run_commands\\` runs several in order.\n\nStart with \\`effect.describe\\` to see what is being edited. To start from something close, use \\`catalog.search\\` then \\`effect.load\\` (with a preset if one fits). Every change is one undo step; \\`history.undo\\` reverts a step.\n\nShader effects are GLSL ES 3.00 fragment shaders written Shadertoy-style:\n- Define \\`void mainImage(out vec4 fragColor, in vec2 fragCoord)\\`; the output is opaque (alpha is ignored).\n- Built-in uniforms (do not declare them): ${BUILTIN_UNIFORMS.map((u) => `\\`${u.type} ${u.name}\\` (${u.doc})`).join(\"; \")}.\n- Declare controls as uniforms with an annotation on the same line, e.g.\n uniform float uSpeed; // @control min=0 max=4 default=1 label=\"Speed\"\n uniform vec3 uTint; // @control color default=#77C16A label=\"Tint\"\n uniform bool uGrain; // @control default=true label=\"Film grain\"\n uniform sampler2D uPhoto; // @control filter=linear wrap=clamp label=\"Photo\"\n Types: float, int, bool, vec2, vec3, vec4, sampler2D. Every uniform you declare should be a control.\n- Write float literals with a decimal point (1.0, not 1); GLSL does not convert int to float implicitly. Use \\`texture()\\`, not \\`texture2D()\\`.\n- After \\`effect.setSource\\`, call \\`effect.describe\\` and read \\`compile\\`: fix any errors (their line numbers match your source) before going on.\n\nVector effects are animated SVG pictures: change their controls' values; their picture is edited with care, keeping element ids that animations refer to.\n\nMake effects that loop smoothly and read well behind content: keep motion calm unless asked otherwise, keep contrast where text may sit, and expose the few controls a designer would actually want (colours, speed, scale, intensity) with sensible ranges. Use keyframes (\\`keyframe.set\\`, in ms) for changes over the timeline. When you can, look at the result with \\`view_effect\\` and refine it.`;\n","/**\n * Keeps a preview in step with an editor: the editor owns the playhead and\n * the values; the preview draws what it is told.\n */\n\nimport type { ControlValue } from \"../shader/controls.js\";\nimport type { CompileStatus } from \"../shader/renderer.js\";\nimport { svgView, type SvgView } from \"../vector/svg.js\";\nimport { motionAt } from \"./document.js\";\nimport type { EffectEditor, EffectEditorState } from \"./editor.js\";\n\n/** What {@link bindShaderRenderer} needs of a renderer (`ShaderRenderer` fits). */\nexport interface ShaderPreview {\n setSource(source: string): CompileStatus;\n setValues(values: Record<string, ControlValue> | undefined): void;\n /** Seconds. */\n seek(time: number): void;\n pause(): void;\n}\n\n/**\n * Drives a shader preview from the editor: the source (reporting compile\n * results back), the values at the playhead, and the time. The renderer is\n * paused so the editor's playhead is the only clock. Returns an unbind\n * function.\n */\nexport function bindShaderRenderer(editor: EffectEditor, renderer: ShaderPreview): () => void {\n let source: string | undefined;\n renderer.pause();\n const sync = (state: EffectEditorState) => {\n const effect = state.document.effect;\n if (effect.kind !== \"shader\") return;\n if (effect.source !== source) {\n source = effect.source;\n editor.reportCompile(renderer.setSource(source));\n }\n renderer.setValues(state.values);\n renderer.seek(state.time / 1000);\n };\n sync(editor.getState());\n return editor.subscribe(sync);\n}\n\n/**\n * Draws a vector effect into `container` from the editor: the picture with\n * the values at the playhead, at the playhead's time. Returns an unbind\n * function, which also removes the picture.\n */\nexport function bindSvgView(editor: EffectEditor, container: HTMLElement): () => void {\n let view: SvgView | undefined;\n let key: string | undefined;\n const sync = (state: EffectEditorState) => {\n const motion = motionAt(state.document, state.time);\n if (!motion) {\n view?.element.remove();\n view = undefined;\n key = undefined;\n return;\n }\n const next = JSON.stringify(motion);\n if (next !== key) {\n key = next;\n view?.element.remove();\n view = svgView(motion);\n container.appendChild(view.element);\n }\n view!.draw(state.time);\n };\n sync(editor.getState());\n const unsubscribe = editor.subscribe(sync);\n return () => {\n unsubscribe();\n view?.element.remove();\n };\n}\n"],"mappings":"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","names":["t"]}
|
|
1
|
+
{"version":3,"sources":["../../src/editor/timeline.ts","../../src/editor/document.ts","../../src/editor/editor.ts","../../src/editor/commands.ts","../../src/editor/agent.ts","../../src/editor/bind.ts","../../src/editor/parameters.ts"],"sourcesContent":["/**\n * Keyframed control values. A track holds keyframes `{ time (ms), value,\n * easing }` for one control; the easing governs the segment leaving its\n * keyframe. Easing comes from `@pixerate/editor/animation`, and tracks keep\n * its `AnimationTrack` shape; Flair adds what control values need beyond\n * numbers and colours (vectors, booleans, images, ints).\n */\n\nimport {\n ANIMATION_EASING_PRESETS,\n cubicBezierAt as editorCubicBezierAt,\n evaluateEasing as evaluateAnimationEasing,\n isAnimationEasing,\n type AnimationEasing,\n type AnimationEasingPreset,\n type AnimationKeyframe,\n type AnimationTrack,\n type CubicBezierEasing,\n type SpringEasing,\n} from \"@pixerate/editor/animation\";\nimport type { ControlDef, ControlValue } from \"../shader/controls.js\";\nimport { interpolateOklab, type ColorRGB } from \"../shader/color.js\";\n\n/** Named easings (CSS keywords plus back and spring curves). */\nexport type EasingPreset = AnimationEasingPreset;\nexport type Easing = AnimationEasing;\nexport type { CubicBezierEasing, SpringEasing };\n\n/** Every easing preset, in picker order. */\nexport const EASING_PRESETS: readonly EasingPreset[] = ANIMATION_EASING_PRESETS;\n\n/** Eased progress at `u` (0–1) through a segment `durationMs` long (`@pixerate/editor`'s easing engine). */\nexport const evaluateEasing = evaluateAnimationEasing;\n\n/** CSS `cubic-bezier(x1, y1, x2, y2)` solved for y at progress x. */\nexport const cubicBezierAt = editorCubicBezierAt;\n\n/**\n * Whether `value` is an easing (a preset name, a cubic bezier or a spring),\n * with a spring's stiffness, damping and mass above 0 when given.\n */\nexport function isEasing(value: unknown): value is Easing {\n if (!isAnimationEasing(value)) return false;\n if (typeof value !== \"object\" || value.type !== \"spring\") return true;\n return [value.stiffness, value.damping, value.mass].every((v) => v === undefined || v > 0);\n}\n\n/** A keyframe of a control: like `AnimationKeyframe`, with any control value (vectors and booleans too). */\nexport interface Keyframe extends Omit<AnimationKeyframe, \"value\"> {\n value: ControlValue;\n}\n\n/** A control's keyframes: `AnimationTrack`'s shape, with control values. */\nexport interface Track extends Omit<AnimationTrack, \"keyframes\"> {\n /** Sorted by time, at most one per time. */\n keyframes: Keyframe[];\n}\n\n/**\n * A control value `t` of the way from `a` to `b`: numbers and vectors blend\n * (overshooting with back and spring easings), colours blend in Oklab, `int`s\n * round, and booleans and images switch halfway.\n */\nexport function interpolateControlValue(control: ControlDef | undefined, a: ControlValue, b: ControlValue, t: number): ControlValue {\n if (typeof a === \"number\" && typeof b === \"number\") {\n const v = a + (b - a) * t;\n return control?.type === \"int\" ? Math.round(v) : v;\n }\n if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {\n if (control?.color && a.length >= 3) {\n const rgb = interpolateOklab(a.slice(0, 3) as ColorRGB, b.slice(0, 3) as ColorRGB, t);\n const out = rgb.map((c) => Math.min(1, Math.max(0, c)));\n return a.length === 4 ? [...out, a[3]! + (b[3]! - a[3]!) * t] : out;\n }\n return a.map((v, i) => v + (b[i]! - v) * t);\n }\n return t < 0.5 ? a : b;\n}\n\n/** A track's value at `time` (ms), or undefined when it has no keyframes or is disabled. */\nexport function evaluateTrack(track: Track | undefined, time: number, control?: ControlDef): ControlValue | undefined {\n if (!track || track.enabled === false || track.keyframes.length === 0) return undefined;\n const keys = track.keyframes;\n if (time <= keys[0]!.time) return keys[0]!.value;\n const last = keys[keys.length - 1]!;\n if (time >= last.time) return last.value;\n let i = 0;\n while (i < keys.length - 2 && keys[i + 1]!.time <= time) i++;\n const from = keys[i]!;\n const to = keys[i + 1]!;\n const span = to.time - from.time;\n const u = span > 0 ? (time - from.time) / span : 1;\n return interpolateControlValue(control, from.value, to.value, evaluateEasing(from.easing, u, span));\n}\n\n/** Keyframes sorted by time, one per time (the later of two at the same time wins). */\nexport function normalizeKeyframes(keyframes: readonly Keyframe[]): Keyframe[] {\n const byTime = new Map<number, Keyframe>();\n for (const k of keyframes) byTime.set(Math.round(k.time * 1000) / 1000, { ...k, time: Math.round(k.time * 1000) / 1000 });\n return [...byTime.values()].sort((a, b) => a.time - b.time);\n}\n\n/** The keyframe times across tracks, sorted and without duplicates. */\nexport function keyframeTimes(tracks: Record<string, Track>): number[] {\n return [...new Set(Object.values(tracks).flatMap((t) => t.keyframes.map((k) => k.time)))].sort((a, b) => a - b);\n}\n","/**\n * The document an effect editor edits: an effect (a shader's source, or a\n * vector picture with its controls), the values picked for its controls,\n * keyframed tracks, presets and a timeline. Plain JSON, so it can be saved,\n * diffed, and written by an agent.\n */\n\nimport { getEffect, renderVectorEffect, vectorControlDef } from \"../catalog/index.js\";\nimport type { EffectDefinition, EffectPreset, VectorControl } from \"../catalog/types.js\";\nimport { checkControlValue, controlValue, parseControls, type ControlDef, type ControlValue } from \"../shader/controls.js\";\nimport { svgMotionIssues, type SvgMotion } from \"../vector/motion.js\";\nimport { evaluateTrack, isEasing, normalizeKeyframes, type Track } from \"./timeline.js\";\n\n/** What is being edited: a fragment shader, or an animated SVG with declared controls. */\nexport type EffectSource =\n | { kind: \"shader\"; source: string }\n | { kind: \"vector\"; motion: SvgMotion; controls: VectorControl[]; hold?: number };\n\nexport interface EffectDocument {\n /** Format version (1). */\n version: 1;\n name: string;\n /** The catalog effect it started from, if any. */\n basedOn?: string;\n effect: EffectSource;\n /** Picked control values; controls without one use their default. */\n values: Record<string, ControlValue>;\n /** Keyframed controls, by control name. A track overrides the control's value. */\n tracks: Record<string, Track>;\n /** Timeline length in ms. */\n duration: number;\n loop: boolean;\n presets: EffectPreset[];\n}\n\nconst DEFAULT_SHADER = `// A new effect: edit me.\nuniform vec3 uColorA; // @control color default=#6366F1 label=\"Colour A\"\nuniform vec3 uColorB; // @control color default=#EC4899 label=\"Colour B\"\nuniform float uSpeed; // @control min=0 max=4 default=1 label=\"Speed\"\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n float t = 0.5 + 0.5 * sin(uv.x * 3.0 + iTime * uSpeed);\n fragColor = vec4(mix(uColorA, uColorB, t), 1.0);\n}\n`;\n\n/** Ms of timeline a new document gets (the effect's loop when it has one). */\nconst DEFAULT_DURATION = 5000;\n\n/**\n * A new document: blank (a small starter shader), from a catalog effect (by\n * id or definition), or from a shader's source.\n */\nexport function createEffectDocument(from?: string | EffectDefinition | { source: string; name?: string }): EffectDocument {\n if (from === undefined) return blankDocument(\"Untitled effect\", DEFAULT_SHADER);\n if (typeof from === \"object\" && !(\"kind\" in from)) return blankDocument(from.name ?? \"Untitled effect\", from.source);\n const effect = typeof from === \"string\" ? getEffect(from) : from;\n if (!effect) throw new Error(`There is no effect \"${String(from)}\" in the catalog.`);\n return {\n version: 1,\n name: effect.name,\n basedOn: effect.id,\n effect:\n effect.kind === \"shader\"\n ? { kind: \"shader\", source: effect.source }\n : { kind: \"vector\", motion: structuredClone(effect.motion), controls: effect.controls.map((c) => ({ ...c })), hold: effect.hold },\n values: {},\n tracks: {},\n duration: effect.kind === \"shader\" && effect.loop ? Math.round(effect.loop * 1000) : DEFAULT_DURATION,\n loop: true,\n presets: (effect.presets ?? []).map((p) => ({ name: p.name, values: { ...p.values } })),\n };\n}\n\nfunction blankDocument(name: string, source: string): EffectDocument {\n return { version: 1, name, effect: { kind: \"shader\", source }, values: {}, tracks: {}, duration: DEFAULT_DURATION, loop: true, presets: [] };\n}\n\n/** The document's controls. */\nexport function documentControls(doc: EffectDocument): ControlDef[] {\n if (doc.effect.kind === \"shader\") return parseControls(doc.effect.source);\n return doc.effect.controls.map((c, i) => vectorControlDef(c, i + 1));\n}\n\n/** Every control's value at `time` (ms): its track if it has one, else the picked value, else its default. */\nexport function valuesAt(doc: EffectDocument, time: number, controls = documentControls(doc)): Record<string, ControlValue> {\n const out: Record<string, ControlValue> = {};\n for (const control of controls) {\n const tracked = evaluateTrack(doc.tracks[control.name], time, control);\n out[control.name] = tracked ?? controlValue(control, doc.values);\n }\n return out;\n}\n\n/** A vector document's picture with the values at `time` filled in; undefined for shaders. */\nexport function motionAt(doc: EffectDocument, time: number): SvgMotion | undefined {\n if (doc.effect.kind !== \"vector\") return undefined;\n // Derived placeholders are code, so they come from the catalog effect the document started from.\n const base = doc.basedOn ? getEffect(doc.basedOn) : undefined;\n const derived = base?.kind === \"vector\" ? base.derived : undefined;\n return renderVectorEffect({ motion: doc.effect.motion, controls: doc.effect.controls, derived }, valuesAt(doc, time));\n}\n\n/**\n * Problems with a document, as sentences: unknown controls in values, tracks\n * or presets, values a control cannot take, bad keyframes, and (for vector\n * effects) problems with the picture. Shader compile errors come from the\n * GPU, not from here.\n */\nexport function documentIssues(doc: EffectDocument): string[] {\n const issues: string[] = [];\n if (!(doc.duration > 0)) issues.push(\"`duration` must be above 0 ms\");\n if (doc.effect.kind === \"shader\" && !/void\\s+mainImage\\s*\\(/.test(doc.effect.source))\n issues.push(\"the shader needs `void mainImage(out vec4 fragColor, in vec2 fragCoord)`\");\n const controls = new Map(documentControls(doc).map((c) => [c.name, c]));\n const check = (where: string, name: string, value: unknown) => {\n const control = controls.get(name);\n if (!control) return void issues.push(`${where}: there is no control \"${name}\"`);\n const result = checkControlValue(control, value);\n if (!result.ok) issues.push(`${where}: ${result.message}`);\n };\n for (const [name, value] of Object.entries(doc.values)) check(\"values\", name, value);\n for (const [name, track] of Object.entries(doc.tracks)) {\n track.keyframes.forEach((k, i) => {\n const where = `tracks.${name}[${i}]`;\n if (!(k.time >= 0)) issues.push(`${where}: \\`time\\` must be 0 or more`);\n if (k.easing !== undefined && !isEasing(k.easing)) issues.push(`${where}: unknown easing`);\n check(where, name, k.value);\n });\n }\n for (const preset of doc.presets) for (const [name, value] of Object.entries(preset.values)) check(`preset \"${preset.name}\"`, name, value);\n if (doc.effect.kind === \"vector\") {\n const motion = motionAt(doc, 0)!;\n issues.push(...svgMotionIssues(motion));\n if (/\\{\\{[^}]+\\}\\}/.test(JSON.stringify(motion))) issues.push(\"the picture has placeholders no control fills\");\n }\n return issues;\n}\n\n/** Reads a document from JSON (or a parsed object), checking its shape. */\nexport function parseEffectDocument(input: string | unknown): { ok: true; document: EffectDocument } | { ok: false; errors: string[] } {\n let raw: unknown = input;\n if (typeof input === \"string\") {\n try {\n raw = JSON.parse(input);\n } catch (e) {\n return { ok: false, errors: [`not JSON: ${(e as Error).message}`] };\n }\n }\n const errors: string[] = [];\n const o = (raw ?? {}) as Partial<EffectDocument> & Record<string, unknown>;\n if (o.version !== 1) errors.push(\"`version` must be 1\");\n if (typeof o.name !== \"string\") errors.push(\"`name` must be a string\");\n const effect = o.effect as Partial<EffectSource> | undefined;\n if (!effect || (effect.kind !== \"shader\" && effect.kind !== \"vector\")) errors.push('`effect.kind` must be \"shader\" or \"vector\"');\n else if (effect.kind === \"shader\" && typeof (effect as { source?: unknown }).source !== \"string\") errors.push(\"`effect.source` must be a string\");\n else if (effect.kind === \"vector\") {\n const v = effect as Partial<Extract<EffectSource, { kind: \"vector\" }>>;\n if (!v.motion || typeof v.motion.svg !== \"string\") errors.push(\"`effect.motion.svg` must be a string\");\n if (!Array.isArray(v.controls)) errors.push(\"`effect.controls` must be a list\");\n }\n const isRecord = (v: unknown) => !!v && typeof v === \"object\" && !Array.isArray(v);\n if (o.values !== undefined && !isRecord(o.values)) errors.push(\"`values` must be an object\");\n if (o.tracks !== undefined && !isRecord(o.tracks)) errors.push(\"`tracks` must be an object\");\n for (const [name, track] of Object.entries((isRecord(o.tracks) ? o.tracks : {}) as Record<string, Track>))\n if (!track || !Array.isArray(track.keyframes)) errors.push(`\\`tracks.${name}.keyframes\\` must be a list`);\n if (o.presets !== undefined && !Array.isArray(o.presets)) errors.push(\"`presets` must be a list\");\n if (errors.length) return { ok: false, errors };\n const tracks = Object.fromEntries(\n Object.entries((o.tracks ?? {}) as Record<string, Track>).map(([k, t]) => [k, { ...t, keyframes: normalizeKeyframes(t.keyframes) }]),\n );\n return {\n ok: true,\n document: {\n version: 1,\n name: o.name!,\n basedOn: typeof o.basedOn === \"string\" ? o.basedOn : undefined,\n effect: o.effect as EffectSource,\n values: (o.values ?? {}) as Record<string, ControlValue>,\n tracks,\n duration: typeof o.duration === \"number\" && o.duration > 0 ? o.duration : DEFAULT_DURATION,\n loop: o.loop !== false,\n presets: (o.presets ?? []) as EffectPreset[],\n },\n };\n}\n","/**\n * A UI-agnostic effect editor: the document, its undo history, a playhead,\n * and commands that menus, shortcuts, a palette and agents all run by id.\n * Bring your own UI (any framework, or none) and your own renderer binding\n * (see `bindShaderRenderer` and `bindSvgView`).\n */\n\nimport { createCommandRegistry, createHistoryCommands, type CommandRegistry } from \"@pixerate/editor/commands\";\nimport { createHistoryManager, HistoryManager, type HistoryState } from \"@pixerate/editor/history\";\nimport type { EffectDefinition } from \"../catalog/types.js\";\nimport type { CompileError } from \"../shader/program.js\";\nimport { checkControlValue, type ControlDef, type ControlValue } from \"../shader/controls.js\";\nimport { realClock, type Clock } from \"../time/clock.js\";\nimport type { SvgMotion } from \"../vector/motion.js\";\nimport { effectCommands } from \"./commands.js\";\nimport {\n createEffectDocument,\n documentControls,\n documentIssues,\n parseEffectDocument,\n valuesAt,\n type EffectDocument,\n} from \"./document.js\";\nimport { evaluateTrack, isEasing, normalizeKeyframes, type Easing, type Keyframe } from \"./timeline.js\";\n\n/** What an edit did: `ok`, or why it was refused (in a sentence an agent can act on). */\nexport type EditResult = { ok: true } | { ok: false; message: string };\n\n/** The result of compiling the shader, as reported by a renderer (see `reportCompile`). */\nexport interface CompileReport {\n ok: boolean;\n errors: CompileError[];\n}\n\nexport interface EffectEditorState {\n document: EffectDocument;\n controls: ControlDef[];\n /** Every control's value at the playhead. */\n values: Record<string, ControlValue>;\n /** Problems with the document (see `documentIssues`). */\n issues: string[];\n /** The latest compile report, or null before a renderer reported one (and for vector effects). */\n compile: CompileReport | null;\n /** Playhead, ms. */\n time: number;\n playing: boolean;\n /** Whether changing a value keyframes it at the playhead. */\n recording: boolean;\n history: HistoryState;\n}\n\nexport interface EffectEditorOptions {\n /** The document to edit, or what to start one from (see `createEffectDocument`). */\n document?: EffectDocument;\n from?: string | EffectDefinition | { source: string; name?: string };\n /** Undo history to record into (default: a new one, 100 steps deep). */\n history?: HistoryManager;\n /** Time source for playback (default: animation frames). */\n clock?: Clock;\n /** Ms within which value changes to one control merge into one undo step (default 400). */\n mergeWindowMs?: number;\n}\n\nconst ok: EditResult = { ok: true };\nconst fail = (message: string): EditResult => ({ ok: false, message });\n\n/**\n * Edits one effect. Every change to the document is an undo step (value\n * changes in quick succession, like a slider drag, merge into one), and\n * subscribers hear about every change to the state.\n */\nexport class EffectEditor {\n readonly history: HistoryManager;\n /** Every editing action, by id: for menus, shortcuts, a command palette and agents. */\n readonly commands: CommandRegistry<EffectEditor>;\n private doc: EffectDocument;\n private controlsCache: { doc: EffectDocument; controls: ControlDef[]; issues: string[] } | null = null;\n private compileReport: CompileReport | null = null;\n private time = 0;\n private playing = false;\n private recording = false;\n private readonly clock: Clock;\n private readonly mergeWindowMs: number;\n private readonly listeners = new Set<(state: EffectEditorState) => void>();\n private state: EffectEditorState | null = null;\n private readonly unsubscribeHistory: () => void;\n private disposed = false;\n\n constructor(options: EffectEditorOptions = {}) {\n this.doc = options.document ?? createEffectDocument(options.from);\n this.history = options.history ?? createHistoryManager({ maxDepth: 100 });\n this.clock = options.clock ?? realClock;\n this.mergeWindowMs = options.mergeWindowMs ?? 400;\n this.commands = createCommandRegistry<EffectEditor>({ context: this });\n this.commands.register([...createHistoryCommands(this.history), ...effectCommands()]);\n this.unsubscribeHistory = this.history.subscribe(() => this.changed());\n }\n\n // ── Reading ────────────────────────────────────────────────────────\n\n get document(): EffectDocument {\n return this.doc;\n }\n\n /** The current state; the same object until something changes (safe for `useSyncExternalStore`). */\n getState(): EffectEditorState {\n if (!this.state) {\n const { controls, issues } = this.analysis();\n this.state = {\n document: this.doc,\n controls,\n values: valuesAt(this.doc, this.time, controls),\n issues,\n compile: this.doc.effect.kind === \"shader\" ? this.compileReport : null,\n time: this.time,\n playing: this.playing,\n recording: this.recording,\n history: this.history.getState(),\n };\n }\n return this.state;\n }\n\n /** Calls `listener` with the new state after every change; returns an unsubscribe function. */\n subscribe(listener: (state: EffectEditorState) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** The document's controls. */\n get controls(): ControlDef[] {\n return this.analysis().controls;\n }\n\n /** The document as JSON. */\n toJSON(): EffectDocument {\n return this.doc;\n }\n\n // ── Whole documents ─────────────────────────────────────────────────\n\n /** Replaces the document (one undo step). */\n replaceDocument(next: EffectDocument, label = \"Replace effect\"): EditResult {\n const parsed = parseEffectDocument(next);\n if (!parsed.ok) return fail(parsed.errors.join(\"; \"));\n this.commit(label, parsed.document);\n return ok;\n }\n\n /** Starts over from a catalog effect, a shader source, or blank (one undo step). */\n load(from?: string | EffectDefinition | { source: string; name?: string }, preset?: string): EditResult {\n let next: EffectDocument;\n try {\n next = createEffectDocument(from);\n } catch (e) {\n return fail((e as Error).message);\n }\n if (preset) {\n const values = next.presets.find((p) => p.name === preset)?.values;\n if (!values) return fail(`\"${next.name}\" has no preset \"${preset}\"; it has ${next.presets.map((p) => `\"${p.name}\"`).join(\", \") || \"none\"}.`);\n next = { ...next, values: { ...values } };\n }\n this.compileReport = null;\n this.commit(`Load ${next.name}`, next);\n return ok;\n }\n\n rename(name: string): EditResult {\n if (!name.trim()) return fail(\"A name cannot be empty.\");\n this.commit(\"Rename\", { ...this.doc, name: name.trim() });\n return ok;\n }\n\n // ── The effect itself ───────────────────────────────────────────────\n\n /** Replaces a shader's source. Compile errors arrive later, from the renderer (see `reportCompile`). */\n setSource(source: string): EditResult {\n if (this.doc.effect.kind !== \"shader\") return fail(\"This is a vector effect; use setMotion.\");\n if (source === this.doc.effect.source) return ok;\n this.commit(\"Edit source\", { ...this.doc, effect: { kind: \"shader\", source } }, \"source\");\n return ok;\n }\n\n /** Replaces a vector effect's picture (and optionally its controls). */\n setMotion(motion: SvgMotion, controls?: Extract<EffectDocument[\"effect\"], { kind: \"vector\" }>[\"controls\"]): EditResult {\n if (this.doc.effect.kind !== \"vector\") return fail(\"This is a shader effect; use setSource.\");\n this.commit(\"Edit picture\", { ...this.doc, effect: { ...this.doc.effect, motion, controls: controls ?? this.doc.effect.controls } });\n return ok;\n }\n\n /** Called by a renderer binding with the result of compiling the current source. */\n reportCompile(report: CompileReport): void {\n this.compileReport = { ok: report.ok, errors: [...report.errors] };\n this.changed();\n }\n\n // ── Values ──────────────────────────────────────────────────────────\n\n /**\n * Sets a control's value. With a track on that control (or while\n * recording) it sets a keyframe at the playhead instead. Quick successive\n * changes to one control merge into one undo step.\n */\n setValue(name: string, value: unknown): EditResult {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n const check = checkControlValue(control, value);\n if (!check.ok) return fail(check.message);\n if (this.recording || this.doc.tracks[name]) return this.setKeyframe(name, this.time, check.value, undefined, `value:${name}`);\n this.commit(`Set ${control.label}`, { ...this.doc, values: { ...this.doc.values, [name]: check.value } }, `value:${name}`);\n return ok;\n }\n\n /** Sets several values in one undo step; refuses them all if any is wrong. */\n setValues(values: Record<string, unknown>): EditResult {\n const next = { ...this.doc.values };\n const tracks = { ...this.doc.tracks };\n for (const [name, value] of Object.entries(values)) {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n const check = checkControlValue(control, value);\n if (!check.ok) return fail(check.message);\n if (this.recording || tracks[name]) {\n const keyframes = [...(tracks[name]?.keyframes ?? []), { time: this.time, value: check.value }];\n tracks[name] = { ...tracks[name], keyframes: normalizeKeyframes(keyframes) };\n } else next[name] = check.value;\n }\n this.commit(\"Set values\", { ...this.doc, values: next, tracks });\n return ok;\n }\n\n /** Back to defaults: the named controls, or all of them (values only; tracks stay). */\n resetValues(names?: readonly string[]): EditResult {\n const unknown = names?.find((n) => !this.control(n));\n if (unknown) return this.unknownControl(unknown);\n const values = names ? Object.fromEntries(Object.entries(this.doc.values).filter(([k]) => !names.includes(k))) : {};\n this.commit(\"Reset values\", { ...this.doc, values });\n return ok;\n }\n\n /** Random values in each control's range (colours anywhere), for exploring. */\n randomize(names?: readonly string[], random: () => number = Math.random): EditResult {\n const values: Record<string, ControlValue> = {};\n for (const control of this.controls) {\n if (names ? !names.includes(control.name) : control.type === \"sampler2D\") continue;\n const pick = () => {\n const v = control.min + random() * (control.max - control.min);\n return control.type === \"int\" ? Math.round(v) : +v.toFixed(3);\n };\n if (control.type === \"bool\") values[control.name] = random() < 0.5;\n else if (control.type === \"sampler2D\") continue;\n else if (control.color) values[control.name] = Array.from({ length: control.type === \"vec4\" ? 4 : 3 }, () => +random().toFixed(3));\n else if (control.type === \"float\" || control.type === \"int\") values[control.name] = pick();\n else values[control.name] = Array.from({ length: Number(control.type.slice(3)) }, pick);\n }\n return this.setValues(values);\n }\n\n // ── Presets ─────────────────────────────────────────────────────────\n\n applyPreset(name: string): EditResult {\n const preset = this.doc.presets.find((p) => p.name === name);\n if (!preset) return fail(`There is no preset \"${name}\"; there are ${this.doc.presets.map((p) => `\"${p.name}\"`).join(\", \") || \"none\"}.`);\n this.commit(`Apply ${name}`, { ...this.doc, values: { ...this.doc.values, ...preset.values } });\n return ok;\n }\n\n /** Saves the current values (all, or the named ones) as a preset, replacing one of the same name. */\n savePreset(name: string, names?: readonly string[]): EditResult {\n if (!name.trim()) return fail(\"A preset needs a name.\");\n const all = valuesAt(this.doc, this.time, this.controls);\n const values = Object.fromEntries(Object.entries(all).filter(([k]) => (names ? names.includes(k) : true)));\n const presets = [...this.doc.presets.filter((p) => p.name !== name), { name: name.trim(), values }];\n this.commit(`Save preset ${name}`, { ...this.doc, presets });\n return ok;\n }\n\n deletePreset(name: string): EditResult {\n if (!this.doc.presets.some((p) => p.name === name)) return fail(`There is no preset \"${name}\".`);\n this.commit(`Delete preset ${name}`, { ...this.doc, presets: this.doc.presets.filter((p) => p.name !== name) });\n return ok;\n }\n\n // ── Keyframes ───────────────────────────────────────────────────────\n\n /** Sets (or replaces) a keyframe; `value` defaults to the control's value at that time. */\n setKeyframe(name: string, time: number, value?: unknown, easing?: Easing, mergeKey?: string): EditResult {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n if (!(time >= 0)) return fail(\"A keyframe's time is 0 ms or more.\");\n if (easing !== undefined && !isEasing(easing)) return fail(`Unknown easing ${JSON.stringify(easing)}.`);\n let v: ControlValue;\n if (value === undefined) v = valuesAt(this.doc, time, [control])[name]!;\n else {\n const check = checkControlValue(control, value);\n if (!check.ok) return fail(check.message);\n v = check.value;\n }\n const track = this.doc.tracks[name];\n const existing = track?.keyframes.find((k) => Math.abs(k.time - time) < 1e-3);\n const keyframe: Keyframe = { time, value: v, ...((easing ?? existing?.easing) !== undefined && { easing: easing ?? existing?.easing }) };\n const keyframes = normalizeKeyframes([...(track?.keyframes ?? []), keyframe]);\n this.commit(`Keyframe ${control.label}`, { ...this.doc, tracks: { ...this.doc.tracks, [name]: { ...track, keyframes } } }, mergeKey);\n return ok;\n }\n\n removeKeyframe(name: string, time: number): EditResult {\n const track = this.doc.tracks[name];\n const keyframes = track?.keyframes.filter((k) => Math.abs(k.time - time) >= 1e-3);\n if (!track || keyframes!.length === track.keyframes.length) return fail(`\"${name}\" has no keyframe at ${time} ms.`);\n const tracks = { ...this.doc.tracks };\n if (keyframes!.length) tracks[name] = { ...track, keyframes: keyframes! };\n else delete tracks[name];\n this.commit(\"Delete keyframe\", { ...this.doc, tracks });\n return ok;\n }\n\n setKeyframeEasing(name: string, time: number, easing: Easing): EditResult {\n if (!isEasing(easing)) return fail(`Unknown easing ${JSON.stringify(easing)}.`);\n const track = this.doc.tracks[name];\n if (!track?.keyframes.some((k) => Math.abs(k.time - time) < 1e-3)) return fail(`\"${name}\" has no keyframe at ${time} ms.`);\n const keyframes = track.keyframes.map((k) => (Math.abs(k.time - time) < 1e-3 ? { ...k, easing } : k));\n this.commit(\"Set easing\", { ...this.doc, tracks: { ...this.doc.tracks, [name]: { ...track, keyframes } } });\n return ok;\n }\n\n /** Removes a control's track; it keeps the value it had at the playhead. */\n clearTrack(name: string): EditResult {\n if (!this.doc.tracks[name]) return fail(`\"${name}\" has no keyframes.`);\n const control = this.control(name);\n const value = control ? evaluateTrack(this.doc.tracks[name], this.time, control) : undefined;\n const tracks = { ...this.doc.tracks };\n delete tracks[name];\n const values = value === undefined ? this.doc.values : { ...this.doc.values, [name]: value };\n this.commit(\"Clear keyframes\", { ...this.doc, tracks, values });\n return ok;\n }\n\n /**\n * Replaces a control's whole track (or removes it with `undefined`), one\n * undo step. Every keyframe is checked like `setKeyframe`; values may be\n * any form the control accepts (colours as \"#RRGGBB\" too).\n */\n setTrack(name: string, track: { keyframes: readonly { time: number; value: unknown; easing?: unknown }[]; enabled?: boolean } | undefined): EditResult {\n const control = this.control(name);\n if (!control) return this.unknownControl(name);\n const tracks = { ...this.doc.tracks };\n if (!track || track.keyframes.length === 0) {\n if (!tracks[name]) return ok;\n delete tracks[name];\n this.commit(\"Clear keyframes\", { ...this.doc, tracks });\n return ok;\n }\n const keyframes: Keyframe[] = [];\n for (const k of track.keyframes) {\n if (!(k.time >= 0)) return fail(\"A keyframe's time is 0 ms or more.\");\n if (k.easing !== undefined && !isEasing(k.easing)) return fail(`Unknown easing ${JSON.stringify(k.easing)}.`);\n const check = checkControlValue(control, k.value);\n if (!check.ok) return fail(`At ${k.time} ms: ${check.message}`);\n keyframes.push({ time: k.time, value: check.value, ...(k.easing !== undefined && { easing: k.easing as Easing }) });\n }\n tracks[name] = { keyframes: normalizeKeyframes(keyframes), ...(track.enabled === false && { enabled: false }) };\n this.commit(`Keyframes ${control.label}`, { ...this.doc, tracks });\n return ok;\n }\n\n setTiming(timing: { duration?: number; loop?: boolean }): EditResult {\n if (timing.duration !== undefined && !(timing.duration > 0)) return fail(\"The duration must be above 0 ms.\");\n this.commit(\"Set timing\", { ...this.doc, duration: timing.duration ?? this.doc.duration, loop: timing.loop ?? this.doc.loop });\n return ok;\n }\n\n // ── Playhead ────────────────────────────────────────────────────────\n\n play(): void {\n if (this.playing || this.disposed) return;\n if (!this.doc.loop && this.time >= this.doc.duration) this.time = 0;\n this.playing = true;\n this.changed();\n void this.run();\n }\n\n pause(): void {\n if (!this.playing) return;\n this.playing = false;\n this.changed();\n }\n\n toggle(): void {\n if (this.playing) this.pause();\n else this.play();\n }\n\n /** Moves the playhead (clamped to the timeline). */\n seek(time: number): void {\n const t = Math.min(Math.max(0, time), this.doc.duration);\n if (t === this.time) return;\n this.time = t;\n this.changed();\n }\n\n stop(): void {\n this.playing = false;\n this.time = 0;\n this.changed();\n }\n\n /** While recording, changing a value keyframes it at the playhead. */\n setRecording(recording: boolean): void {\n if (recording === this.recording) return;\n this.recording = recording;\n this.changed();\n }\n\n undo(): boolean {\n return this.history.undo();\n }\n\n redo(): boolean {\n return this.history.redo();\n }\n\n dispose(): void {\n this.disposed = true;\n this.playing = false;\n this.unsubscribeHistory();\n this.listeners.clear();\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private async run(): Promise<void> {\n let last = this.clock.now();\n while (this.playing && !this.disposed) {\n await this.clock.frame();\n if (!this.playing || this.disposed) return;\n const now = this.clock.now();\n let next = this.time + (now - last);\n last = now;\n if (next >= this.doc.duration) {\n if (this.doc.loop) next %= this.doc.duration;\n else {\n this.time = this.doc.duration;\n this.playing = false;\n this.changed();\n return;\n }\n }\n this.time = next;\n this.changed();\n }\n }\n\n private analysis() {\n if (this.controlsCache?.doc !== this.doc) {\n this.controlsCache = { doc: this.doc, controls: documentControls(this.doc), issues: documentIssues(this.doc) };\n }\n return this.controlsCache;\n }\n\n private control(name: string): ControlDef | undefined {\n return this.controls.find((c) => c.name === name);\n }\n\n private unknownControl(name: string): EditResult {\n return fail(`There is no control \"${name}\"; the controls are ${this.controls.map((c) => c.name).join(\", \") || \"none\"}.`);\n }\n\n private commit(label: string, next: EffectDocument, mergeKey?: string): void {\n const before = this.doc;\n const command = HistoryManager.createSnapshotCommand<EffectDocument>({\n name: label,\n before,\n after: next,\n apply: (doc) => {\n this.doc = doc;\n if (this.time > doc.duration) this.time = doc.duration;\n this.changed();\n },\n tag: mergeKey,\n });\n if (mergeKey) this.history.executeMerged(command, { key: mergeKey, windowMs: this.mergeWindowMs });\n else this.history.execute(command);\n }\n\n private changed(): void {\n this.state = null;\n if (this.listeners.size === 0) return;\n const state = this.getState();\n for (const listener of [...this.listeners]) listener(state);\n }\n}\n\n/** A new {@link EffectEditor}. */\nexport function createEffectEditor(options: EffectEditorOptions = {}): EffectEditor {\n return new EffectEditor(options);\n}\n","/**\n * The effect editor's commands. Menus, shortcuts, a command palette and\n * agents (through `createCommandToolProvider`) all run these by id, so every\n * entry point shares the same checks and undo steps.\n */\n\nimport type { CommandSpec } from \"@pixerate/editor/commands\";\nimport { EFFECT_CATEGORIES } from \"../catalog/types.js\";\nimport { findEffects } from \"../catalog/index.js\";\nimport type { EditResult, EffectEditor } from \"./editor.js\";\nimport { EASING_PRESETS, type Easing } from \"./timeline.js\";\n\n/** Throws the refusal, so the command registry reports it as a failed command with its message. */\nfunction done(result: EditResult): { ok: true } {\n if (!result.ok) throw new Error(result.message);\n return result;\n}\n\nconst easingSchema = {\n description: `An easing preset (${EASING_PRESETS.join(\", \")}) or {\"type\":\"cubic-bezier\",\"x1\",\"y1\",\"x2\",\"y2\"} or {\"type\":\"spring\",\"stiffness\",\"damping\",\"mass\"}.`,\n} as const;\n\nconst valueSchema = {\n description:\n 'A control value: a number (float, int), true/false (bool), an array of numbers (vec2–vec4; colours 0–1), a \"#RRGGBB\" colour, or an image URL (sampler2D).',\n} as const;\n\n/** A summary of the editor's state for agents: the document, controls, values at the playhead, issues and compile errors. */\nexport function describeEditor(editor: EffectEditor) {\n const state = editor.getState();\n const doc = state.document;\n return {\n name: doc.name,\n basedOn: doc.basedOn,\n kind: doc.effect.kind,\n ...(doc.effect.kind === \"shader\" ? { source: doc.effect.source } : { motion: doc.effect.motion, vectorControls: doc.effect.controls }),\n controls: state.controls.map((c) => ({\n name: c.name,\n type: c.type,\n label: c.label,\n ...(c.color && { color: true }),\n ...(c.type !== \"bool\" && c.type !== \"sampler2D\" && { min: c.min, max: c.max, step: c.step }),\n default: c.default,\n })),\n values: state.values,\n tracks: doc.tracks,\n presets: doc.presets.map((p) => p.name),\n timeline: { duration: doc.duration, loop: doc.loop, time: state.time, playing: state.playing, recording: state.recording },\n issues: state.issues,\n compile: state.compile,\n history: { canUndo: state.history.canUndo, canRedo: state.history.canRedo },\n };\n}\n\n/** The effect editor's commands (the registry adds `history.undo` / `history.redo`). */\nexport function effectCommands(): CommandSpec<EffectEditor>[] {\n return [\n {\n id: \"effect.describe\",\n label: \"Describe effect\",\n group: \"Effect\",\n description: \"The effect being edited: its source (or picture), controls, values at the playhead, keyframes, presets, timeline, issues and compile errors.\",\n readOnly: true,\n run: (editor) => describeEditor(editor),\n },\n {\n id: \"catalog.search\",\n label: \"Search effects\",\n group: \"Catalog\",\n description: \"Searches the built-in effect catalog. Load one with effect.load to start from it.\",\n readOnly: true,\n params: {\n type: \"object\",\n properties: {\n text: { type: \"string\", description: \"Words to look for (all must match).\" },\n category: { type: \"string\", enum: EFFECT_CATEGORIES },\n kind: { type: \"string\", enum: [\"shader\", \"vector\"] },\n },\n additionalProperties: false,\n },\n run: (_, params: { text?: string; category?: (typeof EFFECT_CATEGORIES)[number]; kind?: \"shader\" | \"vector\" }) =>\n findEffects(params ?? {}).map((e) => ({ id: e.id, name: e.name, kind: e.kind, category: e.category, description: e.description, presets: e.presets?.map((p) => p.name) ?? [] })),\n },\n {\n id: \"effect.load\",\n label: \"Load effect\",\n group: \"Effect\",\n description: \"Starts over from a catalog effect (by id, optionally with one of its presets), or from a shader's source.\",\n params: {\n type: \"object\",\n properties: {\n id: { type: \"string\", description: \"A catalog effect id (see catalog.search).\" },\n preset: { type: \"string\" },\n source: { type: \"string\", description: \"A shader source to start from instead of a catalog effect.\" },\n name: { type: \"string\" },\n },\n additionalProperties: false,\n },\n run: (editor, p: { id?: string; preset?: string; source?: string; name?: string }) =>\n done(p?.source !== undefined ? editor.load({ source: p.source, name: p.name }) : editor.load(p?.id, p?.preset)),\n },\n {\n id: \"effect.setSource\",\n label: \"Set shader source\",\n group: \"Effect\",\n description:\n \"Replaces the shader's GLSL ES 3.00 source (Shadertoy-style mainImage; controls are uniforms annotated `// @control`). Compile errors show up in effect.describe once the preview has compiled it.\",\n params: { type: \"object\", properties: { source: { type: \"string\" } }, required: [\"source\"], additionalProperties: false },\n run: (editor, p: { source: string }) => done(editor.setSource(p.source)),\n },\n {\n id: \"effect.rename\",\n label: \"Rename effect\",\n group: \"Effect\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.rename(p.name)),\n },\n {\n id: \"values.set\",\n label: \"Set values\",\n group: \"Values\",\n description: \"Sets control values by name, all or nothing. A keyframed control (or any, while recording) gets a keyframe at the playhead instead.\",\n params: {\n type: \"object\",\n properties: { values: { type: \"object\", additionalProperties: valueSchema, description: \"Control name → value.\" } },\n required: [\"values\"],\n additionalProperties: false,\n },\n run: (editor, p: { values: Record<string, unknown> }) => done(editor.setValues(p.values)),\n },\n {\n id: \"values.reset\",\n label: \"Reset values\",\n group: \"Values\",\n description: \"Puts controls back to their defaults: the named ones, or all.\",\n params: { type: \"object\", properties: { names: { type: \"array\", items: { type: \"string\" } } }, additionalProperties: false },\n run: (editor, p: { names?: string[] }) => done(editor.resetValues(p?.names)),\n },\n {\n id: \"values.randomize\",\n label: \"Randomize values\",\n group: \"Values\",\n keywords: [\"shuffle\", \"surprise\", \"explore\"],\n params: { type: \"object\", properties: { names: { type: \"array\", items: { type: \"string\" } } }, additionalProperties: false },\n run: (editor, p: { names?: string[] }) => done(editor.randomize(p?.names)),\n },\n {\n id: \"preset.apply\",\n label: \"Apply preset\",\n group: \"Presets\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.applyPreset(p.name)),\n },\n {\n id: \"preset.save\",\n label: \"Save preset\",\n group: \"Presets\",\n description: \"Saves the values at the playhead (all, or the named controls) as a preset.\",\n params: {\n type: \"object\",\n properties: { name: { type: \"string\" }, names: { type: \"array\", items: { type: \"string\" } } },\n required: [\"name\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; names?: string[] }) => done(editor.savePreset(p.name, p.names)),\n },\n {\n id: \"preset.delete\",\n label: \"Delete preset\",\n group: \"Presets\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.deletePreset(p.name)),\n },\n {\n id: \"keyframe.set\",\n label: \"Set keyframe\",\n group: \"Keyframes\",\n description: \"Keyframes a control at a time (ms). Without a value, keyframes its current value there. The easing shapes the segment leaving this keyframe.\",\n params: {\n type: \"object\",\n properties: { name: { type: \"string\" }, time: { type: \"number\", minimum: 0 }, value: valueSchema, easing: easingSchema },\n required: [\"name\", \"time\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; time: number; value?: unknown; easing?: Easing }) => done(editor.setKeyframe(p.name, p.time, p.value, p.easing)),\n },\n {\n id: \"keyframe.remove\",\n label: \"Delete keyframe\",\n group: \"Keyframes\",\n params: { type: \"object\", properties: { name: { type: \"string\" }, time: { type: \"number\" } }, required: [\"name\", \"time\"], additionalProperties: false },\n run: (editor, p: { name: string; time: number }) => done(editor.removeKeyframe(p.name, p.time)),\n },\n {\n id: \"keyframe.setEasing\",\n label: \"Set keyframe easing\",\n group: \"Keyframes\",\n params: {\n type: \"object\",\n properties: { name: { type: \"string\" }, time: { type: \"number\" }, easing: easingSchema },\n required: [\"name\", \"time\", \"easing\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; time: number; easing: Easing }) => done(editor.setKeyframeEasing(p.name, p.time, p.easing)),\n },\n {\n id: \"keyframe.setTrack\",\n label: \"Set keyframes\",\n group: \"Keyframes\",\n description: \"Replaces all of a control's keyframes at once (an empty list removes them). Times are ms; each easing shapes the segment leaving its keyframe.\",\n params: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n keyframes: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: { time: { type: \"number\", minimum: 0 }, value: valueSchema, easing: easingSchema },\n required: [\"time\", \"value\"],\n },\n },\n },\n required: [\"name\", \"keyframes\"],\n additionalProperties: false,\n },\n run: (editor, p: { name: string; keyframes: { time: number; value: unknown; easing?: unknown }[] }) => done(editor.setTrack(p.name, { keyframes: p.keyframes })),\n },\n {\n id: \"keyframe.clear\",\n label: \"Clear keyframes\",\n group: \"Keyframes\",\n description: \"Removes a control's keyframes; it keeps the value it has at the playhead.\",\n params: { type: \"object\", properties: { name: { type: \"string\" } }, required: [\"name\"], additionalProperties: false },\n run: (editor, p: { name: string }) => done(editor.clearTrack(p.name)),\n },\n {\n id: \"timeline.setTiming\",\n label: \"Set timing\",\n group: \"Timeline\",\n params: {\n type: \"object\",\n properties: { duration: { type: \"number\", exclusiveMinimum: 0, description: \"Ms.\" }, loop: { type: \"boolean\" } },\n additionalProperties: false,\n },\n run: (editor, p: { duration?: number; loop?: boolean }) => done(editor.setTiming(p ?? {})),\n },\n {\n id: \"timeline.play\",\n label: \"Play\",\n group: \"Timeline\",\n run: (editor) => editor.play(),\n },\n {\n id: \"timeline.pause\",\n label: \"Pause\",\n group: \"Timeline\",\n run: (editor) => editor.pause(),\n },\n {\n id: \"timeline.toggle\",\n label: \"Play / pause\",\n group: \"Timeline\",\n shortcut: \"Space\",\n run: (editor) => editor.toggle(),\n },\n {\n id: \"timeline.seek\",\n label: \"Go to time\",\n group: \"Timeline\",\n params: { type: \"object\", properties: { time: { type: \"number\", minimum: 0, description: \"Ms.\" } }, required: [\"time\"], additionalProperties: false },\n run: (editor, p: { time: number }) => editor.seek(p.time),\n },\n {\n id: \"timeline.stop\",\n label: \"Stop\",\n group: \"Timeline\",\n run: (editor) => editor.stop(),\n },\n {\n id: \"timeline.toggleRecording\",\n label: \"Record keyframes\",\n group: \"Timeline\",\n description: \"While recording, changing a value keyframes it at the playhead.\",\n run: (editor) => editor.setRecording(!editor.getState().recording),\n },\n ];\n}\n","/**\n * Agents edit effects through the editor's commands. Flair ships the tools\n * and the instructions; the model, its credentials and the agent loop are\n * the host's. Serve the tools with `@pixerate/editor/tools`' `ToolRegistry`\n * (MCP `tools/list` and `tools/call`), or call them directly with `callTool`.\n */\n\nimport { createCommandToolProvider } from \"@pixerate/editor/commands\";\nimport {\n createToolRegistry,\n toToolResult,\n type AgentTool,\n type AgentToolProvider,\n type McpToolDefinition,\n type McpToolResult,\n type ToolRegistry,\n type ToolRegistryOptions,\n} from \"@pixerate/editor/tools\";\nimport { BUILTIN_UNIFORMS } from \"../shader/program.js\";\nimport type { EffectEditor } from \"./editor.js\";\n\nexport interface EffectToolProviderOptions {\n /** Provider id (default `flair-effect`). */\n id?: string;\n /** Prefix every tool name with `<namespace>__`. */\n namespace?: string;\n /**\n * Captures the preview as a data URL (PNG or JPEG), adding a `view_effect`\n * tool so the agent can look at what it made. With `ShaderRenderer`:\n * `() => renderer.capture({ type: \"image/png\" })`.\n */\n capture?: () => string | null | undefined | Promise<string | null | undefined>;\n /** Commands agents may not see or run. */\n exclude?: (id: string) => boolean;\n}\n\n/**\n * The effect editor as agent tools (MCP-shaped, for `@pixerate/editor`'s\n * `ToolRegistry` or any tool loop):\n *\n * - `list_commands`, `run_command`, `run_commands`: every editor command\n * (describe, search the catalog, load, set source, values, presets,\n * keyframes, timing, undo and redo).\n * - `view_effect`, when `capture` is given: an image of the preview.\n */\nexport function createEffectToolProvider(editor: EffectEditor, options: EffectToolProviderOptions = {}): AgentToolProvider {\n const base = createCommandToolProvider(editor.commands, {\n id: options.id ?? \"flair-effect\",\n namespace: options.namespace,\n exclude: options.exclude,\n });\n const extra: AgentTool[] = [];\n if (options.capture) {\n const capture = options.capture;\n extra.push({\n name: options.namespace ? `${options.namespace}__view_effect` : \"view_effect\",\n title: \"View effect\",\n description: \"An image of the effect's preview as it is drawn now, to check what your changes look like.\",\n inputSchema: { type: \"object\", properties: {}, additionalProperties: false },\n annotations: { readOnlyHint: true },\n handler: async () => {\n const url = await capture();\n const match = url ? /^data:([^;,]+);base64,(.*)$/.exec(url) : null;\n if (!match) return { content: [{ type: \"text\", text: \"Nothing to show yet: the preview has not drawn a frame.\" }], isError: true };\n return { content: [{ type: \"image\", data: match[2]!, mimeType: match[1]! }] };\n },\n });\n }\n const baseTools = () => (typeof base.tools === \"function\" ? base.tools() : base.tools);\n return { id: base.id, namespace: base.namespace, subscribe: base.subscribe, tools: () => [...baseTools(), ...extra] };\n}\n\n/**\n * A `ToolRegistry` (from `@pixerate/editor/tools`) serving the effect editor's\n * tools: `listTools()` and `callTool(name, args)` are MCP's `tools/list` and\n * `tools/call`, with arguments checked against each tool's schema. Register\n * more providers on it to give the agent other tools too.\n */\nexport function createEffectToolRegistry(\n editor: EffectEditor,\n options: EffectToolProviderOptions & { registry?: ToolRegistryOptions } = {},\n): ToolRegistry {\n const registry = createToolRegistry({ validateArguments: true, ...options.registry });\n registry.register(createEffectToolProvider(editor, options));\n return registry;\n}\n\n/** A provider's tool definitions without their handlers, to send to a model. */\nexport function toolDefinitions(provider: AgentToolProvider): McpToolDefinition[] {\n const tools = typeof provider.tools === \"function\" ? provider.tools() : provider.tools;\n return tools.map(({ handler: _handler, ...definition }) => definition);\n}\n\n/**\n * Runs one of a provider's tools by name without a registry: the handler's\n * result comes back as an MCP `tools/call` result, and a thrown error as an\n * `isError` result. (A `ToolRegistry` also checks the arguments first.)\n */\nexport async function callTool(provider: AgentToolProvider, name: string, args: Record<string, unknown> = {}): Promise<McpToolResult> {\n const tools = typeof provider.tools === \"function\" ? provider.tools() : provider.tools;\n const tool = tools.find((t) => t.name === name);\n if (!tool) return { content: [{ type: \"text\", text: `There is no tool \"${name}\".` }], isError: true };\n try {\n return toToolResult(await tool.handler(args, { name, providerId: provider.id }));\n } catch (e) {\n return { content: [{ type: \"text\", text: (e as Error).message }], isError: true };\n }\n}\n\n/** A system prompt for agents that author effects with these tools. Add your product's own guidance to it. */\nexport const EFFECT_AGENT_INSTRUCTIONS = `You design web visual effects in an effect editor. Work through its tools: \\`list_commands\\` shows every command with its parameters; \\`run_command\\` runs one; \\`run_commands\\` runs several in order.\n\nStart with \\`effect.describe\\` to see what is being edited. To start from something close, use \\`catalog.search\\` then \\`effect.load\\` (with a preset if one fits). Every change is one undo step; \\`history.undo\\` reverts a step.\n\nShader effects are GLSL ES 3.00 fragment shaders written Shadertoy-style:\n- Define \\`void mainImage(out vec4 fragColor, in vec2 fragCoord)\\`; the output is opaque (alpha is ignored).\n- Built-in uniforms (do not declare them): ${BUILTIN_UNIFORMS.map((u) => `\\`${u.type} ${u.name}\\` (${u.doc})`).join(\"; \")}.\n- Declare controls as uniforms with an annotation on the same line, e.g.\n uniform float uSpeed; // @control min=0 max=4 default=1 label=\"Speed\"\n uniform vec3 uTint; // @control color default=#77C16A label=\"Tint\"\n uniform bool uGrain; // @control default=true label=\"Film grain\"\n uniform sampler2D uPhoto; // @control filter=linear wrap=clamp label=\"Photo\"\n Types: float, int, bool, vec2, vec3, vec4, sampler2D. Every uniform you declare should be a control.\n- Write float literals with a decimal point (1.0, not 1); GLSL does not convert int to float implicitly. Use \\`texture()\\`, not \\`texture2D()\\`.\n- After \\`effect.setSource\\`, call \\`effect.describe\\` and read \\`compile\\`: fix any errors (their line numbers match your source) before going on.\n\nVector effects are animated SVG pictures: change their controls' values; their picture is edited with care, keeping element ids that animations refer to.\n\nMake effects that loop smoothly and read well behind content: keep motion calm unless asked otherwise, keep contrast where text may sit, and expose the few controls a designer would actually want (colours, speed, scale, intensity) with sensible ranges. Use keyframes (\\`keyframe.set\\`, in ms) for changes over the timeline. When you can, look at the result with \\`view_effect\\` and refine it.`;\n","/**\n * Keeps a preview in step with an editor: the editor owns the playhead and\n * the values; the preview draws what it is told.\n */\n\nimport type { ControlValue } from \"../shader/controls.js\";\nimport type { CompileStatus } from \"../shader/renderer.js\";\nimport { svgView, type SvgView } from \"../vector/svg.js\";\nimport { motionAt } from \"./document.js\";\nimport type { EffectEditor, EffectEditorState } from \"./editor.js\";\n\n/** What {@link bindShaderRenderer} needs of a renderer (`ShaderRenderer` fits). */\nexport interface ShaderPreview {\n setSource(source: string): CompileStatus;\n setValues(values: Record<string, ControlValue> | undefined): void;\n /** Seconds. */\n seek(time: number): void;\n pause(): void;\n}\n\n/**\n * Drives a shader preview from the editor: the source (reporting compile\n * results back), the values at the playhead, and the time. The renderer is\n * paused so the editor's playhead is the only clock. Returns an unbind\n * function.\n */\nexport function bindShaderRenderer(editor: EffectEditor, renderer: ShaderPreview): () => void {\n let source: string | undefined;\n renderer.pause();\n const sync = (state: EffectEditorState) => {\n const effect = state.document.effect;\n if (effect.kind !== \"shader\") return;\n if (effect.source !== source) {\n source = effect.source;\n editor.reportCompile(renderer.setSource(source));\n }\n renderer.setValues(state.values);\n renderer.seek(state.time / 1000);\n };\n sync(editor.getState());\n return editor.subscribe(sync);\n}\n\n/**\n * Draws a vector effect into `container` from the editor: the picture with\n * the values at the playhead, at the playhead's time. Returns an unbind\n * function, which also removes the picture.\n */\nexport function bindSvgView(editor: EffectEditor, container: HTMLElement): () => void {\n let view: SvgView | undefined;\n let key: string | undefined;\n const sync = (state: EffectEditorState) => {\n const motion = motionAt(state.document, state.time);\n if (!motion) {\n view?.element.remove();\n view = undefined;\n key = undefined;\n return;\n }\n const next = JSON.stringify(motion);\n if (next !== key) {\n key = next;\n view?.element.remove();\n view = svgView(motion);\n container.appendChild(view.element);\n }\n view!.draw(state.time);\n };\n sync(editor.getState());\n const unsubscribe = editor.subscribe(sync);\n return () => {\n unsubscribe();\n view?.element.remove();\n };\n}\n","/**\n * An effect's controls as a `@pixerate/editor/parameters` schema, and a\n * parameters controller kept in step with an `EffectEditor`. With them, any\n * parameters UI (the ready-made React and Svelte panels, or your own) edits\n * an effect: values, image picks and keyframe diamonds go through the effect\n * editor, so its undo history and agent tools see every change.\n */\n\nimport {\n ParametersController,\n rgba01ToHex,\n type ParameterField,\n type ParameterImageOption,\n type ParameterKeyframes,\n type ParameterSchema,\n type ParameterTrack,\n type ParameterValue,\n type ParameterValues,\n} from \"@pixerate/editor/parameters\";\nimport type { ControlDef, ControlValue } from \"../shader/controls.js\";\nimport type { EffectEditor, EffectEditorState } from \"./editor.js\";\nimport type { Track } from \"./timeline.js\";\n\nexport interface EffectParameterOptions {\n /** Pictures image controls offer to pick from (any URL still works). */\n images?: readonly ParameterImageOption[];\n /** Schema title (default: none). */\n title?: string;\n}\n\n/** One control as a parameter field. */\nexport function parameterFieldFor(control: ControlDef, options: EffectParameterOptions = {}): ParameterField {\n const base = { id: control.name, label: control.label };\n switch (control.type) {\n case \"bool\":\n return { ...base, type: \"boolean\", default: control.default === true };\n case \"int\":\n return { ...base, type: \"int\", min: control.min, max: control.max, step: Math.max(1, Math.round(control.step)), default: Number(control.default) };\n case \"float\":\n return { ...base, type: \"number\", min: control.min, max: control.max, step: control.step, default: Number(control.default) };\n case \"sampler2D\":\n return { ...base, type: \"image\", default: String(control.default), ...(options.images && { options: options.images }) };\n default: {\n const value = control.default as number[];\n if (control.color) {\n const alpha = control.type === \"vec4\";\n return { ...base, type: \"color\", alpha, default: rgba01ToHex(value, alpha) };\n }\n return { ...base, type: \"vector\", size: value.length as 2 | 3 | 4, min: control.min, max: control.max, step: control.step, default: [...value] };\n }\n }\n}\n\n/** An effect's controls as a parameters schema. */\nexport function parameterSchemaFor(controls: readonly ControlDef[], options: EffectParameterOptions = {}): ParameterSchema {\n return { ...(options.title && { title: options.title }), fields: controls.map((c) => parameterFieldFor(c, options)) };\n}\n\n/** A control value as a parameter value: colours become `#rrggbb` (`#rrggbbaa` for `vec4`). */\nexport function toParameterValue(control: ControlDef, value: ControlValue): ParameterValue {\n if (control.color && Array.isArray(value)) return rgba01ToHex(value, control.type === \"vec4\");\n return Array.isArray(value) ? [...value] : value;\n}\n\n/** Control values as parameter values (see {@link toParameterValue}). */\nexport function toParameterValues(controls: readonly ControlDef[], values: Record<string, ControlValue>): ParameterValues {\n const out: ParameterValues = {};\n for (const control of controls) if (values[control.name] !== undefined) out[control.name] = toParameterValue(control, values[control.name]!);\n return out;\n}\n\nfunction toParameterTrack(control: ControlDef | undefined, track: Track | undefined): ParameterTrack | undefined {\n if (!track) return undefined;\n return {\n ...(track.enabled === false && { enabled: false }),\n keyframes: track.keyframes.map((k) => ({ ...k, value: control ? toParameterValue(control, k.value) : (k.value as ParameterValue) })),\n };\n}\n\nexport interface EffectParametersBinding {\n /** Drive a parameters UI with it (e.g. `<ParametersPanel controller={…} />`). */\n readonly controller: ParametersController;\n /** Stops following the editor. */\n dispose(): void;\n}\n\n/**\n * A `ParametersController` for the effect in `editor`, kept in step with it:\n * the schema follows the effect's controls (a recompiled shader updates it),\n * values and the playhead follow the editor, and changes made through the\n * controller (fields, keyframe diamonds, reset, randomize) are applied to the\n * editor, which records the undo steps. Use the editor's undo, not the\n * controller's, and its presets (the controller's own presets are not saved).\n */\nexport function bindEffectParameters(editor: EffectEditor, options: EffectParameterOptions = {}): EffectParametersBinding {\n let state = editor.getState();\n let controls = state.controls;\n let signature = JSON.stringify(controls);\n let syncing = false;\n const byName = () => new Map(controls.map((c) => [c.name, c]));\n\n const keyframes: ParameterKeyframes = {\n getTrack: (id) => toParameterTrack(byName().get(id), editor.getState().document.tracks[id]),\n setTrack: (id, track) => {\n editor.setTrack(id, track);\n },\n subscribe: (listener) => editor.subscribe(() => listener()),\n };\n\n const controller = new ParametersController({\n schema: parameterSchemaFor(controls, options),\n values: toParameterValues(controls, state.values),\n time: state.time,\n keyframes,\n onChange: (values, changed) => {\n if (syncing) return;\n const result =\n changed.length === 1\n ? editor.setValue(changed[0]!, values[changed[0]!])\n : editor.setValues(Object.fromEntries(changed.map((id) => [id, values[id]])));\n // A refused change (or one that keyframed instead): show what the editor holds.\n if (!result.ok) sync(editor.getState());\n },\n });\n\n const sync = (next: EffectEditorState) => {\n state = next;\n const nextSignature = JSON.stringify(next.controls);\n syncing = true;\n try {\n if (nextSignature !== signature) {\n signature = nextSignature;\n controls = next.controls;\n controller.setSchema(parameterSchemaFor(controls, options));\n }\n controller.setTime(next.time, toParameterValues(controls, next.values));\n } finally {\n syncing = false;\n }\n };\n\n const unsubscribe = editor.subscribe(sync);\n return {\n controller,\n dispose: () => {\n unsubscribe();\n controller.destroy();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/flair",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Web visual effects and motion graphics: WebGL2 shader effects, animated vector effects, a catalog of ready-made effects, and a UI-agnostic, agent-ready effect editor.",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"homepage": "https://pixerate.github.io/flair/",
|
|
12
12
|
"bugs": "https://github.com/Pixerate/flair/issues",
|
|
13
13
|
"type": "module",
|
|
14
|
-
"packageManager": "pnpm@10.28.2",
|
|
15
14
|
"sideEffects": false,
|
|
16
15
|
"files": [
|
|
17
16
|
"dist",
|
|
@@ -48,17 +47,6 @@
|
|
|
48
47
|
"bin": {
|
|
49
48
|
"flair": "dist/cli.js"
|
|
50
49
|
},
|
|
51
|
-
"scripts": {
|
|
52
|
-
"build": "tsup",
|
|
53
|
-
"dev": "pnpm --filter demo dev",
|
|
54
|
-
"test": "vitest run",
|
|
55
|
-
"test:watch": "vitest",
|
|
56
|
-
"typecheck": "tsc --noEmit && pnpm --filter demo typecheck",
|
|
57
|
-
"changeset": "changeset",
|
|
58
|
-
"version-packages": "changeset version",
|
|
59
|
-
"release": "changeset publish",
|
|
60
|
-
"prepublishOnly": "pnpm build"
|
|
61
|
-
},
|
|
62
50
|
"keywords": [
|
|
63
51
|
"webgl2",
|
|
64
52
|
"shaders",
|
|
@@ -76,7 +64,7 @@
|
|
|
76
64
|
"gifenc": "^1.0.3"
|
|
77
65
|
},
|
|
78
66
|
"peerDependencies": {
|
|
79
|
-
"@pixerate/editor": ">=0.
|
|
67
|
+
"@pixerate/editor": ">=0.29.0 <1"
|
|
80
68
|
},
|
|
81
69
|
"peerDependenciesMeta": {
|
|
82
70
|
"@pixerate/editor": {
|
|
@@ -85,7 +73,7 @@
|
|
|
85
73
|
},
|
|
86
74
|
"devDependencies": {
|
|
87
75
|
"@changesets/cli": "^2.27.12",
|
|
88
|
-
"@pixerate/editor": "^0.
|
|
76
|
+
"@pixerate/editor": "^0.29.0",
|
|
89
77
|
"@types/node": "^24.19.2",
|
|
90
78
|
"jsdom": "^30.1.3",
|
|
91
79
|
"tsup": "^8.5",
|
|
@@ -94,5 +82,15 @@
|
|
|
94
82
|
},
|
|
95
83
|
"engines": {
|
|
96
84
|
"node": ">=20"
|
|
85
|
+
},
|
|
86
|
+
"scripts": {
|
|
87
|
+
"build": "tsup",
|
|
88
|
+
"dev": "pnpm --filter demo dev",
|
|
89
|
+
"test": "vitest run",
|
|
90
|
+
"test:watch": "vitest",
|
|
91
|
+
"typecheck": "tsc --noEmit && pnpm --filter demo typecheck",
|
|
92
|
+
"changeset": "changeset",
|
|
93
|
+
"version-packages": "changeset version",
|
|
94
|
+
"release": "changeset publish"
|
|
97
95
|
}
|
|
98
|
-
}
|
|
96
|
+
}
|