@pygmalionjs/pygmalion 0.8.1 → 0.9.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 (129) hide show
  1. package/binding-source.d.ts +25 -0
  2. package/dist-lib/{CameraLayer-B2D29VXQ.js → CameraLayer-BdOko8XE.js} +17 -17
  3. package/dist-lib/pygmalion.js +24599 -12746
  4. package/dist-lib/{runtime-BAy8W1mJ.js → runtime-Dxb-IaLN.js} +12414 -11114
  5. package/dist-lib/style.css +1 -1
  6. package/dist-lib/testing.js +10 -10
  7. package/dist-lib/types/binding/catalog.d.ts +10 -0
  8. package/dist-lib/types/binding/contracts.d.ts +202 -0
  9. package/dist-lib/types/binding/documentCommands.d.ts +38 -0
  10. package/dist-lib/types/binding/index.d.ts +6 -0
  11. package/dist-lib/types/binding/preview.d.ts +37 -0
  12. package/dist-lib/types/binding/source.d.ts +109 -0
  13. package/dist-lib/types/binding/useDataProjection.d.ts +9 -0
  14. package/dist-lib/types/binding/validate.d.ts +8 -0
  15. package/dist-lib/types/core/runtime.d.ts +6 -0
  16. package/dist-lib/types/document/appearance.d.ts +112 -0
  17. package/dist-lib/types/document/assets.d.ts +204 -0
  18. package/dist-lib/types/document/components.d.ts +169 -0
  19. package/dist-lib/types/document/contracts.d.ts +171 -0
  20. package/dist-lib/types/document/engine.d.ts +20 -0
  21. package/dist-lib/types/document/exportSettings.d.ts +45 -0
  22. package/dist-lib/types/document/geometry.d.ts +11 -0
  23. package/dist-lib/types/document/layout.d.ts +53 -0
  24. package/dist-lib/types/document/prototype.d.ts +424 -0
  25. package/dist-lib/types/document/prototypeReferences.d.ts +3 -0
  26. package/dist-lib/types/document/richText.d.ts +185 -0
  27. package/dist-lib/types/document/styles.d.ts +216 -0
  28. package/dist-lib/types/document/svgImport.d.ts +17 -0
  29. package/dist-lib/types/document/vector.d.ts +222 -0
  30. package/dist-lib/types/editor/designCompiler.d.ts +7 -0
  31. package/dist-lib/types/editor/designImport.d.ts +7 -0
  32. package/dist-lib/types/editor/inspect.d.ts +8 -0
  33. package/dist-lib/types/editor/revisionCatalogInstall.d.ts +1 -1
  34. package/dist-lib/types/editor/screenInteractions.d.ts +3 -3
  35. package/dist-lib/types/editor/store.d.ts +1 -1
  36. package/dist-lib/types/host/project.d.ts +44 -0
  37. package/dist-lib/types/host/registryConnections.d.ts +11 -0
  38. package/dist-lib/types/host/sourceFilePlan.d.ts +15 -0
  39. package/dist-lib/types/host/sourceWriteback.d.ts +50 -0
  40. package/dist-lib/types/lib.d.ts +101 -3
  41. package/dist-lib/types/token-library/bindings.d.ts +62 -0
  42. package/dist-lib/types/token-library/contracts.d.ts +117 -0
  43. package/dist-lib/types/token-library/engine.d.ts +13 -0
  44. package/dist-lib/types/workspace/WorkspaceShell.d.ts +9 -0
  45. package/dist-lib/types/workspace/application/ApplicationWorkspace.d.ts +3 -0
  46. package/dist-lib/types/workspace/configuration.d.ts +39 -0
  47. package/dist-lib/types/workspace/contracts.d.ts +64 -0
  48. package/dist-lib/types/workspace/controller.d.ts +35 -0
  49. package/dist-lib/types/workspace/documentSessions.d.ts +42 -0
  50. package/dist-lib/types/workspace/edit/AppearancePanel.d.ts +23 -0
  51. package/dist-lib/types/workspace/edit/ArrangeLayers.d.ts +10 -0
  52. package/dist-lib/types/workspace/edit/AssetsPanel.d.ts +16 -0
  53. package/dist-lib/types/workspace/edit/CanvasGuides.d.ts +19 -0
  54. package/dist-lib/types/workspace/edit/ComponentPanel.d.ts +11 -0
  55. package/dist-lib/types/workspace/edit/DataPanel.d.ts +16 -0
  56. package/dist-lib/types/workspace/edit/DesignWorkspace.d.ts +10 -0
  57. package/dist-lib/types/workspace/edit/LayerTree.d.ts +14 -0
  58. package/dist-lib/types/workspace/edit/LayoutPanel.d.ts +13 -0
  59. package/dist-lib/types/workspace/edit/PrototypePanel.d.ts +12 -0
  60. package/dist-lib/types/workspace/edit/ScopeTransferDialog.d.ts +40 -0
  61. package/dist-lib/types/workspace/edit/SelectionHandles.d.ts +52 -0
  62. package/dist-lib/types/workspace/edit/StylesPanel.d.ts +8 -0
  63. package/dist-lib/types/workspace/edit/TextEditor.d.ts +50 -0
  64. package/dist-lib/types/workspace/edit/VectorEditor.d.ts +89 -0
  65. package/dist-lib/types/workspace/edit/VectorToolbar.d.ts +5 -0
  66. package/dist-lib/types/workspace/edit/canvasGuideModel.d.ts +102 -0
  67. package/dist-lib/types/workspace/edit/clipboard.d.ts +35 -0
  68. package/dist-lib/types/workspace/edit/duplicateMetadata.d.ts +2 -0
  69. package/dist-lib/types/workspace/edit/geometryHandles.d.ts +224 -0
  70. package/dist-lib/types/workspace/edit/layerArrangement.d.ts +39 -0
  71. package/dist-lib/types/workspace/edit/layerMoves.d.ts +44 -0
  72. package/dist-lib/types/workspace/edit/projection.d.ts +12 -0
  73. package/dist-lib/types/workspace/edit/scopeTransfer.d.ts +96 -0
  74. package/dist-lib/types/workspace/edit/textStyles.d.ts +75 -0
  75. package/dist-lib/types/workspace/edit/vectorTools.d.ts +136 -0
  76. package/dist-lib/types/workspace/legacyModeAdapter.d.ts +13 -0
  77. package/dist-lib/types/workspace/source/ResponsiveLayoutPanel.d.ts +20 -0
  78. package/dist-lib/types/workspace/source/SourceReview.d.ts +11 -0
  79. package/dist-lib/types/workspace/source/appearanceSource.d.ts +19 -0
  80. package/dist-lib/types/workspace/source/compiler.d.ts +5 -0
  81. package/dist-lib/types/workspace/source/contracts.d.ts +156 -0
  82. package/dist-lib/types/workspace/source/controller.d.ts +50 -0
  83. package/dist-lib/types/workspace/source/documentRevision.d.ts +2 -0
  84. package/dist-lib/types/workspace/source/draftPreviewClient.d.ts +13 -0
  85. package/dist-lib/types/workspace/source/draftRecipe.d.ts +30 -0
  86. package/dist-lib/types/workspace/source/flowContracts.d.ts +72 -0
  87. package/dist-lib/types/workspace/source/prototypeSource.d.ts +4 -0
  88. package/dist-lib/types/workspace/source/responsive.d.ts +13 -0
  89. package/dist-lib/types/workspace/source/responsiveContracts.d.ts +78 -0
  90. package/dist-lib/types/workspace/source/structure.d.ts +21 -0
  91. package/dist-lib/types/workspace/source/textSource.d.ts +28 -0
  92. package/dist-lib/types/workspace/tokens/TokenLibraryContext.d.ts +11 -0
  93. package/dist-lib/types/workspace/tokens/TokenLibraryWorkspace.d.ts +14 -0
  94. package/dist-lib/types/workspace/tokens/contracts.d.ts +74 -0
  95. package/dist-lib/types/workspace/tokens/controller.d.ts +44 -0
  96. package/dist-lib/types/workspace/view/PrototypePlayer.d.ts +17 -0
  97. package/dist-lib/types/workspace/view/StoryboardWorkspace.d.ts +3 -0
  98. package/dist-lib/types/workspace/view/prototypePlayback.d.ts +46 -0
  99. package/draft-preview.d.ts +54 -0
  100. package/inspect.d.ts +2 -0
  101. package/node/design-session.mjs +3 -0
  102. package/node/inspect-plugin.mjs +3 -1
  103. package/node/inspect-writeback.mjs +18 -2
  104. package/node/preview-artifact-store.mjs +10 -7
  105. package/node/source-archive.mjs +88 -0
  106. package/node/source-bindings.mjs +65 -0
  107. package/node/source-file-lock.mjs +15 -0
  108. package/node/source-file-plan.mjs +162 -0
  109. package/node/source-file-proofs.mjs +44 -0
  110. package/node/source-responsive-codec.d.mts +17 -0
  111. package/node/source-responsive-codec.mjs +133 -0
  112. package/node/source-responsive.mjs +189 -0
  113. package/node/source-structure-reconcile.mjs +186 -0
  114. package/node/source-structure-wrapper-codec.d.mts +20 -0
  115. package/node/source-structure-wrapper-codec.mjs +205 -0
  116. package/node/source-structure.mjs +404 -0
  117. package/node/token-source-references.mjs +136 -0
  118. package/node/workspace-draft-preview-plugin.mjs +70 -0
  119. package/node/workspace-draft-preview.mjs +231 -0
  120. package/node/workspace-source-identity.d.mts +1 -0
  121. package/node/workspace-source-identity.mjs +21 -0
  122. package/node/workspace-source-plan.mjs +195 -0
  123. package/package.json +52 -6
  124. package/source-files.d.ts +35 -0
  125. package/source-responsive.d.ts +10 -0
  126. package/source-structure.d.ts +155 -0
  127. package/workspace-source-plan.d.ts +44 -0
  128. package/docs/coverage-contracts.md +0 -444
  129. package/docs/screen-state-contract.md +0 -252
