@snowcone-app/canvas 0.26.0 → 0.28.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 (70) hide show
  1. package/ERROR_CONTRACT.md +51 -0
  2. package/dist/{CanvasRenderer-6_HhR4-q.js → CanvasRenderer-B_3GISlW.js} +1124 -1068
  3. package/dist/CanvasRenderer-B_3GISlW.js.map +1 -0
  4. package/dist/{CanvasStateV1-DmZ1Qvvn.js → CanvasStateV1-kL09wsAn.js} +3 -3
  5. package/dist/{CanvasStateV1-DmZ1Qvvn.js.map → CanvasStateV1-kL09wsAn.js.map} +1 -1
  6. package/dist/ContextualToolbars-C69oYLdw.js +5771 -0
  7. package/dist/ContextualToolbars-C69oYLdw.js.map +1 -0
  8. package/dist/{EditorCanvasImpl-BLZV_el0.js → EditorCanvasImpl-CrI9C_xM.js} +8 -8
  9. package/dist/{EditorCanvasImpl-BLZV_el0.js.map → EditorCanvasImpl-CrI9C_xM.js.map} +1 -1
  10. package/dist/{ExportManager-DoU4VK6h.js → ExportManager-J2xK86uq.js} +12 -16
  11. package/dist/ExportManager-J2xK86uq.js.map +1 -0
  12. package/dist/{FontAnalyzer-CwIpgTGd.js → FontAnalyzer-PE6nysqE.js} +686 -657
  13. package/dist/FontAnalyzer-PE6nysqE.js.map +1 -0
  14. package/dist/{ImagePanel-sLSjaPWS.js → ImagePanel-CmQCxBGm.js} +6 -6
  15. package/dist/{ImagePanel-sLSjaPWS.js.map → ImagePanel-CmQCxBGm.js.map} +1 -1
  16. package/dist/{SnowconeCanvas-4ef4a61J.js → SnowconeCanvas-CQiFVR0X.js} +10 -10
  17. package/dist/{SnowconeCanvas-4ef4a61J.js.map → SnowconeCanvas-CQiFVR0X.js.map} +1 -1
  18. package/dist/{UnifiedFontService-Bx2MxFfy.js → UnifiedFontService-BJzoqfBF.js} +755 -713
  19. package/dist/UnifiedFontService-BJzoqfBF.js.map +1 -0
  20. package/dist/advanced.mjs +14 -14
  21. package/dist/api/embed.d.ts +4 -0
  22. package/dist/components/MultiSelectionToolbar.d.ts +18 -0
  23. package/dist/components/ui/toolbar-button.d.ts +2 -0
  24. package/dist/contexts/EditorContext.d.ts +34 -0
  25. package/dist/core/GestureConstraints.d.ts +92 -0
  26. package/dist/core/HybridHistoryManager.d.ts +7 -0
  27. package/dist/core/InteractionStateMachine.d.ts +16 -2
  28. package/dist/core/ResizeHandler.d.ts +4 -1
  29. package/dist/core/ResizePipeline.d.ts +3 -0
  30. package/dist/core/elementGuards.d.ts +19 -0
  31. package/dist/{custom-icons-DeYIrTK1.js → custom-icons-BWiUGqD6.js} +2 -2
  32. package/dist/{custom-icons-DeYIrTK1.js.map → custom-icons-BWiUGqD6.js.map} +1 -1
  33. package/dist/{design-builder-U7Su9MMC.js → design-builder-bquxIHct.js} +2 -2
  34. package/dist/{design-builder-U7Su9MMC.js.map → design-builder-bquxIHct.js.map} +1 -1
  35. package/dist/editorErrors-8uDRMhow.js +1219 -0
  36. package/dist/editorErrors-8uDRMhow.js.map +1 -0
  37. package/dist/{embed-DXGydZOT.js → embed-L3oRIgsj.js} +10 -10
  38. package/dist/{embed-DXGydZOT.js.map → embed-L3oRIgsj.js.map} +1 -1
  39. package/dist/embed.mjs +39 -33
  40. package/dist/errors/editorError.d.ts +19 -1
  41. package/dist/{guards-8rkFAMBs.js → guards-BlbhM5MX.js} +2 -2
  42. package/dist/{guards-8rkFAMBs.js.map → guards-BlbhM5MX.js.map} +1 -1
  43. package/dist/hooks/elementPatch.d.ts +96 -0
  44. package/dist/hooks/index.d.ts +2 -0
  45. package/dist/hooks/useAlignDistribute.d.ts +19 -0
  46. package/dist/hooks/useCommands.d.ts +20 -0
  47. package/dist/index.mjs +8 -8
  48. package/dist/internals.mjs +9 -9
  49. package/dist/template-fields-ai7vnW8g.js +716 -0
  50. package/dist/template-fields-ai7vnW8g.js.map +1 -0
  51. package/dist/testing.mjs +4 -4
  52. package/dist/{usePieceGuideBundle-CbJJg66S.js → usePieceGuideBundle-CMe5br2a.js} +2 -2
  53. package/dist/{usePieceGuideBundle-CbJJg66S.js.map → usePieceGuideBundle-CMe5br2a.js.map} +1 -1
  54. package/dist/utils/alignDistribute.d.ts +41 -0
  55. package/dist/{validation-DnAkXdUg.js → validation-B8kIYutI.js} +2592 -2562
  56. package/dist/validation-B8kIYutI.js.map +1 -0
  57. package/dist/workers/export-worker.bundle.string.d.ts +1 -1
  58. package/example/package.json +1 -1
  59. package/package.json +3 -3
  60. package/dist/CanvasRenderer-6_HhR4-q.js.map +0 -1
  61. package/dist/ContextualToolbars-DUhBKVxe.js +0 -5556
  62. package/dist/ContextualToolbars-DUhBKVxe.js.map +0 -1
  63. package/dist/ExportManager-DoU4VK6h.js.map +0 -1
  64. package/dist/FontAnalyzer-CwIpgTGd.js.map +0 -1
  65. package/dist/UnifiedFontService-Bx2MxFfy.js.map +0 -1
  66. package/dist/editorErrors-B2mb1sVN.js +0 -1208
  67. package/dist/editorErrors-B2mb1sVN.js.map +0 -1
  68. package/dist/template-fields-DJ157D3E.js +0 -548
  69. package/dist/template-fields-DJ157D3E.js.map +0 -1
  70. package/dist/validation-DnAkXdUg.js.map +0 -1
