@pixerate/editor 0.107.0 → 0.109.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 (78) hide show
  1. package/THIRD_PARTY_NOTICES.md +4 -0
  2. package/dist/animation/index.cjs +5 -5
  3. package/dist/animation/index.cjs.map +1 -1
  4. package/dist/animation/index.js +9 -9
  5. package/dist/animation-editor/index.cjs +59 -59
  6. package/dist/animation-editor/index.cjs.map +1 -1
  7. package/dist/animation-editor/index.d.cts +2 -1
  8. package/dist/animation-editor/index.d.ts +2 -1
  9. package/dist/animation-editor/index.js +29 -29
  10. package/dist/canvas/index.cjs +27 -27
  11. package/dist/canvas/index.cjs.map +1 -1
  12. package/dist/canvas/index.js +11 -11
  13. package/dist/{chunk-2FGRTYJN.cjs → chunk-477UVMNR.cjs} +3 -3
  14. package/dist/{chunk-2FGRTYJN.cjs.map → chunk-477UVMNR.cjs.map} +1 -1
  15. package/dist/{chunk-T3QZ6S47.cjs → chunk-AZTZRLDI.cjs} +40 -2
  16. package/dist/chunk-AZTZRLDI.cjs.map +1 -0
  17. package/dist/{chunk-63EFQCT5.js → chunk-BLWCAQCW.js} +2879 -3362
  18. package/dist/chunk-BLWCAQCW.js.map +1 -0
  19. package/dist/{chunk-G4BIYE6R.js → chunk-CFMWALBM.js} +39 -1
  20. package/dist/chunk-CFMWALBM.js.map +1 -0
  21. package/dist/{chunk-FRD3FUXI.cjs → chunk-D37BX2ZA.cjs} +2081 -460
  22. package/dist/chunk-D37BX2ZA.cjs.map +1 -0
  23. package/dist/chunk-JLYX4V57.cjs +2001 -0
  24. package/dist/chunk-JLYX4V57.cjs.map +1 -0
  25. package/dist/chunk-KB2YBCVS.js +2001 -0
  26. package/dist/chunk-KB2YBCVS.js.map +1 -0
  27. package/dist/{chunk-SOROZ2BF.js → chunk-RE77QYUF.js} +1938 -317
  28. package/dist/chunk-RE77QYUF.js.map +1 -0
  29. package/dist/{chunk-KANRAHTL.cjs → chunk-W3GXSUSQ.cjs} +2830 -3313
  30. package/dist/chunk-W3GXSUSQ.cjs.map +1 -0
  31. package/dist/{chunk-JK6M6V7G.js → chunk-Z3YWVUWT.js} +4 -4
  32. package/dist/{export-a9bnF7Cl.d.ts → export-BLPLaE6Z.d.ts} +417 -16
  33. package/dist/{export-JGWQEPJn.d.cts → export-DSUc4fQD.d.cts} +417 -16
  34. package/dist/image-editor/index.cjs +1527 -1001
  35. package/dist/image-editor/index.cjs.map +1 -1
  36. package/dist/image-editor/index.d.cts +73 -9
  37. package/dist/image-editor/index.d.ts +73 -9
  38. package/dist/image-editor/index.js +814 -288
  39. package/dist/image-editor/index.js.map +1 -1
  40. package/dist/paint-editor/index.cjs +252 -64
  41. package/dist/paint-editor/index.cjs.map +1 -1
  42. package/dist/paint-editor/index.d.cts +101 -4
  43. package/dist/paint-editor/index.d.ts +101 -4
  44. package/dist/paint-editor/index.js +196 -8
  45. package/dist/paint-editor/index.js.map +1 -1
  46. package/dist/raster/index.cjs +4 -2
  47. package/dist/raster/index.cjs.map +1 -1
  48. package/dist/raster/index.d.cts +3 -129
  49. package/dist/raster/index.d.ts +3 -129
  50. package/dist/raster/index.js +3 -1
  51. package/dist/{compositor-D92URMlm.d.cts → render-C-fO_mes.d.cts} +135 -1
  52. package/dist/{compositor-Kt_M4ruo.d.ts → render-OLIMk_v0.d.ts} +135 -1
  53. package/dist/{svg-vme0PLNo.d.ts → svg-D_cfkhlB.d.cts} +6 -608
  54. package/dist/{svg-Bj1MQFh6.d.cts → svg-wIaKLrU0.d.ts} +6 -608
  55. package/dist/types-DtL_qLFz.d.ts +630 -0
  56. package/dist/types-DyQq-WPY.d.cts +630 -0
  57. package/dist/vector/index.cjs +1080 -1051
  58. package/dist/vector/index.cjs.map +1 -1
  59. package/dist/vector/index.d.cts +268 -7
  60. package/dist/vector/index.d.ts +268 -7
  61. package/dist/vector/index.js +495 -466
  62. package/dist/vector/index.js.map +1 -1
  63. package/dist/video-editor/index.cjs +3 -3
  64. package/dist/video-editor/index.js +2 -2
  65. package/package.json +1 -1
  66. package/skills/pixerate-image-editor/SKILL.md +50 -7
  67. package/skills/pixerate-vector/SKILL.md +35 -5
  68. package/dist/chunk-63EFQCT5.js.map +0 -1
  69. package/dist/chunk-DWGZUSES.cjs +0 -694
  70. package/dist/chunk-DWGZUSES.cjs.map +0 -1
  71. package/dist/chunk-FRD3FUXI.cjs.map +0 -1
  72. package/dist/chunk-G4BIYE6R.js.map +0 -1
  73. package/dist/chunk-KANRAHTL.cjs.map +0 -1
  74. package/dist/chunk-KKREERP2.js +0 -694
  75. package/dist/chunk-KKREERP2.js.map +0 -1
  76. package/dist/chunk-SOROZ2BF.js.map +0 -1
  77. package/dist/chunk-T3QZ6S47.cjs.map +0 -1
  78. /package/dist/{chunk-JK6M6V7G.js.map → chunk-Z3YWVUWT.js.map} +0 -0
