@pixerate/editor 0.254.1 → 0.255.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.
Files changed (71) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/animation-editor/index.cjs +59 -59
  4. package/dist/animation-editor/index.js +3 -3
  5. package/dist/brushes/index.cjs +31 -29
  6. package/dist/brushes/index.d.cts +1 -1
  7. package/dist/brushes/index.d.ts +1 -1
  8. package/dist/brushes/index.js +27 -25
  9. package/dist/brushes/library.d.cts +17 -0
  10. package/dist/brushes/library.d.ts +17 -0
  11. package/dist/{brushstrokeRender-5PYSKZIJ.js → brushstrokeRender-HCOQUSWW.js} +53 -18
  12. package/dist/{brushstrokeRender-7FLLSQEC.cjs → brushstrokeRender-ZZOXI5O5.cjs} +55 -20
  13. package/dist/canvas/brushTypes.d.cts +22 -3
  14. package/dist/canvas/brushTypes.d.ts +22 -3
  15. package/dist/canvas/brushstroke.d.cts +34 -12
  16. package/dist/canvas/brushstroke.d.ts +34 -12
  17. package/dist/canvas/brushstrokeRender.d.cts +14 -8
  18. package/dist/canvas/brushstrokeRender.d.ts +14 -8
  19. package/dist/canvas/drawController.d.cts +11 -3
  20. package/dist/canvas/drawController.d.ts +11 -3
  21. package/dist/canvas/freehand.d.cts +6 -1
  22. package/dist/canvas/freehand.d.ts +6 -1
  23. package/dist/canvas/index.cjs +114 -102
  24. package/dist/canvas/index.js +16 -4
  25. package/dist/{chunk-XVIK73LS.js → chunk-5P4JEOIV.js} +4 -4
  26. package/dist/{chunk-JPEHXIB5.cjs → chunk-5YWWQ36H.cjs} +66 -21
  27. package/dist/{chunk-CHAWTXBE.cjs → chunk-6WCQ35GI.cjs} +4 -4
  28. package/dist/{chunk-GYMMNCFT.js → chunk-AF23QK5F.js} +56 -11
  29. package/dist/{chunk-JFHBXNR2.cjs → chunk-B7V34GIH.cjs} +86 -86
  30. package/dist/{chunk-QC3OXRE4.js → chunk-DMTEQ6SH.js} +1 -1
  31. package/dist/{chunk-TLOGFIIV.cjs → chunk-GCTUOTCW.cjs} +35 -35
  32. package/dist/{chunk-34KLWUH4.cjs → chunk-HIMGBZAM.cjs} +1133 -597
  33. package/dist/{chunk-7P6VF3FG.js → chunk-HYWRU5VX.js} +1 -1
  34. package/dist/{chunk-QPHNVZHO.js → chunk-JVRLGV3C.js} +1118 -582
  35. package/dist/{chunk-LFMKWGFL.cjs → chunk-LRNUZKWV.cjs} +22 -8
  36. package/dist/chunk-PGPFWC35.cjs +517 -0
  37. package/dist/{chunk-QLSGTCYT.cjs → chunk-TBAVCLEQ.cjs} +23 -23
  38. package/dist/{chunk-2ZJ4DT3M.js → chunk-VUKUTT2W.js} +1 -1
  39. package/dist/{chunk-5JNA6LLY.js → chunk-WYGSR3IJ.js} +22 -8
  40. package/dist/chunk-YHUMGBZD.js +517 -0
  41. package/dist/commands/builtins.d.cts +1 -1
  42. package/dist/commands/builtins.d.ts +1 -1
  43. package/dist/commands/index.cjs +2 -2
  44. package/dist/commands/index.js +1 -1
  45. package/dist/image-editor/brush-presets.d.cts +32 -0
  46. package/dist/image-editor/brush-presets.d.ts +32 -0
  47. package/dist/image-editor/controller.d.cts +15 -0
  48. package/dist/image-editor/controller.d.ts +15 -0
  49. package/dist/image-editor/index.cjs +848 -775
  50. package/dist/image-editor/index.d.cts +1 -0
  51. package/dist/image-editor/index.d.ts +1 -0
  52. package/dist/image-editor/index.js +81 -8
  53. package/dist/image-editor/types.d.cts +7 -0
  54. package/dist/image-editor/types.d.ts +7 -0
  55. package/dist/paint-editor/index.cjs +80 -80
  56. package/dist/paint-editor/index.js +13 -13
  57. package/dist/vector/brushstroke.d.cts +21 -5
  58. package/dist/vector/brushstroke.d.ts +21 -5
  59. package/dist/vector/controller.d.cts +14 -1
  60. package/dist/vector/controller.d.ts +14 -1
  61. package/dist/vector/index.cjs +1220 -1199
  62. package/dist/vector/index.js +34 -13
  63. package/dist/vector/types.d.cts +7 -0
  64. package/dist/vector/types.d.ts +7 -0
  65. package/package.json +1 -1
  66. package/skills/pixerate-brushes/SKILL.md +9 -3
  67. package/skills/pixerate-canvas/SKILL.md +10 -5
  68. package/skills/pixerate-image-editor/SKILL.md +2 -1
  69. package/skills/pixerate-vector/SKILL.md +1 -1
  70. package/dist/chunk-HS6QDWDF.cjs +0 -1029
  71. package/dist/chunk-MTKY6K3Q.js +0 -1029