package/dist/embed.mjs CHANGED
@@ -1,48 +1,54 @@
1
- import { C as a, a as r, b as o, u as t, d as i, f as d, g as n } from "./UnifiedFontService-Bx2MxFfy.js";
2
- import { E as m, R as l, a as E, b as c, S as g, c as p, d as f, f as S, p as x, e as T, g as v, h as C, v as b } from "./embed-DXGydZOT.js";
3
- import { g as P, i as y, u as R } from "./editorErrors-B2mb1sVN.js";
4
- import { D as k, d as F, a as I, i as L } from "./design-builder-U7Su9MMC.js";
5
- import { D as w, T as M, r as A, u as _, a as z, b as G, c as K, d as N, e as Y, f as j, w as q } from "./template-fields-DJ157D3E.js";
6
- import { s as J } from "./CanvasStateV1-DmZ1Qvvn.js";
1
+ import { C as a, a as r, b as t, u as i, d as o, f as n, g as d } from "./UnifiedFontService-BJzoqfBF.js";
2
+ import { E as l, R as u, a as E, b as c, S as p, c as g, d as S, f, p as x, e as T, g as v, h as C, v as P } from "./embed-L3oRIgsj.js";
3
+ import { g as B, i as h, u as y } from "./editorErrors-8uDRMhow.js";
4
+ import { D as k, d as D, a as I, i as F } from "./design-builder-bquxIHct.js";
5
+ import { D as w, T as M, h as A, i as G, j as K, k as _, l as j, m as z, r as N, u as Y, a as q, b as H, c as J, d as O, e as Q, f as U, w as V } from "./template-fields-ai7vnW8g.js";
6
+ import { s as X } from "./CanvasStateV1-kL09wsAn.js";
7
7
  export {
8
8
  a as CanvasServicesProvider,
9
9
  k as DesignBuilder,
10
10
  w as DesignStateStore,
11
- m as Editor,
12
- P as EditorError,
13
- l as RealtimeCanvas,
11
+ l as Editor,
12
+ B as EditorError,
13
+ u as RealtimeCanvas,
14
14
  E as RealtimeEmbed,
15
15
  c as RealtimeMockup,
16
- g as SnowconeEditor,
16
+ p as SnowconeEditor,
17
17
  M as TEMPLATE_FIELDS_KEY,
18
- p as auditEditor,
19
- f as collectEmbedIssues,
18
+ g as auditEditor,
19
+ S as collectEmbedIssues,
20
20
  r as createProxyGenerate,
21
- o as createProxyImageSearch,
22
- F as design,
23
- S as formatBuyPrice,
21
+ t as createProxyImageSearch,
22
+ D as design,
23
+ A as editorElementKind,
24
+ f as formatBuyPrice,
24
25
  I as isColorToken,
25
- y as isEditorError,
26
- L as isLayerToken,
26
+ h as isEditorError,
27
+ G as isGroupElement,
28
+ K as isImageElement,
29
+ F as isLayerToken,
30
+ _ as isPathElement,
31
+ j as isShapeElement,
32
+ z as isTextElement,
27
33
  x as productToRenderConfig,
28
- A as readTemplateFields,
29
- J as serializeState,
30
- t as useArtboards,
31
- i as useCanvasServices,
32
- _ as useColorBinding,
33
- z as useCommands,
34
- R as useEditor,
35
- G as useElementByName,
36
- d as useExport,
37
- K as useFontBinding,
38
- N as useImageBinding,
39
- n as useLayers,
34
+ N as readTemplateFields,
35
+ X as serializeState,
36
+ i as useArtboards,
37
+ o as useCanvasServices,
38
+ Y as useColorBinding,
39
+ q as useCommands,
40
+ y as useEditor,
41
+ H as useElementByName,
42
+ n as useExport,
43
+ J as useFontBinding,
44
+ O as useImageBinding,
45
+ d as useLayers,
40
46
  T as usePlacements,
41
47
  v as useProductSelection,
42
48
  C as useRealtimeMockups,
43
- Y as useSelectedElement,
44
- j as useTextBinding,
45
- b as validateEmbed,
46
- q as withTemplateFields
49
+ Q as useSelectedElement,
50
+ U as useTextBinding,
51
+ P as validateEmbed,
52
+ V as withTemplateFields
47
53
  };
