@tangle-network/agent-app 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DesignCanvas-3JEEIT6Y.js +10 -0
- package/dist/DesignCanvas-3JEEIT6Y.js.map +1 -0
- package/dist/DesignCanvasEditor-37LPJIIR.js +9 -0
- package/dist/DesignCanvasEditor-37LPJIIR.js.map +1 -0
- package/dist/chunk-2Q73HGDI.js +1743 -0
- package/dist/chunk-2Q73HGDI.js.map +1 -0
- package/dist/{chunk-3WAJWYKD.js → chunk-6UOE5CTA.js} +9 -92
- package/dist/{chunk-3WAJWYKD.js.map → chunk-6UOE5CTA.js.map} +1 -1
- package/dist/chunk-7QCIYDGC.js +1119 -0
- package/dist/chunk-7QCIYDGC.js.map +1 -0
- package/dist/chunk-A76ZHWNF.js +194 -0
- package/dist/chunk-A76ZHWNF.js.map +1 -0
- package/dist/{chunk-CF5DZELC.js → chunk-ABGSFUJQ.js} +2 -2
- package/dist/chunk-F5KTWRO7.js +2276 -0
- package/dist/chunk-F5KTWRO7.js.map +1 -0
- package/dist/chunk-JZAJE3JL.js +990 -0
- package/dist/chunk-JZAJE3JL.js.map +1 -0
- package/dist/design-canvas/drizzle.d.ts +569 -0
- package/dist/design-canvas/drizzle.js +183 -0
- package/dist/design-canvas/drizzle.js.map +1 -0
- package/dist/design-canvas/index.d.ts +261 -0
- package/dist/design-canvas/index.js +96 -0
- package/dist/design-canvas/index.js.map +1 -0
- package/dist/design-canvas-react/index.d.ts +916 -0
- package/dist/design-canvas-react/index.js +423 -0
- package/dist/design-canvas-react/index.js.map +1 -0
- package/dist/export-presets-Dl5Aa5xj.d.ts +284 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +99 -3
- package/dist/mcp-rpc-DLw_r9PQ.d.ts +55 -0
- package/dist/model-BHLN208Z.d.ts +183 -0
- package/dist/sequences/index.d.ts +4 -0
- package/dist/sequences/index.js +2 -2
- package/dist/store-CUStmtdH.d.ts +64 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +6 -2
- package/package.json +28 -3
- package/dist/chunk-IJZJWKUK.js +0 -77
- package/dist/chunk-IJZJWKUK.js.map +0 -1
- /package/dist/{chunk-CF5DZELC.js.map → chunk-ABGSFUJQ.js.map} +0 -0
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
import { c as SceneDocument, d as SceneElement, B as Bounds, N as NewPageOptions, a as PageGuides, P as PageBleed, g as ScenePage } from '../model-BHLN208Z.js';
|
|
2
|
+
import { n as SceneOperation, m as SceneAttrsPatch, h as ExportCropRect, j as ExportPreset } from '../export-presets-Dl5Aa5xj.js';
|
|
3
|
+
export { v as bleedAwareExportBounds, F as scaleForPreset } from '../export-presets-Dl5Aa5xj.js';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import Konva from 'konva';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Seams between the design-canvas editor's engine and components, mirroring
|
|
9
|
+
* the sequences-react pattern: interface-only so layers build independently.
|
|
10
|
+
*
|
|
11
|
+
* Persistence: the command stack executes optimistically against local state
|
|
12
|
+
* and emits `SceneOperation[]` through `DesignCanvasProps.onApplyOperations`.
|
|
13
|
+
* The host's apply returns the new revision (and the server document when it
|
|
14
|
+
* re-minted ids); a rejected promise rolls the command back. Drag gestures
|
|
15
|
+
* coalesce: pointer moves mutate volatile state, ONE command (final attrs,
|
|
16
|
+
* inverse = pre-gesture attrs) executes on release — undo is per-gesture,
|
|
17
|
+
* never per-pixel.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface EditorSceneState {
|
|
21
|
+
document: SceneDocument;
|
|
22
|
+
activePageId: string;
|
|
23
|
+
selectedElementIds: string[];
|
|
24
|
+
/** Pixels per document px. */
|
|
25
|
+
zoom: number;
|
|
26
|
+
panX: number;
|
|
27
|
+
panY: number;
|
|
28
|
+
gridEnabled: boolean;
|
|
29
|
+
/** Document px between grid lines. */
|
|
30
|
+
gridSize: number;
|
|
31
|
+
snapEnabled: boolean;
|
|
32
|
+
showRulers: boolean;
|
|
33
|
+
showBleed: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface SceneCommand {
|
|
36
|
+
label: string;
|
|
37
|
+
execute(state: EditorSceneState): EditorSceneState;
|
|
38
|
+
undo(state: EditorSceneState): EditorSceneState;
|
|
39
|
+
operations(): SceneOperation[];
|
|
40
|
+
/**
|
|
41
|
+
* The inverse operation sequence for server-side persistence of an undo.
|
|
42
|
+
* Most commands return an exact inverse (e.g. set_attrs → prior set_attrs,
|
|
43
|
+
* add_element → delete_element). deletePageCommand returns add_page +
|
|
44
|
+
* per-element add_element ops restoring the full page snapshot.
|
|
45
|
+
*/
|
|
46
|
+
inverseOperations(): SceneOperation[];
|
|
47
|
+
}
|
|
48
|
+
interface SceneCommandStack {
|
|
49
|
+
execute(command: SceneCommand): void;
|
|
50
|
+
/** Apply the top-of-done-stack inverse and return the command (callers use
|
|
51
|
+
* `command.inverseOperations()` to persist the undo to the server). */
|
|
52
|
+
undo(): SceneCommand;
|
|
53
|
+
/** Re-execute the top-of-redo-stack and return the command (callers use
|
|
54
|
+
* `command.operations()` to persist the redo to the server). */
|
|
55
|
+
redo(): SceneCommand;
|
|
56
|
+
canUndo(): boolean;
|
|
57
|
+
canRedo(): boolean;
|
|
58
|
+
subscribe(listener: () => void): () => void;
|
|
59
|
+
getState(): EditorSceneState;
|
|
60
|
+
/** Update volatile view state (zoom/pan/selection/toggles) without touching
|
|
61
|
+
* history — view changes are never undo steps. */
|
|
62
|
+
setView(patch: Partial<Omit<EditorSceneState, 'document'>>): void;
|
|
63
|
+
/** Rebase onto a server refresh WITHOUT clearing history (history holds
|
|
64
|
+
* operations, not snapshots). */
|
|
65
|
+
reset(document: SceneDocument): void;
|
|
66
|
+
/**
|
|
67
|
+
* Remove a specific command from the undo stack and apply its inverse to
|
|
68
|
+
* the current state. Called by the persistence layer when a save rejects
|
|
69
|
+
* AFTER the user may have made further edits.
|
|
70
|
+
*
|
|
71
|
+
* Invariant: for commands that operate on disjoint attributes, rollback is
|
|
72
|
+
* identity for all commands executed after the rolled-back one — their net
|
|
73
|
+
* effect is preserved. For overlapping attr edits (same element, same field)
|
|
74
|
+
* the result is defined but not guaranteed to be semantically correct; the
|
|
75
|
+
* caller should trigger an onResyncRequired fetch in that case.
|
|
76
|
+
*
|
|
77
|
+
* If `command` is not found in the undo stack (stale or double-fire
|
|
78
|
+
* rejection handler), this is a safe no-op.
|
|
79
|
+
*/
|
|
80
|
+
rollback(command: SceneCommand): void;
|
|
81
|
+
}
|
|
82
|
+
type SnapTargetKind = 'grid' | 'element-edge' | 'element-center' | 'page-edge' | 'page-center' | 'guide';
|
|
83
|
+
interface SnapTarget {
|
|
84
|
+
/** Page-coordinate position of the snap line. */
|
|
85
|
+
position: number;
|
|
86
|
+
kind: SnapTargetKind;
|
|
87
|
+
}
|
|
88
|
+
interface SnapTargets {
|
|
89
|
+
vertical: SnapTarget[];
|
|
90
|
+
horizontal: SnapTarget[];
|
|
91
|
+
}
|
|
92
|
+
interface SnapResult {
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
/** Lines to render while the gesture holds the snap. */
|
|
96
|
+
activeVertical: SnapTarget | null;
|
|
97
|
+
activeHorizontal: SnapTarget | null;
|
|
98
|
+
}
|
|
99
|
+
interface SnapEngine {
|
|
100
|
+
/** Collect targets for a gesture: other elements' edges/centers, page
|
|
101
|
+
* edges/center, saved guides, and grid lines when enabled. `excludeIds`
|
|
102
|
+
* removes the dragged elements' own geometry. */
|
|
103
|
+
collectTargets(state: EditorSceneState, excludeIds: string[]): SnapTargets;
|
|
104
|
+
/** Snap a moving AABB. Threshold is SCREEN pixels, divided by zoom. */
|
|
105
|
+
apply(bounds: Bounds, targets: SnapTargets, thresholdPx: number, zoom: number): SnapResult;
|
|
106
|
+
}
|
|
107
|
+
interface ZoomPanMath {
|
|
108
|
+
minZoom: number;
|
|
109
|
+
maxZoom: number;
|
|
110
|
+
/** Zoom about a screen point so the document point under the cursor stays
|
|
111
|
+
* fixed (wheel-zoom-to-cursor). Returns the clamped new view. */
|
|
112
|
+
zoomAtPoint(state: {
|
|
113
|
+
zoom: number;
|
|
114
|
+
panX: number;
|
|
115
|
+
panY: number;
|
|
116
|
+
}, factor: number, screenX: number, screenY: number): {
|
|
117
|
+
zoom: number;
|
|
118
|
+
panX: number;
|
|
119
|
+
panY: number;
|
|
120
|
+
};
|
|
121
|
+
/** Fit the active page into a viewport with padding. */
|
|
122
|
+
fitPage(page: {
|
|
123
|
+
width: number;
|
|
124
|
+
height: number;
|
|
125
|
+
}, viewport: {
|
|
126
|
+
width: number;
|
|
127
|
+
height: number;
|
|
128
|
+
}, paddingPx?: number): {
|
|
129
|
+
zoom: number;
|
|
130
|
+
panX: number;
|
|
131
|
+
panY: number;
|
|
132
|
+
};
|
|
133
|
+
documentToScreen(state: {
|
|
134
|
+
zoom: number;
|
|
135
|
+
panX: number;
|
|
136
|
+
panY: number;
|
|
137
|
+
}, x: number, y: number): {
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
};
|
|
141
|
+
screenToDocument(state: {
|
|
142
|
+
zoom: number;
|
|
143
|
+
panX: number;
|
|
144
|
+
panY: number;
|
|
145
|
+
}, x: number, y: number): {
|
|
146
|
+
x: number;
|
|
147
|
+
y: number;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
interface ApplySceneResult {
|
|
151
|
+
rev: number;
|
|
152
|
+
/** Present when the server re-minted ids or normalized the document; the
|
|
153
|
+
* editor rebases onto it. */
|
|
154
|
+
document?: SceneDocument;
|
|
155
|
+
}
|
|
156
|
+
interface DesignCanvasProps {
|
|
157
|
+
document: SceneDocument;
|
|
158
|
+
/** Revision the document was loaded at; threaded through saves. */
|
|
159
|
+
rev: number;
|
|
160
|
+
canWrite: boolean;
|
|
161
|
+
/** Persist operations. Resolve with the new revision; reject to roll back.
|
|
162
|
+
* A stale-revision failure should resolve AFTER refetch with the fresh
|
|
163
|
+
* document so the editor rebases instead of fighting. */
|
|
164
|
+
onApplyOperations(operations: SceneOperation[]): Promise<ApplySceneResult>;
|
|
165
|
+
onSelectionChange?(elements: SceneElement[]): void;
|
|
166
|
+
/** Host panels: agent chat (right), asset/template browser (left). */
|
|
167
|
+
renderAgentPanel?(ctx: {
|
|
168
|
+
selectedElements: SceneElement[];
|
|
169
|
+
activePageId: string;
|
|
170
|
+
}): React.ReactNode;
|
|
171
|
+
renderSidePanel?(): React.ReactNode;
|
|
172
|
+
/** Export hook — host persists the rendered blob (upload → asset row). */
|
|
173
|
+
onExport?(result: {
|
|
174
|
+
pageId: string;
|
|
175
|
+
format: 'png' | 'jpeg';
|
|
176
|
+
dataUrl: string;
|
|
177
|
+
pixelRatio: number;
|
|
178
|
+
}): Promise<void>;
|
|
179
|
+
className?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Undo/redo command stack over immutable `EditorSceneState`.
|
|
184
|
+
*
|
|
185
|
+
* History entries hold COMMANDS (state transforms + durable operations
|
|
186
|
+
* captured at construction), never snapshots. Rebasing the document via
|
|
187
|
+
* `reset()` therefore cannot stale the history: a later undo re-applies the
|
|
188
|
+
* inverse transform to whatever document is current. If the rebase removed an
|
|
189
|
+
* element a historical command targets, that transform throws (fail loud)
|
|
190
|
+
* rather than silently editing the wrong element.
|
|
191
|
+
*
|
|
192
|
+
* `setView` updates volatile view state (zoom/pan/selection/toggles) without
|
|
193
|
+
* touching history — view changes are never undo steps.
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/** Oldest entries are dropped past this bound; redo stack is cleared on execute. */
|
|
197
|
+
declare const SCENE_COMMAND_HISTORY_LIMIT = 200;
|
|
198
|
+
declare function createSceneCommandStack(document: SceneDocument, activePageId: string): SceneCommandStack;
|
|
199
|
+
|
|
200
|
+
interface AddElementInput {
|
|
201
|
+
pageId: string;
|
|
202
|
+
element: SceneElement;
|
|
203
|
+
/** Insertion z-index within owner; omitted → top. */
|
|
204
|
+
index?: number;
|
|
205
|
+
/** Parent group id; omitted → page root. */
|
|
206
|
+
parentGroupId?: string;
|
|
207
|
+
}
|
|
208
|
+
declare function addElementCommand(input: AddElementInput): SceneCommand;
|
|
209
|
+
interface SetAttrsInput {
|
|
210
|
+
pageId: string;
|
|
211
|
+
elementId: string;
|
|
212
|
+
/** Final attribute values after the gesture. */
|
|
213
|
+
attrs: SceneAttrsPatch;
|
|
214
|
+
/** Attribute values BEFORE the gesture began — inverse is built from these. */
|
|
215
|
+
priorAttrs: SceneAttrsPatch;
|
|
216
|
+
}
|
|
217
|
+
declare function setAttrsCommand(input: SetAttrsInput): SceneCommand;
|
|
218
|
+
interface MultiSetAttrsEntry {
|
|
219
|
+
pageId: string;
|
|
220
|
+
elementId: string;
|
|
221
|
+
attrs: SceneAttrsPatch;
|
|
222
|
+
priorAttrs: SceneAttrsPatch;
|
|
223
|
+
}
|
|
224
|
+
declare function multiSetAttrsCommand(entries: MultiSetAttrsEntry[]): SceneCommand;
|
|
225
|
+
interface ReorderElementInput {
|
|
226
|
+
pageId: string;
|
|
227
|
+
elementId: string;
|
|
228
|
+
toIndex: number;
|
|
229
|
+
}
|
|
230
|
+
declare function reorderElementCommand(input: ReorderElementInput): SceneCommand;
|
|
231
|
+
interface DeleteElementInput {
|
|
232
|
+
document: SceneDocument;
|
|
233
|
+
pageId: string;
|
|
234
|
+
elementId: string;
|
|
235
|
+
}
|
|
236
|
+
declare function deleteElementCommand(input: DeleteElementInput): SceneCommand;
|
|
237
|
+
interface GroupElementsInput {
|
|
238
|
+
document: SceneDocument;
|
|
239
|
+
pageId: string;
|
|
240
|
+
elementIds: string[];
|
|
241
|
+
groupId: string;
|
|
242
|
+
name?: string;
|
|
243
|
+
}
|
|
244
|
+
declare function groupElementsCommand(input: GroupElementsInput): SceneCommand;
|
|
245
|
+
interface UngroupElementInput {
|
|
246
|
+
document: SceneDocument;
|
|
247
|
+
pageId: string;
|
|
248
|
+
groupId: string;
|
|
249
|
+
}
|
|
250
|
+
declare function ungroupElementCommand(input: UngroupElementInput): SceneCommand;
|
|
251
|
+
interface AddPageInput {
|
|
252
|
+
pageId: string;
|
|
253
|
+
options?: NewPageOptions;
|
|
254
|
+
index?: number;
|
|
255
|
+
}
|
|
256
|
+
declare function addPageCommand(input: AddPageInput): SceneCommand;
|
|
257
|
+
interface DuplicatePageInput {
|
|
258
|
+
document: SceneDocument;
|
|
259
|
+
sourcePageId: string;
|
|
260
|
+
/** Caller-minted id for the copy. */
|
|
261
|
+
pageId: string;
|
|
262
|
+
}
|
|
263
|
+
declare function duplicatePageCommand(input: DuplicatePageInput): SceneCommand;
|
|
264
|
+
interface DeletePageInput {
|
|
265
|
+
document: SceneDocument;
|
|
266
|
+
pageId: string;
|
|
267
|
+
}
|
|
268
|
+
declare function deletePageCommand(input: DeletePageInput): SceneCommand;
|
|
269
|
+
interface ReorderPageInput {
|
|
270
|
+
pageId: string;
|
|
271
|
+
toIndex: number;
|
|
272
|
+
}
|
|
273
|
+
declare function reorderPageCommand(input: ReorderPageInput): SceneCommand;
|
|
274
|
+
interface SetPagePropsInput {
|
|
275
|
+
document: SceneDocument;
|
|
276
|
+
pageId: string;
|
|
277
|
+
props: {
|
|
278
|
+
name?: string;
|
|
279
|
+
width?: number;
|
|
280
|
+
height?: number;
|
|
281
|
+
background?: string;
|
|
282
|
+
bleed?: PageBleed | null;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
declare function setPagePropsCommand(input: SetPagePropsInput): SceneCommand;
|
|
286
|
+
interface SetPageGuidesInput {
|
|
287
|
+
document: SceneDocument;
|
|
288
|
+
pageId: string;
|
|
289
|
+
guides: PageGuides;
|
|
290
|
+
}
|
|
291
|
+
declare function setPageGuidesCommand(input: SetPageGuidesInput): SceneCommand;
|
|
292
|
+
interface BindSlotInput {
|
|
293
|
+
document: SceneDocument;
|
|
294
|
+
pageId: string;
|
|
295
|
+
elementId: string;
|
|
296
|
+
slot: string | null;
|
|
297
|
+
}
|
|
298
|
+
declare function bindSlotCommand(input: BindSlotInput): SceneCommand;
|
|
299
|
+
interface SetDocumentTitleInput {
|
|
300
|
+
document: SceneDocument;
|
|
301
|
+
title: string;
|
|
302
|
+
}
|
|
303
|
+
declare function setDocumentTitleCommand(input: SetDocumentTitleInput): SceneCommand;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 2-axis snap engine for the design-canvas editor. Targets come from element
|
|
307
|
+
* AABBs (edges + centers), page edges + center, saved guides, and grid lines
|
|
308
|
+
* near the moving bounds. Grid lines are generated lazily in the neighborhood
|
|
309
|
+
* of the moving element, not the full page — avoids allocating thousands of
|
|
310
|
+
* targets on large pages with fine grids.
|
|
311
|
+
*
|
|
312
|
+
* Threshold is a SCREEN distance (pixels), divided by zoom to convert to
|
|
313
|
+
* document units — what "feels close" is a screen distance.
|
|
314
|
+
*
|
|
315
|
+
* Tie-breaking: non-grid kinds beat grid on equal distance; among equals of
|
|
316
|
+
* the same priority the first in iteration order wins.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
declare function createSnapEngine(): SnapEngine;
|
|
320
|
+
/** Generate grid line targets within a neighborhood around the moving bounds.
|
|
321
|
+
* Call this and append to `SnapTargets.vertical`/`horizontal` before
|
|
322
|
+
* passing to `apply()` when `state.gridEnabled`. */
|
|
323
|
+
declare function collectGridTargets(bounds: Bounds, gridSize: number, page: ScenePage, thresholdDocPx: number): {
|
|
324
|
+
vertical: SnapTarget[];
|
|
325
|
+
horizontal: SnapTarget[];
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Selection math for the design-canvas editor: marquee hit-tests, keyboard
|
|
330
|
+
* nudge deltas, and the duplicate offset constant. Pure functions — no DOM, no
|
|
331
|
+
* Konva, no React.
|
|
332
|
+
*
|
|
333
|
+
* Marquee inclusion: by default any element whose AABB intersects the marquee
|
|
334
|
+
* rect is included (the "touch" model). Pass `requireFullContainment: true` for
|
|
335
|
+
* the "surround" model where the marquee must fully contain the element. Locked
|
|
336
|
+
* and invisible elements are never selected regardless.
|
|
337
|
+
*/
|
|
338
|
+
|
|
339
|
+
interface MarqueeSelectOptions {
|
|
340
|
+
/** When true, the element's AABB must be fully inside the marquee; default is
|
|
341
|
+
* intersection (any overlap selects). */
|
|
342
|
+
requireFullContainment?: boolean;
|
|
343
|
+
}
|
|
344
|
+
/** Returns the ids of selectable elements on `page` whose AABB intersects (or
|
|
345
|
+
* is contained by) `rect`. Locked and invisible elements are excluded. */
|
|
346
|
+
declare function marqueeSelect(page: ScenePage, rect: Bounds, opts?: MarqueeSelectOptions): string[];
|
|
347
|
+
interface NudgeDelta {
|
|
348
|
+
dx: number;
|
|
349
|
+
dy: number;
|
|
350
|
+
}
|
|
351
|
+
/** Map an arrow key to a document-px delta. `shift` engages the 10× step.
|
|
352
|
+
* Throws on unknown keys so callers handle only real nudge keys. */
|
|
353
|
+
declare function nudgeDelta(key: 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown', shift: boolean): NudgeDelta;
|
|
354
|
+
/** Document-px offset applied to duplicated elements so the copy is visually
|
|
355
|
+
* separated from the original (matching common design-tool convention). */
|
|
356
|
+
declare const DUPLICATE_OFFSET: NudgeDelta;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Zoom + pan coordinate math for the design-canvas editor. Zoom is pixels-per-
|
|
360
|
+
* document-px (e.g. 2 = 200% magnification). The key invariant for wheel-zoom
|
|
361
|
+
* is that the document point under the cursor stays fixed in screen space:
|
|
362
|
+
*
|
|
363
|
+
* docPoint = (screenPoint - pan) / zoom
|
|
364
|
+
* newPan = screenPoint - docPoint * newZoom
|
|
365
|
+
*
|
|
366
|
+
* This module is pure math — no DOM, no Konva, no React.
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
interface ZoomPanConfig {
|
|
370
|
+
minZoom: number;
|
|
371
|
+
maxZoom: number;
|
|
372
|
+
}
|
|
373
|
+
declare function createZoomPanMath(config: ZoomPanConfig): ZoomPanMath;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Pure export math for the design-canvas surface — no Konva, no DOM, no React.
|
|
377
|
+
*
|
|
378
|
+
* All stage-interaction code (save/restore, node visibility, toDataURL) lives
|
|
379
|
+
* in export.ts and delegates the decision math here so it can be tested
|
|
380
|
+
* without a canvas environment.
|
|
381
|
+
*/
|
|
382
|
+
|
|
383
|
+
/** Returns true for any Konva node that must be hidden during export. Nodes
|
|
384
|
+
* whose names start with 'overlay:' are editor-only chrome (snap lines,
|
|
385
|
+
* selection indicators, rulers, bleed guides). The transformer node is
|
|
386
|
+
* identified by its canonical name 'Transformer'. */
|
|
387
|
+
declare function isExportHiddenNodeName(name: string): boolean;
|
|
388
|
+
interface ResolvedExportParams {
|
|
389
|
+
cropRect: ExportCropRect;
|
|
390
|
+
pixelRatio: number;
|
|
391
|
+
mimeType: 'image/png' | 'image/jpeg';
|
|
392
|
+
quality: number | undefined;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Resolve the full set of toDataURL params from a page, format, pixel ratio,
|
|
396
|
+
* bleed flag, and optional preset. When a preset is supplied it overrides
|
|
397
|
+
* `pixelRatio` and `includeBleed` — the explicit args are ignored for those
|
|
398
|
+
* two values so the preset is the single source of truth.
|
|
399
|
+
*
|
|
400
|
+
* `quality` is set to 0.92 for jpeg, undefined for png (Konva ignores it).
|
|
401
|
+
*/
|
|
402
|
+
declare function resolveExportParams(page: ScenePage, opts: {
|
|
403
|
+
format: 'png' | 'jpeg';
|
|
404
|
+
pixelRatio?: number;
|
|
405
|
+
includeBleed?: boolean;
|
|
406
|
+
preset?: ExportPreset;
|
|
407
|
+
}): ResolvedExportParams;
|
|
408
|
+
/**
|
|
409
|
+
* Walk image nodes to find the src of any that may have caused a SecurityError
|
|
410
|
+
* when the stage called toDataURL. Returns the first offending src found, or
|
|
411
|
+
* null if none can be identified.
|
|
412
|
+
*
|
|
413
|
+
* `imageSrcs` is a flat list of { name, src } records collected from all image
|
|
414
|
+
* nodes on the stage before calling toDataURL — export.ts builds it before
|
|
415
|
+
* attempting the export so that if the SecurityError fires we have the data.
|
|
416
|
+
*/
|
|
417
|
+
declare function identifyTaintedSrc(imageSrcs: ReadonlyArray<{
|
|
418
|
+
name: string;
|
|
419
|
+
src: string;
|
|
420
|
+
}>): string | null;
|
|
421
|
+
/**
|
|
422
|
+
* Heuristic: a src is potentially cross-origin when it is an absolute http(s)
|
|
423
|
+
* URL. Same-origin relative paths (/api/...) and data: blobs are safe.
|
|
424
|
+
* This is intentionally conservative — the taint check runs only after a
|
|
425
|
+
* SecurityError fires, so a false positive is "blamed" without being silently
|
|
426
|
+
* ignored. NOTE: same-origin https:// assets will also match; if a
|
|
427
|
+
* SecurityError fires, the cross-origin culprit may be a different image than
|
|
428
|
+
* the one this function flags first.
|
|
429
|
+
*/
|
|
430
|
+
declare function isCrossOriginSrc(src: string): boolean;
|
|
431
|
+
/** Minimal snapshot of stage view state that export.ts saves before mutating
|
|
432
|
+
* zoom/pan/visibility and restores afterward. */
|
|
433
|
+
interface ExportViewSnapshot {
|
|
434
|
+
/** Stage scale factor before export. */
|
|
435
|
+
scaleX: number;
|
|
436
|
+
scaleY: number;
|
|
437
|
+
/** Stage translation before export. */
|
|
438
|
+
x: number;
|
|
439
|
+
y: number;
|
|
440
|
+
/** node name → prior visibility, for the nodes that were hidden. */
|
|
441
|
+
hiddenNodeNames: string[];
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Build the toDataURL crop rect translated to stage output coordinates.
|
|
445
|
+
* Konva's `stage.toDataURL({ x, y, width, height })` takes STAGE OUTPUT
|
|
446
|
+
* coordinates, not document coordinates. When the stage has been scaled to fit
|
|
447
|
+
* (zoom × stageScale), the crop rect in document px must be multiplied by the
|
|
448
|
+
* combined scale to land on the right pixels.
|
|
449
|
+
*
|
|
450
|
+
* `stageScale` is the stage's current uniform scale (scaleX == scaleY);
|
|
451
|
+
* `pixelRatio` is separate and handed directly to toDataURL — Konva multiplies
|
|
452
|
+
* it internally when rendering to the backing canvas, so we must NOT include
|
|
453
|
+
* it in the stage-coordinate rect.
|
|
454
|
+
*/
|
|
455
|
+
declare function documentCropToStageCoords(cropRect: ExportCropRect, stageScale: number, stageX: number, stageY: number): {
|
|
456
|
+
x: number;
|
|
457
|
+
y: number;
|
|
458
|
+
width: number;
|
|
459
|
+
height: number;
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Client-side export for design-canvas pages: raster (PNG/JPEG via Konva
|
|
464
|
+
* stage.toDataURL) and JSON (document serialisation).
|
|
465
|
+
*
|
|
466
|
+
* The heavy lifting — crop math, pixel ratio resolution, CORS taint detection
|
|
467
|
+
* — lives in export-math.ts where it can be unit-tested without a canvas
|
|
468
|
+
* context. This file is the thin Konva-wiring layer: hide overlays, call
|
|
469
|
+
* toDataURL, restore, handle the SecurityError.
|
|
470
|
+
*
|
|
471
|
+
* Konva is an OPTIONAL peer. Import this file only from browser-context code
|
|
472
|
+
* that has konva wired in. The types are written against a minimal structural
|
|
473
|
+
* interface so the file compiles even when konva's types are absent.
|
|
474
|
+
*/
|
|
475
|
+
|
|
476
|
+
interface KonvaNodeLike {
|
|
477
|
+
name(): string;
|
|
478
|
+
visible(): boolean;
|
|
479
|
+
visible(v: boolean): void;
|
|
480
|
+
getAttr(key: string): unknown;
|
|
481
|
+
}
|
|
482
|
+
interface KonvaLayerLike {
|
|
483
|
+
getChildren(): KonvaNodeLike[];
|
|
484
|
+
}
|
|
485
|
+
interface KonvaStageLike {
|
|
486
|
+
scaleX(): number;
|
|
487
|
+
scaleY(): number;
|
|
488
|
+
x(): number;
|
|
489
|
+
y(): number;
|
|
490
|
+
getLayers(): KonvaLayerLike[];
|
|
491
|
+
toDataURL(params: {
|
|
492
|
+
mimeType: string;
|
|
493
|
+
quality?: number;
|
|
494
|
+
pixelRatio: number;
|
|
495
|
+
x: number;
|
|
496
|
+
y: number;
|
|
497
|
+
width: number;
|
|
498
|
+
height: number;
|
|
499
|
+
}): string;
|
|
500
|
+
}
|
|
501
|
+
interface ExportPageDataUrlOptions {
|
|
502
|
+
format: 'png' | 'jpeg';
|
|
503
|
+
pixelRatio?: number;
|
|
504
|
+
includeBleed?: boolean;
|
|
505
|
+
preset?: ExportPreset;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Render a single page to a data URL.
|
|
509
|
+
*
|
|
510
|
+
* The function temporarily hides every node whose name starts with 'overlay:'
|
|
511
|
+
* plus any Transformer node, computes the crop rect and pixel ratio from the
|
|
512
|
+
* page model and options, calls stage.toDataURL, then restores all prior view
|
|
513
|
+
* state exactly — zoom, pan, and node visibility.
|
|
514
|
+
*
|
|
515
|
+
* Rejects with a descriptive error when a CORS-tainted image source causes
|
|
516
|
+
* the SecurityError, naming the offending src so the caller can surface it.
|
|
517
|
+
*
|
|
518
|
+
* The `stage` argument must be the Konva stage with the page content already
|
|
519
|
+
* rendered. The caller is responsible for ensuring all async image loads have
|
|
520
|
+
* settled before calling this function.
|
|
521
|
+
*/
|
|
522
|
+
declare function exportPageDataUrl(stage: KonvaStageLike, page: ScenePage, opts: ExportPageDataUrlOptions): Promise<string>;
|
|
523
|
+
/**
|
|
524
|
+
* Serialize a scene document to pretty-printed JSON with schemaVersion
|
|
525
|
+
* asserted. Throws when the document's schemaVersion does not match
|
|
526
|
+
* SCENE_SCHEMA_VERSION — the caller must not smuggle stale documents through.
|
|
527
|
+
*/
|
|
528
|
+
declare function exportDocumentJson(document: SceneDocument): string;
|
|
529
|
+
/**
|
|
530
|
+
* Trigger a browser download for a data URL. Safe to import in SSR — the
|
|
531
|
+
* function is a no-op when `document` is not defined (e.g. server-side render
|
|
532
|
+
* or test environment without a DOM). The integrator must not rely on the
|
|
533
|
+
* download executing in those contexts.
|
|
534
|
+
*/
|
|
535
|
+
declare function downloadDataUrl(dataUrl: string, filename: string): void;
|
|
536
|
+
|
|
537
|
+
/** Callers inject a workspace renderer so this chrome stays Konva-free. The
|
|
538
|
+
* workspace occupies the scrollable area between the rulers and the bottom bar. */
|
|
539
|
+
interface DesignCanvasFullProps extends DesignCanvasProps {
|
|
540
|
+
/**
|
|
541
|
+
* Render the Konva canvas workspace into the slot this shell provides.
|
|
542
|
+
* The shell passes viewport dimensions, view-state, and the shared command
|
|
543
|
+
* stack so `WorkspaceView` can commit gestures through the same stack the
|
|
544
|
+
* chrome uses for undo/redo and layers-panel selection.
|
|
545
|
+
*
|
|
546
|
+
* `onFitRef` is a ref the workspace fills with a fit-page callback; the
|
|
547
|
+
* shell calls it when the user presses F or clicks the Fit button.
|
|
548
|
+
*/
|
|
549
|
+
renderWorkspace(ctx: {
|
|
550
|
+
document: SceneDocument;
|
|
551
|
+
activePageId: string;
|
|
552
|
+
selectedElementIds: string[];
|
|
553
|
+
zoom: number;
|
|
554
|
+
panX: number;
|
|
555
|
+
panY: number;
|
|
556
|
+
gridEnabled: boolean;
|
|
557
|
+
gridSize: number;
|
|
558
|
+
snapEnabled: boolean;
|
|
559
|
+
showBleed: boolean;
|
|
560
|
+
canWrite: boolean;
|
|
561
|
+
/** The chrome's command stack. Pass to WorkspaceView so gestures, undo,
|
|
562
|
+
* and layers-panel selection share a single state machine. */
|
|
563
|
+
stack: ReturnType<typeof createSceneCommandStack>;
|
|
564
|
+
activePage: SceneDocument['pages'][number] | undefined;
|
|
565
|
+
onFitRef: React.MutableRefObject<(() => void) | null>;
|
|
566
|
+
onZoomChange(zoom: number): void;
|
|
567
|
+
onPanChange(panX: number, panY: number): void;
|
|
568
|
+
onSelectElements(ids: string[], additive: boolean): void;
|
|
569
|
+
}): React.ReactNode;
|
|
570
|
+
/**
|
|
571
|
+
* Generates page thumbnails for the PagesStrip. Injected by the integrator
|
|
572
|
+
* (who has Konva access) so the chrome doesn't import Konva directly.
|
|
573
|
+
*/
|
|
574
|
+
renderThumbnail(page: SceneDocument['pages'][number]): Promise<string | null>;
|
|
575
|
+
}
|
|
576
|
+
declare function DesignCanvas({ document: initialDocument, rev: initialRev, canWrite, onApplyOperations, onSelectionChange, renderAgentPanel, renderSidePanel, onExport, className, renderWorkspace, renderThumbnail, }: DesignCanvasFullProps): react.JSX.Element;
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Mount this component to get the full editor: toolbar, rulers, layers panel,
|
|
580
|
+
* pages strip, zoom controls, and the Konva canvas — all sharing one command
|
|
581
|
+
* stack so undo/redo and selection are coherent across every surface.
|
|
582
|
+
*/
|
|
583
|
+
declare function DesignCanvasEditor(props: DesignCanvasProps): react.JSX.Element;
|
|
584
|
+
|
|
585
|
+
interface WorkspaceViewProps {
|
|
586
|
+
/** The command stack this view commits gestures through. Must be the same
|
|
587
|
+
* instance the chrome (DesignCanvasEditor) owns so undo/redo and layers-
|
|
588
|
+
* panel selection are coherent. */
|
|
589
|
+
stack: ReturnType<typeof createSceneCommandStack>;
|
|
590
|
+
/** Active page resolved before render — WorkspaceView has no conditional
|
|
591
|
+
* hook guards; the caller ensures this is never null. */
|
|
592
|
+
activePage: ScenePage;
|
|
593
|
+
canWrite: boolean;
|
|
594
|
+
onApplyOperations: DesignCanvasProps['onApplyOperations'];
|
|
595
|
+
onSelectionChange?: DesignCanvasProps['onSelectionChange'];
|
|
596
|
+
renderAgentPanel?: DesignCanvasProps['renderAgentPanel'];
|
|
597
|
+
renderSidePanel?: DesignCanvasProps['renderSidePanel'];
|
|
598
|
+
className?: string;
|
|
599
|
+
/** Ref the chrome fills with a fit-page callback. The chrome calls it on F /
|
|
600
|
+
* Fit button; when injected via DesignCanvasEditor the ref is shared. */
|
|
601
|
+
onFitRef?: React.MutableRefObject<(() => void) | null>;
|
|
602
|
+
}
|
|
603
|
+
declare function WorkspaceView({ canWrite, onApplyOperations, onSelectionChange, renderAgentPanel, renderSidePanel, className, stack, activePage, onFitRef, }: WorkspaceViewProps): react.JSX.Element;
|
|
604
|
+
/**
|
|
605
|
+
* Self-contained Konva workspace that creates its own command stack. Mount
|
|
606
|
+
* this when you want the canvas without the toolbar/rulers/pages-strip chrome.
|
|
607
|
+
*
|
|
608
|
+
* Products that want the full editor (chrome + workspace sharing one stack)
|
|
609
|
+
* should mount `DesignCanvasEditor` instead.
|
|
610
|
+
*/
|
|
611
|
+
declare function Workspace(props: DesignCanvasProps): react.JSX.Element | null;
|
|
612
|
+
|
|
613
|
+
interface SelectionLayerProps {
|
|
614
|
+
/** Konva stage reference to look up selected nodes by name. */
|
|
615
|
+
stageRef: React.RefObject<Konva.Stage | null>;
|
|
616
|
+
/** Selected element ids from editor state. */
|
|
617
|
+
selectedIds: string[];
|
|
618
|
+
/** The model elements corresponding to selectedIds (pre-gesture snapshot). */
|
|
619
|
+
selectedElements: SceneElement[];
|
|
620
|
+
/** Whether the canvas is writable. False → transformer renders but is not interactive. */
|
|
621
|
+
canWrite: boolean;
|
|
622
|
+
/** Emitted when a transform gesture completes with final attrs per element. */
|
|
623
|
+
onTransformEnd(entries: MultiSetAttrsEntry[]): void;
|
|
624
|
+
/** Active page id — every entry in onTransformEnd carries this. */
|
|
625
|
+
pageId: string;
|
|
626
|
+
}
|
|
627
|
+
declare function SelectionLayer({ stageRef, selectedIds, selectedElements, canWrite, onTransformEnd, pageId, }: SelectionLayerProps): react.JSX.Element;
|
|
628
|
+
|
|
629
|
+
interface PagesStripProps {
|
|
630
|
+
pages: ScenePage[];
|
|
631
|
+
activePageId: string;
|
|
632
|
+
canWrite: boolean;
|
|
633
|
+
/**
|
|
634
|
+
* The host provides this to generate thumbnail data-URLs. The strip calls it
|
|
635
|
+
* on mount and debounces re-calls on document changes. Returns null when the
|
|
636
|
+
* thumbnail is not yet available (the strip renders a placeholder instead).
|
|
637
|
+
*/
|
|
638
|
+
renderThumbnail(page: ScenePage): Promise<string | null>;
|
|
639
|
+
onSelectPage(pageId: string): void;
|
|
640
|
+
onAddPage(): void;
|
|
641
|
+
onDuplicatePage(pageId: string): void;
|
|
642
|
+
onDeletePage(pageId: string): void;
|
|
643
|
+
onReorderPage(pageId: string, toIndex: number): void;
|
|
644
|
+
}
|
|
645
|
+
declare function PagesStrip({ pages, activePageId, canWrite, renderThumbnail, onSelectPage, onAddPage, onDuplicatePage, onDeletePage, onReorderPage, }: PagesStripProps): react.JSX.Element;
|
|
646
|
+
|
|
647
|
+
interface LayersPanelProps {
|
|
648
|
+
page: ScenePage;
|
|
649
|
+
selectedElementIds: string[];
|
|
650
|
+
canWrite: boolean;
|
|
651
|
+
/** Emit a set_attrs command for the given element. */
|
|
652
|
+
onSetAttrs(elementId: string, attrs: Partial<Pick<SceneElement, 'name' | 'visible' | 'locked'>>): void;
|
|
653
|
+
/** Emit a reorder_element command. */
|
|
654
|
+
onReorder(elementId: string, toIndex: number): void;
|
|
655
|
+
onSelect(elementId: string, additive: boolean): void;
|
|
656
|
+
}
|
|
657
|
+
declare function LayersPanel({ page, selectedElementIds, canWrite, onSetAttrs, onReorder, onSelect }: LayersPanelProps): react.JSX.Element;
|
|
658
|
+
|
|
659
|
+
interface ToolbarProps {
|
|
660
|
+
page: ScenePage;
|
|
661
|
+
selectedElements: SceneElement[];
|
|
662
|
+
canWrite: boolean;
|
|
663
|
+
canUndo: boolean;
|
|
664
|
+
canRedo: boolean;
|
|
665
|
+
gridEnabled: boolean;
|
|
666
|
+
snapEnabled: boolean;
|
|
667
|
+
showRulers: boolean;
|
|
668
|
+
showBleed: boolean;
|
|
669
|
+
onUndo(): void;
|
|
670
|
+
onRedo(): void;
|
|
671
|
+
onToggleGrid(): void;
|
|
672
|
+
onToggleSnap(): void;
|
|
673
|
+
onToggleRulers(): void;
|
|
674
|
+
onToggleBleed(): void;
|
|
675
|
+
/** Emit attrs patch for each selected element. */
|
|
676
|
+
onSetAttrs(elementId: string, attrs: SceneAttrsPatch): void;
|
|
677
|
+
onSetPageProps(props: {
|
|
678
|
+
name?: string;
|
|
679
|
+
width?: number;
|
|
680
|
+
height?: number;
|
|
681
|
+
background?: string;
|
|
682
|
+
bleed?: PageBleed | null;
|
|
683
|
+
}): void;
|
|
684
|
+
onSetPageGuides(guides: {
|
|
685
|
+
vertical: number[];
|
|
686
|
+
horizontal: number[];
|
|
687
|
+
}): void;
|
|
688
|
+
onReorder(elementId: string, toIndex: number, ownerLength: number, direction: 'front' | 'back' | 'forward' | 'backward'): void;
|
|
689
|
+
onGroup(elementIds: string[]): void;
|
|
690
|
+
onUngroup(groupId: string): void;
|
|
691
|
+
onDelete(elementIds: string[]): void;
|
|
692
|
+
onBindSlot(elementId: string, slot: string | null): void;
|
|
693
|
+
}
|
|
694
|
+
declare function Toolbar({ page, selectedElements, canWrite, canUndo, canRedo, gridEnabled, snapEnabled, showRulers, showBleed, onUndo, onRedo, onToggleGrid, onToggleSnap, onToggleRulers, onToggleBleed, onSetAttrs, onSetPageProps, onSetPageGuides, onReorder, onGroup, onUngroup, onDelete, onBindSlot, }: ToolbarProps): react.JSX.Element;
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Canvas zoom controls: fit-to-page, 100%, zoom-out/in buttons, and a percent
|
|
698
|
+
* readout. Stateless — zoom lives in the editor's view state, updated through
|
|
699
|
+
* onZoom.
|
|
700
|
+
*/
|
|
701
|
+
interface ZoomControlsProps {
|
|
702
|
+
zoom: number;
|
|
703
|
+
onZoom(zoom: number): void;
|
|
704
|
+
onFit(): void;
|
|
705
|
+
}
|
|
706
|
+
declare function ZoomControls({ zoom, onZoom, onFit }: ZoomControlsProps): react.JSX.Element;
|
|
707
|
+
|
|
708
|
+
interface ElementNodeProps {
|
|
709
|
+
element: SceneElement;
|
|
710
|
+
isSelected: boolean;
|
|
711
|
+
zoom: number;
|
|
712
|
+
onClick?(elementId: string): void;
|
|
713
|
+
onDragStart?(elementId: string): void;
|
|
714
|
+
onDragMove?(elementId: string, dx: number, dy: number): void;
|
|
715
|
+
onDragEnd?(elementId: string, finalX: number, finalY: number): void;
|
|
716
|
+
onDoubleClick?(elementId: string): void;
|
|
717
|
+
}
|
|
718
|
+
declare function ElementNode(props: ElementNodeProps): react.JSX.Element | null;
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Grid overlay rendered beneath page content. Uses Konva.Layer with Konva.Line
|
|
722
|
+
* nodes at `gridSize` document-px spacing, scaled by zoom. Grid lines are
|
|
723
|
+
* skipped entirely when they would be closer than 4 screen pixels apart —
|
|
724
|
+
* below that density the grid becomes visual noise rather than guidance.
|
|
725
|
+
*
|
|
726
|
+
* All nodes carry the name prefix 'overlay:' so export logic can exclude this
|
|
727
|
+
* layer from rasterization.
|
|
728
|
+
*/
|
|
729
|
+
interface GridLayerProps {
|
|
730
|
+
/** Page width in document px. */
|
|
731
|
+
pageWidth: number;
|
|
732
|
+
/** Page height in document px. */
|
|
733
|
+
pageHeight: number;
|
|
734
|
+
/** Document px between grid lines. */
|
|
735
|
+
gridSize: number;
|
|
736
|
+
/** Screen px per document px. */
|
|
737
|
+
zoom: number;
|
|
738
|
+
/** Grid line color. */
|
|
739
|
+
color?: string;
|
|
740
|
+
/** Grid line opacity (0–1). */
|
|
741
|
+
opacity?: number;
|
|
742
|
+
}
|
|
743
|
+
declare function GridLayer({ pageWidth, pageHeight, gridSize, zoom, color, opacity, }: GridLayerProps): react.JSX.Element | null;
|
|
744
|
+
|
|
745
|
+
interface RulersProps {
|
|
746
|
+
/** Page width in document px. */
|
|
747
|
+
pageWidth: number;
|
|
748
|
+
/** Page height in document px. */
|
|
749
|
+
pageHeight: number;
|
|
750
|
+
zoom: number;
|
|
751
|
+
/** How many doc-px of the canvas are scrolled off-screen left/top. */
|
|
752
|
+
scrollLeft: number;
|
|
753
|
+
scrollTop: number;
|
|
754
|
+
showRulers: boolean;
|
|
755
|
+
guides: PageGuides;
|
|
756
|
+
/** Emitted when the user drops a guide or deletes one back into the ruler. */
|
|
757
|
+
onGuidesChange(guides: PageGuides): void;
|
|
758
|
+
}
|
|
759
|
+
declare function Rulers({ pageWidth, pageHeight, zoom, scrollLeft, scrollTop, showRulers, guides, onGuidesChange }: RulersProps): react.JSX.Element | null;
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Pure layer-tree helpers: flatten a page's element array into an ordered list
|
|
763
|
+
* for the LayersPanel, preserving group nesting. Extracted so render order,
|
|
764
|
+
* depth, and selection math can be unit-tested independently of React.
|
|
765
|
+
*/
|
|
766
|
+
|
|
767
|
+
interface LayerRow {
|
|
768
|
+
element: SceneElement;
|
|
769
|
+
depth: number;
|
|
770
|
+
/** True when this row is a group whose children follow it in the list. */
|
|
771
|
+
isGroup: boolean;
|
|
772
|
+
/** Index within its owner (page.elements or group.children). */
|
|
773
|
+
ownerIndex: number;
|
|
774
|
+
/** Length of the owner array — needed for z-order bound math. */
|
|
775
|
+
ownerLength: number;
|
|
776
|
+
/** Id of the containing group, or null for page-root elements. */
|
|
777
|
+
parentGroupId: string | null;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Flatten `page.elements` into display order for the layers panel.
|
|
781
|
+
*
|
|
782
|
+
* Z-order is bottom→top in the element array; the layers panel shows
|
|
783
|
+
* top→bottom (highest z-index first). A group row precedes its children
|
|
784
|
+
* (the group is "above" its children in the panel, reflecting that the group
|
|
785
|
+
* node itself paints last in Konva when empty; children paint inside it).
|
|
786
|
+
*
|
|
787
|
+
* Depth increases by 1 for each group level, starting at 0 for page-root
|
|
788
|
+
* elements. Groups are expanded; collapsed state is a view concern the caller
|
|
789
|
+
* tracks separately.
|
|
790
|
+
*/
|
|
791
|
+
declare function flattenLayerTree(page: ScenePage): LayerRow[];
|
|
792
|
+
/** The maximum number of rows the layers panel renders before truncating. */
|
|
793
|
+
declare const LAYERS_PANEL_ROW_LIMIT = 500;
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Pure geometry helpers extracted from component drag/transform logic so
|
|
797
|
+
* every interactive math path is unit-testable without Konva or a DOM.
|
|
798
|
+
*
|
|
799
|
+
* Invariants:
|
|
800
|
+
* - All inputs and outputs are in document-coordinate pixels unless noted.
|
|
801
|
+
* - Rotation angles are degrees, clockwise, matching the Konva + model convention.
|
|
802
|
+
* - "Baking" scale into width/height resets Konva's scaleX/scaleY to 1; that
|
|
803
|
+
* is required so the model's width/height always reflects true size, never a
|
|
804
|
+
* scaled-but-uncollapsed state.
|
|
805
|
+
*/
|
|
806
|
+
interface TransformerNode {
|
|
807
|
+
x: number;
|
|
808
|
+
y: number;
|
|
809
|
+
width: number;
|
|
810
|
+
height: number;
|
|
811
|
+
/** Konva's scale after the transformer gesture; 1 when already baked. */
|
|
812
|
+
scaleX: number;
|
|
813
|
+
scaleY: number;
|
|
814
|
+
rotation: number;
|
|
815
|
+
}
|
|
816
|
+
interface BakedNodeAttrs {
|
|
817
|
+
x: number;
|
|
818
|
+
y: number;
|
|
819
|
+
/** True pixel size after scale is collapsed into dimensions. */
|
|
820
|
+
width: number;
|
|
821
|
+
height: number;
|
|
822
|
+
rotation: number;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Collapse Konva scaleX/scaleY into width/height so the model always stores
|
|
826
|
+
* true pixel dimensions. The transformer mutates scale on drag; we bake it
|
|
827
|
+
* once at dragend/transformend and emit width/height — scale resets to 1
|
|
828
|
+
* implicitly (the emitted attrs do not include scale, so the next render
|
|
829
|
+
* starts from scaleX=1).
|
|
830
|
+
*
|
|
831
|
+
* Konva rotates about the top-left origin and also shifts x/y to compensate
|
|
832
|
+
* for scale — the transformer gives us the post-rotation, post-scale x/y
|
|
833
|
+
* directly, so no further rotation math is needed here.
|
|
834
|
+
*/
|
|
835
|
+
declare function bakeRectTransform(node: TransformerNode): BakedNodeAttrs;
|
|
836
|
+
/**
|
|
837
|
+
* Lines store points as a flat [x0, y0, x1, y1, ...] array relative to the
|
|
838
|
+
* line element's (x, y). When the transformer scales the group containing the
|
|
839
|
+
* line, we bake scaleX into every x-component of points and scaleY into every
|
|
840
|
+
* y-component, then reset scale to 1. The element x/y is the Konva group
|
|
841
|
+
* origin and is taken directly from the transformer output.
|
|
842
|
+
*/
|
|
843
|
+
declare function bakeLineTransform(node: TransformerNode & {
|
|
844
|
+
points: number[];
|
|
845
|
+
}): BakedNodeAttrs & {
|
|
846
|
+
points: number[];
|
|
847
|
+
};
|
|
848
|
+
/**
|
|
849
|
+
* Text nodes have a fixed wrap width; scaling that width is what the
|
|
850
|
+
* transformer controls. Height is content-derived and is NOT baked here —
|
|
851
|
+
* it re-derives from text content at render time via estimateTextHeight.
|
|
852
|
+
* Only scaleX is baked into width; scaleY into fontSize so text scales
|
|
853
|
+
* proportionally when the user resizes with keepRatio.
|
|
854
|
+
*/
|
|
855
|
+
declare function bakeTextTransform(node: TransformerNode & {
|
|
856
|
+
fontSize: number;
|
|
857
|
+
}): BakedNodeAttrs & {
|
|
858
|
+
fontSize: number;
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Pure ruler tick-step and label math for the canvas rulers. Nothing here
|
|
863
|
+
* touches React or the DOM — all interaction geometry is extracted so it can
|
|
864
|
+
* be unit-tested without a browser.
|
|
865
|
+
*
|
|
866
|
+
* Canvas rulers show document-coordinate values (CSS px). Tick density adapts
|
|
867
|
+
* to the current zoom so major ticks never sit closer than `minMajorSpacingPx`
|
|
868
|
+
* screen pixels apart. The step table covers typical design zoom ranges; beyond
|
|
869
|
+
* the table the step grows by doubling the last candidate.
|
|
870
|
+
*/
|
|
871
|
+
interface TickStep {
|
|
872
|
+
/** Document-coordinate step between major ticks. */
|
|
873
|
+
major: number;
|
|
874
|
+
/** Document-coordinate step between minor ticks (major / 5). */
|
|
875
|
+
minor: number;
|
|
876
|
+
/** True when minor ticks should be drawn (they sit ≥ minMinorSpacingPx apart). */
|
|
877
|
+
drawMinor: boolean;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Select the major tick step that keeps major ticks ≥ minMajorSpacingPx apart
|
|
881
|
+
* at the given zoom. Minor ticks are rendered when they'd clear minMinorSpacingPx.
|
|
882
|
+
*
|
|
883
|
+
* Both spacing thresholds are SCREEN pixels — the caller provides `zoom` (screen
|
|
884
|
+
* px per document px) so the result is zoom-independent.
|
|
885
|
+
*/
|
|
886
|
+
declare function selectTickStep(input: {
|
|
887
|
+
zoom: number;
|
|
888
|
+
minMajorSpacingPx?: number;
|
|
889
|
+
minMinorSpacingPx?: number;
|
|
890
|
+
}): TickStep;
|
|
891
|
+
interface RulerTick {
|
|
892
|
+
/** Position in document coordinates. */
|
|
893
|
+
position: number;
|
|
894
|
+
/** Label text, or null for a minor tick. */
|
|
895
|
+
label: string | null;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Generate all ticks visible in a ruler of `documentLength` document-px,
|
|
899
|
+
* given the current tick step. The caller clips to the viewport; this produces
|
|
900
|
+
* all ticks for the full document extent so the ruler can be rendered
|
|
901
|
+
* declaratively without a separate clipping pass.
|
|
902
|
+
*/
|
|
903
|
+
declare function buildRulerTicks(input: {
|
|
904
|
+
documentLength: number;
|
|
905
|
+
step: TickStep;
|
|
906
|
+
}): RulerTick[];
|
|
907
|
+
/** Format a document-px position as a compact label: integers stay whole,
|
|
908
|
+
* decimals are rounded to 1 place. Values ≥ 1000 are compacted to "1k" etc. */
|
|
909
|
+
declare function formatRulerLabel(value: number): string;
|
|
910
|
+
|
|
911
|
+
/** Batteries-included editor: chrome + workspace on one shared stack. */
|
|
912
|
+
declare const DesignCanvasLazy: react.LazyExoticComponent<typeof DesignCanvasEditor>;
|
|
913
|
+
/** Raw chrome only — use when supplying a custom renderWorkspace/renderThumbnail. */
|
|
914
|
+
declare const DesignCanvasChromeLazy: react.LazyExoticComponent<typeof DesignCanvas>;
|
|
915
|
+
|
|
916
|
+
export { type AddElementInput, type AddPageInput, type ApplySceneResult, type BakedNodeAttrs, type BindSlotInput, DUPLICATE_OFFSET, type DeleteElementInput, type DeletePageInput, DesignCanvas, DesignCanvasChromeLazy, DesignCanvasEditor, type DesignCanvasFullProps, DesignCanvasLazy, type DesignCanvasProps, type DuplicatePageInput, type EditorSceneState, ElementNode, type ElementNodeProps, ExportCropRect, type ExportPageDataUrlOptions, ExportPreset, type ExportViewSnapshot, GridLayer, type GridLayerProps, type GroupElementsInput, LAYERS_PANEL_ROW_LIMIT, type LayerRow, LayersPanel, type LayersPanelProps, type MarqueeSelectOptions, type MultiSetAttrsEntry, type NudgeDelta, PagesStrip, type PagesStripProps, type ReorderElementInput, type ReorderPageInput, type ResolvedExportParams, type RulerTick, Rulers, type RulersProps, SCENE_COMMAND_HISTORY_LIMIT, type SceneCommand, type SceneCommandStack, SelectionLayer, type SelectionLayerProps, type SetAttrsInput, type SetDocumentTitleInput, type SetPageGuidesInput, type SetPagePropsInput, type SnapEngine, type SnapResult, type SnapTarget, type SnapTargetKind, type SnapTargets, type TickStep, Toolbar, type ToolbarProps, type TransformerNode, type UngroupElementInput, Workspace, WorkspaceView, type WorkspaceViewProps, ZoomControls, type ZoomControlsProps, type ZoomPanConfig, type ZoomPanMath, addElementCommand, addPageCommand, bakeLineTransform, bakeRectTransform, bakeTextTransform, bindSlotCommand, buildRulerTicks, collectGridTargets, createSceneCommandStack, createSnapEngine, createZoomPanMath, deleteElementCommand, deletePageCommand, documentCropToStageCoords, downloadDataUrl, duplicatePageCommand, exportDocumentJson, exportPageDataUrl, flattenLayerTree, formatRulerLabel, groupElementsCommand, identifyTaintedSrc, isCrossOriginSrc, isExportHiddenNodeName, marqueeSelect, multiSetAttrsCommand, nudgeDelta, reorderElementCommand, reorderPageCommand, resolveExportParams, selectTickStep, setAttrsCommand, setDocumentTitleCommand, setPageGuidesCommand, setPagePropsCommand, ungroupElementCommand };
|