@pixerate/flair 0.33.0 → 0.35.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.
@@ -6,6 +6,7 @@ import {
6
6
  } from "../chunk-HDYBY3PQ.js";
7
7
  import {
8
8
  EFFECT_CATEGORIES,
9
+ EFFECT_CATEGORY_GROUPS,
9
10
  EFFECT_INPUTS,
10
11
  EFFECT_INTERACTIONS,
11
12
  VIDEO_USES,
@@ -15,7 +16,7 @@ import {
15
16
  renderVectorEffect,
16
17
  vectorControlIssues,
17
18
  vectorEffectControls
18
- } from "../chunk-4FFBIVPI.js";
19
+ } from "../chunk-IHUNCBMF.js";
19
20
  import "../chunk-EHDJ3CPJ.js";
20
21
  import {
21
22
  checkControlValue,
@@ -23,6 +24,7 @@ import {
23
24
  parseControls
24
25
  } from "../chunk-VYOMXE6I.js";
25
26
  import {
27
+ svgDuration,
26
28
  svgView
27
29
  } from "../chunk-JETRHCP3.js";
28
30
  import {
@@ -115,7 +117,7 @@ function createEffectDocument(from) {
115
117
  },
116
118
  values: {},
117
119
  tracks: {},
118
- duration: effect.kind === "shader" && effect.loop ? Math.round(effect.loop * 1e3) : DEFAULT_DURATION,
120
+ duration: effect.kind === "vector" ? Math.round(svgDuration(effect.motion) + (effect.hold ?? 600)) : effect.loop ? Math.round(effect.loop * 1e3) : DEFAULT_DURATION,
119
121
  loop: true,
120
122
  presets: (effect.presets ?? []).map((p) => ({ name: p.name, values: { ...p.values } }))
121
123
  };
@@ -232,6 +234,12 @@ var easingSchema = {
232
234
  var valueSchema = {
233
235
  description: 'A control value: a number (float, int), true/false (bool), an array of numbers (vec2\u2013vec4; colours 0\u20131), a "#RRGGBB" colour, an image URL (sampler2D), or a string (text).'
234
236
  };
237
+ var CATALOG_SEARCH_LIMIT = 20;
238
+ function compileStatus(editor) {
239
+ const state = editor.getState();
240
+ if (state.document.effect.kind !== "shader") return void 0;
241
+ return state.compile ?? "not compiled yet: no renderer has compiled this source, so compile errors are unknown";
242
+ }
235
243
  function describeEditor(editor) {
236
244
  const state = editor.getState();
237
245
  const doc = state.document;
@@ -253,7 +261,7 @@ function describeEditor(editor) {
253
261
  presets: doc.presets.map((p) => p.name),
254
262
  timeline: { duration: doc.duration, loop: doc.loop, time: state.time, playing: state.playing, recording: state.recording },
255
263
  issues: state.issues,
256
- compile: state.compile,
264
+ ...doc.effect.kind === "shader" && { compile: compileStatus(editor) },
257
265
  history: { canUndo: state.history.canUndo, canRedo: state.history.canRedo }
258
266
  };
259
267
  }
@@ -271,13 +279,18 @@ function effectCommands() {
271
279
  id: "catalog.search",
272
280
  label: "Search effects",
273
281
  group: "Catalog",
274
- description: "Searches the built-in effect catalog. Load one with effect.load to start from it.",
282
+ description: `Searches the built-in effect catalog: {total, effects}, at most \`limit\` effects (default ${CATALOG_SEARCH_LIMIT}). Narrow with words and filters rather than raising the limit. Load one with effect.load to start from it.`,
275
283
  readOnly: true,
276
284
  params: {
277
285
  type: "object",
278
286
  properties: {
279
287
  text: { type: "string", description: "Words to look for (all must match)." },
280
288
  category: { type: "string", enum: EFFECT_CATEGORIES },
289
+ group: {
290
+ type: "string",
291
+ enum: EFFECT_CATEGORY_GROUPS.map((g) => g.name),
292
+ description: `A set of related categories: ${EFFECT_CATEGORY_GROUPS.map((g) => `${g.name} (${g.categories.join(", ")})`).join("; ")}.`
293
+ },
281
294
  kind: { type: "string", enum: ["shader", "vector"] },
282
295
  video: {
283
296
  type: "array",
@@ -294,25 +307,37 @@ function effectCommands() {
294
307
  items: { type: "string", enum: EFFECT_INTERACTIONS },
295
308
  description: "Only effects that answer the pointer in one of these ways: hover, press, click, trail (follows where it has been), pointer (follows where it is). An empty list finds effects that don't answer it."
296
309
  },
297
- transparent: { type: "boolean", description: "Only overlays (true), drawn over other content, or only effects that fill their frame (false)." }
310
+ transparent: { type: "boolean", description: "Only overlays (true), drawn over other content, or only effects that fill their frame (false)." },
311
+ limit: { type: "integer", minimum: 1, description: `Most effects to return (default ${CATALOG_SEARCH_LIMIT}).` },
312
+ offset: { type: "integer", minimum: 0, description: "Effects to skip first, to page through more (default 0)." }
298
313
  },
299
314
  additionalProperties: false
300
315
  },
301
- run: (_, params) => findEffects(params ?? {}).map((e) => {
302
- const inputs = effectInputs(e);
316
+ run: (_, params) => {
317
+ const { limit = CATALOG_SEARCH_LIMIT, offset = 0, ...filters } = params ?? {};
318
+ const found = findEffects(filters);
319
+ const effects = found.slice(offset, offset + limit).map((e) => {
320
+ const inputs = effectInputs(e);
321
+ return {
322
+ id: e.id,
323
+ name: e.name,
324
+ kind: e.kind,
325
+ category: e.category,
326
+ description: e.description,
327
+ ...e.kind === "shader" && e.video ? { video: e.video } : {},
328
+ accepts: inputs.accepts,
329
+ ...inputs.interactions.length ? { interactions: inputs.interactions } : {},
330
+ ...inputs.transparent ? { transparent: true } : {},
331
+ presets: e.presets?.map((p) => p.name) ?? []
332
+ };
333
+ });
334
+ const more = found.length - offset - effects.length;
303
335
  return {
304
- id: e.id,
305
- name: e.name,
306
- kind: e.kind,
307
- category: e.category,
308
- description: e.description,
309
- ...e.kind === "shader" && e.video ? { video: e.video } : {},
310
- accepts: inputs.accepts,
311
- ...inputs.interactions.length ? { interactions: inputs.interactions } : {},
312
- ...inputs.transparent ? { transparent: true } : {},
313
- presets: e.presets?.map((p) => p.name) ?? []
336
+ total: found.length,
337
+ effects,
338
+ ...more > 0 && { more: `${more} more match; narrow the search, or pass offset ${offset + effects.length} for the next ones.` }
314
339
  };
315
- })
340
+ }
316
341
  },
317
342
  {
318
343
  id: "effect.load",
@@ -335,9 +360,40 @@ function effectCommands() {
335
360
  id: "effect.setSource",
336
361
  label: "Set shader source",
337
362
  group: "Effect",
338
- description: "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.",
363
+ description: "Replaces a shader effect's GLSL ES 3.00 source (Shadertoy-style mainImage; controls are uniforms annotated `// @control`). Returns the compile report when a preview compiled it (errors carry your source's line numbers), and the document's issues.",
339
364
  params: { type: "object", properties: { source: { type: "string" } }, required: ["source"], additionalProperties: false },
340
- run: (editor, p) => done(editor.setSource(p.source))
365
+ run: (editor, p) => {
366
+ done(editor.setSource(p.source));
367
+ return { ok: true, compile: compileStatus(editor), issues: editor.getState().issues };
368
+ }
369
+ },
370
+ {
371
+ id: "effect.setMotion",
372
+ label: "Set vector picture",
373
+ group: "Effect",
374
+ description: "Replaces a vector effect's picture: {svg, animate?}. `svg` is one self-contained <svg> with a viewBox (no scripts); `{{name: type \u2026}}` placeholders in it declare controls and `{{name}}` fills them. `animate` lists changes to elements by id: {id, step?, delay?, ms?, ease?, attrs?: {name: [from, to]}, draw?, view?, along?, count?, write?}. Returns the document's issues.",
375
+ params: {
376
+ type: "object",
377
+ properties: {
378
+ motion: {
379
+ type: "object",
380
+ properties: { svg: { type: "string" }, animate: { type: "array", items: { type: "object" } } },
381
+ required: ["svg"],
382
+ description: "The picture and its animation."
383
+ },
384
+ controls: {
385
+ type: "array",
386
+ items: { type: "object" },
387
+ description: "Controls declared outside the picture, for older pictures: {name, type, default, label?, color?, min?, max?, step?}. Leave out to keep the current list."
388
+ }
389
+ },
390
+ required: ["motion"],
391
+ additionalProperties: false
392
+ },
393
+ run: (editor, p) => {
394
+ done(editor.setMotion(p.motion, p.controls));
395
+ return { ok: true, issues: editor.getState().issues };
396
+ }
341
397
  },
342
398
  {
343
399
  id: "effect.rename",
@@ -614,14 +670,16 @@ var EffectEditor = class {
614
670
  // ── The effect itself ───────────────────────────────────────────────
615
671
  /** Replaces a shader's source. Compile errors arrive later, from the renderer (see `reportCompile`). */
616
672
  setSource(source) {
617
- if (this.doc.effect.kind !== "shader") return fail("This is a vector effect; use setMotion.");
673
+ if (this.doc.effect.kind !== "shader") return fail("This is a vector effect; change its picture with setMotion (the effect.setMotion command).");
618
674
  if (source === this.doc.effect.source) return ok;
619
675
  this.commit("Edit source", { ...this.doc, effect: { kind: "shader", source } }, "source");
620
676
  return ok;
621
677
  }
622
678
  /** Replaces a vector effect's picture, whose `{{name: type …}}` placeholders declare its controls (and optionally a `controls` list). */
623
679
  setMotion(motion, controls) {
624
- if (this.doc.effect.kind !== "vector") return fail("This is a shader effect; use setSource.");
680
+ if (this.doc.effect.kind !== "vector") return fail("This is a shader effect; change its source with setSource (the effect.setSource command).");
681
+ if (!motion || typeof motion.svg !== "string" || !motion.svg.trim()) return fail("The picture needs `svg`: one <svg>\u2026</svg> element with a viewBox.");
682
+ if (motion.animate !== void 0 && !Array.isArray(motion.animate)) return fail("`animate` must be a list of changes to the picture's elements.");
625
683
  this.commit("Edit picture", { ...this.doc, effect: { ...this.doc.effect, motion, controls: controls ?? this.doc.effect.controls } });
626
684
  return ok;
627
685
  }
@@ -951,7 +1009,7 @@ async function callTool(provider, name, args = {}) {
951
1009
  }
952
1010
  var 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.
953
1011
 
954
- Start 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.
1012
+ Start with \`effect.describe\` to see what is being edited. To start from something close, use \`catalog.search\` (it returns the first matches and a \`total\`; narrow it with words and filters) then \`effect.load\` (with a preset if one fits). Every change is one undo step; \`history.undo\` reverts a step.
955
1013
 
956
1014
  Shader effects are GLSL ES 3.00 fragment shaders written Shadertoy-style:
957
1015
  - Define \`void mainImage(out vec4 fragColor, in vec2 fragCoord)\`; the output is opaque unless the host renders with \`alpha: true\` (overlays), when \`fragColor.a\` is coverage.
@@ -963,11 +1021,11 @@ Shader effects are GLSL ES 3.00 fragment shaders written Shadertoy-style:
963
1021
  uniform sampler2D uPhoto; // @control filter=linear wrap=clamp label="Photo"
964
1022
  Types: float, int, bool, vec2, vec3, vec4, sampler2D. Every uniform you declare should be a control.
965
1023
  - Write float literals with a decimal point (1.0, not 1); GLSL does not convert int to float implicitly. Use \`texture()\`, not \`texture2D()\`.
966
- - After \`effect.setSource\`, call \`effect.describe\` and read \`compile\`: fix any errors (their line numbers match your source) before going on.
1024
+ - \`effect.setSource\` returns \`compile\`: fix any errors (their line numbers match your source) before going on. When it says the source is not compiled yet, no preview is attached; check again with \`effect.describe\` or \`view_effect\` later.
967
1025
 
968
1026
  Vector effects are animated SVG pictures that declare their controls in themselves, like shader uniforms: the first \`{{name: type \u2026}}\` declares a control (types: float, int, bool, color, text; attributes as for \`@control\`, values with spaces in single quotes), and every \`{{name}}\` is replaced by its value, e.g.
969
1027
  <!-- {{title: text default='Hello' label='Title'}} {{accent: color default=#6366F1}} --><text fill="{{accent}}">{{title}}</text>
970
- Change their controls' values freely (a text control takes any words); edit the picture with care, keeping element ids that animations refer to.
1028
+ Change their controls' values freely (a text control takes any words). Replace the picture with \`effect.setMotion\` ({svg, animate}), with care: keep the viewBox and the element ids that animations refer to, and read the issues it returns.
971
1029
 
972
1030
  Make 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.`;
973
1031
 
@@ -1112,6 +1170,7 @@ function bindEffectParameters(editor, options = {}) {
1112
1170
  };
1113
1171
  }
1114
1172
  export {
1173
+ CATALOG_SEARCH_LIMIT,
1115
1174
  EASING_PRESETS,
1116
1175
  EFFECT_AGENT_INSTRUCTIONS,
1117
1176
  EffectEditor,