@@ -1,11 +1,15 @@
1
- import { R as RasterLayer, a as RasterGroupLayer, b as RasterPixelLayer, C as CompositorLayer, c as RasterCompositor, d as RasterSources, L as LayerLocks } from '../compositor-D92URMlm.cjs';
1
+ import { R as RasterLayer, a as RasterGroupLayer, b as RasterPixelLayer, C as CompositorLayer, c as RasterCompositor, d as RasterSources, L as LayerLocks } from '../render-C-fO_mes.cjs';
2
2
  import { B as BrushSettings, M as MixerSettings, C as CoverageMap, D as Dab, S as StrokePoint, a as MixerLoad } from '../mixer-5ziHU8OS.cjs';
3
3
  export { b as MAX_BRUSH_SIZE } from '../mixer-5ziHU8OS.cjs';
4
4
  import { R as RasterRect, a as TiledSurface, B as BlendMode, b as BlendModeInfo } from '../surface-CkKOHGPm.cjs';
5
5
  export { c as BLEND_MODES, d as PAINTERLY_BLEND_MODES, e as Tile, i as isBlendMode, f as isPainterlyBlendMode, s as standardBlendMode } from '../surface-CkKOHGPm.cjs';
6
- import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-JGWQEPJn.cjs';
6
+ import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-DSUc4fQD.cjs';
7
+ import { g as PresenceStore, e as PresenceClock, d as PresenceActorInfo, c as PresenceActor, f as PresenceState } from '../presence-Cb6WhcrQ.cjs';
7
8
  import { a as AgentToolProvider } from '../registry-BqA1AZt0.cjs';
8
9
  import '../types-CwMPoRMU.cjs';
10
+ import '../types-DyQq-WPY.cjs';
11
+ import '../bezier-BfwIbVTl.cjs';
12
+ import '../types-BFkYA4eF.cjs';
9
13
  import '../warp-D5jz_tN7.cjs';
10
14
  import '../schema-Di0CS6Pe.cjs';
11
15
 