48
54
  //# sourceMappingURL=embed.mjs.map
@@ -62,6 +62,18 @@ export type EditorErrorCode =
62
62
  * `"host-owned"` with your own indicator. Silence-then-pop-in is a
63
63
  * composition mistake, not a preference. */
64
64
  | 'E_PREVIEW_NO_LOADER'
65
+ /** `updateElement(target, patch)`: a patch key doesn't apply to the target
66
+ * element's kind (e.g. `fontSize` on a shape), or a value is out of range
67
+ * (opacity outside 0..1, fontSize/width/height <= 0, non-finite number,
68
+ * wrong primitive type). The message names the element kind and lists the
69
+ * valid keys for it. Dev AND prod. */
70
+ | 'E_INVALID_PATCH'
71
+ /** `addElement(spec)`: there is no active artboard to add the element to.
72
+ * Create or select an artboard first (`<Editor.Root>` seeds one
73
+ * automatically; with a raw `EditorProvider` use `executeCreateArtboard`).
74
+ * Replaces the old `handleAddElement` window.alert — agents read throws,
75
+ * not dialogs. */
76
+ | 'E_NO_ACTIVE_ARTBOARD'
65
77
  /** `assertRenders`: the shop's asset-origin allowlist denied a design asset
66
78
  * (translated from the server's 403 — F4). */
67
79
  | 'E_ASSET_FORBIDDEN'
@@ -75,7 +87,13 @@ export type EditorErrorCode =
75
87
  | 'E_NO_MUTABLE_BINDING'
