@pixerate/editor 0.254.1 → 0.256.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/dist/agent/index.cjs +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/animation-editor/index.cjs +59 -59
- package/dist/animation-editor/index.js +3 -3
- package/dist/brushes/index.cjs +31 -29
- package/dist/brushes/index.d.cts +1 -1
- package/dist/brushes/index.d.ts +1 -1
- package/dist/brushes/index.js +27 -25
- package/dist/brushes/library.d.cts +17 -0
- package/dist/brushes/library.d.ts +17 -0
- package/dist/{brushstrokeRender-5PYSKZIJ.js → brushstrokeRender-HCOQUSWW.js} +53 -18
- package/dist/{brushstrokeRender-7FLLSQEC.cjs → brushstrokeRender-ZZOXI5O5.cjs} +55 -20
- package/dist/canvas/brushTypes.d.cts +22 -3
- package/dist/canvas/brushTypes.d.ts +22 -3
- package/dist/canvas/brushstroke.d.cts +34 -12
- package/dist/canvas/brushstroke.d.ts +34 -12
- package/dist/canvas/brushstrokeRender.d.cts +14 -8
- package/dist/canvas/brushstrokeRender.d.ts +14 -8
- package/dist/canvas/drawController.d.cts +11 -3
- package/dist/canvas/drawController.d.ts +11 -3
- package/dist/canvas/freehand.d.cts +6 -1
- package/dist/canvas/freehand.d.ts +6 -1
- package/dist/canvas/index.cjs +114 -102
- package/dist/canvas/index.js +16 -4
- package/dist/{chunk-XVIK73LS.js → chunk-5P4JEOIV.js} +4 -4
- package/dist/{chunk-JPEHXIB5.cjs → chunk-5YWWQ36H.cjs} +66 -21
- package/dist/{chunk-CHAWTXBE.cjs → chunk-6WCQ35GI.cjs} +4 -4
- package/dist/{chunk-GYMMNCFT.js → chunk-AF23QK5F.js} +56 -11
- package/dist/{chunk-JFHBXNR2.cjs → chunk-B7V34GIH.cjs} +86 -86
- package/dist/{chunk-QC3OXRE4.js → chunk-DMTEQ6SH.js} +1 -1
- package/dist/{chunk-TLOGFIIV.cjs → chunk-GCTUOTCW.cjs} +35 -35
- package/dist/{chunk-34KLWUH4.cjs → chunk-HIMGBZAM.cjs} +1133 -597
- package/dist/{chunk-7P6VF3FG.js → chunk-HYWRU5VX.js} +1 -1
- package/dist/{chunk-QPHNVZHO.js → chunk-JVRLGV3C.js} +1118 -582
- package/dist/{chunk-LFMKWGFL.cjs → chunk-LRNUZKWV.cjs} +22 -8
- package/dist/chunk-PGPFWC35.cjs +517 -0
- package/dist/{chunk-QLSGTCYT.cjs → chunk-TBAVCLEQ.cjs} +23 -23
- package/dist/{chunk-2ZJ4DT3M.js → chunk-VUKUTT2W.js} +1 -1
- package/dist/{chunk-5JNA6LLY.js → chunk-WYGSR3IJ.js} +22 -8
- package/dist/chunk-YHUMGBZD.js +517 -0
- package/dist/commands/builtins.d.cts +1 -1
- package/dist/commands/builtins.d.ts +1 -1
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.js +1 -1
- package/dist/image-editor/brush-presets.d.cts +32 -0
- package/dist/image-editor/brush-presets.d.ts +32 -0
- package/dist/image-editor/controller.d.cts +15 -0
- package/dist/image-editor/controller.d.ts +15 -0
- package/dist/image-editor/index.cjs +848 -775
- package/dist/image-editor/index.d.cts +1 -0
- package/dist/image-editor/index.d.ts +1 -0
- package/dist/image-editor/index.js +81 -8
- package/dist/image-editor/types.d.cts +7 -0
- package/dist/image-editor/types.d.ts +7 -0
- package/dist/paint-editor/collaboration-tools.d.cts +19 -0
- package/dist/paint-editor/collaboration-tools.d.ts +19 -0
- package/dist/paint-editor/collaboration.d.cts +180 -0
- package/dist/paint-editor/collaboration.d.ts +180 -0
- package/dist/paint-editor/controller.d.cts +54 -0
- package/dist/paint-editor/controller.d.ts +54 -0
- package/dist/paint-editor/index.cjs +816 -283
- package/dist/paint-editor/index.d.cts +2 -0
- package/dist/paint-editor/index.d.ts +2 -0
- package/dist/paint-editor/index.js +567 -34
- package/dist/vector/brushstroke.d.cts +21 -5
- package/dist/vector/brushstroke.d.ts +21 -5
- package/dist/vector/controller.d.cts +14 -1
- package/dist/vector/controller.d.ts +14 -1
- package/dist/vector/index.cjs +1220 -1199
- package/dist/vector/index.js +34 -13
- package/dist/vector/types.d.cts +7 -0
- package/dist/vector/types.d.ts +7 -0
- package/package.json +1 -1
- package/skills/pixerate-brushes/SKILL.md +9 -3
- package/skills/pixerate-canvas/SKILL.md +10 -5
- package/skills/pixerate-image-editor/SKILL.md +2 -1
- package/skills/pixerate-paint-editor/SKILL.md +31 -46
- package/skills/pixerate-paint-editor/references/painting-alongside.md +19 -0
- package/skills/pixerate-vector/SKILL.md +1 -1
- package/dist/chunk-HS6QDWDF.cjs +0 -1029
- package/dist/chunk-MTKY6K3Q.js +0 -1029
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
parseOpenTypeFont,
|
|
9
9
|
removeAnchorsKeepingShape,
|
|
10
10
|
traceField
|
|
11
|
-
} from "../chunk-
|
|
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-
|
|
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-
|
|
747
|
-
import
|
|
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
|
|
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
|
-
|
|
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'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools for paint proposals: `propose_paint`, `list_paint_proposals`,
|
|
3
|
+
* `accept_paint_proposal` and `reject_paint_proposal`. An agent suggests paint as a ghost the user
|
|
4
|
+
* can take or leave, instead of painting it outright. They drive a {@link PaintProposals} store.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentToolProvider } from '../tools/registry.cjs';
|
|
7
|
+
import type { PaintProposals } from './collaboration.cjs';
|
|
8
|
+
export interface PaintProposalToolProviderOptions {
|
|
9
|
+
/** Provider id (unique per registry). Default `paint-proposals`. */
|
|
10
|
+
id?: string;
|
|
11
|
+
/** Tool-name prefix (`<namespace>__<name>`); defaults to the provider id, `''` for bare names. */
|
|
12
|
+
namespace?: string;
|
|
13
|
+
/** Accepting is not allowed: `accept_paint_proposal` refuses with this reason (a sentence). */
|
|
14
|
+
readOnly?: boolean | string | (() => boolean | string);
|
|
15
|
+
/** Origin of proposals from calls that name no actor (default the store's). */
|
|
16
|
+
fallbackOrigin?: string;
|
|
17
|
+
}
|
|
18
|
+
/** The proposal tools for a paint editor (see the module comment). */
|
|
19
|
+
export declare function createPaintProposalToolProvider(proposals: PaintProposals, options?: PaintProposalToolProviderOptions): AgentToolProvider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools for paint proposals: `propose_paint`, `list_paint_proposals`,
|
|
3
|
+
* `accept_paint_proposal` and `reject_paint_proposal`. An agent suggests paint as a ghost the user
|
|
4
|
+
* can take or leave, instead of painting it outright. They drive a {@link PaintProposals} store.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentToolProvider } from '../tools/registry.js';
|
|
7
|
+
import type { PaintProposals } from './collaboration.js';
|
|
8
|
+
export interface PaintProposalToolProviderOptions {
|
|
9
|
+
/** Provider id (unique per registry). Default `paint-proposals`. */
|
|
10
|
+
id?: string;
|
|
11
|
+
/** Tool-name prefix (`<namespace>__<name>`); defaults to the provider id, `''` for bare names. */
|
|
12
|
+
namespace?: string;
|
|
13
|
+
/** Accepting is not allowed: `accept_paint_proposal` refuses with this reason (a sentence). */
|
|
14
|
+
readOnly?: boolean | string | (() => boolean | string);
|
|
15
|
+
/** Origin of proposals from calls that name no actor (default the store's). */
|
|
16
|
+
fallbackOrigin?: string;
|
|
17
|
+
}
|
|
18
|
+
/** The proposal tools for a paint editor (see the module comment). */
|
|
19
|
+
export declare function createPaintProposalToolProvider(proposals: PaintProposals, options?: PaintProposalToolProviderOptions): AgentToolProvider;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Painting alongside an agent: a change feed that says what the user (or an agent) just painted,
|
|
3
|
+
* and proposals, the paint an agent suggests without painting it. A proposal is a few operations
|
|
4
|
+
* (strokes, a wash, an echo of strokes already on the canvas) that `previewPaint` renders as a
|
|
5
|
+
* ghost the user sees over the canvas; accepting paints them as one undo step recorded as the
|
|
6
|
+
* proposer's, on a layer of their own, and dismissing changes nothing. The canvas equivalents are
|
|
7
|
+
* `createCanvasChangeFeed` and `createCanvasProposals`.
|
|
8
|
+
*/
|
|
9
|
+
import type { RasterRect } from '../raster/surface.cjs';
|
|
10
|
+
import { type PaintCommitEvent, type PaintEditorController } from './controller.cjs';
|
|
11
|
+
import type { PaintEvent } from './document.cjs';
|
|
12
|
+
import type { RecordedStroke } from './strokes.cjs';
|
|
13
|
+
/** A layer as the feed reports it. */
|
|
14
|
+
export interface PaintChangeLayer {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
/** What changed in the painting since the feed's last report. */
|
|
19
|
+
export interface PaintChange {
|
|
20
|
+
/** Counts up from 1 for each change reported. */
|
|
21
|
+
seq: number;
|
|
22
|
+
/** `user`, or `agent:<id>` when an agent's step made it (see `PaintEditorController.withOrigin`). */
|
|
23
|
+
origin: string;
|
|
24
|
+
/** The history step behind it, when there was one. */
|
|
25
|
+
commit?: PaintCommitEvent;
|
|
26
|
+
addedStrokes: RecordedStroke[];
|
|
27
|
+
removedStrokes: RecordedStroke[];
|
|
28
|
+
/** Fills, clears and other recorded operations that aren't strokes. */
|
|
29
|
+
events: PaintEvent[];
|
|
30
|
+
addedLayers: PaintChangeLayer[];
|
|
31
|
+
removedLayers: PaintChangeLayer[];
|
|
32
|
+
/** Layers whose name, visibility, opacity or blend changed, with the fields. */
|
|
33
|
+
changedLayers: (PaintChangeLayer & {
|
|
34
|
+
fields: string[];
|
|
35
|
+
})[];
|
|
36
|
+
/** A stroke is being painted: more of the same gesture will follow. */
|
|
37
|
+
inGesture: boolean;
|
|
38
|
+
/** A stroke started since the last report (reported even when nothing else changed). */
|
|
39
|
+
gestureStarted?: true;
|
|
40
|
+
/** The stroke in progress ended (reported even when it painted nothing). */
|
|
41
|
+
gestureEnded?: true;
|
|
42
|
+
/** Milliseconds since the epoch. */
|
|
43
|
+
at: number;
|
|
44
|
+
}
|
|
45
|
+
export interface PaintChangeFeed {
|
|
46
|
+
/** Compares the painting with the last snapshot and reports the change, if any. Runs on every controller change. */
|
|
47
|
+
check(): PaintChange | null;
|
|
48
|
+
subscribe(listener: (change: PaintChange) => void): () => void;
|
|
49
|
+
/** Takes a new snapshot without reporting the difference (e.g. after opening a document). */
|
|
50
|
+
reset(): void;
|
|
51
|
+
dispose(): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What changed in a painting, after each controller change: strokes added or removed (undo
|
|
55
|
+
* included), fills and clears, layers added, removed or changed, and the start and end of each
|
|
56
|
+
* stroke, tagged with who made it. Agents that paint alongside the user read it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createPaintChangeFeed(controller: PaintEditorController, options?: {
|
|
59
|
+
now?: () => number;
|
|
60
|
+
}): PaintChangeFeed;
|
|
61
|
+
/** A stroke to paint: pen samples in document pixels. */
|
|
62
|
+
export interface PaintProposalStrokeOp {
|
|
63
|
+
kind: 'stroke';
|
|
64
|
+
points: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
pressure?: number;
|
|
68
|
+
}[];
|
|
69
|
+
/** A dry brush from the library (default the editor's brush). */
|
|
70
|
+
brushId?: string;
|
|
71
|
+
/** #rrggbb (default the editor colour). */
|
|
72
|
+
color?: string;
|
|
73
|
+
/** Brush size in document pixels (default the brush's). */
|
|
74
|
+
size?: number;
|
|
75
|
+
}
|
|
76
|
+
/** A wash over the suggestion's whole layer, like a ground or a glaze. */
|
|
77
|
+
export interface PaintProposalWashOp {
|
|
78
|
+
kind: 'wash';
|
|
79
|
+
color: string;
|
|
80
|
+
/** 0..1 (default 0.35). */
|
|
81
|
+
opacity?: number;
|
|
82
|
+
/** Paper grain response 0..1 (default 0.4). */
|
|
83
|
+
grain?: number;
|
|
84
|
+
}
|
|
85
|
+
/** Strokes already on the canvas, painted again moved, turned, scaled or mirrored. */
|
|
86
|
+
export interface PaintProposalEchoOp {
|
|
87
|
+
kind: 'echo';
|
|
88
|
+
strokeIds: string[];
|
|
89
|
+
dx?: number;
|
|
90
|
+
dy?: number;
|
|
91
|
+
/** Degrees clockwise about the strokes' centre (default 0). */
|
|
92
|
+
rotate?: number;
|
|
93
|
+
scale?: number;
|
|
94
|
+
flipX?: boolean;
|
|
95
|
+
/** Repeat the move this many times, each on top of the last (default 1). */
|
|
96
|
+
repeat?: number;
|
|
97
|
+
/** Recolour (default each stroke's colour). */
|
|
98
|
+
color?: string;
|
|
99
|
+
}
|
|
100
|
+
export type PaintProposalOp = PaintProposalStrokeOp | PaintProposalWashOp | PaintProposalEchoOp;
|
|
101
|
+
/** Where accepted paint goes: a new layer of its own (above a layer, on top, or at the bottom, behind everything). */
|
|
102
|
+
export interface PaintProposalPlacement {
|
|
103
|
+
/** Default `top`. `above` needs `layerId`. */
|
|
104
|
+
place?: 'top' | 'bottom' | 'above';
|
|
105
|
+
layerId?: string;
|
|
106
|
+
/** The new layer's name (default the proposal's title). */
|
|
107
|
+
layerName?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface PaintProposalInput extends PaintProposalPlacement {
|
|
110
|
+
/** What the suggestion adds, in a few words. Also the undo step's name once accepted. */
|
|
111
|
+
title: string;
|
|
112
|
+
/** Why it fits what the user is doing, in a sentence. */
|
|
113
|
+
rationale?: string;
|
|
114
|
+
/** Who suggests it (`agent:<id>`); defaults to the store's `origin`. */
|
|
115
|
+
origin?: string;
|
|
116
|
+
ops: PaintProposalOp[];
|
|
117
|
+
}
|
|
118
|
+
export interface PaintProposal {
|
|
119
|
+
id: string;
|
|
120
|
+
title: string;
|
|
121
|
+
rationale?: string;
|
|
122
|
+
origin: string;
|
|
123
|
+
ops: PaintProposalOp[];
|
|
124
|
+
placement: PaintProposalPlacement;
|
|
125
|
+
/** Where the ghost sits (document pixels), or `null` when it painted nothing. */
|
|
126
|
+
bounds: RasterRect | null;
|
|
127
|
+
/** The ghost: the picture as it would look, inside `bounds` (straight RGBA). */
|
|
128
|
+
ghost: Uint8ClampedArray;
|
|
129
|
+
/** Strokes on the canvas it builds on (echo ops). */
|
|
130
|
+
anchors: string[];
|
|
131
|
+
createdAt: number;
|
|
132
|
+
}
|
|
133
|
+
export interface PaintProposalAcceptResult {
|
|
134
|
+
proposalId: string;
|
|
135
|
+
layerId: string;
|
|
136
|
+
/** The strokes it painted. */
|
|
137
|
+
strokeIds: string[];
|
|
138
|
+
/** The undo step's name. */
|
|
139
|
+
label: string;
|
|
140
|
+
}
|
|
141
|
+
export interface PaintProposalsState {
|
|
142
|
+
proposals: readonly PaintProposal[];
|
|
143
|
+
}
|
|
144
|
+
export interface PaintProposals {
|
|
145
|
+
getState(): PaintProposalsState;
|
|
146
|
+
subscribe(listener: (state: PaintProposalsState) => void): () => void;
|
|
147
|
+
/** Validates and previews a suggestion, and adds it to the pending list. Throws, changing nothing, when it can't be painted. */
|
|
148
|
+
propose(input: PaintProposalInput): PaintProposal;
|
|
149
|
+
/** Paints a pending proposal as one undo step recorded as the proposer's. The active layer stays the user's. */
|
|
150
|
+
accept(id: string): PaintProposalAcceptResult;
|
|
151
|
+
/** Drops a pending proposal; the painting is unchanged. */
|
|
152
|
+
reject(id: string): PaintProposal;
|
|
153
|
+
/** Drops every pending proposal (or those of one origin). Returns the ids dropped. */
|
|
154
|
+
clear(origin?: string): string[];
|
|
155
|
+
get(id: string): PaintProposal | undefined;
|
|
156
|
+
/** Strokes an echo builds on that are no longer on the canvas (it can't be accepted until re-proposed). */
|
|
157
|
+
missing(id: string): string[];
|
|
158
|
+
}
|
|
159
|
+
export interface PaintProposalsOptions {
|
|
160
|
+
controller: PaintEditorController;
|
|
161
|
+
/** Origin of proposals that name none (default `agent:anonymous`). */
|
|
162
|
+
origin?: string;
|
|
163
|
+
/** The most points one proposal may paint (default 4000). */
|
|
164
|
+
maxPoints?: number;
|
|
165
|
+
now?: () => number;
|
|
166
|
+
idGenerator?: () => string;
|
|
167
|
+
}
|
|
168
|
+
/** A store of paint an agent suggests (see the module comment). */
|
|
169
|
+
export declare function createPaintProposals(options: PaintProposalsOptions): PaintProposals;
|
|
170
|
+
/**
|
|
171
|
+
* Where to draw a proposal's ghost and its pixels, for a canvas element placed at `bounds` in
|
|
172
|
+
* document pixels (`new ImageData(data, width, height)`), or `null` when it paints nothing.
|
|
173
|
+
*/
|
|
174
|
+
export declare function paintProposalGhost(proposal: PaintProposal): {
|
|
175
|
+
x: number;
|
|
176
|
+
y: number;
|
|
177
|
+
width: number;
|
|
178
|
+
height: number;
|
|
179
|
+
data: Uint8ClampedArray;
|
|
180
|
+
} | null;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Painting alongside an agent: a change feed that says what the user (or an agent) just painted,
|
|
3
|
+
* and proposals, the paint an agent suggests without painting it. A proposal is a few operations
|
|
4
|
+
* (strokes, a wash, an echo of strokes already on the canvas) that `previewPaint` renders as a
|
|
5
|
+
* ghost the user sees over the canvas; accepting paints them as one undo step recorded as the
|
|
6
|
+
* proposer's, on a layer of their own, and dismissing changes nothing. The canvas equivalents are
|
|
7
|
+
* `createCanvasChangeFeed` and `createCanvasProposals`.
|
|
8
|
+
*/
|
|
9
|
+
import type { RasterRect } from '../raster/surface.js';
|
|
10
|
+
import { type PaintCommitEvent, type PaintEditorController } from './controller.js';
|
|
11
|
+
import type { PaintEvent } from './document.js';
|
|
12
|
+
import type { RecordedStroke } from './strokes.js';
|
|
13
|
+
/** A layer as the feed reports it. */
|
|
14
|
+
export interface PaintChangeLayer {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
/** What changed in the painting since the feed's last report. */
|
|
19
|
+
export interface PaintChange {
|
|
20
|
+
/** Counts up from 1 for each change reported. */
|
|
21
|
+
seq: number;
|
|
22
|
+
/** `user`, or `agent:<id>` when an agent's step made it (see `PaintEditorController.withOrigin`). */
|
|
23
|
+
origin: string;
|
|
24
|
+
/** The history step behind it, when there was one. */
|
|
25
|
+
commit?: PaintCommitEvent;
|
|
26
|
+
addedStrokes: RecordedStroke[];
|
|
27
|
+
removedStrokes: RecordedStroke[];
|
|
28
|
+
/** Fills, clears and other recorded operations that aren't strokes. */
|
|
29
|
+
events: PaintEvent[];
|
|
30
|
+
addedLayers: PaintChangeLayer[];
|
|
31
|
+
removedLayers: PaintChangeLayer[];
|
|
32
|
+
/** Layers whose name, visibility, opacity or blend changed, with the fields. */
|
|
33
|
+
changedLayers: (PaintChangeLayer & {
|
|
34
|
+
fields: string[];
|
|
35
|
+
})[];
|
|
36
|
+
/** A stroke is being painted: more of the same gesture will follow. */
|
|
37
|
+
inGesture: boolean;
|
|
38
|
+
/** A stroke started since the last report (reported even when nothing else changed). */
|
|
39
|
+
gestureStarted?: true;
|
|
40
|
+
/** The stroke in progress ended (reported even when it painted nothing). */
|
|
41
|
+
gestureEnded?: true;
|
|
42
|
+
/** Milliseconds since the epoch. */
|
|
43
|
+
at: number;
|
|
44
|
+
}
|
|
45
|
+
export interface PaintChangeFeed {
|
|
46
|
+
/** Compares the painting with the last snapshot and reports the change, if any. Runs on every controller change. */
|
|
47
|
+
check(): PaintChange | null;
|
|
48
|
+
subscribe(listener: (change: PaintChange) => void): () => void;
|
|
49
|
+
/** Takes a new snapshot without reporting the difference (e.g. after opening a document). */
|
|
50
|
+
reset(): void;
|
|
51
|
+
dispose(): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What changed in a painting, after each controller change: strokes added or removed (undo
|
|
55
|
+
* included), fills and clears, layers added, removed or changed, and the start and end of each
|
|
56
|
+
* stroke, tagged with who made it. Agents that paint alongside the user read it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createPaintChangeFeed(controller: PaintEditorController, options?: {
|
|
59
|
+
now?: () => number;
|
|
60
|
+
}): PaintChangeFeed;
|
|
61
|
+
/** A stroke to paint: pen samples in document pixels. */
|
|
62
|
+
export interface PaintProposalStrokeOp {
|
|
63
|
+
kind: 'stroke';
|
|
64
|
+
points: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
pressure?: number;
|
|
68
|
+
}[];
|
|
69
|
+
/** A dry brush from the library (default the editor's brush). */
|
|
70
|
+
brushId?: string;
|
|
71
|
+
/** #rrggbb (default the editor colour). */
|
|
72
|
+
color?: string;
|
|
73
|
+
/** Brush size in document pixels (default the brush's). */
|
|
74
|
+
size?: number;
|
|
75
|
+
}
|
|
76
|
+
/** A wash over the suggestion's whole layer, like a ground or a glaze. */
|
|
77
|
+
export interface PaintProposalWashOp {
|
|
78
|
+
kind: 'wash';
|
|
79
|
+
color: string;
|
|
80
|
+
/** 0..1 (default 0.35). */
|
|
81
|
+
opacity?: number;
|
|
82
|
+
/** Paper grain response 0..1 (default 0.4). */
|
|
83
|
+
grain?: number;
|
|
84
|
+
}
|
|
85
|
+
/** Strokes already on the canvas, painted again moved, turned, scaled or mirrored. */
|
|
86
|
+
export interface PaintProposalEchoOp {
|
|
87
|
+
kind: 'echo';
|
|
88
|
+
strokeIds: string[];
|
|
89
|
+
dx?: number;
|
|
90
|
+
dy?: number;
|
|
91
|
+
/** Degrees clockwise about the strokes' centre (default 0). */
|
|
92
|
+
rotate?: number;
|
|
93
|
+
scale?: number;
|
|
94
|
+
flipX?: boolean;
|
|
95
|
+
/** Repeat the move this many times, each on top of the last (default 1). */
|
|
96
|
+
repeat?: number;
|
|
97
|
+
/** Recolour (default each stroke's colour). */
|
|
98
|
+
color?: string;
|
|
99
|
+
}
|
|
100
|
+
export type PaintProposalOp = PaintProposalStrokeOp | PaintProposalWashOp | PaintProposalEchoOp;
|
|
101
|
+
/** Where accepted paint goes: a new layer of its own (above a layer, on top, or at the bottom, behind everything). */
|
|
102
|
+
export interface PaintProposalPlacement {
|
|
103
|
+
/** Default `top`. `above` needs `layerId`. */
|
|
104
|
+
place?: 'top' | 'bottom' | 'above';
|
|
105
|
+
layerId?: string;
|
|
106
|
+
/** The new layer's name (default the proposal's title). */
|
|
107
|
+
layerName?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface PaintProposalInput extends PaintProposalPlacement {
|
|
110
|
+
/** What the suggestion adds, in a few words. Also the undo step's name once accepted. */
|
|
111
|
+
title: string;
|
|
112
|
+
/** Why it fits what the user is doing, in a sentence. */
|
|
113
|
+
rationale?: string;
|
|
114
|
+
/** Who suggests it (`agent:<id>`); defaults to the store's `origin`. */
|
|
115
|
+
origin?: string;
|
|
116
|
+
ops: PaintProposalOp[];
|
|
117
|
+
}
|
|
118
|
+
export interface PaintProposal {
|
|
119
|
+
id: string;
|
|
120
|
+
title: string;
|
|
121
|
+
rationale?: string;
|
|
122
|
+
origin: string;
|
|
123
|
+
ops: PaintProposalOp[];
|
|
124
|
+
placement: PaintProposalPlacement;
|
|
125
|
+
/** Where the ghost sits (document pixels), or `null` when it painted nothing. */
|
|
126
|
+
bounds: RasterRect | null;
|
|
127
|
+
/** The ghost: the picture as it would look, inside `bounds` (straight RGBA). */
|
|
128
|
+
ghost: Uint8ClampedArray;
|
|
129
|
+
/** Strokes on the canvas it builds on (echo ops). */
|
|
130
|
+
anchors: string[];
|
|
131
|
+
createdAt: number;
|
|
132
|
+
}
|
|
133
|
+
export interface PaintProposalAcceptResult {
|
|
134
|
+
proposalId: string;
|
|
135
|
+
layerId: string;
|
|
136
|
+
/** The strokes it painted. */
|
|
137
|
+
strokeIds: string[];
|
|
138
|
+
/** The undo step's name. */
|
|
139
|
+
label: string;
|
|
140
|
+
}
|
|
141
|
+
export interface PaintProposalsState {
|
|
142
|
+
proposals: readonly PaintProposal[];
|
|
143
|
+
}
|
|
144
|
+
export interface PaintProposals {
|
|
145
|
+
getState(): PaintProposalsState;
|
|
146
|
+
subscribe(listener: (state: PaintProposalsState) => void): () => void;
|
|
147
|
+
/** Validates and previews a suggestion, and adds it to the pending list. Throws, changing nothing, when it can't be painted. */
|
|
148
|
+
propose(input: PaintProposalInput): PaintProposal;
|
|
149
|
+
/** Paints a pending proposal as one undo step recorded as the proposer's. The active layer stays the user's. */
|
|
150
|
+
accept(id: string): PaintProposalAcceptResult;
|
|
151
|
+
/** Drops a pending proposal; the painting is unchanged. */
|
|
152
|
+
reject(id: string): PaintProposal;
|
|
153
|
+
/** Drops every pending proposal (or those of one origin). Returns the ids dropped. */
|
|
154
|
+
clear(origin?: string): string[];
|
|
155
|
+
get(id: string): PaintProposal | undefined;
|
|
156
|
+
/** Strokes an echo builds on that are no longer on the canvas (it can't be accepted until re-proposed). */
|
|
157
|
+
missing(id: string): string[];
|
|
158
|
+
}
|
|
159
|
+
export interface PaintProposalsOptions {
|
|
160
|
+
controller: PaintEditorController;
|
|
161
|
+
/** Origin of proposals that name none (default `agent:anonymous`). */
|
|
162
|
+
origin?: string;
|
|
163
|
+
/** The most points one proposal may paint (default 4000). */
|
|
164
|
+
maxPoints?: number;
|
|
165
|
+
now?: () => number;
|
|
166
|
+
idGenerator?: () => string;
|
|
167
|
+
}
|
|
168
|
+
/** A store of paint an agent suggests (see the module comment). */
|
|
169
|
+
export declare function createPaintProposals(options: PaintProposalsOptions): PaintProposals;
|
|
170
|
+
/**
|
|
171
|
+
* Where to draw a proposal's ghost and its pixels, for a canvas element placed at `bounds` in
|
|
172
|
+
* document pixels (`new ImageData(data, width, height)`), or `null` when it paints nothing.
|
|
173
|
+
*/
|
|
174
|
+
export declare function paintProposalGhost(proposal: PaintProposal): {
|
|
175
|
+
x: number;
|
|
176
|
+
y: number;
|
|
177
|
+
width: number;
|
|
178
|
+
height: number;
|
|
179
|
+
data: Uint8ClampedArray;
|
|
180
|
+
} | null;
|