@@ -1385,7 +1389,8 @@ declare class PaintEditorController {
1385
1389
  paintStroke(init: PaintStrokeInit & {
1386
1390
  points: readonly PenPoint[];
1387
1391
  }): PaintStrokeResult;
1388
- private checkPoints;
1392
+ /** Checks stroke samples (finite x and y, pressure 0..1) and returns them; throws a sentence naming the bad point. */
1393
+ checkPoints(points: readonly PenPoint[]): PenPoint[];
1389
1394
  /** Every recorded stroke, oldest first. */
1390
1395
  getStrokes(): readonly RecordedStroke[];
1391
1396
  /**
@@ -1533,6 +1538,91 @@ declare class PaintEditorController {
1533
1538
  /** Largest tracing or reference image side, in pixels. */
1534
1539
  declare const MAX_IMAGE_SIDE = 8192;
1535
1540
 
1541
+ type PaintPresenceActor = PresenceActor;
1542
+ type PaintPresenceState = PresenceState;
1543
+ /** Cursor positions (document pixels) and notes of agents painting, for an overlay. */
1544
+ declare class PaintPresence extends PresenceStore {
1545
+ }
1546
+ /** What an agent asks for. Each command that paints commits as one undo step. */
1547
+ type PaintAgentCommand = {
1548
+ kind: 'stroke';
1549
+ stroke: PaintStrokeInit & {
1550
+ points: readonly PenPoint[];
1551
+ };
1552
+ note?: string;
1553
+ } | {
1554
+ kind: 'fill';
1555
+ fill: FillLayerOptions;
1556
+ note?: string;
1557
+ } | {
1558
+ kind: 'replay';
1559
+ replay: ReplayOptions;
1560
+ note?: string;
1561
+ } | {
1562
+ kind: 'point';
1563
+ at: {
1564
+ x: number;
1565
+ y: number;
1566
+ };
1567
+ note?: string;
1568
+ } | {
1569
+ kind: 'say';
1570
+ text: string;
1571
+ };
1572
+ type PaintPerformanceResult = {
1573
+ ok: true;
1574
+ command: PaintAgentCommand;
1575
+ result?: unknown;
1576
+ } | {
1577
+ ok: false;
1578
+ command: PaintAgentCommand;
1579
+ message: string;
1580
+ };
1581
+ interface PaintDirectorOptions {
1582
+ clock?: PresenceClock;
1583
+ /** 1 = natural speed; 2 = twice as fast. */
1584
+ speed?: number;
1585
+ /** Hide each actor's cursor this long after its last command (ms, default 1500; 0 keeps it). */
1586
+ linger?: number;
1587
+ /** Longest a stroke takes to paint at speed 1 (ms, default 1800); longer strokes are sped up. */
1588
+ maxStrokeMs?: number;
1589
+ }
1590
+ /** Where along a stroke each point is reached, 0..1: by its time `t` when the points have times, else by distance. */
1591
+ declare function strokeSchedule(points: readonly PenPoint[]): number[];
1592
+ /** How long a stroke takes to paint at speed 1 (ms): its recorded duration, else ~1.5 px/ms, between 250 ms and `max`. */
1593
+ declare function strokeDuration(points: readonly PenPoint[], max?: number): number;
1594
+ /** A zig-zag across `rect` (for a fill rubbed over the sheet), `rows` passes from the top. */
1595
+ declare function sweepPath(rect: RasterRect, rows?: number): Array<{
1596
+ x: number;
1597
+ y: number;
1598
+ }>;
1599
+ /**
1600
+ * Acts out agent commands on a paint canvas: the actor's cursor glides to the stroke's start and
1601
+ * paints it live along its points (paced by their times, or evenly), the stroke committing as
1602
+ * one undo step; fills are rubbed across the sheet first; replays start from the strokes they
1603
+ * copy and end on what they painted. Commands queue per actor. A command that cannot apply (an
1604
+ * unknown or locked layer, a stroke the user is still painting) fails with the controller's
1605
+ * sentence before anything is painted.
1606
+ */
1607
+ declare class PaintDirector {
1608
+ private readonly controller;
1609
+ readonly presence: PaintPresence;
1610
+ private readonly options;
1611
+ private readonly clock;
1612
+ private readonly queue;
1613
+ constructor(controller: PaintEditorController, presence: PaintPresence, options?: PaintDirectorOptions);
1614
+ /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
1615
+ perform(actor: PresenceActorInfo, commands: readonly PaintAgentCommand[]): Promise<PaintPerformanceResult[]>;
1616
+ private ms;
1617
+ private where;
1618
+ /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
1619
+ private glide;
1620
+ private say;
1621
+ private performOne;
1622
+ /** Paints a stroke live: begins it, pushes its points frame by frame as the cursor reaches them, then ends it (one undo step). */
1623
+ private stroke;
1624
+ }
1625
+
1536
1626
  /**
1537
1627
  * Agent tools for the paint editor (`createPaintEditorToolProvider`). Strict schemas, every
1538
1628
  * argument described, all four annotations. Each write is one undo step (or editor state, said so)
@@ -1545,6 +1635,13 @@ interface PaintEditorToolProviderOptions {
1545
1635
  id?: string;
1546
1636
  /** Always prefix tool names with `<namespace>__`. */
1547
1637
  namespace?: string;
1638
+ /**
1639
+ * Acts out strokes, fills and replays with an agent cursor on the canvas (see `PaintDirector`):
1640
+ * strokes paint live along their points. Each call is still one undo step. Without it, they apply at once.
1641
+ */
1642
+ director?: PaintDirector;
1643
+ /** The agent's cursor (default `{ id: 'agent', name: 'Agent' }`). */
1644
+ actor?: PresenceActorInfo;
1548
1645
  }
1549
1646
  /** Agent tools for one paint editor. */
1550
1647
  declare function createPaintEditorToolProvider(controller: PaintEditorController, options?: PaintEditorToolProviderOptions): AgentToolProvider;