@@ -0,0 +1,224 @@
1
+ import type { DataProjection } from '../../binding/preview.js';
2
+ import type { DesignDocument, DocumentDiagnostic, DocumentMatrix, DocumentNodeConstraints, DocumentTransaction } from '../../document/contracts.js';
3
+ import { documentTransformedBounds } from '../../document/geometry.js';
4
+ /**
5
+ * Pure geometry for the selection handles: which layers may be edited on the
6
+ * canvas, how a handle drag becomes a new size and placement, how a rotation
7
+ * gesture becomes a transform, where a moving box snaps, and how several free
8
+ * layers resize and rotate together as one selection box.
9
+ *
10
+ * Every function here is deterministic and free of DOM access so the overlay
11
+ * stays a thin event adapter and the math can be exercised in plain node tests.
12
+ * Angles follow the design tool convention: degrees are positive
13
+ * counter-clockwise on screen. The document y-axis points down, so a CSS
14
+ * `rotate(θ)` equals `-θ` in that convention.
15
+ */
16
+ export type GeometryHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
17
+ export declare const GEOMETRY_HANDLES: readonly GeometryHandle[];
18
+ export type DocumentBounds = ReturnType<typeof documentTransformedBounds>;
19
+ export interface LayerPlacement {
20
+ readonly transform: DocumentMatrix;
21
+ readonly width: number;
22
+ readonly height: number;
23
+ }
24
+ export interface LayerGeometry extends LayerPlacement {
25
+ readonly nodeId: string;
26
+ readonly name: string;
27
+ /** The projected parent; the document root for top-level layers. */
28
+ readonly parentId: string;
29
+ readonly parentWorld: DocumentMatrix;
30
+ readonly world: DocumentMatrix;
31
+ /** World-axis bounding box of the rendered layer. */
32
+ readonly bounds: DocumentBounds;
33
+ /** False when the size is derived (hug contents, repeated examples); rotation and movement stay available. */
34
+ readonly resizable: boolean;
35
+ readonly resizeAxes?: {
36
+ readonly width: boolean;
37
+ readonly height: boolean;
38
+ };
39
+ readonly notes: readonly DocumentDiagnostic[];
40
+ readonly constraints: DocumentNodeConstraints | undefined;
41
+ /** Constraints only act inside a free-placement container that can itself be resized. */
42
+ readonly constraintsEditable: boolean;
43
+ }
44
+ export interface SelectionHandlesPlan {
45
+ readonly layers: readonly LayerGeometry[];
46
+ readonly blocked: readonly DocumentDiagnostic[];
47
+ }
48
+ export interface ResizeGesture {
49
+ readonly handle: GeometryHandle;
50
+ /** Pointer travel in canvas units (screen pixels divided by zoom). */
51
+ readonly delta: {
52
+ readonly x: number;
53
+ readonly y: number;
54
+ };
55
+ readonly aspectLock?: boolean;
56
+ readonly fromCenter?: boolean;
57
+ }
58
+ /** A placement of a layer inside a shared box (parent-relative, like the layer's own transform). */
59
+ export interface SelectionPlacement extends LayerPlacement {
60
+ readonly nodeId: string;
61
+ }
62
+ export type SnapReference = 'start' | 'center' | 'end';
63
+ export interface SnapEdges {
64
+ readonly x?: readonly SnapReference[];
65
+ readonly y?: readonly SnapReference[];
66
+ }
67
+ /** One distance between two boxes along an axis, drawn at the cross-axis coordinate `at`. */
68
+ export interface SnapGap {
69
+ readonly from: number;
70
+ readonly to: number;
71
+ readonly at: number;
72
+ }
73
+ export interface SnapGuide {
74
+ readonly axis: 'x' | 'y';
75
+ readonly position: number;
76
+ /** `edge` and `center` come from boxes, `guide` from persisted guides, `grid` from layout grid tracks, `spacing` from equal sibling distances. */
77
+ readonly kind: 'edge' | 'center' | 'guide' | 'grid' | 'spacing';
78
+ readonly nodeId: string;
79
+ /** Extent along the other axis, for drawing the guide line; equal values mean "the moving box only". */
80
+ readonly start: number;
81
+ readonly end: number;
82
+ /** When set, only this reference of the moving box may land on the line. */
83
+ readonly reference?: SnapReference;
84
+ /** The equal distances a `spacing` candidate reproduces, for drawing. */
85
+ readonly gaps?: readonly SnapGap[];
86
+ }
87
+ export interface SnapResult {
88
+ readonly dx: number;
89
+ readonly dy: number;
90
+ readonly guides: readonly SnapGuide[];
91
+ }
92
+ export interface SnapCandidateOptions {
93
+ /** Include the moving layers' own children: right for a resize, wrong for a move (the children travel along). */
94
+ readonly children?: boolean;
95
+ }
96
+ export interface MoveSnapSession {
97
+ readonly nodeIds: readonly string[];
98
+ readonly bounds: DocumentBounds;
99
+ readonly candidates: readonly SnapGuide[];
100
+ readonly tolerance: number;
101
+ }
102
+ /**
103
+ * Decide, per selected layer, whether canvas handles may edit it. The policy matches
104
+ * layer arrangement: repeated rows, auto layout children, and source-layout fragments are
105
+ * refused with a diagnostic instead of guessed bounds. Derived sizes keep move and
106
+ * rotation but drop the resize handles.
107
+ */
108
+ export declare function planSelectionHandles(document: DesignDocument, nodeIds: readonly string[], projection: DataProjection | null): SelectionHandlesPlan;
109
+ export declare function layerResizeHandles(layer: Pick<LayerGeometry, 'resizable' | 'resizeAxes'>): readonly GeometryHandle[];
110
+ /**
111
+ * Turn a handle drag into a new local size and placement. The handle's opposite edge or
112
+ * corner stays fixed in the layer's own frame, so the anchor is exact for rotated and
113
+ * skewed layers. Aspect lock keeps the starting ratio; center mode grows both sides.
114
+ */
115
+ export declare function resizeLayer(layer: LayerPlacement & {
116
+ readonly parentWorld: DocumentMatrix;
117
+ }, gesture: ResizeGesture): LayerPlacement;
118
+ /** Rotation of a local transform relative to its parent, in degrees, positive counter-clockwise on screen. */
119
+ export declare function layerRotationDegrees(transform: DocumentMatrix): number;
120
+ /** Rotate a layer about any world point; the result is the new parent-relative transform. */
121
+ export declare function rotateLayerAbout(layer: LayerPlacement & {
122
+ readonly parentWorld: DocumentMatrix;
123
+ }, degrees: number, center: {
124
+ readonly x: number;
125
+ readonly y: number;
126
+ }): DocumentMatrix;
127
+ /**
128
+ * Rotate a layer about its own center. `world` space rotates what the user sees regardless of
129
+ * parent transforms; `parent` space changes the inspector's parent-relative angle exactly.
130
+ */
131
+ export declare function rotateLayer(layer: LayerPlacement & {
132
+ readonly parentWorld: DocumentMatrix;
133
+ }, degrees: number, space?: 'world' | 'parent'): DocumentMatrix;
134
+ /** Set the parent-relative rotation shown in the inspector, keeping the center in place. */
135
+ export declare function setLayerRotation(layer: LayerPlacement & {
136
+ readonly parentWorld: DocumentMatrix;
137
+ }, degrees: number): DocumentMatrix;
138
+ /** Angle swept by a pointer around a center, in degrees positive counter-clockwise on screen. */
139
+ export declare function rotationGestureDegrees(center: {
140
+ x: number;
141
+ y: number;
142
+ }, from: {
143
+ x: number;
144
+ y: number;
145
+ }, to: {
146
+ x: number;
147
+ y: number;
148
+ }): number;
149
+ export declare function snapRotation(degrees: number, step?: number): number;
150
+ /** Snap distance in canvas units for a constant on-screen tolerance. */
151
+ export declare function snapTolerance(zoom: number, screenPixels?: number): number;
152
+ /** World-axis box around several layers; null without layers. */
153
+ export declare function selectionBounds(layers: readonly Pick<LayerGeometry, 'bounds'>[]): DocumentBounds | null;
154
+ /**
155
+ * Layers of a selection that transform independently: a layer whose ancestor is also selected
156
+ * travels with that ancestor, so applying the box transform to both would move it twice.
157
+ */
158
+ export declare function independentLayers(document: DesignDocument, layers: readonly LayerGeometry[]): LayerGeometry[];
159
+ /** Exact affine scaling of the shared world box, retaining finite local sizes.
160
+ * Nonuniform scaling of rotated layers changes their angle/skew rather than approximating bounds.
161
+ */
162
+ export declare function scaleLayersToBox(layers: readonly LayerGeometry[], from: DocumentBounds, to: DocumentBounds): SelectionPlacement[];
163
+ /** A resize handle dragged on the selection box: the box resizes like a layer, its members follow. */
164
+ export declare function resizeSelection(layers: readonly LayerGeometry[], box: DocumentBounds, gesture: ResizeGesture): {
165
+ readonly box: DocumentBounds;
166
+ readonly placements: readonly SelectionPlacement[];
167
+ };
168
+ /** Rotate every member about the center of the selection box in world space. */
169
+ export declare function rotateSelection(layers: readonly LayerGeometry[], box: DocumentBounds, degrees: number): SelectionPlacement[];
170
+ /**
171
+ * Edges and centers of visible siblings, the parent's own bounds, the parent's persisted guides
172
+ * and layout grid tracks, and (for a resize) the moving layers' own children, excluding the
173
+ * moving layers themselves.
174
+ */
175
+ export declare function snapCandidates(document: DesignDocument, nodeIds: readonly string[], projection: DataProjection | null, options?: SnapCandidateOptions): readonly SnapGuide[];
176
+ /**
177
+ * Positions where a moving box would sit at the same distance from a sibling as two siblings
178
+ * sit from each other: beyond either end of an aligned pair, or centered in the gap between
179
+ * them when it fits. Each candidate names the reference of the box that lands and the two
180
+ * equal gaps to draw.
181
+ */
182
+ export declare function spacingCandidates(box: DocumentBounds, siblings: readonly {
183
+ readonly nodeId: string;
184
+ readonly bounds: DocumentBounds;
185
+ }[]): SnapGuide[];
186
+ /** Move a box by the smallest offset that lands one of its references on a candidate line, per axis. */
187
+ export declare function snapBounds(bounds: DocumentBounds, candidates: readonly SnapGuide[], tolerance: number, edges?: SnapEdges): SnapResult;
188
+ /** References that move for a handle: one edge per axis, or both edges when growing from the center. */
189
+ export declare function resizeSnapEdges(handle: GeometryHandle, fromCenter?: boolean): SnapEdges;
190
+ /**
191
+ * Resize with snapping: the moving edges of the world box are pulled onto candidate lines by
192
+ * adjusting the pointer travel. The box is affine in that travel for a fixed handle, so a few
193
+ * passes converge; only references that land exactly are reported as guides.
194
+ */
195
+ export declare function snapResizeGesture(layer: LayerPlacement & {
196
+ readonly parentWorld: DocumentMatrix;
197
+ }, gesture: ResizeGesture, candidates: readonly SnapGuide[], tolerance: number): {
198
+ placement: LayerPlacement;
199
+ guides: readonly SnapGuide[];
200
+ };
201
+ /**
202
+ * Snapshot for a move gesture: the moving box, its candidates (siblings, parent, guides, grids,
203
+ * and equal sibling distances for this box size), and the zoom-aware tolerance.
204
+ */
205
+ export declare function beginMoveSnap(document: DesignDocument, nodeIds: readonly string[], projection: DataProjection | null, zoom: number): MoveSnapSession | null;
206
+ /** Adjust world pointer travel so the moving box snaps; exact in one pass because only translation changes. */
207
+ export declare function snapMove(session: MoveSnapSession, delta: {
208
+ x: number;
209
+ y: number;
210
+ }): {
211
+ delta: {
212
+ x: number;
213
+ y: number;
214
+ };
215
+ guides: readonly SnapGuide[];
216
+ };
217
+ /**
218
+ * Commit a gesture as canonical commands inside the caller's single transaction: a size change
219
+ * uses `resize` so child constraints apply, a placement change uses `move`, and a rotation
220
+ * replaces the transform. Nothing is issued when the placement is unchanged.
221
+ */
222
+ export declare function applyLayerPlacement(transaction: DocumentTransaction, nodeId: string, before: LayerPlacement, after: LayerPlacement): boolean;
223
+ /** Commit every member of a selection gesture in the caller's transaction; true when any layer changed. */
224
+ export declare function applySelectionPlacements(transaction: DocumentTransaction, layers: readonly LayerGeometry[], placements: readonly SelectionPlacement[]): boolean;
@@ -0,0 +1,39 @@
1
+ import type { DataProjection } from '../../binding/preview.js';
2
+ import type { DesignDocument, DocumentDiagnostic, DocumentEngineOptions, DocumentTransaction } from '../../document/contracts.js';
3
+ export type DocumentLayerArrangement = 'align-left' | 'align-center-x' | 'align-right' | 'align-top' | 'align-center-y' | 'align-bottom' | 'distribute-horizontal' | 'distribute-vertical';
4
+ export interface DocumentLayerArrangementRequest {
5
+ nodeIds: readonly string[];
6
+ action: DocumentLayerArrangement;
7
+ }
8
+ export interface DocumentLayerArrangementOptions extends Pick<DocumentEngineOptions, 'preflight'> {
9
+ projection?: DataProjection | null;
10
+ }
11
+ export interface ReadyDocumentLayerArrangement {
12
+ readonly status: 'ready';
13
+ readonly nodeIds: readonly string[];
14
+ readonly action: DocumentLayerArrangement;
15
+ readonly beforeFingerprint: string;
16
+ /** Only geometry is retained; fixture values never enter a command or document history. */
17
+ readonly geometryFingerprint: string;
18
+ readonly operations: readonly {
19
+ readonly nodeId: string;
20
+ readonly x: number;
21
+ readonly y: number;
22
+ }[];
23
+ }
24
+ export type DocumentLayerArrangementPlan = ReadyDocumentLayerArrangement | {
25
+ readonly status: 'unchanged';
26
+ readonly nodeIds: readonly string[];
27
+ readonly operations: readonly [];
28
+ } | {
29
+ readonly status: 'blocked';
30
+ readonly diagnostics: readonly DocumentDiagnostic[];
31
+ };
32
+ /**
33
+ * Align selected topmost subtrees by world-axis bounding boxes, or distribute equal edge gaps.
34
+ * Distribution keeps the first and last layer positions fixed; ties use canonical tree order.
35
+ * A disposable transaction validates the entire result, including current host policy and layout.
36
+ */
37
+ export declare function planDocumentLayerArrangement(document: DesignDocument, request: DocumentLayerArrangementRequest, options?: DocumentLayerArrangementOptions): DocumentLayerArrangementPlan;
38
+ /** Run as the only move batch in one live transaction; let validation errors roll it back. */
39
+ export declare function applyDocumentLayerArrangement(transaction: DocumentTransaction, plan: ReadyDocumentLayerArrangement, currentDocument: DesignDocument, projection?: DataProjection | null): void;
@@ -0,0 +1,44 @@
1
+ import type { DesignDocument, DocumentDiagnostic, DocumentEngineOptions, DocumentTransaction } from '../../document/contracts.js';
2
+ export interface DocumentLayerMoveRequest {
3
+ nodeIds: readonly string[];
4
+ targetId: string;
5
+ placement: 'before' | 'inside' | 'after';
6
+ }
7
+ export interface DocumentLayerMoveOperation {
8
+ readonly nodeId: string;
9
+ readonly parentId: string;
10
+ /** Index in the destination after removing this operation's node. */
11
+ readonly index: number;
12
+ }
13
+ export interface ReadyDocumentLayerMove {
14
+ readonly status: 'ready';
15
+ readonly nodeIds: readonly string[];
16
+ readonly operations: readonly DocumentLayerMoveOperation[];
17
+ /** Complete authoring identity, including geometry, bindings, and source revision. */
18
+ readonly beforeFingerprint: string;
19
+ readonly parentId: string;
20
+ /** Final insertion index after removing every selected subtree from the destination. */
21
+ readonly index: number;
22
+ }
23
+ export type DocumentLayerMovePlan = ReadyDocumentLayerMove | {
24
+ readonly status: 'unchanged';
25
+ readonly nodeIds: readonly string[];
26
+ readonly operations: readonly [];
27
+ } | {
28
+ readonly status: 'blocked';
29
+ readonly diagnostics: readonly DocumentDiagnostic[];
30
+ };
31
+ /** Canonical preorder; selecting an ancestor includes its subtree exactly once. */
32
+ export declare function normalizeDocumentLayerSelection(document: DesignDocument, nodeIds: readonly string[]): readonly string[];
33
+ /**
34
+ * Plan against the current canonical snapshot, preferably again at drop time.
35
+ * A disposable real transaction owns tree, layout, slot, and binding policy. Free placement keeps
36
+ * world transforms; a destination layout owns the resulting positions and dimensions.
37
+ * Preflight must be pure and should match the live engine's current host/binding preflight.
38
+ */
39
+ export declare function planDocumentLayerMove(document: DesignDocument, request: DocumentLayerMoveRequest, options?: Pick<DocumentEngineOptions, 'preflight'>): DocumentLayerMovePlan;
40
+ /**
41
+ * Apply as the first/only move batch in the live engine's transaction, using its current snapshot.
42
+ * Callers must let errors escape that transaction so the engine can roll back the complete move.
43
+ */
44
+ export declare function applyDocumentLayerMove(transaction: DocumentTransaction, plan: ReadyDocumentLayerMove, currentDocument: DesignDocument): void;
@@ -0,0 +1,12 @@
1
+ import type { BindingRuntimeScope } from '../../binding/contracts.js';
2
+ import type { DataProjection } from '../../binding/preview.js';
3
+ import type { DesignDocument, DocumentNode, DocumentResolveOptions } from '../../document/contracts.js';
4
+ export interface ProjectedDocumentNode {
5
+ sourceNodeId: string;
6
+ instanceId?: string;
7
+ rowPath: BindingRuntimeScope['rowPath'];
8
+ node: DocumentNode;
9
+ children: ProjectedDocumentNode[];
10
+ }
11
+ /** Fixture rows affect this disposable scene only, never canonical document geometry. */
12
+ export declare function projectDocumentScene(document: DesignDocument, data: DataProjection | null, resolveOptions?: DocumentResolveOptions): ProjectedDocumentNode;
@@ -0,0 +1,96 @@
1
+ import type { BindingScopeUse, DataValueType, HostDataCapabilities } from '../../binding/contracts.js';
2
+ import { type DataScopeDescription } from '../../binding/documentCommands.js';
3
+ import type { DesignDocument, DocumentDiagnostic, DocumentEngineOptions, DocumentTransaction } from '../../document/contracts.js';
4
+ import { type DocumentLayerMovePlan, type DocumentLayerMoveRequest, type ReadyDocumentLayerMove } from './layerMoves.js';
5
+ export interface ScopeTransferOptions extends Pick<DocumentEngineOptions, 'preflight'> {
6
+ /** Host declarations supply scope evidence and compatible fields. Without them a scope escape stays a plain rejection. */
7
+ capabilities?: HostDataCapabilities;
8
+ }
9
+ /** One declared field the designer may connect instead, resolved from the catalog and the destination position. */
10
+ export interface ScopeTransferChoice {
11
+ /** `dataFieldChoiceKey(fieldId, scope)`; a decision names exactly this key. */
12
+ readonly key: string;
13
+ readonly fieldId: string;
14
+ readonly fieldLabel: string;
15
+ readonly valueKind: DataValueType['kind'];
16
+ readonly scope: BindingScopeUse;
17
+ readonly scopeLabel: string;
18
+ readonly templateName?: string;
19
+ }
20
+ export interface ScopeTransferItem {
21
+ /** A binding leaves its repeated item, or a nested repeat leaves the item that supplies its collection. */
22
+ readonly kind: 'binding' | 'repeat';
23
+ readonly id: string;
24
+ readonly nodeId: string;
25
+ readonly nodeName: string;
26
+ readonly slot?: {
27
+ readonly id: string;
28
+ readonly label: string;
29
+ };
30
+ readonly field: {
31
+ readonly id: string;
32
+ readonly label: string;
33
+ readonly valueKind: DataValueType['kind'];
34
+ readonly sourceRef: string;
35
+ };
36
+ readonly current: DataScopeDescription;
37
+ readonly destination: DataScopeDescription;
38
+ readonly choices: readonly ScopeTransferChoice[];
39
+ /** Written content is the existing text conversion: a text-content slot on an actual text layer. */
40
+ readonly written: boolean;
41
+ /** Plain disconnection removes the binding without inventing properties; repeats offer no disconnection. */
42
+ readonly disconnect: boolean;
43
+ }
44
+ export interface ScopeTransferDecisionPlan {
45
+ readonly status: 'decision';
46
+ /** The document this plan was made against; the identity used for staleness and dialog expiry. */
47
+ readonly document: DesignDocument;
48
+ /** The structural move, planned without binding policy, that the decisions complete. */
49
+ readonly move: ReadyDocumentLayerMove;
50
+ readonly nodeIds: readonly string[];
51
+ readonly parentId: string;
52
+ readonly index: number;
53
+ /** The innermost scope at the destination parent. */
54
+ readonly destination: DataScopeDescription;
55
+ readonly items: readonly ScopeTransferItem[];
56
+ }
57
+ export type ScopeTransferPlan = DocumentLayerMovePlan | ScopeTransferDecisionPlan;
58
+ export type ScopeTransferDecision = {
59
+ readonly id: string;
60
+ readonly kind: 'retarget';
61
+ readonly choice: string;
62
+ } | {
63
+ readonly id: string;
64
+ readonly kind: 'written';
65
+ readonly text: string;
66
+ } | {
67
+ readonly id: string;
68
+ readonly kind: 'disconnect';
69
+ };
70
+ export type ResolvedScopeTransfer = {
71
+ readonly status: 'ready';
72
+ readonly move: ReadyDocumentLayerMove;
73
+ apply(transaction: DocumentTransaction, currentDocument: DesignDocument): void;
74
+ } | {
75
+ readonly status: 'blocked';
76
+ readonly diagnostics: readonly DocumentDiagnostic[];
77
+ };
78
+ /**
79
+ * Plan a layer move that may carry bound layers out of, into, or between repeated item templates.
80
+ * A move the live policy accepts, or rejects for any reason other than scope escape, is returned as
81
+ * the ordinary move plan. A move blocked solely by scope escapes becomes a decision plan: every
82
+ * escaping binding or nested repeat is listed with its field, current scope, destination scope, and
83
+ * the compatible fields declared at the destination. Nothing here reads displayed values.
84
+ */
85
+ export declare function planScopeTransfer(document: DesignDocument, request: DocumentLayerMoveRequest, options?: ScopeTransferOptions): ScopeTransferPlan;
86
+ /**
87
+ * Apply the move and every binding decision as one batch of the caller's live transaction.
88
+ * Validation errors escape so the engine rolls back the complete move; there is no partial state.
89
+ */
90
+ export declare function applyScopeTransfer(transaction: DocumentTransaction, plan: ScopeTransferDecisionPlan, decisions: readonly ScopeTransferDecision[], currentDocument: DesignDocument, options?: ScopeTransferOptions): void;
91
+ /**
92
+ * Check a complete decision set in a disposable engine under the live policy before anything is committed.
93
+ * The binding validator runs independently as well, so an invalid decision is rejected even when the
94
+ * caller's preflight is not the binding preflight.
95
+ */
96
+ export declare function resolveScopeTransfer(plan: ScopeTransferDecisionPlan, decisions: readonly ScopeTransferDecision[], options?: ScopeTransferOptions): ResolvedScopeTransfer;
@@ -0,0 +1,75 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { DesignDocument, DocumentValue } from '../../document/contracts.js';
3
+ import { type RichText, type RichTextAlign, type RichTextCase, type RichTextCharacterKey, type RichTextCharacterStyle, type RichTextList, type RichTextParagraph, type RichTextParagraphKey, type RichTextParagraphStyle, type RichTextRange, type TextNodeCharacterStyle, type TextResizeMode } from '../../document/richText.js';
4
+ /** Inspector vocabularies. Values are the document values; labels are what the editor shows. */
5
+ export declare const TEXT_FONT_WEIGHT_OPTIONS: readonly {
6
+ value: number;
7
+ label: string;
8
+ }[];
9
+ export declare const TEXT_CASE_OPTIONS: readonly {
10
+ value: RichTextCase;
11
+ label: string;
12
+ }[];
13
+ export declare const TEXT_LIST_OPTIONS: readonly {
14
+ value: RichTextList;
15
+ label: string;
16
+ }[];
17
+ export declare const TEXT_ALIGN_OPTIONS: readonly {
18
+ value: RichTextAlign;
19
+ label: string;
20
+ }[];
21
+ export declare const TEXT_RESIZE_OPTIONS: readonly {
22
+ value: TextResizeMode;
23
+ label: string;
24
+ }[];
25
+ export declare const TEXT_INDENT_STEP = 24;
26
+ /** True when any binding targets the node; bound text renders projected data and keeps no rich runs. */
27
+ export declare function isBoundTextNode(document: DesignDocument, nodeId: string): boolean;
28
+ /** Inherited character properties only; decorations paint through descendants, so they are emitted per run. */
29
+ export declare function characterStyleCss(style: TextNodeCharacterStyle): CSSProperties;
30
+ /** The effective decoration of a run, or undefined when nothing is decorated. */
31
+ export declare function decorationCss(effective: TextNodeCharacterStyle): string | undefined;
32
+ export declare function paragraphStyleCss(style: RichTextParagraphStyle): CSSProperties;
33
+ /** The component measures; these rules let the surface grow while the document keeps the recorded mode. */
34
+ export declare function textResizeCss(mode: TextResizeMode): CSSProperties;
35
+ export type TextBlock = {
36
+ kind: 'paragraph';
37
+ index: number;
38
+ paragraph: RichTextParagraph;
39
+ } | {
40
+ kind: 'list';
41
+ list: 'bullet' | 'numbered';
42
+ items: readonly {
43
+ index: number;
44
+ paragraph: RichTextParagraph;
45
+ }[];
46
+ };
47
+ /** Consecutive paragraphs sharing an effective list kind render as one real `<ul>`/`<ol>`. */
48
+ export declare function richTextBlocks(model: RichText, base: RichTextParagraphStyle): TextBlock[];
49
+ export interface TextStyleTarget {
50
+ scope: 'layer' | 'range';
51
+ range: RichTextRange;
52
+ /** Connected content: the node never stores runs, so every change is node-level. */
53
+ plain: boolean;
54
+ }
55
+ /** A non-collapsed selection on written text styles a range; anything else styles the whole layer. */
56
+ export declare function textStyleTarget(plain: boolean, selection: RichTextRange | null, length: number): TextStyleTarget;
57
+ export declare function textEditorModel(properties: Readonly<Record<string, DocumentValue>>, target: TextStyleTarget): RichText;
58
+ export interface TextStyleValues {
59
+ character: TextNodeCharacterStyle;
60
+ paragraph: RichTextParagraphStyle;
61
+ /** Keys whose value differs inside the target, so a control shows "Mixed" instead of one value. */
62
+ mixed: readonly string[];
63
+ }
64
+ /** What the inspector shows for the target: effective values, with keys that vary reported as mixed. */
65
+ export declare function textStyleValues(properties: Readonly<Record<string, DocumentValue>>, model: RichText, target: TextStyleTarget): TextStyleValues;
66
+ /**
67
+ * Applies one character key. A range edit writes runs; a layer edit writes the node property and clears the
68
+ * key from every run so the whole text follows it, matching a whole-layer selection. `undefined` clears.
69
+ */
70
+ export declare function applyTextCharacterStyle(properties: Readonly<Record<string, DocumentValue>>, model: RichText, target: TextStyleTarget, key: RichTextCharacterKey, value: RichTextCharacterStyle[RichTextCharacterKey] | string | undefined): Record<string, DocumentValue>;
71
+ export declare function applyTextParagraphStyle(properties: Readonly<Record<string, DocumentValue>>, model: RichText, target: TextStyleTarget, key: RichTextParagraphKey, value: RichTextParagraphStyle[RichTextParagraphKey] | undefined): Record<string, DocumentValue>;
72
+ /** The resize mode is a layer property in every scope. */
73
+ export declare function setTextResize(properties: Readonly<Record<string, DocumentValue>>, mode: TextResizeMode): Record<string, DocumentValue>;
74
+ /** Folds the textarea result into the model as one span replacement so untouched runs keep their styles. */
75
+ export declare function commitTextEdit(properties: Readonly<Record<string, DocumentValue>>, model: RichText, target: TextStyleTarget, draft: string): Record<string, DocumentValue>;
@@ -0,0 +1,136 @@
1
+ import type { DataProjection } from '../../binding/preview.js';
2
+ import type { DesignDocument, DocumentDiagnostic, DocumentMatrix, DocumentNode, DocumentNodePatch, DocumentTransaction } from '../../document/contracts.js';
3
+ import { booleanVectorPrecision, type VectorAnchor, type VectorAnchorAddress, type VectorBooleanOperation, type VectorFillRule, type VectorPoint, type VectorProperties, type VectorShape, type VectorStroke } from '../../document/vector.js';
4
+ import { type LayerGeometry } from './geometryHandles.js';
5
+ import type { ProjectedDocumentNode } from './projection.js';
6
+ /**
7
+ * Pure editing model for `vector` layers. The document stores path data under
8
+ * `node.properties.vector` in the layer's own space; the layer box is the geometry's
9
+ * bounds. Everything the toolbar, the canvas overlay, and the inspector do goes through
10
+ * these functions, so a gesture can be planned, previewed, and committed as one document
11
+ * transaction without touching the DOM.
12
+ */
13
+ export type VectorTool = 'select' | 'pen' | 'marquee';
14
+ export type VectorAnchorAction = 'smooth' | 'corner' | 'delete' | 'split' | 'close' | 'join';
15
+ export type VectorStyle = Pick<VectorProperties, 'fill' | 'fillRule' | 'stroke' | 'corners' | 'mask'>;
16
+ export interface VectorRenderModel {
17
+ d: string;
18
+ fill: string | undefined;
19
+ fillRule: VectorFillRule;
20
+ stroke: VectorStroke | undefined;
21
+ mask: boolean;
22
+ }
23
+ export interface VectorEditTarget {
24
+ readonly node: DocumentNode;
25
+ readonly layer: LayerGeometry;
26
+ /** Geometry in the layer box (the stored geometry with its fit onto `width` × `height` applied). */
27
+ readonly vector: VectorProperties;
28
+ /** Canvas placement of the layer box, as rendered. */
29
+ readonly world: DocumentMatrix;
30
+ }
31
+ export interface VectorEditPlan {
32
+ /** The single selected vector layer when its anchors may be edited on the canvas. */
33
+ readonly target: VectorEditTarget | null;
34
+ /** The single selected vector layer, editable or not; style controls apply to it. */
35
+ readonly styleNode: DocumentNode | null;
36
+ readonly blocked: readonly DocumentDiagnostic[];
37
+ }
38
+ export interface VectorAnchorActions {
39
+ readonly smooth: boolean;
40
+ readonly corner: boolean;
41
+ readonly delete: boolean;
42
+ readonly split: boolean;
43
+ readonly close: boolean;
44
+ readonly join: {
45
+ readonly from: number;
46
+ readonly to: number;
47
+ } | null;
48
+ }
49
+ export interface VectorAnchorActionResult {
50
+ readonly vector: VectorProperties;
51
+ readonly label: string;
52
+ readonly anchors: readonly VectorAnchorAddress[];
53
+ }
54
+ export type VectorBooleanPlan = {
55
+ readonly status: 'ready';
56
+ readonly lower: DocumentNode;
57
+ readonly upper: DocumentNode;
58
+ readonly parentId: string;
59
+ readonly index: number;
60
+ } | {
61
+ readonly status: 'blocked';
62
+ readonly diagnostics: readonly DocumentDiagnostic[];
63
+ };
64
+ export interface VectorInsertion {
65
+ readonly node: DocumentNode;
66
+ readonly parentId: string;
67
+ }
68
+ export declare const VECTOR_TOOLS: readonly VectorTool[];
69
+ export declare const VECTOR_SHAPE_LABELS: Readonly<Record<VectorShape, string>>;
70
+ export declare const VECTOR_BOOLEAN_LABELS: Readonly<Record<VectorBooleanOperation, string>>;
71
+ export declare const VECTOR_DEFAULT_FILL = "#d9d9d9";
72
+ export declare const VECTOR_DEFAULT_STROKE: VectorStroke;
73
+ export declare const VECTOR_DEFAULT_SIZE = 100;
74
+ /** Screen pixels within which a pen click lands on the first anchor and closes the path. */
75
+ export declare const VECTOR_CLOSE_TOLERANCE = 8;
76
+ /** Screen pixels a pen pointer may travel and still count as a click. */
77
+ export declare const VECTOR_CLICK_TRAVEL = 4;
78
+ export declare const VECTOR_NO_ANCHOR_ACTIONS: VectorAnchorActions;
79
+ export declare const applyVectorMatrix: (matrix: DocumentMatrix, point: VectorPoint) => VectorPoint;
80
+ export declare const vectorAnchorKey: (address: VectorAnchorAddress) => string;
81
+ export declare const sameVectorAnchor: (a: VectorAnchorAddress, b: VectorAnchorAddress) => boolean;
82
+ export declare function parseVectorAnchorKey(key: string): VectorAnchorAddress | null;
83
+ /** Valid stored path data of a layer, detached; null for other layers or invalid data. */
84
+ export declare function vectorNodeGeometry(node: DocumentNode): VectorProperties | null;
85
+ /** The stored geometry with its fit onto the layer box applied; anchors are then in box units. */
86
+ export declare function vectorBoxGeometry(vector: VectorProperties, width: number, height: number): VectorProperties;
87
+ /** What the surface draws for a layer box; `override` is box geometry a gesture is previewing. */
88
+ export declare function vectorRenderModel(node: DocumentNode, width?: number, height?: number, override?: VectorProperties): VectorRenderModel | null;
89
+ /** Stores box geometry back on the layer: the box is refit to the new bounds and the layer keeps its world position. */
90
+ export declare function vectorGeometryPatch(node: DocumentNode, next: VectorProperties): DocumentNodePatch;
91
+ /** Changes paint and corner keys without touching geometry; an `undefined` value removes the key. */
92
+ export declare function vectorStylePatch(node: DocumentNode, style: Partial<VectorStyle>): DocumentNodePatch;
93
+ /** Canvas placement of a layer: the world transform with the root's own transform removed, as the canvas renders it. */
94
+ export declare function renderedWorldTransform(document: DesignDocument, nodeId: string): DocumentMatrix;
95
+ /** New layers join the selected container, or the root; the rule matches text insertion. */
96
+ export declare function vectorInsertParent(document: DesignDocument, selection: readonly string[]): string;
97
+ /** A default-sized shape placed inside the parent, offset by the number of existing children so repeated inserts stay visible. */
98
+ export declare function shapeInsertion(document: DesignDocument, selection: readonly string[], shape: VectorShape, id: string): VectorInsertion;
99
+ /** True when a pen click near the first anchor should close the path. */
100
+ export declare function penClosesOnFirst(points: readonly VectorPoint[], candidate: VectorPoint, tolerance: number): boolean;
101
+ /** A stroked path through the points, given in the parent's space; null when there is nothing to draw. */
102
+ export declare function penPathNode(points: readonly VectorAnchor[], closed: boolean, id: string): DocumentNode | null;
103
+ /**
104
+ * Which selected layer the canvas overlay may edit. The geometry policy is the one the
105
+ * selection handles use, so an auto layout child, a source layout fragment, or a repeated
106
+ * row is refused with the same diagnostic instead of a guessed box.
107
+ */
108
+ export declare function planVectorEdit(document: DesignDocument, selection: readonly string[], projection: DataProjection | null): VectorEditPlan;
109
+ /** Anchor operations the current anchor selection allows. */
110
+ export declare function vectorAnchorActions(vector: VectorProperties, anchors: readonly VectorAnchorAddress[]): VectorAnchorActions;
111
+ /** Applies one anchor action to the box geometry; the caller commits the result as one transaction. */
112
+ export declare function applyVectorAnchorAction(vector: VectorProperties, anchors: readonly VectorAnchorAddress[], action: VectorAnchorAction): VectorAnchorActionResult;
113
+ /** Two vector layers sharing a parent; the lower layer (earlier in the parent order) is the base of a subtraction. */
114
+ export declare function planVectorBoolean(document: DesignDocument, selection: readonly string[]): VectorBooleanPlan;
115
+ /** The combined layer, in the parent's space, with the lower layer's paint. Throws when the result has no shape. */
116
+ export declare function vectorBooleanNode(plan: Extract<VectorBooleanPlan, {
117
+ status: 'ready';
118
+ }>, operation: VectorBooleanOperation, id: string): DocumentNode;
119
+ /** Precision of the operands after the same fit and placement used by the boolean command. */
120
+ export declare function vectorBooleanPrecision(plan: Extract<VectorBooleanPlan, {
121
+ status: 'ready';
122
+ }>): ReturnType<typeof booleanVectorPrecision>;
123
+ /** Replaces both operands with the combined layer at the lower operand's position, inside one transaction. */
124
+ export declare function applyVectorBoolean(transaction: DocumentTransaction, plan: Extract<VectorBooleanPlan, {
125
+ status: 'ready';
126
+ }>, node: DocumentNode): void;
127
+ /**
128
+ * CSS `clip-path` values for layers a mask clips: every later sibling of a mask vector,
129
+ * until the next mask in the same parent, in that sibling's own coordinates. Computed on
130
+ * the projected scene so layout positions and repeated rows are the rendered ones.
131
+ */
132
+ export declare function vectorMaskClips(root: ProjectedDocumentNode): Map<ProjectedDocumentNode, string>;
133
+ /** Dragging a pen anchor produces a mirrored pair in the same coordinate space. */
134
+ export declare function penDragAnchor(start: VectorPoint, end: VectorPoint): VectorAnchor;
135
+ /** Inclusive anchor marquee in canvas coordinates; handles alone do not count as anchors. */
136
+ export declare function vectorAnchorsInBox(vector: VectorProperties, world: DocumentMatrix, start: VectorPoint, end: VectorPoint): VectorAnchorAddress[];
@@ -0,0 +1,13 @@
1
+ import type { WorkspaceController } from './controller.js';
2
+ import type { WorkspaceTarget } from './contracts.js';
3
+ export interface LegacyWorkspaceModePort {
4
+ getTarget(): WorkspaceTarget | null;
5
+ /** Writes compatibility indicators; it must not navigate or mutate a draft. */
6
+ projectMode(edit: boolean, preview: boolean): void;
7
+ onPreviewOpenChange?(open: boolean): void;
8
+ }
9
+ /** A one-way projection; controlled host input re-enters through this adapter. */
10
+ export declare function createLegacyModeAdapter(controller: WorkspaceController, port: LegacyWorkspaceModePort): {
11
+ setPreviewOpen(open: boolean): void;
12
+ dispose: () => void;
13
+ };