@@ -73,3 +73,4 @@ export * from './paths.cjs';
73
73
  export * from './path-edit.cjs';
74
74
  export * from './content-aware-fill-session.cjs';
75
75
  export * from './stickers.cjs';
76
+ export * from './brush-presets.cjs';
@@ -73,3 +73,4 @@ export * from './paths.js';
73
73
  export * from './path-edit.js';
74
74
  export * from './content-aware-fill-session.js';
75
75
  export * from './stickers.js';
76
+ export * from './brush-presets.js';
@@ -8,7 +8,7 @@ import {
8
8
  parseOpenTypeFont,
9
9
  removeAnchorsKeepingShape,
10
10
  traceField
11
- } from "../chunk-2ZJ4DT3M.js";
11
+ } from "../chunk-VUKUTT2W.js";
12
12
  import {
13
13
  ColorGmm,
14
14
  GAMMA,
@@ -413,7 +413,7 @@ import {
413
413
  writePsd,
414
414
  xyToTempTint,
415
415
  xyToUv
416
- } from "../chunk-QC3OXRE4.js";
416
+ } from "../chunk-DMTEQ6SH.js";
417
417
  import {
418
418
  parseCssColor
419
419
  } from "../chunk-3CI7FRMK.js";
@@ -743,8 +743,14 @@ import {
743
743
  warpMeshParamAt,
744
744
  warpMeshRows,
745
745
  warpPresetMesh
746
- } from "../chunk-GYMMNCFT.js";
747
- import "../chunk-MTKY6K3Q.js";
746
+ } from "../chunk-AF23QK5F.js";
747
+ import {
748
+ IMAGE_PROFILE,
749
+ checkBrush,
750
+ closest,
751
+ createBrushLibrary,
752
+ resolveBrush
753
+ } from "../chunk-JVRLGV3C.js";
748
754
  import "../chunk-APX63ILU.js";
749
755
  import "../chunk-M6DNZNVY.js";