@@ -1688,4 +1785,4 @@ interface BlendMenuGroup {
1688
1785
  */
1689
1786
  declare const BLEND_MENU_GROUPS: readonly BlendMenuGroup[];
1690
1787
 
1691
- export { type Affine, BLEND_MENU_GROUPS, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, type BlendMenuGroup, BlendMode, BlendModeInfo, type BrushDynamics, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_PAPER, DEFAULT_SWATCHES, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MixingPad, type MixingPadTool, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, type Paint2DContext, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintPixelLayer, type PaintPointerEvent, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, SYMMETRY_MODES, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, TileableNoise, type TracingImage, type ViewLayer, type ViewportBox, affineCopy, applyAffine, brushToJSON, clonePaper, colorHarmony, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, newPaintLayerId, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizePaintBrush, normalizePaper, normalizePerspective, normalizeRotation, normalizeSymmetry, normalizeView, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, rand01, randSigned, readPath, renderBrushThumbnail, renderColorWheel, renderPaper, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thumbnailStroke, tiltToOrientation, toStrokePoint, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wheelAngle, wheelPick, wheelPoint, wheelToHue };
1788
+ export { type Affine, BLEND_MENU_GROUPS, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, type BlendMenuGroup, BlendMode, BlendModeInfo, type BrushDynamics, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_PAPER, DEFAULT_SWATCHES, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MixingPad, type MixingPadTool, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, type Paint2DContext, type PaintAgentCommand, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDirector, type PaintDirectorOptions, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintPerformanceResult, type PaintPixelLayer, type PaintPointerEvent, PaintPresence, type PaintPresenceActor, type PaintPresenceState, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, SYMMETRY_MODES, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, TileableNoise, type TracingImage, type ViewLayer, type ViewportBox, affineCopy, applyAffine, brushToJSON, clonePaper, colorHarmony, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, newPaintLayerId, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizePaintBrush, normalizePaper, normalizePerspective, normalizeRotation, normalizeSymmetry, normalizeView, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, rand01, randSigned, readPath, renderBrushThumbnail, renderColorWheel, renderPaper, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, strokeDuration, strokeSchedule, sweepPath, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thumbnailStroke, tiltToOrientation, toStrokePoint, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wheelAngle, wheelPick, wheelPoint, wheelToHue };
@@ -1,11 +1,15 @@
1
- import { R as RasterLayer, a as RasterGroupLayer, b as RasterPixelLayer, C as CompositorLayer, c as RasterCompositor, d as RasterSources, L as LayerLocks } from '../compositor-Kt_M4ruo.js';
1
+ import { R as RasterLayer, a as RasterGroupLayer, b as RasterPixelLayer, C as CompositorLayer, c as RasterCompositor, d as RasterSources, L as LayerLocks } from '../render-OLIMk_v0.js';
2
2
  import { B as BrushSettings, M as MixerSettings, C as CoverageMap, D as Dab, S as StrokePoint, a as MixerLoad } from '../mixer-DNqiYXYQ.js';
3
3
  export { b as MAX_BRUSH_SIZE } from '../mixer-DNqiYXYQ.js';
4
4
  import { R as RasterRect, a as TiledSurface, B as BlendMode, b as BlendModeInfo } from '../surface-CkKOHGPm.js';
5
5
  export { c as BLEND_MODES, d as PAINTERLY_BLEND_MODES, e as Tile, i as isBlendMode, f as isPainterlyBlendMode, s as standardBlendMode } from '../surface-CkKOHGPm.js';
6
- import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-a9bnF7Cl.js';
6
+ import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-BLPLaE6Z.js';
7
+ import { g as PresenceStore, e as PresenceClock, d as PresenceActorInfo, c as PresenceActor, f as PresenceState } from '../presence-Cb6WhcrQ.js';
7
8
  import { a as AgentToolProvider } from '../registry-D-4fit9C.js';
8
9
  import '../types-CwMPoRMU.js';
10
+ import '../types-DtL_qLFz.js';
11
+ import '../bezier-DYBvLgUH.js';
12
+ import '../types-BFkYA4eF.js';
9
13
  import '../warp-CWHZzE3x.js';
10
14
  import '../schema-Di0CS6Pe.js';
11
15
 
@@ -1385,7 +1389,8 @@ declare class PaintEditorController {
1385
1389
  paintStroke(init: PaintStrokeInit & {
1386
1390
  points: readonly PenPoint[];
1387
1391
  }): PaintStrokeResult;
1388
- private checkPoints;
1392
+ /** Checks stroke samples (finite x and y, pressure 0..1) and returns them; throws a sentence naming the bad point. */
1393
+ checkPoints(points: readonly PenPoint[]): PenPoint[];
1389
1394
  /** Every recorded stroke, oldest first. */
1390
1395
  getStrokes(): readonly RecordedStroke[];
1391
1396
  /**
@@ -1533,6 +1538,91 @@ declare class PaintEditorController {
1533
1538
  /** Largest tracing or reference image side, in pixels. */
1534
1539
  declare const MAX_IMAGE_SIDE = 8192;
1535
1540
 
1541
+ type PaintPresenceActor = PresenceActor;
1542
+ type PaintPresenceState = PresenceState;
1543
+ /** Cursor positions (document pixels) and notes of agents painting, for an overlay. */
1544
+ declare class PaintPresence extends PresenceStore {
1545
+ }
1546
+ /** What an agent asks for. Each command that paints commits as one undo step. */
1547
+ type PaintAgentCommand = {
1548
+ kind: 'stroke';
1549
+ stroke: PaintStrokeInit & {
1550
+ points: readonly PenPoint[];
1551
+ };
1552
+ note?: string;
1553
+ } | {
1554
+ kind: 'fill';
1555
+ fill: FillLayerOptions;
1556
+ note?: string;
1557
+ } | {
1558
+ kind: 'replay';
1559
+ replay: ReplayOptions;
1560
+ note?: string;
1561
+ } | {
1562
+ kind: 'point';
1563
+ at: {
1564
+ x: number;
1565
+ y: number;
1566
+ };
1567
+ note?: string;
1568
+ } | {
1569
+ kind: 'say';
1570
+ text: string;
1571
+ };
1572
+ type PaintPerformanceResult = {
1573
+ ok: true;
1574
+ command: PaintAgentCommand;
1575
+ result?: unknown;
1576
+ } | {
1577
+ ok: false;
1578
+ command: PaintAgentCommand;
1579
+ message: string;
1580
+ };
1581
+ interface PaintDirectorOptions {
1582
+ clock?: PresenceClock;
1583
+ /** 1 = natural speed; 2 = twice as fast. */
1584
+ speed?: number;
1585
+ /** Hide each actor's cursor this long after its last command (ms, default 1500; 0 keeps it). */
1586
+ linger?: number;
1587
+ /** Longest a stroke takes to paint at speed 1 (ms, default 1800); longer strokes are sped up. */
1588
+ maxStrokeMs?: number;
1589
+ }
1590
+ /** Where along a stroke each point is reached, 0..1: by its time `t` when the points have times, else by distance. */
1591
+ declare function strokeSchedule(points: readonly PenPoint[]): number[];
1592
+ /** How long a stroke takes to paint at speed 1 (ms): its recorded duration, else ~1.5 px/ms, between 250 ms and `max`. */
1593
+ declare function strokeDuration(points: readonly PenPoint[], max?: number): number;
1594
+ /** A zig-zag across `rect` (for a fill rubbed over the sheet), `rows` passes from the top. */
1595
+ declare function sweepPath(rect: RasterRect, rows?: number): Array<{
1596
+ x: number;
1597
+ y: number;
1598
+ }>;
1599
+ /**
1600
+ * Acts out agent commands on a paint canvas: the actor's cursor glides to the stroke's start and
1601
+ * paints it live along its points (paced by their times, or evenly), the stroke committing as
1602
+ * one undo step; fills are rubbed across the sheet first; replays start from the strokes they
1603
+ * copy and end on what they painted. Commands queue per actor. A command that cannot apply (an
1604
+ * unknown or locked layer, a stroke the user is still painting) fails with the controller's
1605
+ * sentence before anything is painted.
1606
+ */
1607
+ declare class PaintDirector {
1608
+ private readonly controller;
1609
+ readonly presence: PaintPresence;
1610
+ private readonly options;
1611
+ private readonly clock;
1612
+ private readonly queue;
1613
+ constructor(controller: PaintEditorController, presence: PaintPresence, options?: PaintDirectorOptions);
1614
+ /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
1615
+ perform(actor: PresenceActorInfo, commands: readonly PaintAgentCommand[]): Promise<PaintPerformanceResult[]>;
1616
+ private ms;
1617
+ private where;
1618
+ /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
1619
+ private glide;
1620
+ private say;
1621
+ private performOne;
1622
+ /** Paints a stroke live: begins it, pushes its points frame by frame as the cursor reaches them, then ends it (one undo step). */
1623
+ private stroke;
1624
+ }
1625
+
1536
1626
  /**
1537
1627
  * Agent tools for the paint editor (`createPaintEditorToolProvider`). Strict schemas, every
1538
1628
  * argument described, all four annotations. Each write is one undo step (or editor state, said so)
@@ -1545,6 +1635,13 @@ interface PaintEditorToolProviderOptions {
1545
1635
  id?: string;
1546
1636
  /** Always prefix tool names with `<namespace>__`. */
1547
1637
  namespace?: string;
1638
+ /**
1639
+ * Acts out strokes, fills and replays with an agent cursor on the canvas (see `PaintDirector`):
1640
+ * strokes paint live along their points. Each call is still one undo step. Without it, they apply at once.
1641
+ */
1642
+ director?: PaintDirector;
1643
+ /** The agent's cursor (default `{ id: 'agent', name: 'Agent' }`). */
1644
+ actor?: PresenceActorInfo;
1548
1645
  }
1549
1646
  /** Agent tools for one paint editor. */
1550
1647
  declare function createPaintEditorToolProvider(controller: PaintEditorController, options?: PaintEditorToolProviderOptions): AgentToolProvider;
@@ -1688,4 +1785,4 @@ interface BlendMenuGroup {
1688
1785
  */
1689
1786
  declare const BLEND_MENU_GROUPS: readonly BlendMenuGroup[];
1690
1787
 
1691
- export { type Affine, BLEND_MENU_GROUPS, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, type BlendMenuGroup, BlendMode, BlendModeInfo, type BrushDynamics, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_PAPER, DEFAULT_SWATCHES, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MixingPad, type MixingPadTool, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, type Paint2DContext, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintPixelLayer, type PaintPointerEvent, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, SYMMETRY_MODES, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, TileableNoise, type TracingImage, type ViewLayer, type ViewportBox, affineCopy, applyAffine, brushToJSON, clonePaper, colorHarmony, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, newPaintLayerId, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizePaintBrush, normalizePaper, normalizePerspective, normalizeRotation, normalizeSymmetry, normalizeView, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, rand01, randSigned, readPath, renderBrushThumbnail, renderColorWheel, renderPaper, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thumbnailStroke, tiltToOrientation, toStrokePoint, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wheelAngle, wheelPick, wheelPoint, wheelToHue };
1788
+ export { type Affine, BLEND_MENU_GROUPS, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, type BlendMenuGroup, BlendMode, BlendModeInfo, type BrushDynamics, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_PAPER, DEFAULT_SWATCHES, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MixingPad, type MixingPadTool, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, type Paint2DContext, type PaintAgentCommand, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDirector, type PaintDirectorOptions, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintPerformanceResult, type PaintPixelLayer, type PaintPointerEvent, PaintPresence, type PaintPresenceActor, type PaintPresenceState, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, SYMMETRY_MODES, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, TileableNoise, type TracingImage, type ViewLayer, type ViewportBox, affineCopy, applyAffine, brushToJSON, clonePaper, colorHarmony, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, newPaintLayerId, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizePaintBrush, normalizePaper, normalizePerspective, normalizeRotation, normalizeSymmetry, normalizeView, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, rand01, randSigned, readPath, renderBrushThumbnail, renderColorWheel, renderPaper, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, strokeDuration, strokeSchedule, sweepPath, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thumbnailStroke, tiltToOrientation, toStrokePoint, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wheelAngle, wheelPick, wheelPoint, wheelToHue };
@@ -4,7 +4,7 @@ import {
4
4
  resolveExternalAssets,
5
5
  serializeLayers,
6
6
  serializeLayersExternal
7
- } from "../chunk-SOROZ2BF.js";
7
+ } from "../chunk-RE77QYUF.js";
8
8
  import {
9
9
  RasterCompositor,
10
10
  bytesToBase64,
@@ -17,7 +17,7 @@ import {
17
17
  mapLayer,
18
18
  parseDataUrl,
19
19
  pngDataUrl
20
- } from "../chunk-G4BIYE6R.js";
20
+ } from "../chunk-CFMWALBM.js";
21
21
  import "../chunk-GX332W2C.js";
22
22
  import {
23
23
  CoverageMap,
@@ -54,7 +54,16 @@ import {
54
54
  tileKey,
55
55
  tilesInRect
56
56
  } from "../chunk-WMBIJCHA.js";
57
- import "../chunk-KKREERP2.js";
57
+ import {
58
+ PerformanceQueue,
59
+ PresenceStore,
60
+ realPresenceClock,
61
+ tweenPresenceCursor
62
+ } from "../chunk-B6DSIEXA.js";
63
+ import "../chunk-BLWCAQCW.js";
64
+ import "../chunk-YCSFS3GK.js";
65
+ import "../chunk-ZRJMJIQU.js";
66
+ import "../chunk-TGD52FEZ.js";
58
67
 
59
68
  // src/paint-editor/noise.ts
60
69
  function mix32(h) {
@@ -2716,6 +2725,7 @@ var PaintEditorController = class {
2716
2725
  else this.emit([]);
2717
2726
  return result;
2718
2727
  }
2728
+ /** Checks stroke samples (finite x and y, pressure 0..1) and returns them; throws a sentence naming the bad point. */
2719
2729
  checkPoints(points) {
2720
2730
  if (!Array.isArray(points) || !points.length) throw new PaintEditorError("points is empty; give at least one {x, y} in document pixels.");
2721
2731
  const out = [];
@@ -3250,6 +3260,169 @@ function resampleInto(src, rect, width, height) {
3250
3260
  return { width, height, data: out };
3251
3261
  }
3252
3262
 
3263
+ // src/paint-editor/presence.ts
3264
+ var PaintPresence = class extends PresenceStore {
3265
+ };
3266
+ var FRAME = 16;
3267
+ function strokeSchedule(points) {
3268
+ if (points.length < 2) return points.map(() => 1);
3269
+ const t0 = points[0].t;
3270
+ const t1 = points[points.length - 1].t;
3271
+ if (points.every((p) => Number.isFinite(p.t)) && t1 > t0) {
3272
+ return points.map((p) => Math.min(1, Math.max(0, (p.t - t0) / (t1 - t0))));
3273
+ }
3274
+ const acc = [0];
3275
+ for (let i = 1; i < points.length; i++) acc.push(acc[i - 1] + Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y));
3276
+ const total = acc[acc.length - 1];
3277
+ return total > 0 ? acc.map((d) => d / total) : points.map(() => 1);
3278
+ }
3279
+ function strokeDuration(points, max = 1800) {
3280
+ const t0 = points[0]?.t;
3281
+ const t1 = points[points.length - 1]?.t;
3282
+ let ms;
3283
+ if (points.every((p) => Number.isFinite(p.t)) && t1 > t0) ms = t1 - t0;
3284
+ else {
3285
+ let length = 0;
3286
+ for (let i = 1; i < points.length; i++) length += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);
3287
+ ms = length / 1.5;
3288
+ }
3289
+ return Math.min(max, Math.max(250, ms));
3290
+ }
3291
+ function sweepPath(rect, rows = 4) {
3292
+ const inset = Math.min(rect.width, rect.height) * 0.08;
3293
+ const left = rect.x + inset;
3294
+ const right = rect.x + rect.width - inset;
3295
+ const out = [];
3296
+ for (let r = 0; r < rows; r++) {
3297
+ const y = rect.y + inset + (rect.height - 2 * inset) * (r + 0.5) / rows;
3298
+ out.push(r % 2 ? { x: right, y } : { x: left, y }, r % 2 ? { x: left, y } : { x: right, y });
3299
+ }
3300
+ return out;
3301
+ }
3302
+ var centre = (r) => ({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
3303
+ var PaintDirector = class {
3304
+ constructor(controller, presence, options = {}) {
3305
+ this.controller = controller;
3306
+ this.presence = presence;
3307
+ this.options = options;
3308
+ this.clock = options.clock ?? realPresenceClock;
3309
+ this.queue = new PerformanceQueue(presence, { clock: this.clock, linger: options.linger });
3310
+ }
3311
+ controller;
3312
+ presence;
3313
+ options;
3314
+ clock;
3315
+ queue;
3316
+ /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
3317
+ perform(actor, commands) {
3318
+ return this.queue.run(actor, async () => {
3319
+ const results = [];
3320
+ for (const command of commands) {
3321
+ try {
3322
+ results.push({ ok: true, command, result: await this.performOne(actor.id, command) });
3323
+ } catch (error) {
3324
+ results.push({ ok: false, command, message: error instanceof Error ? error.message : String(error) });
3325
+ }
3326
+ }
3327
+ return results;
3328
+ });
3329
+ }
3330
+ ms(n) {
3331
+ return Math.round(n / Math.max(0.1, this.options.speed ?? 1));
3332
+ }
3333
+ where(id) {
3334
+ const a = this.presence.get(id);
3335
+ return { x: a?.x ?? 0, y: a?.y ?? 0 };
3336
+ }
3337
+ /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
3338
+ async glide(id, to, ms = 380) {
3339
+ const a = this.presence.get(id);
3340
+ if (!a || a.x === 0 && a.y === 0) {
3341
+ this.presence.set({ id, x: to.x, y: to.y });
3342
+ return;
3343
+ }
3344
+ await tweenPresenceCursor(this.presence, this.clock, id, this.where(id), to, this.ms(ms));
3345
+ }
3346
+ async say(id, text, ms) {
3347
+ if (!text) return;
3348
+ this.presence.set({ id, message: text });
3349
+ await this.clock.wait(this.ms(ms));
3350
+ }
3351
+ async performOne(id, command) {
3352
+ const c = this.controller;
3353
+ switch (command.kind) {
3354
+ case "say":
3355
+ await this.say(id, command.text, 900);
3356
+ return void 0;
3357
+ case "point":
3358
+ await this.say(id, command.note, 500);
3359
+ await this.glide(id, command.at);
3360
+ this.presence.set({ id, pressed: true });
3361
+ await this.clock.wait(this.ms(110));
3362
+ this.presence.set({ id, pressed: false });
3363
+ return void 0;
3364
+ case "stroke":
3365
+ await this.say(id, command.note, 500);
3366
+ return this.stroke(id, command.stroke);
3367
+ case "fill": {
3368
+ await this.say(id, command.note, 500);
3369
+ const { width, height } = c.getState().document.canvas;
3370
+ const path = sweepPath({ x: 0, y: 0, width, height });
3371
+ await this.glide(id, path[0]);
3372
+ this.presence.set({ id, pressed: true });
3373
+ try {
3374
+ for (let i = 1; i < path.length; i++) await tweenPresenceCursor(this.presence, this.clock, id, path[i - 1], path[i], this.ms(i % 2 ? 260 : 90));
3375
+ } finally {
3376
+ this.presence.set({ id, pressed: false });
3377
+ }
3378
+ return c.fillLayer(command.fill);
3379
+ }
3380
+ case "replay": {
3381
+ await this.say(id, command.note, 500);
3382
+ const source = c.getStrokes().filter((s) => command.replay.strokeIds?.includes(s.id));
3383
+ const first = source[0]?.points[0];
3384
+ if (first) await this.glide(id, first);
3385
+ const result = c.replayStrokes(command.replay);
3386
+ if (result.bounds) await this.glide(id, centre(result.bounds), 520);
3387
+ return result;
3388
+ }
3389
+ }
3390
+ }
3391
+ /** Paints a stroke live: begins it, pushes its points frame by frame as the cursor reaches them, then ends it (one undo step). */
3392
+ async stroke(id, init) {
3393
+ const c = this.controller;
3394
+ const points = c.checkPoints(init.points);
3395
+ const { points: _points, ...rest } = init;
3396
+ const session = c.beginStroke({ snapToPerspective: false, start: points[0], ...rest });
3397
+ try {
3398
+ await this.glide(id, points[0]);
3399
+ this.presence.set({ id, pressed: true });
3400
+ const at = strokeSchedule(points);
3401
+ const duration = this.ms(strokeDuration(points, this.options.maxStrokeMs));
3402
+ let next = 0;
3403
+ const frames = Math.max(1, Math.round(duration / FRAME));
3404
+ for (let f = 1; f <= frames; f++) {
3405
+ const t = f / frames;
3406
+ const from = next;
3407
+ while (next < points.length && at[next] <= t + 1e-9) next++;
3408
+ if (next > from) {
3409
+ session.push(points.slice(from, next));
3410
+ const p = points[next - 1];
3411
+ this.presence.set({ id, x: p.x, y: p.y });
3412
+ }
3413
+ if (f < frames) await this.clock.wait(duration / frames);
3414
+ }
3415
+ if (next < points.length) session.push(points.slice(next));
3416
+ this.presence.set({ id, pressed: false });
3417
+ return session.end();
3418
+ } catch (error) {
3419
+ if (c.activeStroke === session) session.cancel();
3420
+ this.presence.set({ id, pressed: false });
3421
+ throw error;
3422
+ }
3423
+ }
3424
+ };
3425
+
3253
3426
  // src/paint-editor/guide-tools.ts
3254
3427
  var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
3255
3428
  var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
@@ -3685,6 +3858,12 @@ function createPaintEditorToolProvider(controller, options = {}) {
3685
3858
  const c = controller;
3686
3859
  const tools = [];
3687
3860
  const add = (t) => tools.push(t);
3861
+ const act = async (command, direct) => {
3862
+ if (!options.director) return direct();
3863
+ const [r] = await options.director.perform(options.actor ?? { id: "agent", name: "Agent" }, [command]);
3864
+ if (!r || !r.ok) throw new Error(r && !r.ok ? r.message : "The director did not run the command.");
3865
+ return r.result;
3866
+ };
3688
3867
  add({
3689
3868
  name: "get_canvas_info",
3690
3869
  title: "Get the canvas",
@@ -3949,7 +4128,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
3949
4128
  additionalProperties: false
3950
4129
  },
3951
4130
  annotations: { title: "Fill a layer", ...WRITE2 },
3952
- handler: (a) => c.fillLayer(a)
4131
+ handler: (a) => act({ kind: "fill", fill: a }, () => c.fillLayer(a))
3953
4132
  });
3954
4133
  add({
3955
4134
  name: "clear_layer",
@@ -4132,7 +4311,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
4132
4311
  add({
4133
4312
  name: "paint_stroke",
4134
4313
  title: "Paint a brush stroke",
4135
- description: "Paints one stroke through points (document pixels, with optional pressure, time, tilt and barrel rotation) on a pixel layer with a library brush or the active one, optionally changed for this stroke. Grain-responsive brushes follow the paper; the mixer engine (mixer-brush, blender, smudge) picks up and moves paint already on the layer. The symmetry guide adds its mirror copies; with a tracing image that samples colours, a stroke without color takes the image colour at its first point. One undo step. Returns the layer id, changed bounds, dab count, the recorded strokeId (for replay_strokes) and, for the mixer, its load after the stroke.",
4314
+ description: "Paints one stroke through points (document pixels, with optional pressure, time, tilt and barrel rotation) on a pixel layer with a library brush or the active one, optionally changed for this stroke. Grain-responsive brushes follow the paper; the mixer engine (mixer-brush, blender, smudge) picks up and moves paint already on the layer. The symmetry guide adds its mirror copies; with a tracing image that samples colours, a stroke without color takes the image colour at its first point. With an agent cursor (a director), the stroke is painted live along the points at the pace of their times. One undo step. Returns the layer id, changed bounds, dab count, the recorded strokeId (for replay_strokes) and, for the mixer, its load after the stroke.",
4136
4315
  inputSchema: {
4137
4316
  type: "object",
4138
4317
  properties: {
@@ -4148,8 +4327,9 @@ function createPaintEditorToolProvider(controller, options = {}) {
4148
4327
  additionalProperties: false
4149
4328
  },
4150
4329
  annotations: { title: "Paint a brush stroke", ...WRITE2 },
4151
- handler: (a) => {
4152
- const r = c.paintStroke({ points: a.points, layerId: a.layerId, brushId: a.brushId, brush: a.brush, color: a.color, seed: a.seed, snapToPerspective: a.snapToPerspective });
4330
+ handler: async (a) => {
4331
+ const stroke = { points: a.points, layerId: a.layerId, brushId: a.brushId, brush: a.brush, color: a.color, seed: a.seed, snapToPerspective: a.snapToPerspective };
4332
+ const r = await act({ kind: "stroke", stroke }, () => c.paintStroke(stroke));
4153
4333
  return r.mixerLoad ? { ...r, mixerLoad: loadResult(r.mixerLoad) } : r;
4154
4334
  }
4155
4335
  });
@@ -4223,7 +4403,10 @@ function createPaintEditorToolProvider(controller, options = {}) {
4223
4403
  additionalProperties: false
4224
4404
  },
4225
4405
  annotations: { title: "Replay strokes, varied", ...WRITE2 },
4226
- handler: (a) => c.replayStrokes(a)
4406
+ handler: (a) => {
4407
+ const replay = a;
4408
+ return act({ kind: "replay", replay }, () => c.replayStrokes(replay));
4409
+ }
4227
4410
  });
4228
4411
  add({
4229
4412
  name: "set_view",
@@ -4675,10 +4858,12 @@ export {
4675
4858
  PAPER_NODE_ID,
4676
4859
  PAPER_SLIDERS,
4677
4860
  PaintCanvasPresenter,
4861
+ PaintDirector,
4678
4862
  PaintDocumentError,
4679
4863
  PaintEditorController,
4680
4864
  PaintEditorError,
4681
4865
  PaintGesture,
4866
+ PaintPresence,
4682
4867
  PaintStrokeSession,
4683
4868
  PaperGrain,
4684
4869
  PenDabWalker,
@@ -4762,6 +4947,9 @@ export {
4762
4947
  serializePaintDocumentExternal,
4763
4948
  standardBlendMode,
4764
4949
  strokeBounds,
4950
+ strokeDuration,
4951
+ strokeSchedule,
4952
+ sweepPath,
4765
4953
  symmetryAxisAngles,
4766
4954
  symmetryAxisSegments,
4767
4955
  symmetryCopies,