76
88
  /** `auditEditor()` called with no `<Editor.Root>` mounted — the imperative
77
89
  * audit reads the most-recent mounted Root's registry (#10). */
78
- | 'E_AUDIT_NO_ROOT';
90
+ | 'E_AUDIT_NO_ROOT'
91
+ /** `computeDistribute()` called with fewer than 3 movable elements —
92
+ * distributing needs something between the two ends. Gate the action on
93
+ * `useAlignDistribute().canDistribute` instead of calling below the
94
+ * threshold (the toolbar hides its distribute buttons for exactly this
95
+ * reason). */
96
+ | 'E_DISTRIBUTE_MIN_THREE';
79
97
  export interface EditorErrorData {
80
98
  code: EditorErrorCode;
81
99
  /** Slug under https://developers.snowcone.app/ for the violated contract. */
@@ -1,4 +1,4 @@
1
- import { G as n, I as t, P as a, S as s, T as i } from "./FontAnalyzer-CwIpgTGd.js";
1
+ import { G as n, I as t, P as a, S as s, T as i } from "./FontAnalyzer-PE6nysqE.js";
2
2
  function r(e) {
3
3
  return e instanceof i;
4
4
  }
@@ -21,4 +21,4 @@ export {
21
21
  r as d,
22
22
  u as i
23
23
  };
24
- //# sourceMappingURL=guards-8rkFAMBs.js.map
24
+ //# sourceMappingURL=guards-BlbhM5MX.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"guards-8rkFAMBs.js","sources":["../src/types/guards.ts"],"sourcesContent":["/**\n * Type guards for EditorElement discrimination\n *\n * These guards use instanceof checks against the concrete element classes,\n * matching the pattern established in EditorContext.tsx. They accept\n * BaseElement | EditorElement | undefined | null for maximum flexibility\n * at call sites.\n */\n\nimport { BaseElement } from '../core/BaseElement.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\n\ntype Narrowable = BaseElement | TextElement | ImageElement | GroupElement | ShapeElement | PathElement | undefined | null;\n\n/**\n * Type guard: checks if an element is a TextElement (or any text-based transform subclass).\n *\n * TextElement is the base class for all text transforms (custom, circle, arch, wave, etc.),\n * so this returns true for any text-based element regardless of its specific transformType.\n */\nexport function isTextElement(element: Narrowable): element is TextElement {\n return element instanceof TextElement;\n}\n\n/**\n * Type guard: checks if an element is an ImageElement.\n */\nexport function isImageElement(element: Narrowable): element is ImageElement {\n return element instanceof ImageElement;\n}\n\n/**\n * Type guard: checks if an element is a GroupElement.\n */\nexport function isGroupElement(element: Narrowable): element is GroupElement {\n return element instanceof GroupElement;\n}\n\n/**\n * Type guard: checks if an element is a ShapeElement.\n */\nexport function isShapeElement(element: Narrowable): element is ShapeElement {\n return element instanceof ShapeElement;\n}\n\n/**\n * Type guard: checks if an element is a PathElement.\n */\nexport function isPathElement(element: Narrowable): element is PathElement {\n return element instanceof PathElement;\n}\n"],"names":["isTextElement","element","TextElement","isImageElement","ImageElement","isGroupElement","GroupElement","isShapeElement","ShapeElement","isPathElement","PathElement"],"mappings":";AAwBO,SAASA,EAAcC,GAA6C;AACzE,SAAOA,aAAmBC;AAC5B;AAKO,SAASC,EAAeF,GAA8C;AAC3E,SAAOA,aAAmBG;AAC5B;AAKO,SAASC,EAAeJ,GAA8C;AAC3E,SAAOA,aAAmBK;AAC5B;AAKO,SAASC,EAAeN,GAA8C;AAC3E,SAAOA,aAAmBO;AAC5B;AAKO,SAASC,EAAcR,GAA6C;AACzE,SAAOA,aAAmBS;AAC5B;"}
1
+ {"version":3,"file":"guards-BlbhM5MX.js","sources":["../src/types/guards.ts"],"sourcesContent":["/**\n * Type guards for EditorElement discrimination\n *\n * These guards use instanceof checks against the concrete element classes,\n * matching the pattern established in EditorContext.tsx. They accept\n * BaseElement | EditorElement | undefined | null for maximum flexibility\n * at call sites.\n */\n\nimport { BaseElement } from '../core/BaseElement.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\n\ntype Narrowable = BaseElement | TextElement | ImageElement | GroupElement | ShapeElement | PathElement | undefined | null;\n\n/**\n * Type guard: checks if an element is a TextElement (or any text-based transform subclass).\n *\n * TextElement is the base class for all text transforms (custom, circle, arch, wave, etc.),\n * so this returns true for any text-based element regardless of its specific transformType.\n */\nexport function isTextElement(element: Narrowable): element is TextElement {\n return element instanceof TextElement;\n}\n\n/**\n * Type guard: checks if an element is an ImageElement.\n */\nexport function isImageElement(element: Narrowable): element is ImageElement {\n return element instanceof ImageElement;\n}\n\n/**\n * Type guard: checks if an element is a GroupElement.\n */\nexport function isGroupElement(element: Narrowable): element is GroupElement {\n return element instanceof GroupElement;\n}\n\n/**\n * Type guard: checks if an element is a ShapeElement.\n */\nexport function isShapeElement(element: Narrowable): element is ShapeElement {\n return element instanceof ShapeElement;\n}\n\n/**\n * Type guard: checks if an element is a PathElement.\n */\nexport function isPathElement(element: Narrowable): element is PathElement {\n return element instanceof PathElement;\n}\n"],"names":["isTextElement","element","TextElement","isImageElement","ImageElement","isGroupElement","GroupElement","isShapeElement","ShapeElement","isPathElement","PathElement"],"mappings":";AAwBO,SAASA,EAAcC,GAA6C;AACzE,SAAOA,aAAmBC;AAC5B;AAKO,SAASC,EAAeF,GAA8C;AAC3E,SAAOA,aAAmBG;AAC5B;AAKO,SAASC,EAAeJ,GAA8C;AAC3E,SAAOA,aAAmBK;AAC5B;AAKO,SAASC,EAAeN,GAA8C;AAC3E,SAAOA,aAAmBO;AAC5B;AAKO,SAASC,EAAcR,GAA6C;AACzE,SAAOA,aAAmBS;AAC5B;"}
@@ -0,0 +1,96 @@
1
+ import { LayerToken } from '../design/design-builder.js';
2
+ import { EditorElementKind } from '../core/elementGuards.js';
3
+ import { ElementStore } from '../core/ElementStore.js';
4
+ import { EditorElement } from '../contexts/EditorContext.js';
5
+ import { TextAlign } from '../types/index.js';
6
+ /**
7
+ * A serializable element patch for {@link UseCommandsReturn.updateElement}.
8
+ *
9
+ * Keys are validated against the TARGET element's kind at call time — a key
10
+ * that doesn't apply (e.g. `fontSize` on a shape) throws `E_INVALID_PATCH`
11
+ * naming the kind and listing its valid keys, dev AND prod.
12
+ *
13
+ * | keys | applies to |
14
+ * |---|---|
15
+ * | `x`, `y`, `rotation`, `opacity`, `name`, `visible`, `locked` | every kind |
16
+ * | `text`, `fontSize`, `color`, `fontFamily`, `bold`, `italic`, `textAlign` | text |
17
+ * | `fillColor`, `width`, `height` | shape |
18
+ * | `imageUrl`, `width`, `height` | image |
19
+ */
20
+ export interface ElementPatch {
21
+ /** Center x in artboard coordinates. Any kind. */
22
+ x?: number;
23
+ /** Center y in artboard coordinates. Any kind. */
24
+ y?: number;
25
+ /** Rotation in degrees. Any kind. */
26
+ rotation?: number;
27
+ /** 0..1. Any kind. Out of range throws `E_INVALID_PATCH`. */
28
+ opacity?: number;
29
+ /** Layer name (renaming a bound layer throws `E_BOUND_LAYER_MUTATION`). Any kind. */
30
+ name?: string;
31
+ /** Layer visibility. Any kind. */
32
+ visible?: boolean;
33
+ /** Layer lock. Any kind. */
34
+ locked?: boolean;
35
+ /** Text content. Text elements only. */
36
+ text?: string;
37
+ /** Font size (> 0; clamped to the editor's min/max). Text elements only. */
38
+ fontSize?: number;
39
+ /** Ink color (CSS color). Text elements only. */
40
+ color?: string;
41
+ /** Font family name. Text elements only. */
42
+ fontFamily?: string;
43
+ /** Bold. Text elements only. */
44
+ bold?: boolean;
45
+ /** Italic. Text elements only. */
46
+ italic?: boolean;
47
+ /** Text alignment. Text elements only. */
48
+ textAlign?: TextAlign;
49
+ /** Fill color (CSS color). Shape elements only. */
50
+ fillColor?: string;
51
+ /** Frame width (> 0). Shape and image elements only. */
52
+ width?: number;
53
+ /** Frame height (> 0). Shape and image elements only. */
54
+ height?: number;
55
+ /** Image source URL — swaps the image and reloads it. Image elements only.
56
+ * Combined with explicit `width`/`height` the frame is preserved;
57
+ * otherwise the frame height re-fits the loaded aspect ratio. */
58
+ imageUrl?: string;
59
+ }
60
+ /** Options for {@link UseCommandsReturn.updateElement}. */
61
+ export interface UpdateElementOptions {
62
+ /**
63
+ * Coalesce with the immediately-preceding coalesced `updateElement` to the
64
+ * SAME element: consecutive calls land as ONE history entry, so one
65
+ * `undo()` returns to the state before the run (finding #16 — host
66
+ * inspectors that commit per keystroke stop flooding history). Any other
67
+ * command in between, or an undo/redo, breaks the run.
68
+ */
69
+ coalesce?: boolean;
70
+ }
71
+ /** Patchable keys per element kind — the validation source of truth. */
72
+ export declare const PATCH_KEYS_BY_KIND: Record<EditorElementKind, readonly string[]>;
73
+ /**
74
+ * Resolve an `updateElement` target — an element id, a layer name, or a
75
+ * `design.layer()` token — to an element in the store. Unknown targets throw
76
+ * the existing `E_LAYER_NOT_FOUND` listing the valid ids and layer names.
77
+ * When several elements share a layer name, the first (render order) wins.
78
+ */
79
+ export declare function resolveElementTarget(elementStore: ElementStore, target: string | LayerToken): EditorElement;
80
+ /**
81
+ * Validate a patch against the element's kind. Throws `E_INVALID_PATCH`
82
+ * (dev AND prod) for: a key that doesn't apply to the kind (message names the
83
+ * kind and lists its valid keys), opacity outside 0..1, fontSize/width/height
84
+ * <= 0, non-finite numbers, or wrong primitive types.
85
+ */
86
+ export declare function validateElementPatch(el: EditorElement, patch: ElementPatch): EditorElementKind;
87
+ /**
88
+ * Apply a VALIDATED patch onto a clone (mutates `clone` in place). Uses the
89
+ * element's own normalizing setters where they exist (`setText`,
90
+ * `setFontSize` clamps, `setColor` keeps rich-text spans in sync).
91
+ * Image-source swaps need a post-command `loadImage` — signalled by the
92
+ * return value so `updateElement` can run the load path.
93
+ */
94
+ export declare function applyElementPatch(clone: EditorElement, patch: ElementPatch): {
95
+ needsImageLoad: boolean;
96
+ };
@@ -40,3 +40,5 @@ export { useCanvasEvents } from './useCanvasEvents';
40
40
  export { useCommandHistory } from './useCommandHistory';
41
41
  export { useKeyboardShortcuts } from './useKeyboardShortcuts';
42
42
  export { useElementProperties } from './useElementProperties';
43
+ export { useAlignDistribute } from './useAlignDistribute';
44
+ export type { UseAlignDistributeResult, AlignMode, DistributeMode } from './useAlignDistribute';
@@ -0,0 +1,19 @@
1
+ import { AlignMode, DistributeMode } from '../utils/alignDistribute.js';
2
+ export type { AlignMode, DistributeMode } from '../utils/alignDistribute.js';
3
+ export interface UseAlignDistributeResult {
4
+ /** Number of movable (selected, unlocked) elements. */
5
+ movableCount: number;
6
+ /** True when align commands have something to act on. */
7
+ canAlign: boolean;
8
+ /** True when distribute commands are available (≥3 movable elements).
9
+ * UI must gate rendering of distribute affordances on this. */
10
+ canDistribute: boolean;
11
+ /** Align the movable selection. ≥2 elements: relative to the selection's
12
+ * combined bbox; exactly 1: relative to the active artboard. */
13
+ align: (mode: AlignMode) => void;
14
+ /** Distribute the movable selection with equal gaps between bbox edges.
15
+ * Throws `E_DISTRIBUTE_MIN_THREE` when `canDistribute` is false. */
16
+ distribute: (mode: DistributeMode) => void;
17
+ }
18
+ export declare function useAlignDistribute(): UseAlignDistributeResult;
19
+ export default useAlignDistribute;
@@ -1,4 +1,6 @@
1
1
  import { useEditor } from '../contexts/EditorContext.js';
2
+ import { ElementPatch, UpdateElementOptions } from './elementPatch.js';
3
+ import { LayerToken } from '../design/design-builder.js';
2
4
  export interface UseCommandsReturn {
3
5
  undo: () => void;
4
6
  redo: () => void;
@@ -6,6 +8,24 @@ export interface UseCommandsReturn {
6
8
  canRedo: boolean;
7
9
  clearHistory: () => void;
8
10
  clearArtboardHistory: (artboardId: string) => void;
11
+ /**
12
+ * Declarative, validated, undoable element mutation — the agent write path.
13
+ * `target` is an element id, a layer name, or a `design.layer()` token
14
+ * (unknown targets throw `E_LAYER_NOT_FOUND` listing the valid names).
15
+ * The patch is validated against the element's kind and applied as ONE
16
+ * history entry; invalid keys/values throw `E_INVALID_PATCH` naming the
17
+ * kind and its valid keys — dev AND prod.
18
+ *
19
+ * `{ coalesce: true }`: consecutive coalesced calls to the SAME element
20
+ * fold into one history entry (one `undo()` returns to the pre-run state).
21
+ */
22
+ updateElement: (target: string | LayerToken, patch: ElementPatch, opts?: UpdateElementOptions) => void;
23
+ /**
24
+ * Create an element and get `{ id, name }` back; the element arrives
25
+ * selected. Throws `E_NO_ACTIVE_ARTBOARD` when there is no artboard.
26
+ * (Also available on `useEditor()`.)
27
+ */
28
+ addElement: ReturnType<typeof useEditor>['addElement'];
9
29
  executeElementUpdate: ReturnType<typeof useEditor>['executeElementUpdate'];
10
30
  executeAddElement: ReturnType<typeof useEditor>['executeAddElement'];
11
31
  executeRemoveElement: ReturnType<typeof useEditor>['executeRemoveElement'];
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
- import { e as u } from "./UnifiedFontService-Bx2MxFfy.js";
2
- import { C as l, c as x, a as I, b as v, u as A, d as T, f as g, g as B } from "./UnifiedFontService-Bx2MxFfy.js";
3
- import { E as _, S as y, S as L, u as O, a as M } from "./SnowconeCanvas-4ef4a61J.js";
4
- import { u as d, a as m } from "./editorErrors-B2mb1sVN.js";
5
- import { T as z, r as w, u as F, a as D, b as K, c as N, d as V, e as b, f as h, w as U } from "./template-fields-DJ157D3E.js";
6
- import { A as Z, C as G, E as H, M as j, P as k, r as q, v as J } from "./validation-DnAkXdUg.js";
7
- import { C as W, D as X, d as $, s as ee } from "./design-builder-U7Su9MMC.js";
1
+ import { e as u } from "./UnifiedFontService-BJzoqfBF.js";
2
+ import { C as l, c as x, a as I, b as v, u as A, d as T, f as g, g as B } from "./UnifiedFontService-BJzoqfBF.js";
3
+ import { E as _, S as y, S as L, u as O, a as M } from "./SnowconeCanvas-CQiFVR0X.js";
4
+ import { u as d, a as m } from "./editorErrors-8uDRMhow.js";
5
+ import { T as z, r as w, u as F, a as D, b as K, c as N, d as V, e as b, f as h, w as U } from "./template-fields-ai7vnW8g.js";
6
+ import { A as Z, C as G, E as H, M as j, P as k, r as q, v as J } from "./validation-B8kIYutI.js";
7
+ import { C as W, D as X, d as $, s as ee } from "./design-builder-bquxIHct.js";
8
8
  import { c as se, e as te } from "./compose-DtQL_oit.js";
9
- import { d as oe, m as ie, s as ne, v as ue } from "./CanvasStateV1-DmZ1Qvvn.js";
9
+ import { d as oe, m as ie, s as ne, v as ue } from "./CanvasStateV1-kL09wsAn.js";
10
10
  function C() {
11
11
  const { isCanvasReady: e } = d();
12
12
  return e;
@@ -1,8 +1,8 @@
1
- import { A as w, a as b, C as I, I as R, R as W, S as A, T as E } from "./CanvasRenderer-6_HhR4-q.js";
2
- import { r as p } from "./ExportManager-DoU4VK6h.js";
3
- import { D as O, E as F, I as L, W as X, a as z, b as K, c as Y, d as B, e as _, f as J, h as N, i as V, j as U, k as j, l as q, m as Q, n as Z, o as $, p as m, q as G, s as x, t as D, u as rr, v as or, w as sr, x as ir, y as er, z as ar, A as nr, B as tr, C as hr } from "./ExportManager-DoU4VK6h.js";
4
- import { D as cr, E as fr, H as pr, J as gr, K as lr, L as ur, M as Tr, N as kr, O as Mr, Q as yr, W as Cr, U as vr, V as Sr, X as Pr, c as wr, Y as br, Z as Ir, _ as Rr, $ as Wr, a0 as Ar } from "./FontAnalyzer-CwIpgTGd.js";
5
- import { A as Hr, e as Or, C as Fr, S as Lr } from "./CanvasStateV1-DmZ1Qvvn.js";
1
+ import { A as w, a as b, C as I, I as R, R as W, S as A, T as E } from "./CanvasRenderer-B_3GISlW.js";
2
+ import { r as p } from "./ExportManager-J2xK86uq.js";
3
+ import { D as O, E as F, I as L, W as X, a as z, b as K, c as Y, d as B, e as _, f as J, h as N, i as V, j, k as q, l as Q, m as U, n as Z, o as $, p as m, q as G, s as x, t as D, u as rr, v as or, w as sr, x as ir, y as er, z as ar, A as nr, B as tr, C as hr } from "./ExportManager-J2xK86uq.js";
4
+ import { D as cr, E as fr, H as pr, J as gr, K as lr, L as ur, M as Tr, N as kr, O as Mr, Q as yr, W as Cr, V as vr, X as Sr, Y as Pr, c as wr, Z as br, _ as Ir, $ as Rr, a0 as Wr, a1 as Ar } from "./FontAnalyzer-PE6nysqE.js";
5
+ import { A as Hr, e as Or, C as Fr, S as Lr } from "./CanvasStateV1-kL09wsAn.js";
6
6
  function y(r, o) {
7
7
  return !r.needsCropPixelConversion || r.cropWidth === void 0 ? r : {
8
8
  ...r,
@@ -198,10 +198,10 @@ export {
198
198
  N as createCirclePath,
199
199
  V as createImagePath,
200
200
  wr as createLogger,
201
- U as createRectPath,
202
- j as createTextPath,
203
- q as getFontMetrics,
204
- Q as getSharedWorkerExportManager,
201
+ j as createRectPath,
202
+ q as createTextPath,
203
+ Q as getFontMetrics,
204
+ U as getSharedWorkerExportManager,
205
205
  br as hitTestCircle,
206
206
  Ir as hitTestRect,
207
207
  Z as invalidateMaskCache,