@pixerate/flair 0.34.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.
@@ -234,6 +234,12 @@ var easingSchema = {
234
234
  var valueSchema = {
235
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).'
236
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
+ }
237
243
  function describeEditor(editor) {
238
244
  const state = editor.getState();
239
245
  const doc = state.document;
@@ -255,7 +261,7 @@ function describeEditor(editor) {
255
261
  presets: doc.presets.map((p) => p.name),
256
262
  timeline: { duration: doc.duration, loop: doc.loop, time: state.time, playing: state.playing, recording: state.recording },
257
263
  issues: state.issues,
258
- compile: state.compile,
264
+ ...doc.effect.kind === "shader" && { compile: compileStatus(editor) },
259
265
  history: { canUndo: state.history.canUndo, canRedo: state.history.canRedo }
260
266
  };
261
267
  }
@@ -273,7 +279,7 @@ function effectCommands() {
273
279
  id: "catalog.search",
274
280
  label: "Search effects",
275
281
  group: "Catalog",
276
- 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.`,
277
283
  readOnly: true,
278
284
  params: {
279
285
  type: "object",
@@ -301,25 +307,37 @@ function effectCommands() {
301
307
  items: { type: "string", enum: EFFECT_INTERACTIONS },
302
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."
303
309
  },
304
- 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)." }
305
313
  },
306
314
  additionalProperties: false
307
315
  },
308
- run: (_, params) => findEffects(params ?? {}).map((e) => {
309
- 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;
310
335
  return {
311
- id: e.id,
312
- name: e.name,
313
- kind: e.kind,
314
- category: e.category,
315
- description: e.description,
316
- ...e.kind === "shader" && e.video ? { video: e.video } : {},
317
- accepts: inputs.accepts,
318
- ...inputs.interactions.length ? { interactions: inputs.interactions } : {},
319
- ...inputs.transparent ? { transparent: true } : {},
320
- 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.` }
321
339
  };
322
- })
340
+ }
323
341
  },
324
342
  {
325
343
  id: "effect.load",
@@ -342,9 +360,40 @@ function effectCommands() {
342
360
  id: "effect.setSource",
343
361
  label: "Set shader source",
344
362
  group: "Effect",
345
- 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.",
346
364
  params: { type: "object", properties: { source: { type: "string" } }, required: ["source"], additionalProperties: false },
347
- 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
+ }
348
397
  },
349
398
  {
350
399
  id: "effect.rename",
@@ -621,14 +670,16 @@ var EffectEditor = class {
621
670
  // ── The effect itself ───────────────────────────────────────────────
622
671
  /** Replaces a shader's source. Compile errors arrive later, from the renderer (see `reportCompile`). */
623
672
  setSource(source) {
624
- 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).");
625
674
  if (source === this.doc.effect.source) return ok;
626
675
  this.commit("Edit source", { ...this.doc, effect: { kind: "shader", source } }, "source");
627
676
  return ok;
628
677
  }
629
678
  /** Replaces a vector effect's picture, whose `{{name: type …}}` placeholders declare its controls (and optionally a `controls` list). */
630
679
  setMotion(motion, controls) {
631
- 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.");
632
683
  this.commit("Edit picture", { ...this.doc, effect: { ...this.doc.effect, motion, controls: controls ?? this.doc.effect.controls } });
633
684
  return ok;
634
685
  }
@@ -958,7 +1009,7 @@ async function callTool(provider, name, args = {}) {
958
1009
  }
959
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.
960
1011
 
961
- 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.
962
1013
 
963
1014
  Shader effects are GLSL ES 3.00 fragment shaders written Shadertoy-style:
964
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.
@@ -970,11 +1021,11 @@ Shader effects are GLSL ES 3.00 fragment shaders written Shadertoy-style:
970
1021
  uniform sampler2D uPhoto; // @control filter=linear wrap=clamp label="Photo"
971
1022
  Types: float, int, bool, vec2, vec3, vec4, sampler2D. Every uniform you declare should be a control.
972
1023
  - Write float literals with a decimal point (1.0, not 1); GLSL does not convert int to float implicitly. Use \`texture()\`, not \`texture2D()\`.
973
- - 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.
974
1025
 
975
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.
976
1027
  <!-- {{title: text default='Hello' label='Title'}} {{accent: color default=#6366F1}} --><text fill="{{accent}}">{{title}}</text>
977
- 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.
978
1029
 
979
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.`;
980
1031
 
@@ -1119,6 +1170,7 @@ function bindEffectParameters(editor, options = {}) {
1119
1170
  };
1120
1171
  }
1121
1172
  export {
1173
+ CATALOG_SEARCH_LIMIT,
1122
1174
  EASING_PRESETS,
1123
1175
  EFFECT_AGENT_INSTRUCTIONS,
1124
1176
  EffectEditor,