750
756
  import {
@@ -5742,6 +5748,32 @@ function convertSurfaceColors(surface, t) {
5742
5748
  return new TiledSurface(4, tiles, surface.defaultValue);
5743
5749
  }
5744
5750
 
5751
+ // src/image-editor/brush-presets.ts
5752
+ var ImageBrushPresetError = class extends Error {
5753
+ constructor(message) {
5754
+ super(message);
5755
+ this.name = "ImageBrushPresetError";
5756
+ }
5757
+ };
5758
+ function imageBrushPreset(brush, ids = []) {
5759
+ const check = checkBrush(brush, IMAGE_PROFILE);
5760
+ if (!check.ok) {
5761
+ const why = check.issues.filter((i) => i.severity === "error").map((i) => `${i.reason} ${i.fix}`).join(" ");
5762
+ throw new ImageBrushPresetError(`Brush "${brush.id}" (${brush.name}) can't paint in the image editor: ${why}${ids.length ? ` ${closest(brush.id, ids)}list_brush_presets lists the ones it can.` : ""}`);
5763
+ }
5764
+ const resolved2 = resolveBrush(brush);
5765
+ const tool = resolved2.engine === "eraser" ? "eraser" : resolved2.engine === "mixer" ? "mixer" : "brush";
5766
+ return {
5767
+ id: brush.id,
5768
+ name: brush.name,
5769
+ tool,
5770
+ brush: { ...resolved2.tip },
5771
+ ...tool === "mixer" ? { mixer: { ...resolved2.mixer } } : {},
5772
+ ...resolved2.paint && tool === "brush" ? { paint: resolved2.paint } : {},
5773
+ warnings: check.issues.filter((i) => i.severity === "warning").map((i) => i.reason)
5774
+ };
5775
+ }
5776
+
5745
5777
  // src/image-editor/controller.ts
5746
5778
  var IMAGE_EDITOR_SCHEMA_VERSION = LAYERED_SCHEMA_VERSION;
5747
5779
  var RETOUCH_TOOLS = ["clone", "heal", "spot-heal", "smudge", "dodge", "burn", "sponge", "blur", "sharpen", "history"];
@@ -6230,6 +6262,7 @@ var ImageEditorController = class {
6230
6262
  effectsEngine = new ImageEffectsEngine({ slot: this.effects });
6231
6263
  effectRefresh = "none";
6232
6264
  constructor(options = {}) {
6265
+ this.brushLibrary = options.brushLibrary ?? createBrushLibrary();
6233
6266
  this.effects.subscribe(() => this.queueEffectRefresh("all"));
6234
6267
  this.effectsEngine.subscribe(() => this.queueEffectRefresh("incomplete"));
6235
6268
  if (options.effects) this.effects.set(options.effects);
@@ -9008,6 +9041,34 @@ var ImageEditorController = class {
9008
9041
  return pngDataUrl(thumb.width, thumb.height, thumb.data, 4);
9009
9042
  }
9010
9043
  // --- Painting ---
9044
+ /** The brush library the brush presets come from (shared with other editors when the host passes one). */
9045
+ brushLibrary;
9046
+ /** The brushes this editor can paint with (`IMAGE_PROFILE`): built-in presets, then saved brushes. */
9047
+ async listBrushPresets() {
9048
+ return this.brushLibrary.list({ profile: IMAGE_PROFILE });
9049
+ }
9050
+ /**
9051
+ * Paints with a brush from the brush library: sets the paint tool it needs (brush, Mixer Brush
9052
+ * or eraser), its brush and mixer settings and, for a brush with its own gradient, the paint.
9053
+ * View state, no undo. Throws for an unknown id (with the closest ones) or a brush the image
9054
+ * editor can't paint (wet, thick, particles, stickers, light), saying why.
9055
+ */
9056
+ async setBrushPreset(id) {
9057
+ const entry = await this.brushLibrary.get(id);
9058
+ const ids = (await this.listBrushPresets()).map((e) => e.brush.id);
9059
+ if (!entry) throw new LayerOperationError(`Unknown brush "${id}". ${closest(id, ids)}list_brush_presets lists the brushes the image editor can paint with.`);
9060
+ let preset;
9061
+ try {
9062
+ preset = imageBrushPreset(entry.brush, ids);
9063
+ } catch (e) {
9064
+ throw new LayerOperationError(e.message);
9065
+ }
9066
+ this.setPaintOptions({ brush: preset.brush, ...preset.mixer ? { mixer: preset.mixer } : {}, ...preset.paint ? { paint: preset.paint } : {} });
9067
+ this.state.paint = { ...this.state.paint, brushPreset: preset.id };
9068
+ if (this.state.activeTool !== preset.tool) this.setTool(preset.tool);
9069
+ else this.notify();
9070
+ return preset;
9071
+ }
9011
9072
  /** Brush settings, colours and tool options of the paint tools (view state, no undo). */
9012
9073
  setPaintOptions(options) {
9013
9074
  const cur = this.state.paint;
@@ -9022,6 +9083,8 @@ var ImageEditorController = class {
9022
9083
  }
9023
9084
  if (source.type === "color" || options.foreground !== void 0) source = { type: "color", color: foreground };
9024
9085
  const next = {
9086
+ // Brush settings changed by hand are no longer the preset's.
9087
+ brushPreset: options.brush || options.mixer ? null : cur.brushPreset ?? null,
9025
9088
  brush: options.brush ? normalizeBrush({ ...cur.brush, ...options.brush }) : cur.brush,
9026
9089
  foreground,
9027
9090
  background: options.background !== void 0 ? toHex(parseColor(options.background)) : cur.background,
@@ -13625,7 +13688,7 @@ var DEPTH_MASK_CONTROLS = [
13625
13688
 
13626
13689
  // src/image-editor/playback.ts
13627
13690
  var IMAGE_PLAYBACK_KIND = "layer";
13628
- var PAINT = /^(paint_stroke|clone_stamp|heal_brush|spot_heal|dodge|burn|sponge|smudge|blur_stroke|sharpen_stroke|history_brush|set_clone_source|set_clone_transform|fill_pixels|clear_pixels|set_paint|sample_color|set_retouch_options|load_mixer_brush|clean_mixer_brush)$/;
13691
+ var PAINT = /^(paint_stroke|clone_stamp|heal_brush|spot_heal|dodge|burn|sponge|smudge|blur_stroke|sharpen_stroke|history_brush|set_clone_source|set_clone_transform|fill_pixels|clear_pixels|set_paint|sample_color|set_retouch_options|load_mixer_brush|clean_mixer_brush|set_brush_preset)$/;
13629
13692
  var SELECT = /^(make_work_path|add_path|set_path_geometry|rename_path|duplicate_path|delete_path|fill_path|stroke_path|select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|quick_select|select_object|select_focus_area|select_sky|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask|path_to_selection|select_text_shape|compare_selections|edit_selection_step|insert_selection_step|remove_selection_step|freeze_selection|separate_subject)$/;
13630
13693
  var ANNOTATE = /^(add_shape_layer|update_shape|add_shape_component|set_shape_component|remove_shape_component|merge_shape_components|add_text_layer|set_text|set_text_style|set_paragraph_style|set_text_frame|set_text_orientation|convert_text_to_shape)$/;
13631
13694
  var CROP = /^(set_crop|crop_to_selection|reset_crop|set_rotation|flip_image|transform_layer|warp_layer|flip_layer|offset_layer)$/;
@@ -17191,7 +17254,7 @@ function createImagePaintTools(controller) {
17191
17254
  };
17192
17255
  const paintView = () => {
17193
17256
  const p = controller.getState().paint;
17194
- return { paint: p.paint, foreground: p.foreground, background: p.background, brush: p.brush };
17257
+ return { paint: p.paint, foreground: p.foreground, background: p.background, brush: p.brush, brushPreset: p.brushPreset ?? null };
17195
17258
  };
17196
17259
  const setPaint = {
17197
17260
  name: "set_paint",
@@ -17207,7 +17270,7 @@ function createImagePaintTools(controller) {
17207
17270
  const getPaint = {
17208
17271
  name: "get_paint",
17209
17272
  title: "Get the paint",
17210
- description: "Reads what the brush paints (paint: the foreground colour or gradient paint), the foreground and background colours and the brush settings (size, hardness, spacing, opacity, flow \u2026). Changes nothing.",
17273
+ description: "Reads what the brush paints (paint: the foreground colour or gradient paint), the foreground and background colours, the brush settings (size, hardness, spacing, opacity, flow \u2026) and brushPreset (the set_brush_preset id they came from, null once changed by hand). Changes nothing.",
17211
17274
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
17212
17275
  annotations: { title: "Get the paint", ...READ3 },
17213
17276
  handler: () => paintView()
@@ -17277,7 +17340,15 @@ function createImagePaintTools(controller) {
17277
17340
  return { gradient, css: gradientToCss(gradient), pointsRead: read };
17278
17341
  }
17279
17342
  };
17280
- return [paintStroke, fill, clear, getMixer, loadMixerTool, cleanMixerTool, setPaint, getPaint, sampleColor2, sampleGradient];
17343
+ const setBrushPreset = {
17344
+ name: "set_brush_preset",
17345
+ title: "Paint with a brush preset",
17346
+ description: "Chooses a brush from the brush library (a built-in preset or a saved brush; list_brush_presets lists the ones the image editor can paint, describe_brush reads one) for the brush tools: it sets the paint tool the brush needs (brush, mixer for a Mixer Brush, eraser), its brush settings (size, hardness, spacing, angle, roundness, opacity, flow, smoothing, pressure, blend), its Mixer Brush settings and, for a brush with its own gradient, the paint. paint_stroke with that tool then paints with it. Brushes the image editor cannot paint (wet or thick paint, particles, stickers, light, the wet tools) are refused with the reason. View state, not an undo step. Returns the id, name, tool, brush, mixer, paint and warnings (settings it paints without, such as paper grain).",
17347
+ inputSchema: { type: "object", properties: { id: { type: "string", minLength: 1, maxLength: 64, description: 'Brush id from list_brush_presets (e.g. "pencil-hb", "marker", "mixer-brush", "eraser-soft").' } }, required: ["id"], additionalProperties: false },
17348
+ annotations: { title: "Paint with a brush preset", ...SET },
17349
+ handler: async ({ id }) => controller.setBrushPreset(id)
17350
+ };
17351
+ return [paintStroke, fill, clear, getMixer, loadMixerTool, cleanMixerTool, setPaint, getPaint, sampleColor2, sampleGradient, setBrushPreset];
17281
17352
  }
17282
17353
 
17283
17354
  // src/image-editor/color-tools.ts
@@ -22616,6 +22687,7 @@ export {
22616
22687
  INTENTS,
22617
22688
  INTENT_OPTIONS,
22618
22689
  INTERPOLATIONS,
22690
+ ImageBrushPresetError,
22619
22691
  ImageEditorController,
22620
22692
  ImageEditorRenderer,
22621
22693
  ImageEffectsEngine,
@@ -23127,6 +23199,7 @@ export {
23127
23199
  identityLevels,
23128
23200
  identityTransform,
23129
23201
  identityWarpMesh,
23202
+ imageBrushPreset,
23130
23203
  imageEditorStateMigrations,
23131
23204
  imageLayerAppliedEffects,
23132
23205
  imagePanelTool,
@@ -9,6 +9,7 @@ import type { ImageLayer, LayerTarget } from './layers/model.cjs';
9
9
  import type { SerializedLayeredImageEditorState } from './layers/serialize.cjs';
10
10
  import type { PaintOptions } from './controller.cjs';
11
11
  import type { TextShaper } from './layers/text.cjs';
12
+ import type { BrushLibrary } from '../brushes/library.cjs';
12
13
  export interface ImageDimensions {
13
14
  width: number;
14
15
  height: number;
@@ -452,6 +453,12 @@ export interface ExportOptions {
452
453
  import type { EffectProvider } from '../effects/types.cjs';
453
454
  import type { SegmentationProvider } from './selection/segmentation.cjs';
454
455
  export interface ImageEditorOptions {
456
+ /**
457
+ * The brush library the brush presets come from (`setBrushPreset`): built-in presets and saved
458
+ * brushes, filtered by `IMAGE_PROFILE`. Share one with the paint editor so a brush saved there
459
+ * paints here (default: a library in memory).
460
+ */
461
+ brushLibrary?: BrushLibrary;
455
462
  /**
456
463
  * What lays out and draws type layers. Default: the browser's fonts through Canvas 2D
457
464
  * (`createCanvasTextShaper`) where there is a 2D canvas; `null` (or no canvas) keeps type layers'
@@ -9,6 +9,7 @@ import type { ImageLayer, LayerTarget } from './layers/model.js';
9
9
  import type { SerializedLayeredImageEditorState } from './layers/serialize.js';
10
10
  import type { PaintOptions } from './controller.js';
11
11
  import type { TextShaper } from './layers/text.js';
12
+ import type { BrushLibrary } from '../brushes/library.js';
12
13
  export interface ImageDimensions {
13
14
  width: number;
14
15
  height: number;
@@ -452,6 +453,12 @@ export interface ExportOptions {
452
453
  import type { EffectProvider } from '../effects/types.js';
453
454
  import type { SegmentationProvider } from './selection/segmentation.js';
454
455
  export interface ImageEditorOptions {
456
+ /**
457
+ * The brush library the brush presets come from (`setBrushPreset`): built-in presets and saved
458
+ * brushes, filtered by `IMAGE_PROFILE`. Share one with the paint editor so a brush saved there
459
+ * paints here (default: a library in memory).
460
+ */
461
+ brushLibrary?: BrushLibrary;
455
462
  /**
456
463
  * What lays out and draws type layers. Default: the browser's fonts through Canvas 2D
457
464
  * (`createCanvasTextShaper`) where there is a 2D canvas; `null` (or no canvas) keeps type layers'