@hankchen/canvas-editor 0.1.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.
@@ -0,0 +1,2678 @@
1
+ import { LayerBox, Canvas, TextLayoutPolicies, Layer, LayerType, RenderBackend, Padding, HorizontalAlign, VerticalAlign, TableLayer, Anchor, TableRowLayer, TableCellLayer, TableRowTemplateLayer } from '@hankchen/canvas';
2
+ export { ANCHORS, Anchor, Border, BorderSide, Canvas, HORIZONTAL_ALIGNS, HorizontalAlign, LAYER_TYPES, Layer, LayerBox, LayerType, Padding, TableCellLayer, TableLayer, TableRowLayer, TextLayer, VERTICAL_ALIGNS, VerticalAlign } from '@hankchen/canvas';
3
+ import { Draft, Patch } from 'immer';
4
+ export { canvasFontCssFamily } from '@hankchen/canvas-browser-renderer';
5
+
6
+ /**
7
+ * 相机纯函数(工单 05)。统一坐标定义(impl 研究文档 §2.6):
8
+ *
9
+ * screen = (scene - cam) * zoom
10
+ *
11
+ - 场景坐标全程保持画布整数像素语义(与后端整型口径一致),换算只发生在
12
+ * 输入端(指针 → 场景)与呈现端(ctx transform);本模块只做数值换算,
13
+ * 不触及任何 DOM/渲染对象。
14
+ * - 相机平移可对齐物理像素(snapViewportToPhysicalPixels),避免缩放/平移
15
+ * 时的亚像素抖动;zoom 数值本身不取整,缩放百分比显示用原值。
16
+ */
17
+ /** 视口:相机(场景坐标下的视口左上角)+ 缩放倍率 */
18
+ interface Viewport {
19
+ x: number;
20
+ y: number;
21
+ zoom: number;
22
+ }
23
+ interface Point {
24
+ x: number;
25
+ y: number;
26
+ }
27
+ interface Size {
28
+ width: number;
29
+ height: number;
30
+ }
31
+ /** 场景矩形(适应选区的目标盒等) */
32
+ interface Rect {
33
+ x: number;
34
+ y: number;
35
+ width: number;
36
+ height: number;
37
+ }
38
+ /** 缩放范围(可配置;缺省 5%–800%,工单 05 拍板) */
39
+ interface ZoomBounds {
40
+ min: number;
41
+ max: number;
42
+ }
43
+ declare const DEFAULT_ZOOM_BOUNDS: ZoomBounds;
44
+ /** 场景 → 屏幕(呈现端换算) */
45
+ declare function sceneToScreen(viewport: Viewport, sceneX: number, sceneY: number): Point;
46
+ /** 屏幕 → 场景(输入端换算:命中测试等) */
47
+ declare function screenToScene(viewport: Viewport, screenX: number, screenY: number): Point;
48
+ /** 缩放倍率钳位到范围 */
49
+ declare function clampZoom(zoom: number, bounds: ZoomBounds): number;
50
+ /**
51
+ * 平移:屏幕位移按 zoom 折算回场景位移(cam -= delta / zoom),
52
+ * 保证平移速度与缩放倍率无关。
53
+ */
54
+ declare function panBy(viewport: Viewport, deltaScreenX: number, deltaScreenY: number): Viewport;
55
+ /**
56
+ * 缩放至指针(zoom-to-pointer):保持指针下的场景点不动——
57
+ * scene₀ = screen₀ / zoom + cam ⇒ cam' = scene₀ - screen₀ / zoom'
58
+ * 目标倍率先钳位到范围,锚点关系按钳位后的倍率成立。
59
+ */
60
+ declare function zoomAtPoint(viewport: Viewport, screenX: number, screenY: number, nextZoom: number, bounds: ZoomBounds): Viewport;
61
+ /**
62
+ * 适应画布:整页可见(宽高取更紧一侧)、画布中心对齐视口中心。
63
+ * margin 为可视区域内缩(屏幕像素)。缩放范围只钳**上界**(防小画布被荒谬放大),
64
+ * 不钳下界——「适应」的语义是整页可见,超大画布允许低于交互缩放下界。
65
+ * 零尺寸输入防御回落恒等视口。
66
+ */
67
+ declare function fitViewport(canvas: Size, surface: Size, bounds: ZoomBounds, margin: number): Viewport;
68
+ /** 适应任意场景矩形(工单 06 适应选区):矩形整块可见、矩形中心对齐视口中心 */
69
+ declare function fitRect(rect: Rect, surface: Size, bounds: ZoomBounds, margin: number): Viewport;
70
+ /**
71
+ * 保 zoom 平移入视的小边距(屏幕 px,canvas-web-find-replace 工单 02 定死):
72
+ * 出视入视后盒缘与视口缘的留白。
73
+ */
74
+ declare const PAN_TO_BOX_MARGIN_PX = 12;
75
+ /**
76
+ * 保 zoom 平移入视(canvas-web-find-replace 工单 02,spec 决策 6):查找导航的
77
+ * 视口跟随语义,与 fitRect 的变焦语义分立——缩放倍率恒不变,只在目标盒出视时
78
+ * 最小平移使整盒入视。
79
+ *
80
+ * - 盒完全在视口内(贴边也算)→ 原视口原样返回(引用相等,调用方可据此短路通知);
81
+ * - 出视才平移,两轴独立求位,入视带含 margin 留白(屏幕 px):
82
+ * - 盒装得下「视口 − 2×margin」:cam 钳进可行区间 = 各轴最小平移(非居中);
83
+ * - 盒装得下视口但 margin 两侧放不下:margin 让位,钳进裸视口可行区间 = 贴缘
84
+ * 最小平移(整盒可见优先于边距,spec「最小平移」字面口径);
85
+ * - 盒大于视口:对齐盒左上(左上角落在 margin 处,右/下侧自然截断——工单
86
+ * 定死取「阅读起点可见」而非居中)。
87
+ * 零尺寸表面/非正 zoom 防御回落原视口。
88
+ */
89
+ declare function panToBox(viewport: Viewport, box: Rect, surface: Size, margin: number): Viewport;
90
+ /**
91
+ * 相机平移对齐物理像素:cam = round(cam * zoom * dpr) / (zoom * dpr)。
92
+ * 只动 x/y,zoom 保持原值(百分比显示用原值)。
93
+ */
94
+ declare function snapViewportToPhysicalPixels(viewport: Viewport, dpr: number): Viewport;
95
+ /**
96
+ * 滚轮/触控板捏合的下一档缩放(excalidraw App.wheel.ts 同式,加性曲线):
97
+ * - 单档步长 ZOOM_STEP,delta 钳位到 ±ZOOM_STEP*100(防大 delta 过冲);
98
+ * - >100% 区按 log10(zoom) 增幅(越放大步长越大);
99
+ * - 增幅项按 min(1, |Δ|/20) 衰减,防触控板小位移微抖。
100
+ * deltaY > 0(向下滚)= 缩小。结果钳位到范围。
101
+ */
102
+ declare const WHEEL_ZOOM_STEP = 0.1;
103
+ declare function nextZoomByWheel(zoom: number, deltaY: number, bounds: ZoomBounds): number;
104
+
105
+ /**
106
+ * 吸附数学 + 参考线轴几何(ruler-guides-snap 工单 01,ADR 0012;工单 07 起缩放
107
+ * 手势共用同一条求位缝——拖动九点与缩放移动缘是 resolveSnapPoints 的两个特例)。
108
+ *
109
+ * 吸附(snap)语义(术语见根 CONTEXT.md):拖动图层时其盒缘与中心(九点)自动
110
+ * 对齐到吸附源的编辑器行为——源为其他可见根层盒的缘与中心、画布水平/垂直中轴、
111
+ * 参考线轴;画布四边不设轴(精确贴边归第一批对齐画布按钮,ADR 0011)。命中阈值
112
+ * 按屏幕像素常量计(缩放无关,换算场景值后使用),多轴命中取最近,一次拖动至多
113
+ * 吸一垂直轴 + 一水平轴。吸附是拖动过程的连续行为,与对齐画布的一次性动作分立。
114
+ *
115
+ * 参考线(guide)是使用者从标尺拖出的会话级对位轴:住内核 ui 分支——不进历史、
116
+ * 不写 graph、保存零改动;本模块只定义其形态与「供轴」,增删动作在会话门面。
117
+ * 全模块纯函数、无 DOM。
118
+ */
119
+
120
+ /** 轴向:vertical = 竖直线(修正 x)、horizontal = 水平线(修正 y),场景坐标 */
121
+ type GuideOrientation = 'vertical' | 'horizontal';
122
+ /** 参考线:会话级对位轴(id 会话内自增,position 为场景坐标) */
123
+ interface Guide {
124
+ readonly id: number;
125
+ readonly orientation: GuideOrientation;
126
+ readonly position: number;
127
+ }
128
+ /** 吸附轴来源:其他可见根层盒 / 画布中轴 / 参考线 */
129
+ type SnapAxisSource = 'layer' | 'canvas-center' | 'guide';
130
+ /** 吸附轴:orientation 为线的方向,position 为场景坐标(源与命中同形) */
131
+ interface SnapAxis {
132
+ readonly orientation: GuideOrientation;
133
+ readonly position: number;
134
+ readonly source: SnapAxisSource;
135
+ }
136
+ /** 吸附求位的盒几何入参(LayerBox 的位置尺寸切片) */
137
+ type SnapBoxGeometry = Pick<LayerBox, 'x' | 'y' | 'width' | 'height'>;
138
+ /** 吸附求位结果:两轴修正量 + 命中轴(至多一垂直 + 一水平,吸附线呈现消费) */
139
+ interface SnapResolution {
140
+ readonly dx: number;
141
+ readonly dy: number;
142
+ readonly axes: readonly SnapAxis[];
143
+ }
144
+ /** 拖动吸附屏幕阈值(css 像素,缩放无关;场景阈值 = 阈值 / zoom) */
145
+ declare const SNAP_THRESHOLD_SCREEN_PX = 6;
146
+ /** 屏幕阈值 → 场景阈值(放大时场景窗口收紧,屏幕手感恒定) */
147
+ declare function snapThresholdScene(zoom: number): number;
148
+ /**
149
+ * 吸附层盒来源:可见根层盒(渲染端整层跳过、命中测试整子树退出——同一 visible
150
+ * 过滤,hitTest 同款),并排除拖动层自身根(自缘不供轴,防自身 delta 0 命中噪声)。
151
+ * excludeRootIndex 传 null = 排除面为空(copyMode 首移:副本未入库、源层供轴,
152
+ * alt-drag-paste 工单 01)。
153
+ */
154
+ declare function visibleRootBoxes(doc: Canvas, excludeRootIndex: number | null, policies?: TextLayoutPolicies): LayerBox[];
155
+ /**
156
+ * 吸附轴集合:根层盒左/中/右 + 上/中/下缘(source=layer)+ 画布水平/垂直中轴
157
+ * (source=canvas-center,四边不设轴)+ 参考线轴(source=guide)。集合次序即
158
+ * 多轴平局时的取先次序:层盒 → 画布中轴 → 参考线。
159
+ */
160
+ declare function snapAxesFromBoxes(canvasWidth: number, canvasHeight: number, boxes: readonly LayerBox[], guides: readonly Guide[]): SnapAxis[];
161
+ /**
162
+ * 按轴向分组的求位点集(resolveSnapPoints 入参):拖动九点与缩放移动缘共用
163
+ * (工单 07——缩放只给移动缘位点,见 spatial/resize.resizeSnapPoints)。
164
+ */
165
+ interface SnapPoints {
166
+ readonly vertical: readonly number[];
167
+ readonly horizontal: readonly number[];
168
+ }
169
+ /**
170
+ * 任意位点集吸附求位:垂直位点对垂直轴、水平位点对水平轴各取最近命中(已对齐
171
+ * delta=0 也回显命中轴——吸附线呈现「已对齐」状态);每轴至多一条命中轴,两轴
172
+ * 修正量由调用方并入本次几何写入(同一事务、同一历史步)。拖动盒九点吸附
173
+ * (resolveSnap)是本函数位点集的特例,缩放移动缘吸附(工单 07)是另一特例。
174
+ */
175
+ declare function resolveSnapPoints(points: SnapPoints, axes: readonly SnapAxis[], threshold: number): SnapResolution;
176
+ /**
177
+ * 拖动盒九点吸附求位:盒左/中/右三点对垂直轴、上/中/下三点对水平轴各取最近
178
+ * 命中(已对齐 delta=0 也回显命中轴——吸附线呈现「已对齐」状态);返回的修正量
179
+ * 由调用方并入本次拖动的 position 写入(同一事务、同一历史步)。
180
+ */
181
+ declare function resolveSnap(box: SnapBoxGeometry, axes: readonly SnapAxis[], threshold: number): SnapResolution;
182
+
183
+ /**
184
+ * 选择身份 = 图层路径(工单 06)。
185
+ *
186
+ * graph 无 id 字段且往返恒等契约禁止私加(spec「内核(store 与历史)」),
187
+ * 选中身份用「从画布根到图层的数组路径」表达,语法即 immer patch path 前缀:
188
+ *
189
+ * ['layers', i] 根层
190
+ * ['layers', i, 'rows', j] 表行
191
+ * ['layers', i, 'rows', j, 'cells', k] 单元格
192
+ * ['layers', i, 'rows', j, 'cells', k, 'content'] 格内容层
193
+ * ['layers', i, 'template', 'cells', k] 行模板格(无下标的 template 段)
194
+ * ['layers', i, 'template'] 行模板替身(表级,spec §2.2)
195
+ *
196
+ * 于是「对选中层改字段」= path 拼上字段段(如 + ['position', 'x'])直接得到
197
+ * patch path;选择态住 ui 分支(不进历史),文档写入一律经路径导航。
198
+ * 全模块纯函数、无 DOM。
199
+ */
200
+
201
+ /** 图层路径:patch path 前缀形态的只读数组(语法由 isLayerPath 校验) */
202
+ type LayerPath = readonly (string | number)[];
203
+ /** 结构校验(不触文档):只回答「像不像一条图层路径」;越界由 resolveLayer 判定 */
204
+ declare function isLayerPath(value: unknown): value is LayerPath;
205
+ /** 路径是否落在行模板子树内(含 'template' 段;选中/属性写入的模板态分流用) */
206
+ declare function isTemplateSubtreePath(path: LayerPath): boolean;
207
+ /**
208
+ * 根层路径判定(工单 14):置顶/置底等 priority 语义只作用于根层(容器内行/格
209
+ * 是数组序语义,priority 不参与排序)——右键菜单与快捷键入口据此裁剪动作可用态。
210
+ */
211
+ declare function isRootLayerPath(path: LayerPath): boolean;
212
+ /** 路径值相等(选择态变更短路依赖它;引用比较对每次新造的数组无效) */
213
+ declare function pathsEqual(a: LayerPath | null, b: LayerPath | null): boolean;
214
+ /**
215
+ * 前缀判定:path 落在 prefix 子树内(含相等)。跨容器移动后重挂选择路径用
216
+ * (工单 12)——子树整体换容器时,移动前捕获的路径按前缀重挂到新位置。
217
+ */
218
+ declare function pathStartsWith(path: LayerPath, prefix: LayerPath): boolean;
219
+ /**
220
+ * 路径是否处于锁定子树(canvas-web-layer-lock 工单 01):lockedPaths 中存在其
221
+ * 前缀路径即锁定(锁定恒为根层路径,即根祖先被锁)。谓词收口一处——hitTest
222
+ * 过滤、动作 guard(beginDrag/deleteLayer)、gizmo、面板投影共用,防各处手写
223
+ * 路径比对;语义与 spec §2 同源:锁根层即整子树受保护。
224
+ */
225
+ declare function isLockedPath(path: LayerPath, lockedPaths: readonly LayerPath[]): boolean;
226
+ /** 对文档解析路径:越界/形态与文档不符返回 null(doc 侧校验) */
227
+ declare function resolveLayer(doc: Canvas, path: LayerPath): Layer | null;
228
+ /**
229
+ * 路径所属根层(layer-panel-ux 工单 10):LayerBase 面字段(visible 等)只作用
230
+ * 根层,任意深度的路径第 2 段即根层下标——gizmo 隐藏过滤与右键菜单显隐标签等
231
+ * 绑定层读取位共用,收口「path[1] 是根层」的路径结构知识。越界返回 null。
232
+ */
233
+ declare function rootLayerOf(doc: Canvas, path: LayerPath): Layer | null;
234
+ /**
235
+ * 级联归属链(结构纯函数,无需文档):格内容 → 格 → 行 → 表;根层无父级。
236
+ * Escape 升级沿此链逐级取父,链尽即清空选择。模板子树链(spec §2.2):模板格
237
+ * 内容 → 模板格 → 行模板(替身路径,可选中)→ 表——行模板是链上正式一级。
238
+ */
239
+ declare function selectionParentPath(path: LayerPath): LayerPath | null;
240
+ /**
241
+ * 路径处图层的绝对盒(下钻几何经 canvas 的 resolveChildAt,与渲染模板
242
+ * walkLayer 同一套推进公式:行纵向累加、格横向累加、格内容与格同原点)。
243
+ * gizmo 选择框、命中测试、适应选区共用,保证与绘制不漂移。
244
+ */
245
+ declare function layerBoxByPath(doc: Canvas, path: LayerPath, policies?: TextLayoutPolicies): LayerBox | null;
246
+ /**
247
+ * 结构变更后的路径重映射(纯函数,工单 10):containerPath 容器的 key 子列表发生
248
+ * 「摘除 from 处 1 项、插回 to 处」(to = 插入后的最终下标;to === from 即纯删除)。
249
+ * 前缀匹配容器外的路径原样返回;被移出容器的子树返回 null(调用方落地为清除选择)。
250
+ */
251
+ declare function remapPathAfterSplice(path: LayerPath, containerPath: LayerPath, key: 'layers' | 'rows' | 'cells', from: number, to: number): LayerPath | null;
252
+
253
+ /**
254
+ * 八柄缩放几何(工单 07)。
255
+ *
256
+ * 缩放语义(工单验收项 1/2,术语见根 CONTEXT.md「八柄缩放」词条):编辑者拖动
257
+ * 选中框上的八柄(四角 + 四边中点)调整图层盒——对缘固定、移动缘随指针;
258
+ * minSize 钳位保证不产生负宽高、不翻转(盒模型契约无通用 angle,明确不做
259
+ * 旋转/翻转)。全部纯函数、无 DOM,缩放手势的文档写回在会话门面(editor.ts
260
+ * resizeTo:position 反解 + auto 标志采纳 + 表格强同步)。
261
+ *
262
+ * 角色可缩放面与属性面板的字段权威过滤(editor-vue fieldSchema ROLE_HIDDEN_KEYS
263
+ * 语义同构):解码/fromGraph 强同步的尺寸字段不可经柄改——行宽=表宽、格内容
264
+ * 宽高=格尺寸;柄集合 = 轴可缩放面的柄投影,gizmo 渲染与手势起点共用同一条缝。
265
+ */
266
+
267
+ /** 八柄:四角 + 四边中点(命名 = 柄在盒上的方位) */
268
+ declare const RESIZE_HANDLES: readonly ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
269
+ type ResizeHandle = (typeof RESIZE_HANDLES)[number];
270
+ /** 缩放最小边长(场景 px):钳位下限,杜绝负宽高与零尺寸不可见层 */
271
+ declare const RESIZE_MIN_SIZE_PX = 1;
272
+ /** 缩放几何的盒入参(LayerBox 的位置尺寸切片,与吸附求位入参同形) */
273
+ type ResizeBoxGeometry = Pick<LayerBox, 'x' | 'y' | 'width' | 'height'>;
274
+ /** 缩放求位的盒结果(绝对坐标,写回面消费) */
275
+ interface ResizeBoxResult {
276
+ readonly x: number;
277
+ readonly y: number;
278
+ readonly width: number;
279
+ readonly height: number;
280
+ }
281
+ /** 柄是否作用于横向(宽轴):东/西缘与四角 */
282
+ declare function handleResizesWidth(handle: ResizeHandle): boolean;
283
+ /** 柄是否作用于纵向(高轴):南/北缘与四角 */
284
+ declare function handleResizesHeight(handle: ResizeHandle): boolean;
285
+ /**
286
+ * 八柄缩放求位:对缘固定、移动缘 = 起始缘 + 位移,minSize 钳位(先钳尺寸再
287
+ * 反推移动缘坐标——拖过对缘时停在 minSize 而不翻转,对缘坐标严格不动)。
288
+ * 非本柄轴的位移分量忽略(n 不吃 dx、e 不吃 dy)。
289
+ */
290
+ declare function resizeBox(start: ResizeBoxGeometry, handle: ResizeHandle, dx: number, dy: number, minSize?: number): ResizeBoxResult;
291
+ /**
292
+ * 本步位移下的移动缘场景坐标(钳位前——吸附修正施加在指针位移上,再进
293
+ * resizeBox 钳位,吸附不破「对缘固定 + minSize」语义)。只收真正被写动的缘:
294
+ * 角柄双缘、边柄单缘;固定缘与盒中心不参与缩放吸附(中心对齐是拖动期行为,
295
+ * 缩放期跟随动缘即可)。
296
+ */
297
+ declare function resizeSnapPoints(start: ResizeBoxGeometry, handle: ResizeHandle, dx: number, dy: number): SnapPoints;
298
+ /** 可缩放轴(width = 横向宽字段可写、height = 纵向高字段可写) */
299
+ interface ResizeAxes {
300
+ readonly width: boolean;
301
+ readonly height: boolean;
302
+ }
303
+ /**
304
+ * 路径处图层的可缩放轴(角色权威过滤,与 editor-vue fieldSchema
305
+ * ROLE_HIDDEN_KEYS 逐角色同构——面板隐藏的尺寸字段即柄不可写的轴):
306
+ * - root:双轴(根表宽写经 canonicalize 联动行宽,面板同门放行);
307
+ * - row:仅纵向(行宽 = 表宽强同步,面板同门隐藏 width);
308
+ * - cell:双轴(内容宽/行高随 canonicalize 强同步跟随,面板同门放行);
309
+ * - content / templateContent:content 双向强同步全隐藏;模板格内容宽度耦合
310
+ * 沿用、高度豁免放行(ADR 0006 同门)→ 仅纵向。模板内容路径形如
311
+ * ['layers', i, 'template', 'cells', c, 'content']——'template' 段在
312
+ * len−4(len−1 content、len−2 格下标、len−3 'cells'),按尾段回数定位;
313
+ * - 行模板替身(路径尾 'template'):声明体尺寸不经画布柄改(转换入口/面板
314
+ * 语义面),双轴皆禁。
315
+ */
316
+ declare function resizableAxesAt(doc: Canvas, path: LayerPath): ResizeAxes;
317
+ /**
318
+ * 路径处图层的可用柄集合:柄的每一条作用轴都在可缩放面内才可用——角柄要求
319
+ * 双轴皆可(行上角柄退化成边柄会造成「拖角只动一边」的歧义,不出),gizmo
320
+ * 渲染与手势起点共用(beginResize 同缝拒绝不可用柄)。
321
+ */
322
+ declare function resizeHandlesAt(doc: Canvas, path: LayerPath): readonly ResizeHandle[];
323
+ /** 手柄命中半径(css 像素,缩放无关,场景半径 = 半径 / zoom;视觉柄 8px 见 gizmo) */
324
+ declare const RESIZE_HANDLE_HIT_PX = 6;
325
+ /** 柄在盒上的中心点(场景坐标:四角 + 四边中点;gizmo 柄绘制同源消费) */
326
+ declare function resizeHandlePoint(box: ResizeBoxGeometry, handle: ResizeHandle): {
327
+ x: number;
328
+ y: number;
329
+ };
330
+ /**
331
+ * 场景点命中可用柄:距柄中心 ≤ 屏幕半径折算的场景半径即命中;多柄同时命中
332
+ * (极小盒边中点与角点重合)取 RESIZE_HANDLES 次序的先者。只测传入的可用柄
333
+ * 集合(内核 resizeHandlesAt 的投影),不在集合内的柄不命中。
334
+ */
335
+ declare function resizeHandleAt(handles: readonly ResizeHandle[], box: ResizeBoxGeometry, sceneX: number, sceneY: number, zoom: number): ResizeHandle | null;
336
+
337
+ /**
338
+ * 对齐画布 action 族的几何核(layer-align-snap 工单 01,ADR 0011):
339
+ * 「对齐画布/贴边/贴角」是把选中图层盒整体对齐到画布几何的通用动作——与属性
340
+ * 面板的盒内对齐(写图层 spec 的 align 字段,移动盒内内容)是两类动作,并存
341
+ * 不回退;不绑定任何具体图层或文档数据(专属贴角预设不做,ADR 0011)。
342
+ *
343
+ * 几何语义:目标盒坐标按 mode 公式确定——贴边零边距、贴角统一边距(缺省
344
+ * ALIGN_CORNER_MARGIN_PX)、居中取精确中点;盒大于画布不钳位(领域语义:
345
+ * position 偏移可为负、溢出摆放),公式原样落位。调用方(alignToCanvas)以
346
+ * layerBoxAt 解析的图层盒为唯一几何来源,把「目标盒坐标 − 当前盒坐标」的位移
347
+ * 写进 position 偏移(anchor 不动——盒坐标对 position 线性,anchorOffset 不
348
+ * 参与差值)。全模块纯函数、无 DOM。
349
+ */
350
+
351
+ /** 对齐画布 mode:居中×2 + 贴边×4 + 贴角×4 */
352
+ type AlignToCanvasMode = 'left' | 'right' | 'top' | 'bottom' | 'h-center' | 'v-center' | 'corner-tl' | 'corner-tr' | 'corner-bl' | 'corner-br';
353
+ /** alignToCanvas 的可选参数:贴角系统一边距 */
354
+ interface AlignToCanvasOptions {
355
+ /** 贴角边距(四角同一取值);缺省 ALIGN_CORNER_MARGIN_PX */
356
+ margin?: number;
357
+ }
358
+ /** 贴角缺省边距(像素,ADR 0011 钉定的通用缺省,不做每图层可配置面) */
359
+ declare const ALIGN_CORNER_MARGIN_PX = 40;
360
+ /** 目标盒坐标的几何入参(LayerBox 的位置尺寸切片) */
361
+ type AlignBoxGeometry = Pick<LayerBox, 'x' | 'y' | 'width' | 'height'>;
362
+ /**
363
+ * mode → 对齐后的目标盒左上坐标(mode 只动一个轴时另一轴保持原值):贴边零边距
364
+ * (left x=0、right x=画布宽−盒宽……)、居中 = (画布尺寸−盒尺寸)/2(精确值不
365
+ * 取整)、贴角 = 统一边距。盒大于画布不钳位,负值即向反侧溢出。未知 mode 返回
366
+ * null(运行时垃圾输入的空转面;TS 层由联合类型收窄)。
367
+ */
368
+ declare function alignToCanvasTarget(mode: AlignToCanvasMode, box: AlignBoxGeometry, canvasWidth: number, canvasHeight: number, margin: number): {
369
+ x: number;
370
+ y: number;
371
+ } | null;
372
+
373
+ /**
374
+ * 查找替换的内核纯函数(canvas-web-find-replace 工单 01,CONTEXT「查找替换」词条)。
375
+ *
376
+ * 扫描(scanTextMatches):沿视觉序遍历全画布未标记(expression === null)的
377
+ * TextLayer.text,大小写敏感子串匹配——数组尾→头(hitTest 同门:视觉最上先命中)、
378
+ * 表格递归下钻(rows/cells 尾→头、content 收尾),两形态都参与(V1 rows 格内容 +
379
+ * 模板态 template.cells 格内容,template ⊕ rows 恰存其一);隐藏层参与(隐藏是
380
+ * 渲染排除语义非保护语义);标记字段整字段豁免(字面前缀也不替换——改走双形态
381
+ * 编辑器既有通道:解标→改→重标),QrCode.value / ImageLayer.src / 层名不参与。
382
+ *
383
+ * 替换(replaceHitsInDraft):in-draft 助手按「原文快照命中」应用——命中(path +
384
+ * 偏移对)在事务前一次算定,同字段多命中按偏移降序写入(前面的偏移不受后面长度
385
+ * 变化影响)、替换产物不重扫(replacement 含查询串不死循环);写值 text 直写 +
386
+ * expression 保持 null(与文本编辑提交 updateData 同门——参与者本就未标记,
387
+ * expression=null 副作用为零)。
388
+ *
389
+ * 全模块纯函数、无 DOM。
390
+ */
391
+
392
+ /** 一处命中:字段路径 + 文本偏移对 [start, end)(同字段多命中按序不重叠) */
393
+ interface FindTextHit {
394
+ readonly path: LayerPath;
395
+ readonly start: number;
396
+ readonly end: number;
397
+ }
398
+ /**
399
+ * 全画布静态文本扫描:query 为空串恒零命中(查找条初态空转)。返回视觉序命中
400
+ * 列表(同字段多命中按偏移升序),每次从 doc 现算、不驻留(免失配维护)。
401
+ */
402
+ declare function scanTextMatches(doc: Canvas, query: string): FindTextHit[];
403
+ /**
404
+ * in-draft 替换:应用事务前算定的原文快照命中,返回实际应用处数。同字段命中
405
+ * 聚组后按偏移降序写入;快照失效(路径不可解析/类型不符/偏移越界)的命中静默
406
+ * 跳过(文档在快照后被结构编辑的防御分支)。零应用时不产生文档变更(immer 对
407
+ * 同值写不记 patch,事务空转不进历史)。
408
+ */
409
+ declare function replaceHitsInDraft(draft: Draft<Canvas>, hits: readonly FindTextHit[], replacement: string): number;
410
+
411
+ /**
412
+ * 数据源 schema 方言编译器(content-completion 工单 02/08,spec §2 + §6 D6–D8)。
413
+ *
414
+ * D6 钉定:注入边界一次性转译为内部形状树(不在查询期惰性解析原始 schema,
415
+ * 行业惯例见 schema-completion-research.md)。方言子集 = JSON Schema draft-07 的
416
+ * 结构关键词(`properties` / `items` / `type`)+ 注解关键词(`description` /
417
+ * `title` / `additionalProperties: true`)+ 引用关键词(`$ref` 内部 JSON Pointer、
418
+ * `definitions`/`$defs` 命名池);其余关键词(`required`/`format`/`enum`/`const`/
419
+ * `allOf` 族/`patternProperties`/`$schema`/`$id` 及一切未识别键)宽松忽略,节点按
420
+ * 已识别字段归一(可能变叶子)。
421
+ *
422
+ * 声明契约(D1 钉定):schema 声明即 `compile(canvas, dataset)` 收到的 data
423
+ * 载荷形状——根上下文候选 = 载荷顶层键;根必须是带 properties 的对象声明。
424
+ *
425
+ * 降级分级(D7):
426
+ * - **整份拒绝**仅保留根级结构性问题(在原始文档上判定,拒绝面与 walker 时代
427
+ * 全等):根非对象 / 根缺 properties 层级(含根级 `$ref`——根不解引用)/
428
+ * `reserved_root_key`(根级键 `row` 或 `$` 前缀,前移填充期硬错误,对应
429
+ * `CanvasHydrator::assertReservedRootKeys`;仅根级保留,嵌套层同名键合法)。
430
+ * - **局部故障**(`$ref` 断链 / 外部指针 / 环引用(活跃路径 seen-set 判定)/
431
+ * 目标形态不符)→ 该节点降级为叶子(保留本地 type/description/title 注解)+
432
+ * diagnostics 逐条记录,树其余部分照常服务;注入期由
433
+ * normalizeExpressionSchemaSource 汇总 console.warn 一次(D12 单点收口,
434
+ * 诊断明细可经 parseExpressionSchema 直取,工单 11 面板展示)。
435
+ * - 深层识别关键词**形态不符**(如 type: 3 / property 子声明非对象)视同未识别
436
+ * 键宽松忽略:不诊断不降级,节点按剩余字段归一(可能变叶子)。
437
+ *
438
+ * $ref 语义(D7):任意内部 `#/` JSON Pointer(先整段百分号解码——`%25`→`%` 等,
439
+ * 解码失败记 broken_ref——再按 `/` 切段、逐段 `~1`→`/`、`~0`→`~` 反转义),
440
+ * definitions/$defs 命名池只作指针目标不进候选树;外部/远程指针不支持,归宿主
441
+ * 预内联。目标为基底,本地 `description` 覆盖目标同名注解(对 draft-07 兄弟语义
442
+ * 的唯一偏离——兄弟 description 是现成候选说明,严格性无收益;其余兄弟关键词按
443
+ * draft-07 忽略)。环引用防环用活跃路径 seen-set(对齐 VS Code
444
+ * resolveSchemaContent 范式):seen 只含当前 DFS 祖先链,菱形引用不误判;
445
+ * 该守卫兼防宿主程序化构造的对象别名自嵌套(JSON 文档天然无环)。
446
+ *
447
+ * D3 钉定(2026-09-30 由 D9 修订):数组类型节点 items 键树下钻保留在**下钻层**
448
+ * (schemaNodeAtPath / resolveRowSchema,rowsPath 行 schema 解析与 `row.*`/`$index`
449
+ * 机制零影响);**枚举层**(schemaChildEntries 候选视图)已分叉——数组节点
450
+ * (type 声明为 array 或声明了 items)收敛为叶子、不沿 items 链穿透:具名段下钻
451
+ * 数组在求值器(InterpolationEvaluator::navigateValue)是静默空串(缺字段信号),
452
+ * 补全不得怂恿写渲染为空白的表达式;类型徽标仍显示 array 本身,不特供「供表格
453
+ * rowsPath 使用」文案(为表达式内置函数留前瞻口,spec D9)。open 节点
454
+ * (additionalProperties: true)即随枚举结果携带 open 信号(D10,供工单 10 浮层
455
+ * 占位提示;信号面与候选空否无关)。
456
+ *
457
+ * 全模块零 DOM、零内部依赖(editor-shared-isolation);除
458
+ * normalizeExpressionSchemaSource 的 console 警告收口(注入期汇总一次)外均为纯函数。
459
+ */
460
+ /** schema 方言形状树节点(parse 归一化产物,properties 冻结为 Map 防宿主变异) */
461
+ interface ExpressionSchemaNode {
462
+ /** 主类型徽标数据:type 关键词归一(数组取首个非 null;未声明为 undefined) */
463
+ type?: string;
464
+ /** 展示回落注解(D8):浮层取 description ?? title */
465
+ title?: string;
466
+ description?: string;
467
+ /** 开放映射标记(D10):`additionalProperties: true`;动态字段实际键由宿主合入 properties */
468
+ open?: boolean;
469
+ properties?: ReadonlyMap<string, ExpressionSchemaNode>;
470
+ items?: ExpressionSchemaNode;
471
+ }
472
+ /** schema 子集一条目(枚举子候选用:键名 + 节点元信息) */
473
+ interface ExpressionSchemaChildEntry {
474
+ key: string;
475
+ node: ExpressionSchemaNode;
476
+ }
477
+ /** 声明期拒绝原因:reserved_root_key = 前移的填充期硬错误;invalid_schema = 形态非法 */
478
+ type ExpressionSchemaRejectReason = 'reserved_root_key' | 'invalid_schema';
479
+ /** 局部故障诊断码(D7 四类,全部 $ref 解析面;节点降叶子 + 树其余照常服务) */
480
+ type ExpressionSchemaDiagnosticCode = 'broken_ref' | 'external_ref' | 'circular_ref' | 'target_shape_mismatch';
481
+ /** 一条局部故障诊断:path = 降级节点在原始 schema 文档中的声明路径(JSON Pointer 风格) */
482
+ interface ExpressionSchemaDiagnostic {
483
+ path: string;
484
+ code: ExpressionSchemaDiagnosticCode;
485
+ detail: string;
486
+ }
487
+ type ExpressionSchemaParse = {
488
+ ok: true;
489
+ schema: ExpressionSchemaNode;
490
+ diagnostics: readonly ExpressionSchemaDiagnostic[];
491
+ } | {
492
+ ok: false;
493
+ reason: ExpressionSchemaRejectReason;
494
+ detail: string;
495
+ };
496
+ /**
497
+ * 声明期编译(D6 注入边界一次性转译):合法 → 节点树 + diagnostics(局部故障
498
+ * 逐条,可为空);根级保留键 → reserved_root_key;根级结构非法 → invalid_schema。
499
+ * 根级判定在原始文档上进行(根级 `$ref` 不解引用,拒绝面与 walker 时代全等)。
500
+ */
501
+ declare function parseExpressionSchema(raw: unknown): ExpressionSchemaParse;
502
+ /**
503
+ * 注入缝收口(工单 03 接线用):parse 失败降级为 null + console.warn(含原因,
504
+ * 不抛错不弹错);编译成功而存在局部故障诊断 → 汇总 warn **恰一次**(D7/D12:
505
+ * 内核单点收口,不逐条告警;诊断明细可经 parseExpressionSchema 直取供工单 11
506
+ * 面板展示)。注入期调用一次,枚举期拿 null 直接无候选、不再逐键告警。
507
+ */
508
+ declare function normalizeExpressionSchemaSource(raw: unknown): ExpressionSchemaNode | null;
509
+ /**
510
+ * 点路径下钻 walker:逐段经对象形态视图解析(见 objectShapeOf)。全程不抛错:
511
+ * schema 与数据源可能漂移,walker 只管声明树。
512
+ */
513
+ declare function schemaNodeAtPath(node: ExpressionSchemaNode, segments: readonly string[]): ExpressionSchemaNode | null;
514
+ /**
515
+ * 一层子候选枚举结果(D9 候选视图的返回面):候选条目 + open 信号。信号走结果
516
+ * 面扩展,不污染候选条目契约(ExpressionSchemaChildEntry 不加字段)。
517
+ */
518
+ interface ExpressionSchemaChildView {
519
+ /** 一层子候选(按声明序);数组节点收敛为叶子 = 空(D9) */
520
+ entries: readonly ExpressionSchemaChildEntry[];
521
+ /** 开放映射信号(D10):节点标 open(additionalProperties: true)时为 true;非 open 恒缺省 */
522
+ open?: boolean;
523
+ }
524
+ /**
525
+ * 一层子候选枚举(枚举层候选视图,D9 分叉点):数组节点(type 声明为 array 或
526
+ * 声明了 items)**一律**收敛为叶子——具名段下钻数组在求值器是静默空串
527
+ * (navigateValue 缺字段信号),补全不得怂恿写渲染为空白的表达式(spec §2 D9,
528
+ * 2026-09-30 修订 D3);声明自相矛盾(type array 而又带 properties/items)时从疑
529
+ * 同样收敛,对齐 D7 疑点降叶哲学。其余节点取**自身** properties(按声明序),
530
+ * 不沿 items 链穿透。open 信号(D10)随视图透出,供浮层占位提示(工单 10)。
531
+ * 下钻层(schemaNodeAtPath/resolveRowSchema)机制不动,rowsPath/`row.*`/`$index`
532
+ * 零影响(数组的数组穿透不回归)。
533
+ */
534
+ declare function schemaChildEntries(node: ExpressionSchemaNode): ExpressionSchemaChildView;
535
+ /**
536
+ * 行上下文行键树解析(rowsPath 数组 items,CanvasHydrator 行注入 'row' 的形状
537
+ * 本体):rowsPath 走点路径下钻(下钻层 items 链机制,D9 分叉不影响——数组的
538
+ * 数组穿透不回归),命中数组节点取 items;节点无 items(schema 未声明行形状或
539
+ * 漂移)→ null(行子树无候选,结构头 row 仍可用)。
540
+ * 嵌套模板表的行相对 rowsPath 以当前行 schema 为基准解析,由调用方传入对应根。
541
+ */
542
+ declare function resolveRowSchema(root: ExpressionSchemaNode, rowsPath: string): ExpressionSchemaNode | null;
543
+
544
+ /** 单条资源的物化态(与渲染端物化状态机的三态同构,宿主桥按值透传) */
545
+ type ResourceStatus = 'pending' | 'done' | 'failed';
546
+ /**
547
+ * 资源物化状态切片(store ui 分支 `resourceStatuses` 的形态):key = 图片 src
548
+ * 原串,value = 物化态。整体替换语义,只住会话态——不进历史、不写 graph、
549
+ * wire 零键;openDocument 换文档重置(资源态描述当次文档)。
550
+ */
551
+ type ResourceStatusMap = Readonly<Record<string, ResourceStatus>>;
552
+ /**
553
+ * 图片层设计态内容未绘制(占位态)判定:命中即「当前层内容未绘制,padding 不
554
+ * 影响预览」。非图片层恒 false;statuses 缺省/无记录时物化维度不参与(不可知
555
+ * 不假报——headless 用法与未桥接宿主的静态可加载层不误报)。
556
+ */
557
+ declare function isImageContentUndrawn(layer: Layer, statuses?: ResourceStatusMap): boolean;
558
+
559
+ /**
560
+ * headless 编辑器内核的 observable store。
561
+ *
562
+ * - doc 分支:解码后的领域画布(@hankchen/canvas 的 Canvas),唯一事实源。
563
+ * 写入口唯一:transact —— immer produceWithPatches 产物浅替换 + patch 广播。
564
+ * 历史(工单 08 双栈):mergeKey 相同的连续事务合并为一步(一次拖动 = 一步
565
+ * 历史),pointerup 后 closeMerge 闭合;undo/redo 手写双栈回放 patch,
566
+ * undo 后新事务改写 redo 栈(Figma 语义),上限 100 步、不跨会话。
567
+ * patch 本身可 JSON 序列化(协作的将来之路,v1 不做协作)。
568
+ * - ui 分支:视口/选择/悬停/拖动会话等易变状态,整体替换、**永不进历史**
569
+ * (红线 3 的编辑器延伸:派生状态只住这里,不写文档)。
570
+ * - 通知携带变更位置(scope/branch)与 patch 组,绑定层与渲染调度据此细分脏区。
571
+ */
572
+
573
+ /** 拖动会话(ui 分支):目标路径 + 起点场景坐标 + 起始 position 偏移 + 起始盒 */
574
+ interface DragGesture {
575
+ path: LayerPath;
576
+ startScene: Point;
577
+ startPosition: {
578
+ x: number;
579
+ y: number;
580
+ };
581
+ /**
582
+ * 拖动开始时的绝对盒(吸附求位基准,工单 01):盒坐标对 position 线性,
583
+ * 暂定盒 = startBox + 本步位移;住会话态,文档零接触。
584
+ */
585
+ startBox: LayerBox;
586
+ /**
587
+ * Alt+拖快速复制(alt-drag-paste 工单 01):beginDrag 起手一次性判定(源可
588
+ * 复制才置位,缺省/undefined = 普通拖动)。会话内定死——中途无修饰键读数,
589
+ * 松/按 Alt 均不影响(spec 决策 2,与「手势模式 pointerdown 一次性定死」同构)。
590
+ */
591
+ copy?: boolean;
592
+ /**
593
+ * copyMode 副本尚未插入(首移未越过死区):drag.path 仍是源层、源层不动;
594
+ * 首移越阈(ALT_DRAG_DEAD_ZONE_SCREEN_PX)单事务插入副本后置 false,path/
595
+ * startPosition 重指向副本(换基准)。普通拖动恒 undefined。
596
+ */
597
+ copyPending?: boolean;
598
+ }
599
+ /**
600
+ * 缩放会话(ui 分支,工单 07):目标路径 + 八柄 + 起点场景坐标 + 起始解析盒
601
+ * (吸附求位与几何基准,spatial/resize 消费)。会话对象住 ui 分支,文档只收
602
+ * resizeTo 的合并事务;与 drag 会话互斥(手势起点一次性定死)。
603
+ */
604
+ interface ResizeGesture {
605
+ path: LayerPath;
606
+ handle: ResizeHandle;
607
+ startScene: Point;
608
+ /**
609
+ * 缩放开始时的绝对解析盒(几何/吸附基准):对缘固定、移动缘 = 起始缘 +
610
+ * 位移;auto 标志采纳把本盒解析尺寸落地为声明值(首个位移事务)。
611
+ */
612
+ startBox: LayerBox;
613
+ }
614
+ /**
615
+ * 画拉建层橡皮筋会话(ui 分支,drag-create 工单 01):武装层型 + 起点场景坐标
616
+ * + 当前点 + 橡皮筋矩形。拖拽期纯视觉——文档零变更(spec 决策 4,比先建后
617
+ * resize 少踩零尺寸渲染/命中两坑),rect 是 createTo 求位产物(两点正规化 +
618
+ * QR 钳方 + 吸附修正并入),overlay 直读绘制虚线矩形与 W×H 气泡;落库在
619
+ * endCreate 单事务(返回新层路径),与 drag/resize 会话互斥。
620
+ */
621
+ interface CreateGesture {
622
+ /** 本次画拉的层型(beginLayerCreate 时从 armedCreate 捕获,会话内定死) */
623
+ type: LayerType;
624
+ /** 起点场景坐标(两点正规化与死区判定基准) */
625
+ startScene: Point;
626
+ /** 当前指针场景坐标 */
627
+ currentScene: Point;
628
+ /** 当前橡皮筋矩形(吸附修正已并入),绑定层 overlay 直读 */
629
+ rect: Rect;
630
+ }
631
+ /**
632
+ * 文本编辑会话(ui 分支,工单 11):编辑中的文本层路径。live 文本住在绑定层的
633
+ * textarea(非受控),提交经 commitTextEdit 一次性落文档——「每个拼音音节一个
634
+ * undo」被会话缓冲天然避免(impl 研究 §2.4)。
635
+ *
636
+ * expression 是内容类型会话标志(canvas-web-expression-editing 工单 01,spec 决策
637
+ * 2):beginTextEdit 按进入瞬间 `layer.expression !== null` 锚定的会话快照(与
638
+ * verticalAnchorY「进入时计、编辑中稳定」同门),裁决提交去向(表达式会话含合法
639
+ * 片段走标记写,字面会话恒字面写)与补全 enabled。住 ui 分支不进历史;面板编辑
640
+ * 中改动 layer.expression 不回写标志(打标是显式动作,快照是提交去向的裁决依据);
641
+ * 内容类型 pill 切换仅翻转本标志(零文档变更零历史步)。
642
+ */
643
+ interface TextEditingSession {
644
+ path: LayerPath;
645
+ expression: boolean;
646
+ }
647
+ /**
648
+ * 查找替换会话(canvas-web-find-replace 工单 01,CONTEXT「查找替换」词条):
649
+ * 面板开合(open)与查找/替换两词 + 命中游标(派生命中列表的偏移下标)。匹配
650
+ * 列表派生不驻留——每次从 doc + query 现算,游标越界在读侧钳位;同文档内
651
+ * 关开(closeFind→beginFind)保留查询词与游标。会话态族——不进历史、不写
652
+ * graph;openDocument 换文档整体重置(换文档带旧查询词开面板反而怪)。
653
+ */
654
+ interface FindSession {
655
+ open: boolean;
656
+ query: string;
657
+ replacement: string;
658
+ cursor: number;
659
+ }
660
+ interface EditorUi {
661
+ viewport: Viewport;
662
+ /** 当前选中图层路径(数组路径,patch path 前缀);null = 无选择 */
663
+ selection: LayerPath | null;
664
+ /** 悬停图层路径(gizmo hover 高亮);null = 无悬停 */
665
+ hovered: LayerPath | null;
666
+ /** 进行中的拖动会话;null = 无拖动 */
667
+ drag: DragGesture | null;
668
+ /** 进行中的缩放会话(工单 07 八柄);null = 无缩放 */
669
+ resize: ResizeGesture | null;
670
+ /**
671
+ * 画拉建层武装态(drag-create 工单 01,CONTEXT「武装」词条):面板新增项/
672
+ * 层型快捷键选定的一次性待命层型——下一次画布按下即开画拉,建层或 Esc 即
673
+ * 解除。无粘性工具态(spec 决策 2);openDocument 重置。
674
+ */
675
+ armedCreate: LayerType | null;
676
+ /** 进行中的画拉建层橡皮筋会话(拖拽期纯视觉,文档零变更);null = 无 */
677
+ create: CreateGesture | null;
678
+ /** 进行中的文本编辑会话;null = 非编辑态 */
679
+ editing: TextEditingSession | null;
680
+ /**
681
+ * 重命名编辑会话(layer-panel-ux 工单 09):正在行内改名的根层路径。与
682
+ * editing 同款会话语义——住 ui 分支不进历史,提交经 commitRename 漏斗一次
683
+ * 落文档;null = 非重命名态。
684
+ */
685
+ renaming: LayerPath | null;
686
+ /**
687
+ * 属性面板锚点折叠区开合(layer-panel-ux 工票 03):默认收起,会话内记忆
688
+ * (与 viewport 同款的面板偏好——openDocument 换文档不重置,永不进历史)。
689
+ */
690
+ anchorExpanded: boolean;
691
+ /**
692
+ * 数据源 schema 声明(content-completion 工单 03/08,D2 宿主随会话注入):
693
+ * 编译产物形状树或降级 null(根级结构性拒绝,注入期经
694
+ * normalizeExpressionSchemaSource 一次性收口 + console 警告;局部故障不拒绝,
695
+ * 该节点降叶子 + 注入期汇总告警一次,树其余照常服务)。只住会话态——
696
+ * 不进 graph、不落 localStorage、不进 wire(红线 3 延伸);openDocument 换
697
+ * 文档不重置(声明随会话,重注入/清除走同一入口)。
698
+ */
699
+ dataSourceSchema: ExpressionSchemaNode | null;
700
+ /**
701
+ * 参考线(ruler-guides-snap 工单 01,ADR 0012):使用者从标尺拖出的会话级
702
+ * 对位轴(水平/垂直、场景坐标)。零契约面——不进历史、不写 graph、保存零
703
+ * 改动;openDocument 换文档重置(当次编辑会话语义)。
704
+ */
705
+ guides: readonly Guide[];
706
+ /**
707
+ * 标尺显隐(ruler-guides-snap 工单 01):缺省常显,⇧R 经会话 toggleRulers
708
+ * 翻转。与视口同款的面板偏好——openDocument 换文档保留,永不进历史。
709
+ */
710
+ rulersVisible: boolean;
711
+ /**
712
+ * 当次拖动命中的吸附轴(ruler-guides-snap 工单 01):dragTo 求位的副产物,
713
+ * 供 canvas 域画贯穿吸附线——瞬时回显,松手即清空(endDrag),非拖动态为空。
714
+ */
715
+ snapAxes: readonly SnapAxis[];
716
+ /**
717
+ * 锁定根层路径集合(canvas-web-layer-lock 工单 01):会话级锁定态——锁定的
718
+ * 根层整子树退出画布命中面,变更类动作(拖动起点/删除/微调——微调已随
719
+ * kbd-nav 工单 02 接入同一谓词)在内核空转,属性编辑/显隐/改名/z 序等刻意通道
720
+ * 不受限。零契约面:不进历史、不写 graph、wire 零键、渲染产物不变
721
+ * (红线 3 同门);openDocument 换文档重置(guides 同门——锁定是文档内容
722
+ * 防护非面板偏好,跨文档路径悬空会误锁他人)。锁定恒为根层路径,子树
723
+ * 命中按前缀判定收口 isLockedPath。
724
+ */
725
+ lockedPaths: readonly LayerPath[];
726
+ /**
727
+ * 查找替换会话(canvas-web-find-replace 工单 01):面板开合 + 两词 + 游标的
728
+ * 会话态族——不进历史、不写 graph,openDocument 换文档重置(guides 同门)。
729
+ * 命中列表不在此驻留:由 editing 域扫描纯函数从 doc + query 派生(EditorSession
730
+ * listFindMatches 查询口),overlay 高亮与查找条计数同源消费。
731
+ */
732
+ find: FindSession;
733
+ /**
734
+ * 资源物化状态切片(placeholder-padding-hint 工单 02):key = 图片 src 原串、
735
+ * value = 物化三态,宿主从渲染端物化状态机桥接注入(整体替换,散列键反解在
736
+ * 宿主桥)。占位态判定的物化维度事实源——只住会话态:不进历史、不写 graph、
737
+ * wire 零键(红线 3 延伸,物化状态机切片的 ui 分支落点);openDocument 换
738
+ * 文档重置(资源态描述当次文档,跨文档旧 src 条目无意义)。
739
+ */
740
+ resourceStatuses: ResourceStatusMap;
741
+ }
742
+ /** 一步历史:一次(或同键合并的多次)文档事务的正向/逆向 patch 组 */
743
+ interface HistoryStep {
744
+ /** 事务合并键(拖动/滑杆);null = 已闭合、不再合并 */
745
+ mergeKey: string | null;
746
+ patches: Patch[];
747
+ inversePatches: Patch[];
748
+ }
749
+ type EditorChange = {
750
+ scope: 'doc';
751
+ patches: Patch[];
752
+ inversePatches: Patch[];
753
+ } | {
754
+ scope: 'ui';
755
+ branch: keyof EditorUi;
756
+ };
757
+ /**
758
+ * doc 通知契约注记(canvas-web-layer-lock 工单 03 目验同族三处收口):openDocument
759
+ * 整体重建 uiValue(selection/hovered/renaming/lockedPaths 等重置)但只发 doc 通知、
760
+ * 不逐分支发 ui 通知——订阅侧凡镜像了会被 openDocument 重置的 ui 分支,必须在 doc
761
+ * 通知里一并重读(useSelection 先例,useLayerPanel/usePropertyPanel 同门)。
762
+ */
763
+ /** undo 栈深度上限:更旧的事务被丢弃(不跨会话,会话内也只回溯有限步) */
764
+ declare const MAX_HISTORY_STEPS = 100;
765
+ type Listener$1 = (change: EditorChange) => void;
766
+ /** 文档事务:收到 doc 的 immer draft,原位改字段即可 */
767
+ type DocRecipe = (draft: Draft<Canvas>) => void;
768
+ interface TransactOptions {
769
+ /** 同键连续事务合并为一步历史(拖动的每次 pointermove 传同键) */
770
+ mergeKey?: string;
771
+ }
772
+ declare class EditorStore {
773
+ private docValue;
774
+ private uiValue;
775
+ /** undo 栈:已提交步,栈尾最新 */
776
+ private undoSteps;
777
+ /** redo 栈:被撤销步,栈尾最近一次撤销;transact 落新事务时整体改写 */
778
+ private redoSteps;
779
+ private readonly listeners;
780
+ get doc(): Canvas | null;
781
+ get ui(): EditorUi;
782
+ /** 已提交步(undo 栈,栈尾最新);可撤销性走 canUndo */
783
+ get history(): readonly HistoryStep[];
784
+ get canUndo(): boolean;
785
+ get canRedo(): boolean;
786
+ /** 打开/替换文档:ui 选择/编辑/重命名会话与双向历史一并重置(新文档不继承旧路径/旧事务);
787
+ * 参考线/锁定集合/查找会话/建层武装与画拉会话随当次会话清空(对位轴/文档内容防护/
788
+ * 查询词/待命层型不跨文档),标尺显隐作为偏好保留 */
789
+ openDocument(canvas: Canvas): void;
790
+ /**
791
+ * 文档事务唯一入口:produceWithPatches 求新树,无变化即空转(不进历史不通知);
792
+ * 有变化则浅替换 doc、按 mergeKey 合并或追加 undo 栈(超限丢最旧步)、
793
+ * 改写 redo 栈(undo 后的新事务弃用被撤销分支,Figma 语义)、广播 patch 组。
794
+ */
795
+ transact(recipe: DocRecipe, options?: TransactOptions): void;
796
+ /** 撤销最近一步:逆向 patch 回放,该步整体移入 redo 栈 */
797
+ undo(): void;
798
+ /** 重做最近一次撤销:正向 patch 回放,该步移回 undo 栈 */
799
+ redo(): void;
800
+ /**
801
+ * 闭合拖动/滑杆事务(pointerup、blur 提交,undo/redo 导航同理):最后一步的
802
+ * mergeKey 清空后,同键的后续事务不再并入。给键名时只在键匹配时闭合
803
+ * (防误伤其它键的开放步)。
804
+ */
805
+ closeMerge(mergeKey?: string): void;
806
+ /** 视口更新:ui 分支整体替换,旧切片引用保持原值(computed 引用短路依赖此语义) */
807
+ setViewport(viewport: Viewport): void;
808
+ /** 选中:值等短路(重复点选同一层不重绘) */
809
+ setSelection(path: LayerPath | null): void;
810
+ /** 悬停:值等短路(指针在同一层内移动不重绘覆盖层) */
811
+ setHovered(path: LayerPath | null): void;
812
+ /** 路径类 ui 切片的共同形状:值等短路 → 整体替换 → 分支通知 */
813
+ private setUiPath;
814
+ /** 拖动会话开始/结束(null);会话对象住 ui 分支,文档只收位置事务 */
815
+ setDrag(gesture: DragGesture | null): void;
816
+ /** 缩放会话开始/结束(null,工单 07);会话对象住 ui 分支,文档只收缩放事务 */
817
+ setResize(gesture: ResizeGesture | null): void;
818
+ /** 建层武装置/解除(null,drag-create 工单 01);标量值等短路(重复武装同型不通知) */
819
+ setArmedCreate(type: LayerType | null): void;
820
+ /** 画拉橡皮筋会话开始/更新/结束(null);会话对象住 ui 分支,落库在 endCreate 单事务 */
821
+ setCreate(gesture: CreateGesture | null): void;
822
+ /** 文本编辑会话开始/结束(null);会话住 ui 分支,文本经 commitTextEdit 一次性落文档 */
823
+ setEditing(session: TextEditingSession | null): void;
824
+ /** 重命名会话开始/结束(null);路径类切片,值等短路(重复 begin 同一路径不重绘) */
825
+ setRenaming(path: LayerPath | null): void;
826
+ /** 锚点折叠区开合:布尔值等短路(重复点击同一态不重绘) */
827
+ setAnchorExpanded(open: boolean): void;
828
+ /**
829
+ * 数据源 schema 声明替换(归一产物或降级 null,校验收口在注入缝不在这里):
830
+ * 同引用短路(重复注入同一归一产物不惊动订阅方);仅 ui 通知,不参与重绘
831
+ * 脏标(onStoreChange 对该分支短路)。
832
+ */
833
+ setDataSourceSchema(schema: ExpressionSchemaNode | null): void;
834
+ /** 参考线列表替换(增删动作在会话门面收口);仅 ui 通知,不参与历史 */
835
+ setGuides(guides: readonly Guide[]): void;
836
+ /** 标尺显隐:布尔值等短路(重复置同态不重绘) */
837
+ setRulersVisible(visible: boolean): void;
838
+ /** 当次命中吸附轴替换(dragTo 副产物 / endDrag 清空);内容等短路(未命中步不重绘) */
839
+ setSnapAxes(axes: readonly SnapAxis[]): void;
840
+ /** 锁定集合替换(toggleLayerLock 收口,翻转逻辑在会话门面);内容等短路(集合未变不重绘) */
841
+ setLockedPaths(paths: readonly LayerPath[]): void;
842
+ /**
843
+ * 查找会话替换(beginFind/closeFind/输入写入经会话门面收口到这里);内容等
844
+ * 短路(重复开合/同词写入不通知)。仅 ui 通知,不参与历史。
845
+ */
846
+ setFind(find: FindSession): void;
847
+ /**
848
+ * 资源物化状态替换(placeholder-padding-hint 工单 02):宿主从渲染端物化状态机
849
+ * 桥接注入的唯一写入口,整体替换语义;内容等短路(同态快照不惊动订阅方,
850
+ * 桥接的高频整体替换零噪声)。仅 ui 通知,不参与历史、不写 graph(红线 3 延伸)。
851
+ */
852
+ setResourceStatuses(statuses: ResourceStatusMap): void;
853
+ subscribe(listener: Listener$1): () => void;
854
+ private notify;
855
+ }
856
+
857
+ /** 快捷键动作(会话分派面 executeShortcut 的入参域) */
858
+ type EditorShortcutAction = 'undo' | 'redo' | 'copy' | 'paste' | 'duplicate' | 'copyStyle' | 'pasteStyle' | 'delete' | 'rename' | 'findReplace' | 'toggleRulers' | 'toggleLayerLock' | 'toggleLayerVisibility' | 'bringForward' | 'sendBackward' | 'bringToFront' | 'sendToBack' | 'nudgeUp' | 'nudgeDown' | 'nudgeLeft' | 'nudgeRight' | 'nudgeUpCoarse' | 'nudgeDownCoarse' | 'nudgeLeftCoarse' | 'nudgeRightCoarse' | 'selectNextLayer' | 'selectPrevLayer' | 'armCreateText' | 'armCreateTable' | 'armCreateQrCode' | 'armCreateImage' | 'zoomReset' | 'fitToSurface' | 'fitToSelection' | 'helpShortcuts';
859
+ /** 画拉建层武装四动作的联合(注册表条目与分派、绑定层提示路由的共用键域) */
860
+ type ArmCreateShortcutAction = keyof typeof ARM_CREATE_LAYER_TYPES;
861
+ /**
862
+ * 武装动作 → 层型(canvas-web-drag-create 工单 03):「哪四个动作是武装、各武装
863
+ * 什么层型」的单一事实源——executeShortcut 分派与绑定层桥(useShortcuts 的提示
864
+ * 路由)都从这里取,加层型只改此表与注册表条目两处。
865
+ */
866
+ declare const ARM_CREATE_LAYER_TYPES: {
867
+ readonly armCreateText: "TextLayer";
868
+ readonly armCreateTable: "TableLayer";
869
+ readonly armCreateQrCode: "QrCodeLayer";
870
+ readonly armCreateImage: "ImageLayer";
871
+ };
872
+ /**
873
+ * 帮助面板的展示归组(kbd-nav 工单 04):分节渲染的键位归类。展示名归 Vue,
874
+ * 这里只定枚举域。text 归组自 canvas-web-find-replace 工单 01 入表(查找替换
875
+ * 等文本编辑面动作,与图层/视图操作分节)。
876
+ */
877
+ type EditorShortcutGroup = 'history' | 'clipboard' | 'layer' | 'text' | 'view' | 'help';
878
+ /**
879
+ * 键位组合声明:key 为 KeyboardEvent.key 的小写归一形态;mod/shift/alt 精确匹配。
880
+ * 声明 code 时改按 KeyboardEvent.code(物理键)匹配(⌥/⇧ 变体字符场景),key 降级
881
+ * 为条目自述;未声明 code 的条目按 key 匹配、code 不参与。
882
+ */
883
+ interface ShortcutCombo {
884
+ key: string;
885
+ /** Ctrl(Windows/Linux)或 Cmd(macOS),两平台等价 */
886
+ mod: boolean;
887
+ shift: boolean;
888
+ /** ⌥(两平台等价);缺省 false 与输入精确匹配(带 ⌥ 的按键不失配到无 ⌥ 条目) */
889
+ alt?: boolean;
890
+ /** KeyboardEvent.code:声明即改物理键匹配(mac ⌥ 变体字符 / ⇧ 数字变体) */
891
+ code?: string;
892
+ }
893
+ /** 注册表条目:键位组合 → 动作(label/group 是帮助面板的展示元数据) */
894
+ interface EditorShortcutBinding {
895
+ readonly combo: ShortcutCombo;
896
+ readonly action: EditorShortcutAction;
897
+ /** 中文短句(帮助面板直读,如「前移一层」) */
898
+ readonly label: string;
899
+ /** 展示归组(帮助面板分节) */
900
+ readonly group: EditorShortcutGroup;
901
+ }
902
+ /**
903
+ * 缺省注册表。undo 与 redo 只差 shift,故 shift 精确匹配(不能忽略);⌥⌘]/⌥⌘[ 与
904
+ * ⇧1/⇧2 按 code 匹配(变体字符),其余条目按 key 匹配。
905
+ */
906
+ declare const DEFAULT_EDITOR_SHORTCUTS: readonly EditorShortcutBinding[];
907
+ /**
908
+ * 快捷键输入:绑定层从 KeyboardEvent 与会话状态折算(本类型不出现任何 DOM 类型)。
909
+ * alt/code 为可选——未折算的宿主按缺省(无 ⌥、无 code)参与匹配。
910
+ */
911
+ interface EditorShortcutInput {
912
+ /** KeyboardEvent.key(大小写随 Shift 与大小写锁定,分类器归一) */
913
+ key: string;
914
+ mod: boolean;
915
+ shift: boolean;
916
+ /** KeyboardEvent.altKey 折算;缺省(未折算)视作 false */
917
+ alt?: boolean;
918
+ /** KeyboardEvent.code 折算;声明 code 的注册表条目只认它,缺省(未折算)恒不命中 */
919
+ code?: string;
920
+ /** 输入法合成中:isComposing || keyCode === 229 由绑定层折算 */
921
+ composing: boolean;
922
+ /** 文本编辑态(ui.editing ≠ null):全部文档快捷键让路给 textarea */
923
+ editing: boolean;
924
+ /** 焦点在可编辑元素(输入框/可编辑节点):让路给原生编辑 */
925
+ editableTarget: boolean;
926
+ }
927
+ /**
928
+ * 键位 → 动作分类:让路规则优先(合成中/编辑态/输入态一律 null),再按注册表
929
+ * 匹配——声明 code 的条目按 input.code 全等(mod/shift/alt 精确),其余按 key
930
+ * 小写归一后全等。未命中返回 null。绑定集可注入(宿主扩展键位;缺省
931
+ * DEFAULT_EDITOR_SHORTCUTS)。
932
+ */
933
+ declare function classifyEditorShortcut(input: EditorShortcutInput, bindings?: readonly EditorShortcutBinding[]): EditorShortcutAction | null;
934
+
935
+ /**
936
+ * 字体清单(工单 13):编辑会话级的字体可选清单——「清单 ≠ 物化」,core 只持有
937
+ * 引用与展示名,字体的加载仍走渲染端物化管线(FontFace 注册),本模块零 I/O。
938
+ *
939
+ * - 内置清单:宿主在创建会话时配置(URL 列表可配置;缺省为空——宿主不配置即
940
+ * 只有内置默认字体与自定义上传项)。
941
+ * - 自定义字体:上传动作(uploadFont)成功后追加,会话内即可选可用;清单是
942
+ * 会话状态不是文档数据,不进 graph(往返恒等契约禁止私加字段),保存/打开
943
+ * 不携带。
944
+ */
945
+ interface FontCatalogEntry {
946
+ /** 展示名(清单选择器显示;与引用解耦,URL 不直接暴露给编辑者) */
947
+ readonly label: string;
948
+ /** 字体引用(URL / data URL),与 TextLayer.font 同域;物化管线按引用装载 */
949
+ readonly ref: string;
950
+ }
951
+ type Listener = (entries: readonly FontCatalogEntry[]) => void;
952
+ declare class FontCatalog {
953
+ private readonly builtin;
954
+ private custom;
955
+ private readonly listeners;
956
+ constructor(builtin?: readonly FontCatalogEntry[]);
957
+ /** 当前清单:内置在前(配置序),自定义追加在后(上传序) */
958
+ get entries(): readonly FontCatalogEntry[];
959
+ /** 追加自定义条目;同引用去重(重复上传不产生重复项,首见条目胜出) */
960
+ addCustom(entry: FontCatalogEntry): boolean;
961
+ /** 订阅清单变更(整体快照推送,绑定层的响应式 ref 据此联动);返回退订函数 */
962
+ subscribe(listener: Listener): () => void;
963
+ private notify;
964
+ }
965
+
966
+ /**
967
+ * 上传注入点(工单 13):本机资源 → 可物化引用的宿主实现契约。
968
+ *
969
+ * core 只定义接口与消费动作,**不内置任何上传实现**(红线:图层 setter 禁 I/O
970
+ * 的延伸——文档只持有引用字符串,本机字节到引用的搬运归宿主):生产宿主接自己
971
+ * 的对象存储/上传服务返回 URL;playground 以 data URL 兜底(文件不经网络,可离
972
+ * 线演示,代价是引用随 graph JSON 一起变大——演示语义,不作生产形态)。
973
+ *
974
+ * 文件以 DOM 无关的最小形态描述(core 无 DOM lib):绑定层从宿主 File 对象读出
975
+ * 名称/MIME/字节后传入,内核不接触 File/Blob 等宿主类型。
976
+ */
977
+ /** 本机资源文件的最小描述(DOM 无关) */
978
+ interface UploadFile {
979
+ /** 原始文件名(含扩展名;上传实现可据其推断存储键/展示名) */
980
+ readonly name: string;
981
+ /** MIME 类型(如 image/png、font/ttf;未知时可为空串) */
982
+ readonly mime: string;
983
+ /** 文件字节 */
984
+ readonly bytes: Uint8Array;
985
+ }
986
+ /** 本机资源 → 可物化引用(URL / data URL / 宿主存储地址,渲染端物化管线可装载的引用) */
987
+ type UploadHandler = (file: UploadFile) => Promise<string>;
988
+ /**
989
+ * 上传建层的摆位覆盖(kbd-nav 工单 05,拖文件入画布语义):缺省(两键皆缺)=
990
+ * 编辑器缺省盒(createDefaultLayer 的 ImageLayer 形态 200×150 落 0,0)。
991
+ * - `at`:图层盒左上角的场景坐标(缺省 top-left 锚不改,改写锚点偏移);
992
+ * - `size`:盒尺寸 1:1 落值不缩放(拖放语义传解码出的图片自然尺寸,溢出画布
993
+ * 属预期——Figma 同构)。
994
+ */
995
+ interface UploadImagePlacement {
996
+ readonly at?: {
997
+ x: number;
998
+ y: number;
999
+ };
1000
+ readonly size?: {
1001
+ width: number;
1002
+ height: number;
1003
+ };
1004
+ }
1005
+ /** 上传条目的展示名:去扩展名的文件名基名(空退化全名) */
1006
+ declare function uploadDisplayName(file: UploadFile): string;
1007
+ /** 未注入上传实现时的统一错误(宿主侧降级提示的对位语义) */
1008
+ declare class UploadHandlerMissingError extends Error {
1009
+ constructor();
1010
+ }
1011
+
1012
+ /**
1013
+ * EditorSession:headless 编辑会话——store 之上的渲染调度面(工单 05)。
1014
+ *
1015
+ * 职责(impl 研究文档 §2.1 的「双 canvas + 单 rAF 合帧 + 脏标」):
1016
+ * - store 订阅 → 分层脏标(doc/视口 → 双层;后续工单的选择/gizmo 只脏覆盖层)
1017
+ * → 注入的帧调度器合帧,每屏帧至多一次重绘;rAF 由绑定层注入,内核保持无 DOM。
1018
+ * - 内容层重绘驱动五原语模板(renderCanvas),视口变换喂给后端的可选能力口;
1019
+ * - 覆盖层(gizmo)绘制经 OverlayPainter 缝交给宿主:入参是与内容层一致的
1020
+ * 呈现视口(已对齐物理像素),由宿主自行施加变换后绘制;本层只保证
1021
+ * 「覆盖层脏不触发内容层」与两侧视口的一致性。
1022
+ * - 相机动作全部经 camera.ts 纯函数落到 store.ui.viewport(不进历史)。
1023
+ */
1024
+
1025
+ /** 帧调度器:返回取消函数(绑定层注入 requestAnimationFrame 的包装) */
1026
+ type FrameScheduler = (callback: () => void) => () => void;
1027
+ /** 覆盖层绘制缝的入参:viewport 已对齐物理像素(与内容层同一呈现视口) */
1028
+ interface OverlayPaintArgs {
1029
+ viewport: Viewport;
1030
+ dpr: number;
1031
+ doc: Canvas | null;
1032
+ }
1033
+ type OverlayPainter = (args: OverlayPaintArgs) => void;
1034
+ type InvalidateTarget = 'content' | 'overlay' | 'both';
1035
+ /**
1036
+ * 文本编辑 overlay 的布局描述(textEditLayout 的产物):场景坐标系的盒几何与
1037
+ * 排版字段。CSS 换算(transform 缩放/字体族解析/行盒模型)归绑定层,内核保持
1038
+ * 无 CSS 语义。
1039
+ */
1040
+ interface TextEditLayout {
1041
+ /** 编辑中图层的绝对盒(场景坐标;textarea 覆盖整盒,文字按 padding 内缩) */
1042
+ box: LayerBox;
1043
+ /** 进入编辑时的文档文本(textarea 初值) */
1044
+ text: string;
1045
+ /** 原始字体引用(空串/纯数字 = 内置默认字体;URL 由绑定层解析注册族名) */
1046
+ font: string;
1047
+ fontColor: string;
1048
+ /** 场景字号(textarea 字号固定取它,缩放交给 CSS transform——字号视觉恒定) */
1049
+ fontSize: number;
1050
+ /** 行高像素 = ceil(fontSize × lineHeight),与布局层同式 */
1051
+ lineHeightPx: number;
1052
+ padding: Padding;
1053
+ horizontalAlign: HorizontalAlign;
1054
+ verticalAlign: VerticalAlign;
1055
+ /**
1056
+ * 首行绘制锚点相对内容盒顶部的偏移(textOrigin.y,与 canvas 绘制同一数值)。
1057
+ * 锚点语义随 verticalAlign:top = 字形盒顶、center = 字形盒心、bottom = 字形盒底
1058
+ * (渲染端以字体 metrics 消化基线差,绑定层的 CSS 行盒换算须按同一语义折算,
1059
+ * 不能直接当 padding-top 用——否则 center/bottom 会差半行/一行)。
1060
+ */
1061
+ verticalAnchorY: number;
1062
+ /** autowrap = textarea pre-wrap 软换行,否则 pre 单行(断行允许与预览不同,决策 A) */
1063
+ autowrap: boolean;
1064
+ /**
1065
+ * 内容类型会话模式(canvas-web-expression-editing 工单 01):true = 表达式——
1066
+ * 绑定层 pill 激活态与补全 enabled 的消费面。取会话标志(编辑中 pill 切换
1067
+ * 实时跟随,不受层标记态影响——面板在会话窗口期显示的是提交前旧状态);路径
1068
+ * 无活动会话时按层标记态回落(进入编辑即锚定同值)。
1069
+ */
1070
+ expressionMode: boolean;
1071
+ }
1072
+ /**
1073
+ * Alt+拖快速复制的死区(alt-drag-paste 工单 01,spec 决策 1):屏幕 css 像素、
1074
+ * 缩放无关(场景阈值 = 阈值 / zoom,SNAP_THRESHOLD_SCREEN_PX 同风格)——首移
1075
+ * 越过死区才插入副本,死区内抬手退化为普通点选。
1076
+ */
1077
+ declare const ALT_DRAG_DEAD_ZONE_SCREEN_PX = 4;
1078
+ interface EditorSessionOptions {
1079
+ scheduleFrame: FrameScheduler;
1080
+ /** 缩放范围(可配置,缺省 5%–800%) */
1081
+ zoomBounds?: ZoomBounds;
1082
+ /** 适应画布时的屏幕像素内缩(缺省 0:画布边贴视口边) */
1083
+ fitMargin?: number;
1084
+ /** 文本布局策略(断行/度量注入缝,缺省启发式对齐版) */
1085
+ textPolicies?: TextLayoutPolicies;
1086
+ /**
1087
+ * 上传实现注入点(工单 13):本机资源 → 可物化引用。core 不内置任何实现;
1088
+ * 未注入时 canUpload 为 false、上传动作抛 UploadHandlerMissingError(宿主
1089
+ * 据此做降级提示),生产宿主接自己的存储,playground 以 data URL 兜底。
1090
+ */
1091
+ uploadHandler?: UploadHandler;
1092
+ /** 内置字体清单(URL 列表可配置;缺省空清单,清单 ≠ 物化,加载走渲染端管线) */
1093
+ fontCatalog?: readonly FontCatalogEntry[];
1094
+ }
1095
+ declare class EditorSession {
1096
+ readonly store: EditorStore;
1097
+ private readonly scheduleFrame;
1098
+ private readonly zoomBounds;
1099
+ private readonly fitMargin;
1100
+ /**
1101
+ * 生效的文本布局策略(构造注入值,缺省 undefined = 启发式对齐版):公开只读,
1102
+ * 供导出等旁路面复用同一注入值——导出与编辑画布的断行/盒高才不分叉(工单 13)。
1103
+ */
1104
+ readonly textPolicies?: TextLayoutPolicies;
1105
+ private readonly uploadHandler;
1106
+ private readonly fontCatalogValue;
1107
+ private backend;
1108
+ private overlayPainter;
1109
+ private dpr;
1110
+ private surfaceSize;
1111
+ private dirty;
1112
+ private frameQueued;
1113
+ private cancelFrame;
1114
+ private readonly unsubscribeStore;
1115
+ /**
1116
+ * 预览视图 memo(决策 2026-09):模板态表格实例化一行预览行的文档衍生画布,
1117
+ * 画布渲染/命中/导出消费,永不写回 graph(红线 3 延伸)。Canvas 不可变 +
1118
+ * 结构共享,doc 引用相等即视图仍有效(与 skipContent 的引用比较同纪律)。
1119
+ */
1120
+ private previewSource;
1121
+ private previewValue;
1122
+ /**
1123
+ * 会话级剪贴板(工单 14;alt-drag-paste 工单 02 收窄):复制源的子树深拷贝
1124
+ * 快照 + 复制时点绝对盒(粘贴落位基准,格内容的格内相对 position 据此换算
1125
+ * 画布绝对落位)+ 源路径(粘贴时紧邻插入的解析凭据)。不跨会话、不碰 OS
1126
+ * 剪贴板;openDocument 不清空——粘贴进另一份文档是合法用法(条目是与源树
1127
+ * 断开引用的普通对象;他文档 sourcePath 必失配 → 粘贴退化置顶)。
1128
+ */
1129
+ private clipboardEntry;
1130
+ /**
1131
+ * 样式剪贴板(canvas-web-style-paste 工单 01):源类型 + 适用面逐字段值快照。
1132
+ * 与图层剪贴板**分立互不覆盖**(⌘C 与 ⌥⌘C 并存);不碰 OS 剪贴板、不进历史
1133
+ * (剪贴板态不是文档态);openDocument 不清空——跨文档粘贴样式合法(图层
1134
+ * 剪贴板同门,快照是与源树断开引用的普通对象)。
1135
+ */
1136
+ private styleClipboardEntry;
1137
+ /** 参考线 id 自增序(会话内唯一即可,不持久化——参考线不写 graph) */
1138
+ private guideIdSeq;
1139
+ constructor(options: EditorSessionOptions);
1140
+ attachContentBackend(backend: RenderBackend): void;
1141
+ setOverlayPainter(painter: OverlayPainter | null): void;
1142
+ /** 解绑绘制面(surface 卸载时);挂起帧 flush 空转,可重新挂载 */
1143
+ detachSurfaces(): void;
1144
+ setDevicePixelRatio(dpr: number): void;
1145
+ setSurfaceSize(width: number, height: number): void;
1146
+ getSurfaceSize(): Readonly<Size>;
1147
+ /** 打开/替换文档(转发 store;解码在宿主侧完成,内核只消费领域结构) */
1148
+ openDocument(canvas: Canvas): void;
1149
+ /**
1150
+ * 预览视图(只读衍生):模板态表格在视图中获得一行预览行(文本格 = 表达式
1151
+ * 原文、标记图片/二维码格走占位盒),渲染/命中/导出据此呈现「一行输出效果」;
1152
+ * 文档本身不被改写。宿主(如导出)与内核内部共用同一 memo。
1153
+ */
1154
+ get previewCanvas(): Canvas | null;
1155
+ /** 平移:屏幕位移按 zoom 折算 */
1156
+ panBy(deltaScreenX: number, deltaScreenY: number): void;
1157
+ /** 缩放至指定倍率,以屏幕点(视口坐标系的 css 像素)为锚 */
1158
+ zoomAt(screenX: number, screenY: number, nextZoom: number): void;
1159
+ /** 滚轮/捏合:曲线见 camera.nextZoomByWheel,缩放以指针为中心 */
1160
+ zoomByWheel(screenX: number, screenY: number, deltaY: number): void;
1161
+ /**
1162
+ * 缩放复位 100%(⌘0,kbd-nav 工单 01):以视口中心为锚回到 1.0——中心场景点
1163
+ * 不动、平移不跳变(Sketch Actual Size 同构,playground zoomTo100 先例收编为
1164
+ * 内核面);已在 100% 时 zoomAtPoint 恒等短路。视口走 ui 分支不进历史。
1165
+ */
1166
+ resetZoom(): void;
1167
+ /** 一键适应画布:整页可见、居中(表面尺寸未知时 no-op) */
1168
+ fitToSurface(): void;
1169
+ /**
1170
+ * 路径处图层盒:模板子树路径经预览视图解析(auto 高在视图里合成,文档侧
1171
+ * 声明高为零),其余路径直接解析文档——非模板子树结构共享,两棵树同盒。
1172
+ * gizmo/命中/适应选区/文本编辑 overlay 共用,与绘制不漂移。
1173
+ */
1174
+ private boxByPath;
1175
+ /** 自适应选区视图:视口适配选中盒;无选择/退化盒回落适应画布 */
1176
+ fitToSelection(): void;
1177
+ /**
1178
+ * 保 zoom 平移入视(canvas-web-find-replace 工单 02,spec 决策 6):查找导航
1179
+ * 的视口跟随——目标盒完全在视口内视口不动,出视才最小平移使整盒入视(入视
1180
+ * 小边距与盒大于视口对齐盒左上的边界语义在 camera.panToBox 纯函数内定死)。
1181
+ * 与 fitToSelection 分立不混用:那是变焦语义(fitRect),这是保 zoom 的跟随
1182
+ * 语义。盒几何由调用方解析(工单 03 命中导航经 layerBoxAt 同一解析缝,模板
1183
+ * 子树走预览视图);视口走 ui 分支不进历史,视内不动经恒等短路零通知;表面
1184
+ * 尺寸未知 no-op(fitToSurface 同门)。
1185
+ */
1186
+ panToBox(box: Rect): void;
1187
+ /** 屏幕(css 像素)→ 场景:绑定层的唯一坐标入口(zoom 折算收口在此) */
1188
+ toScenePoint(screenX: number, screenY: number): Point;
1189
+ /**
1190
+ * 场景点命中查询(不产生副作用):在预览视图上执行——模板态表格的预览行
1191
+ * 可命中,命中路径经视图→文档映射落回模板子树身份(选中/属性编辑写文档)。
1192
+ * 锁定集合(canvas-web-layer-lock 工单 01)随 ui 分支传入纯函数:锁定的根层
1193
+ * 整子树退出命中面,selectAt/hoverAt/右键/双击全经此一条缝自动生效。
1194
+ */
1195
+ hitTest(sceneX: number, sceneY: number): LayerPath | null;
1196
+ /** 路径处图层的绝对盒(gizmo 选择框与后续面板共用;与命中同一布局策略) */
1197
+ layerBoxAt(path: LayerPath): LayerBox | null;
1198
+ /** 点选:命中即选中并返回路径,未中清空选择(画布与后续面板同源) */
1199
+ selectAt(sceneX: number, sceneY: number): LayerPath | null;
1200
+ /** 直接设置选择(面板/键盘入口) */
1201
+ setSelection(path: LayerPath | null): void;
1202
+ /** Escape 升级:沿归属链 cell→row→table 逐级取父,链尽清空选择 */
1203
+ escapeSelection(): void;
1204
+ /**
1205
+ * 循环选层(kbd-nav 工单 03,CONTEXT「循环选层」词条 / spec 决策 4):Tab
1206
+ * 沿面板序朝更垫底方向移动选中(面板顶 = 视觉最上层 = 数组尾,故数组下标 −1),
1207
+ * ⇧Tab 反向(selectPrevLayer),端点 wrap 回绕;空选从视觉最上层(面板首行)
1208
+ * 开始(spec 决策 4 字面口径,⇧Tab 同一起点);子层选中以根祖先为基准取相邻
1209
+ * 根层(不下钻表格,循环只落根路径)。跳过面与命中面同门(hitTest 同款判定):
1210
+ * 隐藏根层整子树排除(visible=false 是排除语义非保护语义)、锁定根层整子树
1211
+ * 退出交互面(isLocked 谓词——Tab 是命中面的键盘投影,「画布挡误触」同门,
1212
+ * layer-lock 合入后随谓词自动生效);全部根层被跳过时空转返回 false。
1213
+ * 选中变更走 setSelection(ui 分支零历史步;值等短路使单根层 wrap 自身无害
1214
+ * ——不通知)。返回是否落到选中(executeShortcut 可用态口径)。
1215
+ */
1216
+ selectNextLayer(): boolean;
1217
+ /** ⇧Tab 反向循环选层(语义见 selectNextLayer) */
1218
+ selectPrevLayer(): boolean;
1219
+ /** 循环选层的共同游走:direction = 1 朝数组头(垫底)、−1 朝数组尾(最上),模 count 回绕 */
1220
+ private cycleRootSelection;
1221
+ /** 悬停命中(指针移动时调用;离场传 null 或用 setHovered) */
1222
+ hoverAt(sceneX: number, sceneY: number): void;
1223
+ /** 设置悬停路径 */
1224
+ setHovered(path: LayerPath | null): void;
1225
+ /**
1226
+ * 开始拖动会话:记录目标路径、起点场景坐标、起始 position 偏移与起始盒
1227
+ * (吸附求位基准,ui 分支)。已在拖动中、路径或盒几何无法解析时返回 false;
1228
+ * 路径落在锁定子树同样拒绝(canvas-web-layer-lock 工单 01:拖动是变更类
1229
+ * 动作的 guard 入口——画布命中面已过滤锁定层,此守卫兜住其余手势来源,
1230
+ * 缩放手势接入 resize feature 时同用此门)。
1231
+ *
1232
+ * Alt+拖快速复制(alt-drag-paste 工单 01):options.copy 起手一次性判定——
1233
+ * 源不可复制(canCopyLayerAt 为 false:行/格等容器内结构)静默忽略 Alt 走
1234
+ * 普通拖动(spec 决策 1);置位后 copyMode 会话内定死,首移越过死区才插入
1235
+ * 副本(dragTo 消费,见 ALT_DRAG_DEAD_ZONE_SCREEN_PX)。
1236
+ */
1237
+ beginDrag(path: LayerPath, sceneX: number, sceneY: number, options?: {
1238
+ copy?: boolean;
1239
+ }): boolean;
1240
+ /**
1241
+ * 拖动进行中:position = 起点 + 场景位移,再按吸附修正(工单 01,ADR 0012)。
1242
+ * 吸附求位:暂定盒(startBox + 本步位移)九点对轴集合——其他可见根层盒缘与
1243
+ * 中心、画布水平/垂直中轴、参考线轴——屏幕 6px 阈值按当前缩放换算场景值,
1244
+ * 多轴取最近,至多一垂直 + 一水平;修正并入同一位置事务(同一 mergeKey,
1245
+ * 跟随拖动既有历史合并语义)。命中的吸附轴回写会话态供吸附线呈现(瞬时
1246
+ * 回显,endDrag 清空)。九锚点一视同仁——锚点偏移不动,只累加 x/y(x += dx/
1247
+ * zoom 的 zoom 折算已由 toScenePoint 收口);一次拖动的全部位移经同 mergeKey
1248
+ * 事务并成一步历史,属性面板读同一文档数据即联动。
1249
+ *
1250
+ * Alt+拖 copyMode(alt-drag-paste 工单 01):副本未插入前(copyPending)源层
1251
+ * 不动——未越死区零事务,首移越阈交 dragCopyFirstMove 单事务完成插入 + 位移,
1252
+ * 此后 path 已重指向副本、走普通拖动流程(吸附排除副本、源层供轴)。
1253
+ */
1254
+ dragTo(sceneX: number, sceneY: number): void;
1255
+ /**
1256
+ * copyMode 首移(alt-drag-paste 工单 01,spec 决策 1/3):先过死区——位移按
1257
+ * 屏幕 4px 阈值折算(缩放无关),未越阈零事务、源层不动(Alt+点击退化);
1258
+ * 越阈则单事务完成「插入副本(紧邻源层)+ 副本自源盒起步位移」:副本 = 源
1259
+ * 子树深拷贝,落位按根层语义反解(pastePosition——根层源退化为 position +
1260
+ * 位移、格内容源把格内相对 position 换算画布绝对落位),与吸附修正一并写入,
1261
+ * mergeKey 沿用 DRAG_MERGE_KEY,后续 move 同键合并——undo 一次副本整体消失。
1262
+ * 吸附求位在副本入库前:吸附源 = 全部可见根层(副本尚不存在、源层天然在列
1263
+ * ——重叠起步,spec 决策 4);插入后手势重指向副本(startPosition 换基准为
1264
+ * 副本起步位 = 源绝对盒的根层语义反解,startBox 即副本起步盒不变),副本自动
1265
+ * 选中,命中轴照常回显。
1266
+ */
1267
+ private dragCopyFirstMove;
1268
+ /** 结束拖动:闭合合并事务(下一步历史定格),会话态与命中轴回显清空 */
1269
+ endDrag(): void;
1270
+ /**
1271
+ * 手势可用的吸附轴集合(dragTo/resizeTo 每步重算,几何与其余消费同源):
1272
+ * 可见根层盒(hitTest 同款 visible 过滤)排除手势目标层自身根 + 画布中轴 +
1273
+ * 当前参考线。自身根排除对缩放同样正确——被缩放层的自缘不供轴(对缘与移动
1274
+ * 缘都在自身盒上,供轴只会命中自己)。gesturePath 传 null = 排除面为空
1275
+ * (copyMode 首移:副本未入库,源层供轴,alt-drag-paste 工单 01)。
1276
+ */
1277
+ private snapAxesForGesture;
1278
+ /** 当次手势命中的吸附轴(只读查询,吸附线呈现消费);非手势态/未命中为空 */
1279
+ listSnapAxes(): readonly SnapAxis[];
1280
+ /**
1281
+ * 路径处图层当前可用的八柄集合(gizmo 柄渲染与手势起点同缝):角色权威
1282
+ * 过滤(行宽=表宽、格内容尺寸强同步面不可经柄改,语义见
1283
+ * spatial/resize.resizableAxesAt)+ 锁定子树不出柄(锁定层只画选中框,
1284
+ * gizmo 同门——柄是变换入口,锁定=可定位不可变换)。
1285
+ */
1286
+ resizeHandlesAt(path: LayerPath | null): readonly ResizeHandle[];
1287
+ /**
1288
+ * 场景点命中选中层的可用柄(hitTest/hoverAt 同门的意图级查询,画布接线
1289
+ * 与 gizmo 消费):命中半径按屏幕 6px 常量折算(RESIZE_HANDLE_HIT_PX,随
1290
+ * zoom 收放);无选中/盒不可解析/未命中返回 null。命中判定以当前可用柄
1291
+ * 集合为面——角色过滤与锁定折叠在此一条缝生效。
1292
+ */
1293
+ resizeHandleAt(sceneX: number, sceneY: number): ResizeHandle | null;
1294
+ /**
1295
+ * 开始缩放会话:记录目标路径、柄、起点场景坐标与起始解析盒(ui 分支)。
1296
+ * 起点记录指针位置而非柄中心——位移以抓取点为基准,移动缘不跳到指针下
1297
+ * (与拖动保留抓取偏移同语义,两种主流做法中取不跳变侧)。
1298
+ * 已在手势中(拖动/缩放互斥)、路径或盒几何无法解析、路径落在锁定子树、
1299
+ * 柄的任一作用轴不在角色可缩放面内(行上拖 e/w、格内容上任意柄)时返回
1300
+ * false——gizmo 只渲染可用柄,此守卫兜其余手势来源(键盘/宿主直调)。
1301
+ */
1302
+ beginResize(path: LayerPath, handle: ResizeHandle, sceneX: number, sceneY: number): boolean;
1303
+ /**
1304
+ * 缩放进行中(工单 07):八柄几何求位 + 移动缘吸附 + 文档写回,一个 mergeKey
1305
+ * 事务合并成一步历史。
1306
+ *
1307
+ * 几何:移动缘 = 起始缘 + 场景位移,对缘固定,minSize 钳位不翻转
1308
+ * (resizeBox);非本柄轴的位移分量忽略。
1309
+ * 吸附:移动缘位点(resizeSnapPoints,角柄双缘、边柄单缘;盒中心不参与缩放
1310
+ * 吸附)对轴集合求位——轴集合与拖动同源(snapAxesForGesture),屏幕 6px 阈值
1311
+ * 随 zoom 折算;修正施加在指针位移上、再进钳位,吸附不破「对缘固定 + minSize」
1312
+ * 语义,吸附结果只改坐标不改尺寸语义(auto 标志采纳是手势起点的轴选择,不随
1313
+ * 吸附翻转)。命中的吸附轴回写会话态供吸附线呈现(瞬时回显,endResize 清空)。
1314
+ * 写回(工单验收项 2 的语义收口):被拖轴写声明尺寸并清 auto 标志——autoWidth/
1315
+ * autoHeight 图层首个位移事务把解析尺寸落地为声明值(「改尺寸标志」语义,
1316
+ * 属性面板 auto 前缀输入框的可编辑化同门);未被拖的轴零接触——autoWidth
1317
+ * 文本层拖 s 柄后仍随文本自然宽、QR 拖 e 柄高随宽保持正方形(从动轴不采纳)。
1318
+ * 写后重断言(updateSpec 同门):autoHeight⟹声明高归零的解码不变量 + 表格
1319
+ * 强同步 canonicalize(根表宽联动行宽、格内容宽同步、行高取最高格——行高缩到
1320
+ * 最高格以下被不变量顶回属预期)。position 按目标盒反解(锚点偏移随新尺寸
1321
+ * 重算,任意锚点/任意父级下落点都精确):同步完成后以草稿内的解析尺寸与
1322
+ * 父级盒求锚点偏移,position = 目标盒坐标 − 偏移 − 父级原点(嵌套路径的
1323
+ * position 是父级相对量,父级原点经 gestureParentOrigin 在写前状态反解——
1324
+ * 根层原点恒 (0,0),行为不变)。
1325
+ *
1326
+ * 拖不动写不动的同门守卫:无会话、目标层被结构编辑移除(悬空路径)整体
1327
+ * 空转(不发事务、不发布命中轴)。
1328
+ */
1329
+ resizeTo(sceneX: number, sceneY: number): void;
1330
+ /**
1331
+ * 结束缩放:闭合合并事务(下一步历史定格),会话态与命中轴回显清空。
1332
+ * 无会话空转(幂等,pointercancel 与 pointerup 双路同达)。
1333
+ */
1334
+ endResize(): void;
1335
+ /**
1336
+ * 武装建层(面板新增项/层型快捷键的统一入口,CONTEXT「武装」词条):置 ui
1337
+ * 分支待命层型,下一次画布按下即开画拉;建层提交或 Esc 即解除——一次性
1338
+ * 待命态,无粘性工具(spec 决策 2)。重复武装同型值等短路零通知。
1339
+ */
1340
+ armLayerCreate(type: LayerType): void;
1341
+ /** 解除建层武装(取消入口):仅清待命态,零副作用。 */
1342
+ cancelArmLayerCreate(): void;
1343
+ /**
1344
+ * 开始画拉会话:须已武装(未武装空转防御——画布手势态机的分派前置,此守卫
1345
+ * 兜其余调用来源),与拖动/缩放会话互斥(手势起点一次性定死,beginDrag/
1346
+ * beginResize 同门),文档未打开空转。层型在起点一次性捕获进会话(spec
1347
+ * 决策 7:拖拽中改主意 = Esc 重武装,v1 不做拖拽中层型切换)。会话只进
1348
+ * ui 分支——拖拽期纯视觉、文档零变更(spec 决策 4)。
1349
+ */
1350
+ beginLayerCreate(sceneX: number, sceneY: number): boolean;
1351
+ /**
1352
+ * 画拉进行中:橡皮筋求位(spatial/create.createRubberBandRect——两点正规化
1353
+ * + QR 钳 height := width + 左右/上下缘各两点进 resolveSnapPoints,吸附数学
1354
+ * 零新增)写回会话 rect 供 overlay 直读(虚线矩形 + W×H 气泡),命中轴回写
1355
+ * ui.snapAxes 走 GuidesOverlay 既有通道回显(dragTo 同门)。吸附排除面为空
1356
+ * (snapAxesForGesture(null)——新层尚未入库,全部可见根层供轴,copyMode
1357
+ * 首移同门)。零文档事务:修正只进会话几何,落库在 endCreate。
1358
+ */
1359
+ createTo(sceneX: number, sceneY: number): void;
1360
+ /**
1361
+ * 结束画拉:按死区分流落库(一次手势 = 一步历史,undo 整体回退落位 + 尺寸
1362
+ * + priority)。先清会话再落文档(commitTextEdit 同门——退出路径级联时幂等):
1363
+ * - 死区外:单事务写入「createDefaultLayer 缺省形态 + position 左上角对准
1364
+ * 橡皮筋矩形(含吸附修正)+ shape.width/height」并 insertRootLayerInDraft
1365
+ * 置顶(priority min−1、push 数组尾);
1366
+ * - 死区内(点击兜底,位移按 CREATE_DEAD_ZONE_SCREEN_PX 屏幕 px 随 zoom 折算,
1367
+ * ALT_DRAG_DEAD_ZONE_SCREEN_PX 同门):缺省尺寸、左上角对准点击点。
1368
+ * 落库后自动选中新层并解除武装;返回新层路径(绑定层消费:文本层据此自动
1369
+ * 进文本编辑,spec 决策 6)。无会话/无文档空转返回 null。点击兜底不产生
1370
+ * 零尺寸层(缺省形态自带可见尺寸)。
1371
+ */
1372
+ endCreate(): LayerPath | null;
1373
+ /**
1374
+ * 取消画拉(Esc 语义):清橡皮筋会话、命中轴回显与武装态——零文档写入、
1375
+ * 零历史步(拖拽期纯视觉因此取消零回退成本,spec 决策 4)。幂等:无会话时
1376
+ * 仅解除武装(待命态 Esc 同走此口)。
1377
+ */
1378
+ cancelCreate(): void;
1379
+ /**
1380
+ * 手势空间父级原点(嵌套路径 position 反解的坐标基准,resizeTo 专用):布局
1381
+ * 不变量 box = 父级原点 + 锚点偏移 + position(render.resolveLayerBox 同式)
1382
+ * 对被拖层成立,且父级原点在自身缩放中不动——兄弟/祖先几何不被自身尺寸写回
1383
+ * 牵动(格宽累加/行高累加只受前序兄弟影响)。故以手势同空间(模板子树经预览
1384
+ * 视图解析,与 startBox/startScene 同一坐标系)的当前盒线性反解:原点 = 当前
1385
+ * 盒 − 锚点偏移(手势空间父级盒尺寸入参)− 当前 position。根层恒 (0,0)——
1386
+ * 既有根层语义(父级 = 画布、原点即画布原点)不变;盒不可解析回落实地 (0,0)
1387
+ * (与根层同参,写回仍自洽)。须以写前状态调用:盒/position/解析尺寸三者
1388
+ * 同源才能精确反解。
1389
+ */
1390
+ private gestureParentOrigin;
1391
+ /**
1392
+ * 草稿内求缩放目标的父级盒尺寸(position 反解的锚点偏移入参):根层 = 画布
1393
+ * 尺寸(resolveLayerBox 根层语义同参);行/格取容器路径(rows/cells 段成对、
1394
+ * content 单段),经 layerBoxByPath 在草稿内解析——同步完成后的父级盒(行高
1395
+ * 增长等同步效应已含)。悬空回落画布尺寸(与根层同参,position 仍自洽)。
1396
+ */
1397
+ private parentDimsInDraft;
1398
+ /**
1399
+ * 方向键微调(kbd-nav 工单 02):选中层 position 按场景增量位移(注册表 8
1400
+ * 条目方向 × 步长归并到此,executeShortcut 传 ±1/±10)。作用面与拖动同门——
1401
+ * 选中什么微调什么,位置写法与 dragTo 同一语义(position 原位 += 增量);
1402
+ * 拖不动的空转(无选择/无文档/路径不可解析)。**不吸附**:吸附是拖动过程的
1403
+ * 连续行为(CONTEXT「微调」词条边界),微调是可预测的定量位移,不走
1404
+ * resolveSnap、不回显命中轴。
1405
+ * 连续微调合并一条历史:mergeKey 含选中层路径、方向无关——同层连按(跨方向)
1406
+ * 并成一步,undo 一次回连按前;换层即换步(mergeKey 随路径变);undo/redo
1407
+ * 走 store 既有断开规则(closeMerge)。断开是键控而非选择事件驱动:选择往返
1408
+ * (A→B→A 中途无文档变更)会并回 A 的原串,机制既有规则同款。拖动/缩放会话
1409
+ * 进行中空转(防插入事务拆分开放中的合并步,工单 07 缩放同门)。
1410
+ * 守卫(canvas-web-layer-lock 工单 01 挂账兑现):锁定子树空转(isLocked
1411
+ * 谓词同门——微调是变更类动作)。空转返回 false(executeShortcut 可用态口径)。
1412
+ */
1413
+ nudge(deltaX: number, deltaY: number): boolean;
1414
+ /**
1415
+ * 新增参考线(标尺拖出落线的写入口,工单 03 消费):水平/垂直轴、场景坐标。
1416
+ * 住 ui 分支——不进历史、不写 graph(ADR 0012);id 会话内自增供删除寻址。
1417
+ * 非有限坐标空转返回 null(绑定层异常输入防御,不产生通知)。
1418
+ */
1419
+ addGuide(guide: {
1420
+ orientation: GuideOrientation;
1421
+ position: number;
1422
+ }): Guide | null;
1423
+ /** 删除参考线(拖回标尺语义):按 id 移除;未知 id 空转返回 false */
1424
+ removeGuide(id: number): boolean;
1425
+ /**
1426
+ * 清空全部参考线(工具栏「清空参考线」写入口,工单 03 消费):ui 分支整体置空,
1427
+ * 与 removeGuide 同型广播 guides 分支通知(覆盖层失效重绘)。会话级语义同门——
1428
+ * 不进历史、不写 graph(ADR 0012),不可撤销;无文档或已空时空转(不产生通知)。
1429
+ */
1430
+ clearGuides(): void;
1431
+ /** 参考线列表(只读查询,呈现与吸附供轴同源) */
1432
+ listGuides(): readonly Guide[];
1433
+ /** 标尺显隐开关(⇧R 分派口):翻转 ui 偏好,不进历史 */
1434
+ toggleRulers(): void;
1435
+ /**
1436
+ * 对齐画布 action 族(对齐画布/贴边/贴角,ADR 0011):把 path 处图层盒整体
1437
+ * 对齐到画布几何——mode 十种:贴边 left/right/top/bottom(零边距)、居中
1438
+ * h-center/v-center、贴角 corner-tl/tr/bl/br(opts.margin 统一边距,缺省
1439
+ * ALIGN_CORNER_MARGIN_PX)。与属性面板「盒内对齐」(写 align 字段移动盒内
1440
+ * 内容)是两类动作,并存不回退。
1441
+ * 几何以 layerBoxAt(path) 解析的图层盒为唯一来源、画布尺寸取 doc.canvas;
1442
+ * 盒坐标对 position 线性(box = origin + anchorOffset + position),「目标
1443
+ * 盒坐标 − 当前盒坐标」的位移原样写 position 偏移、anchor 不动;盒大于画布
1444
+ * 不钳位(偏移可为负的领域语义,溢出侧按公式确定)。一次调用 = 一步历史
1445
+ * (无 mergeKey,连续点击不合并);已对齐(零位移)经 store 空 patch 短路
1446
+ * 不进历史;文档未打开/路径不可解析/未知 mode 空转(无历史步)。
1447
+ */
1448
+ alignToCanvas(path: LayerPath, mode: AlignToCanvasMode, options?: AlignToCanvasOptions): void;
1449
+ /**
1450
+ * 进入文本编辑:路径必须解析到 TextLayer。会话只进 ui 分支(不进历史),
1451
+ * live 文本住在绑定层的 textarea,内容层随即跳绘该层文本(防重影)。
1452
+ *
1453
+ * 内容类型会话标志(canvas-web-expression-editing 工单 01,spec 决策 2)按
1454
+ * 进入瞬间 `layer.expression !== null` 锚定进会话快照——「进入时计、编辑中
1455
+ * 稳定」(verticalAnchorY 同门):编辑中面板改标记不回写标志,提交去向由
1456
+ * 快照裁决(打标是显式动作,内容判定制已否决)。同路径重复进入短路(快照
1457
+ * 不重锚);换层进入按新层现状重锚。
1458
+ */
1459
+ beginTextEdit(path: LayerPath): boolean;
1460
+ /**
1461
+ * 提交漏斗的统一出口:Esc / Ctrl+Enter / blur / 点画布他处四条退出路径都收拢
1462
+ * 到这里。先清会话再落文档——退出路径可能级联触发(画布点按的 pointerdown
1463
+ * 提交与随后 blur 竞态),会话清空后后续调用幂等,保证一次退出恰一步历史。
1464
+ *
1465
+ * 提交矩阵(canvas-web-expression-editing 工单 01,spec 决策 3,空串判定先于
1466
+ * 片段判定):
1467
+ * - 串与文档一致:不进历史(进出编辑零噪声),返回 false——对比对象随会话
1468
+ * 模式取:表达式会话对比 `layer.expression`、字面会话对比 `layer.text`(现行
1469
+ * 语义);
1470
+ * - 空串:删除该图层(复用 deleteLayer 的 splice/重映射语义),任何模式同语义;
1471
+ * - 表达式会话且串含 ≥1 语法有效闭合片段(hasValidExpressionFragment):
1472
+ * updateDataExpression 标记写(保持标记、text 自动重镜像);
1473
+ * - 其余(字面会话恒走此支;表达式会话无合法片段回落):updateData 字面写、
1474
+ * 字面接管解标——含 `{{...}}` 按字面保留,杜绝无片段标记层。
1475
+ */
1476
+ commitTextEdit(text: string): boolean;
1477
+ /**
1478
+ * 编辑 overlay 的布局描述:与绘制/命中同一套布局策略求出的盒几何与排版字段
1479
+ * (textarea 以盒左上对位、盒尺寸占位,字号/行高/内边距按场景像素交给 CSS
1480
+ * transform 缩放)。verticalAnchorY 按进入编辑时的文档文本计算,编辑中保持
1481
+ * 稳定不随键入跳动。非文本层/路径失效返回 null。
1482
+ */
1483
+ textEditLayout(path: LayerPath): TextEditLayout | null;
1484
+ /** 编辑会话的目标图层查询:路径须解析到 TextLayer,其余(含无文档)返回 null */
1485
+ private textLayerAt;
1486
+ /**
1487
+ * 打开查找会话(⌘F 分派口):无文档空转返回 false;已开时值等短路零通知
1488
+ * (重复 ⌘F 的重新聚焦归绑定层)。查询词/替换词/游标跨关开保留(closeFind
1489
+ * →beginFind 同文档连续改字),openDocument 才整体重置。
1490
+ */
1491
+ beginFind(): boolean;
1492
+ /** 关闭查找会话(Esc 归面板组件调用):仅翻开合面,查询词与游标保留 */
1493
+ closeFind(): void;
1494
+ /** 查找词写入(查找条输入即扫的源头):查询词变更即重置游标(旧下标对新命中集无意义) */
1495
+ setFindQuery(query: string): void;
1496
+ /** 替换词写入 */
1497
+ setFindReplacement(replacement: string): void;
1498
+ /** 游标写入(上一个/下一个导航口):存原值,越界在读侧钳位 */
1499
+ setFindCursor(cursor: number): void;
1500
+ /**
1501
+ * 派生命中列表(overlay 高亮与查找条计数同源消费):每次从 doc + query 现算、
1502
+ * 不驻留(树小无压力,免 pruneDanglingPaths 式失配维护);视觉序 = 数组尾→头
1503
+ * (hitTest 同门),命中 = path + 偏移对。
1504
+ */
1505
+ listFindMatches(): readonly FindTextHit[];
1506
+ /** 当前游标(读侧钳位到派生命中列表:越界落末处/首处,空命中为 0) */
1507
+ get findCursor(): number;
1508
+ /**
1509
+ * 替换当前命中(查找条「替换」按钮):一次无 mergeKey 事务 = 一步历史;游标
1510
+ * 原地不动——事务后派生列表在被替换处缩一位,原下标即「下一处」(「替换后
1511
+ * 自动跳下一处」靠派生天然成立,末处替换则钳位停在尾)。写值经 replaceHitsInDraft
1512
+ * 与文本编辑提交同门(text 直写 + expression 保持 null)。无文档/空 query/
1513
+ * 无命中空转返回 false。
1514
+ */
1515
+ replaceOne(): boolean;
1516
+ /**
1517
+ * 全部替换(查找条「全部替换」按钮):命中(path + 偏移对)在事务前按原文
1518
+ * 快照一次算定,单事务内按同字段偏移降序应用、替换产物不重扫(replacement
1519
+ * 含查询串不死循环)——一次调用 = 一步历史,undo 一次全回。返回应用处数
1520
+ * (计数反馈「已替换 N 处」);无文档/空 query 空转返回 0。
1521
+ */
1522
+ replaceAll(): number;
1523
+ /**
1524
+ * 更新图层 spec 子树字段(属性面板的权威写入口):key 为图层内字段路径
1525
+ * (领域形态,如 ['shape','backgroundColor']、['position','x']、单段 ['fontSize']),
1526
+ * 拼上图层路径即 patch path。数值收整/钳位等取值策略由注册表层负责,内核透传。
1527
+ * 路径无法解析或字段悬空时静默空转;连续输入经 mergeKey 合并为一步历史。
1528
+ *
1529
+ * 表格子树的权威收口(工单 12):格 autoHeight 是带重算的切换(采纳内容动态高
1530
+ * 并固化,非裸标志写);其余写入后按解码强同步的不变量重断言(行宽=表宽、
1531
+ * 内容宽/高重同步、行高取最高格)——往返恒等在任何字段编辑后不破。
1532
+ */
1533
+ updateSpec(path: LayerPath, key: readonly string[], value: unknown, options?: TransactOptions): void;
1534
+ /**
1535
+ * 更新图层数据字段(wire data.value 的领域展开,按 type 分派):
1536
+ * TextLayer → text、ImageLayer → src、QrCodeLayer → value;
1537
+ * 空串/null 归空语义与解码逐条对齐(Image null、Text/Qr 空串)。
1538
+ * 字面写解除标记(工票 02,镜像 PHP 三内容层 setter):写值即置 expression =
1539
+ * null,值与标记的镜像关系随之解除——标记态文档经字面编辑退化为字面态,
1540
+ * 不可自动恢复属预期(spec §3.7 兼容面)。
1541
+ * 表/行/格无数据字段,空转。
1542
+ */
1543
+ updateData(path: LayerPath, value: string | null, options?: TransactOptions): void;
1544
+ /**
1545
+ * 数据字段表达式标记写入口(打标/解标,spec:canvas-web-expression-marking §1;
1546
+ * 镜像 PHP 三内容层 setExpression 与 decode 标记/未标记分支):
1547
+ * - expression 非 null = 打标:expression 原文 + 值字段恒镜像原文(Text.text /
1548
+ * Image.src / QrCode.value);标记态不做空串 →null 归一(decodeImageLayer
1549
+ * 标记分支同门)。
1550
+ * - expression = null = 解标:值字段保持现值退字面,`expression = null`;
1551
+ * 字面态空值归一交给 updateData/decode 分支(这里不清值,字面接管语义)。
1552
+ * - 仅三内容层生效,表/行/格/行模板空转(与 updateData 同门)。
1553
+ * - updateData 语义不变:字面写解除标记仍成立(spec §1 明示两者并排)。
1554
+ */
1555
+ updateDataExpression(path: LayerPath, expression: string | null, options?: TransactOptions): void;
1556
+ /** 更新画布级字段(未选中图层时面板的宽/高写入口);键由类型收窄为 width/height */
1557
+ updateCanvasProp(key: 'width' | 'height', value: number, options?: TransactOptions): void;
1558
+ /**
1559
+ * 注入数据源 schema 声明(载荷形态,D1 钉定:声明即 compile(canvas, dataset)
1560
+ * 收到的 data 载荷形状,根上下文候选 = 载荷顶层键)。声明经
1561
+ * normalizeExpressionSchemaSource 一次性编译收口——根级结构性问题(保留键
1562
+ * row/$ 前缀,前移填充期 reserved_root_key;根非对象/缺 properties 层级)降级
1563
+ * 为无候选 + console 警告,局部故障($ref 断链/外部指针/环引用/目标形态不符)
1564
+ * 不拒绝:该节点降叶子 + 注入期汇总告警一次,树其余部分照常服务,不抛错不
1565
+ * 弹错(辅助声明不作权威);null/undefined = 清除声明。声明只住 store ui 分支:
1566
+ * 不进 graph、不落 localStorage、不进 wire;openDocument 换文档不重置,
1567
+ * 重注入/清除走同一入口。签名与「注入即整体替换」语义不变(D6)。
1568
+ */
1569
+ setDataSourceSchema(raw: unknown): void;
1570
+ /** 上传可用性:未注入 uploadHandler 时为 false(宿主据此隐藏/禁用上传入口) */
1571
+ get canUpload(): boolean;
1572
+ /** 字体清单(内置可配置 + 自定义上传追加;清单 ≠ 物化,加载走渲染端物化管线) */
1573
+ get fontCatalog(): FontCatalog;
1574
+ /**
1575
+ * 本机图片 → 新增图片图层并写入可物化引用(全流程 = 一步历史):上传经注入
1576
+ * 实现,引用与 addRootLayerInDraft 落同一事务(撤销一次即回退整个上传建层),
1577
+ * 完成后自动选中新层。文档未打开直接返回 null(不产生上传副作用——字节不离开
1578
+ * 本机);上传失败异常上抛、文档不动。placement(kbd-nav 工单 05)缺省 =
1579
+ * 编辑器缺省盒;`at` 图层盒左上角对准场景点、`size` 1:1 落盒不缩放(拖文件
1580
+ * 入画布语义,自然尺寸由绑定层解码传入——内核无 DOM)。
1581
+ */
1582
+ uploadImageAsLayer(file: UploadFile, placement?: UploadImagePlacement): Promise<LayerPath | null>;
1583
+ /**
1584
+ * 本机图片 → 可物化引用(裸上传,不建层不写文档):引用落到哪个图片层由
1585
+ * 消费方决定(属性面板图片控件),与 uploadFont 同门的 bare 形态。文档未打开
1586
+ * 直接返回 null(不产生上传副作用——字节不离开本机);未注入 uploadHandler
1587
+ * 抛 UploadHandlerMissingError;handler 失败异常上抛。
1588
+ */
1589
+ uploadImage(file: UploadFile): Promise<string | null>;
1590
+ /**
1591
+ * 本机字体 → 可物化引用并加入字体清单(自定义条目,上传后即可选可用)。
1592
+ * 不写文档:引用落到哪个文本层由宿主/绑定层决定(属性面板字体控件)。
1593
+ */
1594
+ uploadFont(file: UploadFile): Promise<string>;
1595
+ private requireUpload;
1596
+ /**
1597
+ * 根层重排(图层面板拖动):面板坐标(0 = 视觉最上层),to 为 insert-before
1598
+ * 落点。数组序 splice + 移动层 priority 中点插值(保持「数组按 priority 降序」
1599
+ * 的契约语义,保存再打开顺序不变),一次调用 = 一步历史。语义细节见
1600
+ * layerPanel.moveRootLayerInDraft。
1601
+ */
1602
+ moveRootLayer(fromPanel: number, toPanel: number): void;
1603
+ /**
1604
+ * 表格行重排(图层面板行拖动):直接改 rows 数组序(容器内嵌套数组序语义,
1605
+ * priority 不参与,与根层语义分立),一次调用 = 一步历史。行内后代的选择/
1606
+ * 悬停路径随行重映射。
1607
+ */
1608
+ moveTableRow(tablePath: LayerPath, fromRow: number, toRow: number): void;
1609
+ /**
1610
+ * 新增根层(图层面板「新增」入口):工厂缺省形态 + 置顶 priority = min − 1
1611
+ * (空画布 0),push 到数组尾(视觉最上层)并自动选中新层;一次调用 = 一步历史。
1612
+ */
1613
+ addRootLayer(type: LayerType): void;
1614
+ /**
1615
+ * 删除图层(含子树):根层整删、行/格 splice、格内容置 null(语义见
1616
+ * layerPanel.deleteLayerInDraft);选中/悬停落在被删子树内即清空,其余路径
1617
+ * 平移重映射。一次调用 = 一步历史。
1618
+ * 模板子树删除面(spec §3.2):cells/content 两级放行(内核两型 union),
1619
+ * 仅行模板替身(template 收尾路径)拦截——删行模板 = 表退出模板态,归转换
1620
+ * 入口(convertTableToRows),不混入删除。
1621
+ * 锁定 guard(canvas-web-layer-lock 工单 01):路径落在锁定子树(锁定的根层
1622
+ * 及其行/格/内容)即静默空转——Delete/⌫ 与面板删除按钮的权威挡点,无历史步。
1623
+ */
1624
+ deleteLayer(path: LayerPath): void;
1625
+ /**
1626
+ * 重命名根图层(layer-panel-ux 工单 09):一次调用 = 一步历史(可撤销)。
1627
+ * 仅根图层——行/格是容器内结构(显示层「行 N / 格 N」派生标签),路径非根
1628
+ * 或不可解析一律空转(无历史步);空名 = 回退缺省(文档 name 归空串,
1629
+ * wire 键随之省略,显示层回落派生标签)。不改结构与 priority,选择/悬停
1630
+ * 路径不受影响。
1631
+ */
1632
+ renameLayer(path: LayerPath, name: string): void;
1633
+ /**
1634
+ * 开始重命名会话(图层面板双击/铅笔/F2 的统一入口):路径必须解析到根图层。
1635
+ * 会话只进 ui 分支(不进历史),输入框的草稿文本住绑定层,提交经
1636
+ * commitRename 漏斗一次性落文档。
1637
+ */
1638
+ beginRename(path: LayerPath | null): boolean;
1639
+ /**
1640
+ * 重命名提交漏斗的统一出口:Enter / blur 两条退出路径都收拢到这里。先清
1641
+ * 会话再落文档——退出路径可能级联触发(Enter 提交后 input 卸载的 blur),
1642
+ * 会话清空后后续调用幂等,保证一次退出恰一步历史。空名直传 renameLayer
1643
+ * (回退缺省语义在内核);与现名相同不产生历史步(进出编辑零噪声)。
1644
+ */
1645
+ commitRename(name: string): boolean;
1646
+ /** 取消重命名会话:清 ui 态不落文档(Esc 语义) */
1647
+ cancelRename(): void;
1648
+ /**
1649
+ * 切换根图层显示/隐藏(layer-panel-ux 工单 10):一次调用 = 一步历史(可撤销)。
1650
+ * 仅根图层——visible 在 LayerBase 面(渲染端整层跳过,行/格是容器内结构无独立
1651
+ * 可见性),路径非根或不可解析一律空转(无历史步),与 renameLayer 同门。
1652
+ * 隐藏的下游语义:渲染跳过(工单 01 契约)+ 命中/gizmo 过滤(spatial/hitTest、
1653
+ * 绑定层 gizmo),编辑器 UI 只消费这里的写入口。
1654
+ */
1655
+ toggleLayerVisibility(path: LayerPath): void;
1656
+ /**
1657
+ * 切换根图层锁定(canvas-web-layer-lock 工单 01):会话级锁定态,ui 分支
1658
+ * 变更——不进历史、不写 graph、wire 零键(红线 3 同门),openDocument 重置。
1659
+ * 仅根图层——行/格是容器内结构(锁定语义只在 LayerBase 面),路径非根或
1660
+ * 不可解析一律空转(renameLayer/toggleLayerVisibility 同门)。
1661
+ * 下游语义:锁定层整子树退出画布命中面(hitTest 过滤)、拖动起点/删除在
1662
+ * 内核空转(方向键微调已随 kbd-nav 工单 02 接入同一谓词);属性编辑/duplicate/
1663
+ * 显隐/改名/z 序等刻意通道不受限;⌘D 副本新路径天然无锁。
1664
+ */
1665
+ toggleLayerLock(path: LayerPath): void;
1666
+ /**
1667
+ * 锁定谓词(canvas-web-layer-lock 工单 01,ui 分支读取口):路径落在锁定
1668
+ * 子树即 true,判定收口 isLockedPath——hitTest 过滤、动作 guard、gizmo、
1669
+ * 面板投影共用,绑定层不得自行手写锁定路径比对。
1670
+ */
1671
+ isLocked(path: LayerPath | null): boolean;
1672
+ /**
1673
+ * 复制当前选中层:子树深拷贝快照进会话剪贴板,并捕获复制时点的绝对盒(粘贴
1674
+ * 落位基准)与源路径(sourcePath——粘贴时紧邻插入的解析凭据,alt-drag-paste
1675
+ * 工单 02)。源须为可落根层的类型(行/格是容器内结构,v1 不可复制);无选择/
1676
+ * 不可复制为 false。
1677
+ */
1678
+ copySelection(): boolean;
1679
+ /** 复制可用态(右键菜单「副本」的可用判定与 copySelection 同一语义) */
1680
+ get canCopySelection(): boolean;
1681
+ /**
1682
+ * 路径处图层可复制(alt-drag-paste 工单 01,Alt+拖 copyMode 的绑定层判定面):
1683
+ * 可解析且类型可落根层(canCopyLayerAt 同门——行/格是容器内结构不可复制,
1684
+ * 格内容可复制)。无文档为 false。
1685
+ */
1686
+ canCopyLayerAt(path: LayerPath): boolean;
1687
+ /**
1688
+ * 粘贴剪贴板(alt-drag-paste 工单 02 真原位):子树深拷贝插入根层——落点 =
1689
+ * 复制时点绝对盒、零偏移(重复粘贴同位叠放;锚点补偿保留:dx=0 时根层源
1690
+ * position 恒等原位、格内容源落其视觉位置);z 序紧邻源层(sourcePath 解析
1691
+ * 与退化置顶见 insertRootCopy);自动选中新层;一次调用 = 一步历史。空剪贴板
1692
+ * 为 null。每次粘贴从剪贴板快照重新克隆:粘贴产物之间以及与源文档都不共享
1693
+ * 引用。
1694
+ */
1695
+ pasteFromClipboard(): LayerPath | null;
1696
+ /**
1697
+ * 创建副本 = 复制当前选中层 + 固定一格偏移(+20)+ 置顶 + 自动选中,一步历史。
1698
+ * 落位以当前绝对盒为基准(连续副本基于选中的副本链式偏移,天然不重叠)。
1699
+ * **不覆盖剪贴板**(先复制 A 再副本 B,粘贴仍出 A)。无选择/不可复制为 null。
1700
+ */
1701
+ duplicateSelection(): LayerPath | null;
1702
+ /**
1703
+ * 粘贴/副本的公共落库尾:根层插入 + 自动选中新层;一次调用 = 一步历史。
1704
+ * source 给定时先试紧邻源层(源仍可解析且为根层——alt-drag-paste 工单 02 的
1705
+ * ⌘V 紧邻消费;路径寻址尽力而为:删源后同下标兄弟层/他文档同下标层会顶替
1706
+ * 解析,紧邻彼层是可接受的近似,工单 Comments 记档)——原语返回 null 或条件
1707
+ * 不满足回落置顶;source 缺省恒置顶(⌘D 语义不动)。
1708
+ */
1709
+ private insertRootCopy;
1710
+ /**
1711
+ * 复制样式(⌥⌘C / 右键菜单):按源类型适用面抓逐字段深拷贝快照进会话私有
1712
+ * 槽({sourceType, values})。源为**任意可解析路径**(根/行/格/格内容/模板
1713
+ * 子树皆可);无文档/路径不可解析/源无样式面(行模板替身,适用面为空)空转
1714
+ * 返回 false——空快照不占槽,已拷贝的样式不被无样式源清掉。拷贝不进历史
1715
+ * (剪贴板态不是文档态)。
1716
+ */
1717
+ copyStyle(path: LayerPath): boolean;
1718
+ /** 复制可用态(右键菜单):有可解析选择且源类型有样式面 */
1719
+ get canCopyStyle(): boolean;
1720
+ /**
1721
+ * 粘贴样式(⌥⌘V / 右键菜单):按目标类型适用面 ∩ 快照逐字段 verbatim 覆盖
1722
+ * (null 即值、Border 整对象;快照没有的字段 = 源不适型,静默跳过)。目标为
1723
+ * **任意可解析路径**——模板子树与格内容放行(尺寸字段不在样式集,行/格/
1724
+ * 内容宽高强同步天然不冲突);锁定语义天然放行(属性写通道,CONTEXT「锁定」
1725
+ * 词条)。单事务内循环写入 + autoHeight⟹height=0 断言 + canonicalizeTableSync
1726
+ * 一次收口(updateSpec 同门)——一次粘贴 = 一步历史,连续粘贴不合并(无
1727
+ * mergeKey);全等字段跳写、整事务零变化经 store 空 patch 短路不进历史;
1728
+ * 无文档/空槽/路径不可解析空转返回 false。
1729
+ */
1730
+ pasteStyle(path: LayerPath): boolean;
1731
+ /** 粘贴可用态(右键菜单):样式槽非空且有可解析选择;交集为零变化的空转在动作面兜底 */
1732
+ get canPasteStyle(): boolean;
1733
+ /**
1734
+ * z 序四件套(右键菜单语义 + kbd-nav 工单 01 键位):根层重排,v1 只作用根层
1735
+ * (行/格是数组序语义)。全部复用 moveRootLayer(priority 中点插值同款——
1736
+ * 置顶 = min−1、置底 = max+1、无整数间隙全表归一化兜底),一次调用 = 一步
1737
+ * 历史;已在目标端为无操作(不产生历史步)。返回是否发生移动(executeShortcut
1738
+ * 的可用态口径;右键菜单直调忽略返回值)。锁定层 z 序放行(锁定谓词的刻意
1739
+ * 通道,canvas-web-layer-lock 工单 01 同口径)。
1740
+ */
1741
+ bringToFront(): boolean;
1742
+ sendToBack(): boolean;
1743
+ /**
1744
+ * 前移/后移一格(kbd-nav 工单 01):面板序号 ±1 的单格重排——与置顶/置底、
1745
+ * 面板拖动同一 moveRootLayer 语义(中点插值优先、无间隙全表归一化兜底),仅
1746
+ * 根层(isRoot 同门),已最前/最后空转。连按各成一步历史(无 mergeKey,
1747
+ * shortcut action 不合步的先例)。落点按「insert-before 原始序号」折算:
1748
+ * 前移 = 插到原 panel−1 层之前(to = panel−1),后移 = 插到原 panel+1 层之后
1749
+ * (to = panel+2)。
1750
+ */
1751
+ bringForward(): boolean;
1752
+ sendBackward(): boolean;
1753
+ /** 选中根层的面板序号(0 = 视觉最上层);非根层选择/无文档为 null */
1754
+ private selectedRootPanelIndex;
1755
+ /**
1756
+ * 可分派的选中根层路径(⇧⌘L/⇧⌘H 分派口守卫,工单 02):无选择/无文档/非根
1757
+ * 形状/悬空(不可解析)一律 null——分派口「空转返回 false」与内核 action 的
1758
+ * 静默空转同口径(悬空窗口来自 prune 之外的结构步,兜底裁定可用态)。
1759
+ */
1760
+ private selectableRootPath;
1761
+ /**
1762
+ * 执行快捷键动作(注册表分类的出口):undo/redo/delete/copy/paste/duplicate/
1763
+ * copyStyle/pasteStyle(⌥⌘C/⌥⌘V,canvas-web-style-paste 工单 01)/
1764
+ * rename/findReplace(⌘F,canvas-web-find-replace 工单 01)/
1765
+ * toggleRulers/toggleLayerLock/toggleLayerVisibility 的统一分派面,以及
1766
+ * z 序四件套与缩放三件(kbd-nav 工单 01:bringForward/sendBackward/
1767
+ * bringToFront/sendToBack/zoomReset/fitToSurface/fitToSelection)、微调八动作
1768
+ * (kbd-nav 工单 02:nudgeUp/Down/Left/Right + Coarse 变体,归并到同一 nudge
1769
+ * 实现)与循环选层两动作(kbd-nav 工单 03:selectNextLayer/selectPrevLayer);
1770
+ * helpShortcuts(kbd-nav 工单 04)是 UI 面动作,由绑定层桥拦截路由
1771
+ * 帮助面板、不经此处。让路规则在
1772
+ * 分类器(classifyEditorShortcut)裁决,到达这里的动作不再重复判态;动作为
1773
+ * 空转(无选择/空剪贴板/非根层/已在端点)返回 false,其余 true。rename 开
1774
+ * 选中根层的重命名会话(F2,工单 09),不直接写文档;锁定/显隐作用于选中根层
1775
+ * (canvas-web-layer-lock 工单 02 + feature-status 显隐键位挂账补位),非根/
1776
+ * 无选择按可用态裁剪返回 false;相机三件不进历史、恒 true(surface 未知时
1777
+ * 内核静默空转,同 toggleRulers 的无条件口径)。
1778
+ */
1779
+ executeShortcut(action: EditorShortcutAction): boolean;
1780
+ /**
1781
+ * 新增表格行(面板「加行」入口):缺省行 + 缺省格(带文本内容)走重建路径
1782
+ * (行宽=表宽、内容宽=格宽、固定格压平内容高、行高取最高格——语义见
1783
+ * tableEditing.addTableRowInDraft),并自动选中新行;一次调用 = 一步历史。
1784
+ */
1785
+ addTableRow(tablePath: LayerPath): void;
1786
+ /**
1787
+ * 新增表格单元格(面板「加格」入口):缺省格 + 缺省文本内容走重建路径
1788
+ * (内容宽=格宽、固定格压平内容高、行高取最高格),并自动选中新格;
1789
+ * 一次调用 = 一步历史。
1790
+ */
1791
+ addTableCell(rowPath: LayerPath): void;
1792
+ /**
1793
+ * 新增模板表(「+」选择器入口,spec §2.1):createTemplateTable 缺省形态 +
1794
+ * 置顶 + 自动选中;一次调用 = 一步历史。rowsPath 必填(解码 rows_path_missing
1795
+ * 硬约束的对齐兜底,表单校验前置),空串 no-op。
1796
+ */
1797
+ addTemplateTable(rowsPath: string): void;
1798
+ /**
1799
+ * 模板行加格(spec §3.1):addTemplateCellInDraft 重建路径(末格宽/首格行宽、
1800
+ * 零高度耦合),自动选中新格;一次调用 = 一步历史。非模板态 no-op。
1801
+ */
1802
+ addTemplateCell(tablePath: LayerPath): void;
1803
+ /**
1804
+ * V1→V2 转换(spec §2.3):rowsPath 必填(空串 no-op,表单校验前置);rows
1805
+ * 子树内的选择/悬停回落表路径(rows 已清空不悬空)。一次调用 = 一步历史。
1806
+ */
1807
+ convertTableToTemplate(tablePath: LayerPath, rowsPath: string): void;
1808
+ /**
1809
+ * V2→V1 逆向转换(spec §2.3):模板实例化为单行(V1 归一重断言——auto 格
1810
+ * 采纳/固化、行高取最高格);模板子树内的选择/悬停回落表路径。一次调用 =
1811
+ * 一步历史。
1812
+ */
1813
+ convertTableToRows(tablePath: LayerPath): void;
1814
+ /** 子树内选择/悬停回落到子树根(容器清空后不悬空);子树外原样不动 */
1815
+ private clampSubtreeSelection;
1816
+ /**
1817
+ * 单元格移动:落点为「insert-before 原始序号」的目标行 + 序号。同行退化为
1818
+ * 数组序重排(与行重排同款 moveGuard 语义),跨行走重建路径(目标行高取
1819
+ * 最高格、内容层归属随格重同步)。一次调用 = 一步历史。
1820
+ */
1821
+ moveTableCellToRow(cellPath: LayerPath, targetRowPath: LayerPath, toCell: number): void;
1822
+ /** 同行单元格重排(直接改 cells 数组序,容器内嵌套数组序语义) */
1823
+ moveTableCell(rowPath: LayerPath, fromCell: number, toCell: number): void;
1824
+ /**
1825
+ * 行移动:落点为「insert-before 原始序号」的目标表 + 序号。同表退化为数组序
1826
+ * 重排(moveTableRow),跨表走重建路径(行宽同步目标表宽——addRow 副作用)。
1827
+ * 一次调用 = 一步历史。
1828
+ */
1829
+ moveTableRowToTable(rowPath: LayerPath, targetTablePath: LayerPath, toRow: number): void;
1830
+ /**
1831
+ * 格 autoHeight 切换:开且带内容 = 采纳内容动态高(autowrap 文本即
1832
+ * 「行数×行高+padding」)并固化(解码对带内容的格恒归一为固定高);开且空格 =
1833
+ * 置标志;关 = 清标志。采纳可能增高,行高随之取最高格。一次调用 = 一步历史。
1834
+ */
1835
+ setCellAutoHeight(cellPath: LayerPath, on: boolean): void;
1836
+ get canUndo(): boolean;
1837
+ get canRedo(): boolean;
1838
+ undo(): void;
1839
+ redo(): void;
1840
+ subscribe(listener: (change: EditorChange) => void): () => void;
1841
+ /** 外部脏源入口(如资源物化完成 → 'content') */
1842
+ invalidate(target?: InvalidateTarget): void;
1843
+ /**
1844
+ * 挂起帧同步排空(canvas-web-render-perf 工单 01):绑定层在「物理缓冲已被清空、
1845
+ * 需要背靠背补绘」的时机调用(CanvasSurface 的 resizeBuffers 赋 canvas.width 清屏
1846
+ * 后——ResizeObserver 回调在本帧 rAF 之后,等下帧重绘必现一帧空白)。有挂起帧即
1847
+ * 取消排定并按当前脏标立即 flush;无挂起帧空转幂等。内核保持无 DOM:只操作既有
1848
+ * cancelFrame/flush,不感知帧源(rAF 语义归绑定层注入的调度器)。
1849
+ */
1850
+ flushPendingFrames(): void;
1851
+ /** 取消挂起帧、退订 store、解绑绘制面 */
1852
+ dispose(): void;
1853
+ /**
1854
+ * 结构变更后重映射 ui 分支的路径态(选择/悬停/重命名会话/锁定集合):路径是
1855
+ * 数组下标身份,容器 splice 后按 remapPathAfterSplice 平移,被移出子树的路径
1856
+ * 落地为清除。ui 整体替换、不进历史;值未变的路径 set* 内部值等短路。
1857
+ * 锁定集合(canvas-web-layer-lock 工单 01)作为集合平移——锁「胶在层上」走过
1858
+ * splice(兄弟增删/根层重排后锁仍指原层),被移出容器的锁定路径落地解锁。
1859
+ */
1860
+ private remapPathSlices;
1861
+ /**
1862
+ * 锁定集合经映射平移(canvas-web-layer-lock 工单 01):逐路径过 map、滤除
1863
+ * 落地 null 的悬空路径;集合未变时 setLockedPaths 内容等短路(无锁/未变
1864
+ * 不通知)。remapPathSlices 的 splice 平移与 remapMovedSubtree 的前缀重挂
1865
+ * 共用同一形状——后者必须传移动前捕获的集合(源 splice 步已丢掉移动子树
1866
+ * 内的锁)。
1867
+ */
1868
+ private remappedLocks;
1869
+ /**
1870
+ * 跨容器移动后的选择/悬停/锁定集合重映射(工单 12 + canvas-web-layer-lock
1871
+ * 工单 01):先做源容器纯删除与目标容器纯插入的索引平移(移动子树内的路径
1872
+ * 在源删除步被置 null,不参与平移),再把移动前落在移动子树内的路径按前缀
1873
+ * 重挂到新位置(子树内部相对结构不变,仅容器段整体替换)。锁定集合的重挂
1874
+ * 当前是不变量兜底——锁定恒为根层路径、跨容器移动的子树恒为行/格子树,两集
1875
+ * 天然不相交;将来锁面扩展或根层跨容器移动接入时语义已在。
1876
+ */
1877
+ private remapMovedSubtree;
1878
+ /**
1879
+ * 悬空路径清理:选择/悬停/重命名会话指向已不存在的图层即清空(撤销/重做与
1880
+ * 采纳类收口共用)。锁定集合同门(canvas-web-layer-lock 工单 01):锁定路径
1881
+ * 悬空即解锁。已知限制(spec §3 记档不修):undo/redo 只有 prune 无平移——
1882
+ * 跨结构步撤销后锁可能错位(路径仍可解析、指到别的层)或清失,锁定钮常显
1883
+ * 可目视纠正;wire 持久化(锁长在层对象上)根治。
1884
+ */
1885
+ private pruneDanglingPaths;
1886
+ private onStoreChange;
1887
+ private requestFrame;
1888
+ private flush;
1889
+ private presentedViewport;
1890
+ private repaintContent;
1891
+ private repaintOverlay;
1892
+ }
1893
+
1894
+ /**
1895
+ * 命中测试纯函数(工单 06,spec「渲染与交互」):
1896
+ * 从视觉最上层向下命中——图层数组尾(后画在上)→ 头线性遍历 + 矩形包含,
1897
+ * 表格递归下钻(格内容 → 格 → 行 → 表)。盒几何与渲染模板共用
1898
+ * (resolveLayerBox + resolveChildAt,与 walkLayer 同一套推进公式),
1899
+ * 负溢出(盒出画布)照常包含,不做空间索引(图元量级小)。
1900
+ */
1901
+
1902
+ /** 命中选项:过滤集合由调用方注入(内核 ui 分支只读投影,纯函数不自取状态) */
1903
+ interface HitTestOptions {
1904
+ /**
1905
+ * 锁定根层路径集合(canvas-web-layer-lock 工单 01):锁定的根层整子树退出
1906
+ * 命中面、命中穿透下方层,与 visible 过滤同门;与底图豁免叠加时锁优先
1907
+ * (豁免只跳自身盒,锁整子树退出)。
1908
+ */
1909
+ readonly lockedPaths?: readonly LayerPath[];
1910
+ }
1911
+ /**
1912
+ * 场景坐标命中:返回视觉最上被中图层的路径(未中返回 null)。
1913
+ * 隐藏根层(layer-panel-ux 工单 10)与锁定根层(canvas-web-layer-lock 工单 01)
1914
+ * 整子树退出命中面——渲染端整层跳过 / 锁定层只退交互面(渲染照常),不可见/
1915
+ * 被锁定即不可点选,命中穿透其下方的层。
1916
+ */
1917
+ declare function hitTest(canvas: Canvas, sceneX: number, sceneY: number, policies?: TextLayoutPolicies, options?: HitTestOptions): LayerPath | null;
1918
+
1919
+ /**
1920
+ * 画拉建层橡皮筋求位几何(drag-create 工单 01,术语见根 CONTEXT.md「画拉建层」
1921
+ * 「武装」词条)。
1922
+ *
1923
+ * 拖拽期两点正规化出橡皮筋矩形(x = min、width = |dx|,y 同,反向拖不翻转);
1924
+ * QrCodeLayer 钳 height := width(高随宽从动保持方形,八柄缩放「QR 从动轴不
1925
+ * 采纳」同门);橡皮筋左右/上下缘各两点进 resolveSnapPoints——吸附数学零新增,
1926
+ * 命中修正整矩形平移并入几何(两点正规化与 QR 方形在修正后原样保持)。死区常量
1927
+ * 屏幕像素口径同 SNAP_THRESHOLD_SCREEN_PX(场景阈值 = 阈值 / zoom,缩放无关)。
1928
+ * 全模块纯函数、无 DOM;会话编排(武装/落库/解除)在会话门面。
1929
+ */
1930
+
1931
+ /** 画拉死区阈值(css 像素,缩放无关;场景阈值 = 阈值 / zoom):位移不越过即视作
1932
+ * 点击,endCreate 回落「缺省尺寸、左上角对准点击点」,不产生零尺寸层 */
1933
+ declare const CREATE_DEAD_ZONE_SCREEN_PX = 4;
1934
+ /** 橡皮筋求位结果:并入吸附修正后的矩形 + 命中轴(至多一垂直 + 一水平) */
1935
+ interface CreateRectResult {
1936
+ readonly rect: SnapBoxGeometry;
1937
+ readonly axes: readonly SnapAxis[];
1938
+ }
1939
+ /**
1940
+ * 画拉橡皮筋求位:两点正规化(起点场景坐标 + 当前点 → 左上角原点矩形)→
1941
+ * QR 钳 height := width → 左右/上下缘各两点对轴集合求位(已有已对齐 delta=0
1942
+ * 也回显命中轴的语义,dragTo/resizeTo 同门)→ 修正整矩形平移并入。钳方先于
1943
+ * 吸附——QR 的方边(y + width)才是真实的下缘吸附位点。
1944
+ */
1945
+ declare function createRubberBandRect(type: LayerType, start: Point, current: Point, axes: readonly SnapAxis[], threshold: number): CreateRectResult;
1946
+
1947
+ /**
1948
+ * 滚轮意图分类(工单 05,三态)。纯函数:输入只取事件修饰键快照,
1949
+ * 由绑定层(CanvasSurface)把 WheelEvent 映射进来后在内核判定。
1950
+ *
1951
+ * 语义(spec 拍板):ctrl/cmd + wheel 与触控板双指捏合 = 缩放(浏览器把
1952
+ * pinch 一律派发为 ctrl+wheel)、plain wheel = 平移、shift + wheel = 横向平移。
1953
+ */
1954
+ type WheelIntent = 'zoom' | 'pan' | 'pan-x';
1955
+ /** WheelEvent 的语义字段快照(内核不持有 DOM 事件类型) */
1956
+ interface WheelInput {
1957
+ ctrlKey: boolean;
1958
+ metaKey: boolean;
1959
+ shiftKey: boolean;
1960
+ }
1961
+ declare function classifyWheel(input: WheelInput): WheelIntent;
1962
+
1963
+ /**
1964
+ * 图层面板的内核语义(工单 10)。
1965
+ *
1966
+ * 顺序映射(spec「Vue 绑定与面板」拍板,防反直觉):
1967
+ * - 渲染顺序 = graph.layers 数组序 = priority 降序(数组头先画垫底,越大越垫底);
1968
+ * - 面板从上到下 = 数组逆序:面板顶 = 视觉最上层 = 数组尾 = 最小 priority。
1969
+ *
1970
+ * 两套重排语义分立:
1971
+ * - 根层重排走 priority 中点插值(保持「数组按 priority 降序」的契约语义,
1972
+ * 保存再打开顺序不变);插到上下邻之间 → priority = ⌊(上邻 + 下邻) / 2⌋,
1973
+ * 置顶 = min − 1、置底 = max + 1;priority 恒为整数(wire 解码 intval 同门,
1974
+ * 小数会在保存→打开时漂移),无整数间隙/同值冲突时对全表做一次按视觉序的
1975
+ * 带间隙阶梯归一化重赋(patch 大,仅兜底)。
1976
+ * - 表格行重排直接改 rows 数组序(行/格/内容是嵌套数组序语义,绘制按序遍历,
1977
+ * priority 字段在容器内不参与排序)。
1978
+ *
1979
+ * 全模块纯函数、无 DOM;draft 侧变换经 immer 事务落库,查询侧只读。
1980
+ */
1981
+
1982
+ /** 图层在容器树里的角色(决定面板缩进与可拖动性);templateRow = 行模板替身节点 */
1983
+ type LayerOutlineRole = 'root' | 'row' | 'cell' | 'content' | 'templateRow';
1984
+ /** 面板大纲节点:领域子树的纯投影(无文档写语义) */
1985
+ interface LayerOutlineNode {
1986
+ readonly type: LayerType;
1987
+ readonly path: LayerPath;
1988
+ readonly role: LayerOutlineRole;
1989
+ /**
1990
+ * 用户命名(layer-panel-ux 工单 09):原样携带文档 name(根层重命名后由此
1991
+ * 上屏),空串 = 未命名,显示层回落派生标签。行/格/内容同字段照带(不消费)。
1992
+ */
1993
+ readonly name: string;
1994
+ /**
1995
+ * 可见性(layer-panel-ux 工单 10):原样携带文档 visible,根层面板行的眼睛
1996
+ * 钮状态与隐藏行降不透明度据此投影。行/格/内容同字段照带(不消费——显隐
1997
+ * 语义只在 LayerBase 面)。
1998
+ */
1999
+ readonly visible: boolean;
2000
+ /**
2001
+ * 锁定投影(canvas-web-layer-lock 工单 02):isLockedPath 前缀判定——锁定的
2002
+ * 根层及其整棵子树(行/格/内容)都携 true。锁定态住 ui 分支(会话级、零
2003
+ * wire 键),经 buildLayerOutline 的 ui 分支入参喂入;面板锁定钮状态、锁定
2004
+ * 行样式与删除钮/菜单删除项置灰据此投影(内核空转为权威,UI 呈现一致)。
2005
+ */
2006
+ readonly locked: boolean;
2007
+ /**
2008
+ * 表达式前置(layer-panel-expression-prefix 工单 01):带标记内容层(三内容层
2009
+ * 且 expression !== null)的全部闭合片段按 expr 重构 `{{…}}` 外壳、单空格连接,
2010
+ * 面板零计算直读;空串 = 无前置(未标记 / 非内容层),对齐 name 字段先例;
2011
+ * 全部片段无效得常量 `{{…}}`。全角色节点照带。
2012
+ */
2013
+ readonly expressionPrefix: string;
2014
+ /** TableLayer 专属:是否模板态(+行按钮置灰与 title 的判定面,spec §2.5) */
2015
+ readonly templated?: boolean;
2016
+ readonly children: readonly LayerOutlineNode[];
2017
+ }
2018
+ /**
2019
+ * 面板大纲:根层按视觉层级排列(数组逆序——面板顶部 = 视觉最上层 = 数组尾),
2020
+ * 表格内行/格/内容保持数组序(行 0 在视觉顶部、格 0 在左)。
2021
+ * lockedPaths 为 ui 分支入参(canvas-web-layer-lock 工单 02):锁定态不写 graph,
2022
+ * 投影按 isLockedPath 前缀判定携带到锁定子树全树;缺省 [](无锁投影)。
2023
+ */
2024
+ declare function buildLayerOutline(doc: Canvas, lockedPaths?: readonly LayerPath[]): readonly LayerOutlineNode[];
2025
+ /**
2026
+ * 编辑器新建图层的缺省形态:可见尺寸(0×0 无法命中/抓取)、top-left 摆位、
2027
+ * 中性配色。与 wire 解码缺省是两回事(那是「手写 JSON 宽进」语义),这里是
2028
+ * 编辑器 UX 语义;priority 统一由 addRootLayer 按「置顶 min−1」赋值。
2029
+ */
2030
+ declare function createDefaultLayer(type: LayerType): Layer;
2031
+ /**
2032
+ * 模板表工厂(spec §2.1):V1 表缺省形态(400×120)+ 行模板装配——auto 行(声明
2033
+ * 高按解码归一为 0、宽 := 表宽关 autoWidth)+ 满宽 60 高缺省格带缺省文本内容
2034
+ * (内容宽度同步、零高度耦合,ADR 0006)。rowsPath 由调用方必填(解码
2035
+ * rows_path_missing 硬约束,创建表单校验前置);缺省格 60 高保证空态预览行可见
2036
+ * 可点(合成高 = 最高格,03 票 E1 机制)。
2037
+ */
2038
+ declare function createTemplateTable(options: {
2039
+ rowsPath: string;
2040
+ }): TableLayer;
2041
+ /**
2042
+ * 「insert-before 原始序号 to」移动的公共守卫与落点折算:from/to 须为安全整数、
2043
+ * from ∈ [0, n)、to ∈ [0, n];to ∈ {from, from+1}(原位/相邻落点)为无操作。
2044
+ * 返回摘除自身后的插入下标;null = 无操作(n < 2 时任何入参都落入无操作集)。
2045
+ * 工单 12 的同行格重排复用同一守卫(与行重排同款语义)。
2046
+ */
2047
+ declare function moveGuard(n: number, from: number, to: number): number | null;
2048
+ /**
2049
+ * 根层重排(draft 原位变换):面板坐标(0 = 视觉最上)折算数组坐标做 splice,
2050
+ * 再对移动层做 priority 中点插值。from 为被拖面板序号,to 为「insert-before
2051
+ * 原始面板序号」落点(∈ [0, N];to = N 即面板底);to ∈ {from, from+1} 或越界
2052
+ * 为无操作。数组序全程保持 priority 降序(渲染/解码同源的不变量)。
2053
+ * 返回移动的 (from, to) 数组下标(供选择路径重映射);null = 无变化。
2054
+ */
2055
+ declare function moveRootLayerInDraft(draft: Draft<Canvas>, fromPanel: number, toPanel: number): {
2056
+ from: number;
2057
+ to: number;
2058
+ } | null;
2059
+ /**
2060
+ * 表格行重排(draft 原位变换):直接改 rows 数组序——容器内是嵌套数组序语义,
2061
+ * 行的 priority 不参与排序、不插值(与根层重排语义分立)。from/to 为行序号
2062
+ * (行 0 在视觉顶部),to 为「insert-before 原始序号」落点 ∈ [0, rows.length]。
2063
+ * 返回 (from, to)(to 为插入后最终下标,供路径重映射);null = 无变化/入参不合法。
2064
+ */
2065
+ declare function moveTableRowInDraft(draft: Draft<Canvas>, tablePath: LayerPath, fromRow: number, toRow: number): {
2066
+ from: number;
2067
+ to: number;
2068
+ } | null;
2069
+ /**
2070
+ * 新增根层(draft 原位变换):工厂缺省形态 + priority = min − 1(空画布取 0),
2071
+ * push 到数组尾(视觉最上层)。返回新层的数组下标。
2072
+ */
2073
+ declare function addRootLayerInDraft(draft: Draft<Canvas>, type: LayerType): number;
2074
+ /**
2075
+ * 根层插入(draft 原位变换,工单 14 剪贴板粘贴共用):priority = min − 1(空画布
2076
+ * 取 0)+ push 到数组尾(视觉最上层)。数组序保持 priority 降序的解码不变量
2077
+ * (min−1 严格小于现最小值)。调用方负责传入的图层形态(如粘贴子树的深拷贝 +
2078
+ * 位置偏移)。返回新层的数组下标。
2079
+ */
2080
+ declare function insertRootLayerInDraft(draft: Draft<Canvas>, layer: Draft<Layer>): number;
2081
+ /**
2082
+ * 根层紧邻插入(draft 原位变换,alt-drag-paste 工单 01 原语,⌘V 粘贴工单 02 共用):
2083
+ * 新层 splice 到源层后一格 = 源层视觉上方一格,priority 取源与其视觉上一层(数组
2084
+ * 序 +1 位)之间的中点插值——数组序全程保持 priority 降序的解码不变量,整数插值
2085
+ * 同 moveRootLayerInDraft 门规(midpointPriority:无整数间隙时全表归一化兜底);
2086
+ * 源为视觉最顶/数组尾时取 source.priority − 1(置顶 min−1 同门)。
2087
+ * source 按根下标或路径寻址:路径取根祖先段(path[1],格内容等子树源紧邻其根层)。
2088
+ * 返回新层的数组下标;源下标越界/路径无数字根段返回 null(退化语义归调用方——
2089
+ * ⌘V 的「源不可解析退化置顶」在消费面回落 insertRootLayerInDraft)。
2090
+ */
2091
+ declare function insertRootLayerAdjacentInDraft(draft: Draft<Canvas>, layer: Draft<Layer>, source: number | LayerPath): number | null;
2092
+ /** 删除定位:list = 容器列表 splice(roots/rows/cells),content = 格内容置 null */
2093
+ type DeletedLayerRef = {
2094
+ kind: 'list';
2095
+ containerPath: LayerPath;
2096
+ key: 'layers' | 'rows' | 'cells';
2097
+ index: number;
2098
+ } | {
2099
+ kind: 'content';
2100
+ path: LayerPath;
2101
+ };
2102
+ /**
2103
+ * 删除图层(draft 原位变换):根层连子树整删、行/格走列表 splice、格内容置 null
2104
+ * (镜像 impl 研究 §2.8「条目操作」)。路径越界/形态不符/内容已空返回 null(无操作)。
2105
+ */
2106
+ declare function deleteLayerInDraft(draft: Draft<Canvas>, path: LayerPath): DeletedLayerRef | null;
2107
+
2108
+ /**
2109
+ * 剪贴板内核语义(工单 14):复制/粘贴/创建副本的纯函数面。
2110
+ *
2111
+ * - 剪贴板是**会话级**(不碰 OS 剪贴板、不跨会话):条目 = 选中层子树的深拷贝
2112
+ * 快照 + 复制时点的绝对盒(粘贴落位的基准)。graph 领域类型是纯 JSON 形态
2113
+ * (往返恒等契约),深拷贝用 JSON 往返——与源文档树彻底断开引用,复制后的
2114
+ * 原件编辑不影响粘贴产物。
2115
+ * - 可复制类型 = 可落根层的类型(文/图/码/表)。行/格是容器内结构(数组序语义、
2116
+ * priority 不参与根层排序),v1 粘贴统一落根层,故不可复制;格内容(文本/图片/
2117
+ * 码)可复制——粘贴出表成为根层。
2118
+ * - 粘贴落位(alt-drag-paste 工单 02 真原位):目标盒左上 = 复制时点绝对盒 +
2119
+ * 偏移,按**根层语义**反解 position(锚点补偿用画布尺寸,见 pastePosition)——
2120
+ * 格内容的 position 是格内相对值,直接平移会落到画布左上;根层源在此式下退化
2121
+ * 为 position + 偏移。⌘V 粘贴偏移入参恒为零(重复粘贴同位叠放,递增偏移链已
2122
+ * 删):dx=0 时根层源 position 恒等原位(offset 相消)、格内容源落其视觉位置;
2123
+ * PASTE_OFFSET_PX 保留给 ⌘D/副本。z 序紧邻源层由消费面(editor.pasteFromClipboard
2124
+ * 解析 sourcePath)选择 insertRootLayerAdjacentInDraft / 退化置顶。
2125
+ * - 创建副本 = 复制态 + 固定一格偏移,**不覆盖剪贴板**(excalidraw 同款语义);
2126
+ * 连续副本基于当前选中的副本链式偏移,天然不重叠。
2127
+ */
2128
+
2129
+ /** 粘贴/副本的位置偏移基数(场景像素,整数) */
2130
+ declare const PASTE_OFFSET_PX = 20;
2131
+ /** 类型是否可粘贴为根层(= 是否可复制) */
2132
+ declare function isRootPasteableType(type: LayerType): boolean;
2133
+ /** 路径处图层是否可复制(可解析且类型可落根层);剪贴板入口与菜单可用态共用 */
2134
+ declare function canCopyLayerAt(doc: Canvas, path: LayerPath): boolean;
2135
+ /**
2136
+ * 子树深拷贝:JSON 往返(领域类型纯 JSON 形态)。输入可能是 immer draft(复制
2137
+ * 发生在文档树读取时),JSON 序列化天然产出普通对象,与 draft/源树断开引用。
2138
+ */
2139
+ declare function cloneLayerSubtree<T extends Layer>(layer: T): T;
2140
+ /**
2141
+ * 粘贴落位的 position 反解:目标盒左上 = 源绝对盒左上 + (dx, dy),按根层语义
2142
+ * (父盒 = 画布)回推 position = 目标 − anchorOffset(锚点, 画布尺寸, 盒尺寸)。
2143
+ * 根层源退化为 position + 偏移(offset 相消);格内容源把格内相对 position 换算
2144
+ * 成画布绝对落位——副本出现在源内容视觉位置附近而非画布左上。
2145
+ */
2146
+ declare function pastePosition(sourceBox: LayerBox, anchor: Anchor, docWidth: number, docHeight: number, dx: number, dy: number): {
2147
+ x: number;
2148
+ y: number;
2149
+ };
2150
+ /**
2151
+ * 粘贴产物准备:深拷贝 + 按源绝对盒反解根层 position(语义见 pastePosition)。
2152
+ * 锚点保持不变(副本忠实于源),盒尺寸用复制时点的布局结果(autoHeight 文本
2153
+ * 与副本的布局结果一致)。
2154
+ */
2155
+ declare function prepareRootPaste<T extends Layer>(layer: T, sourceBox: LayerBox, docWidth: number, docHeight: number, dx: number, dy: number): T;
2156
+
2157
+ /**
2158
+ * 样式剪贴板内核语义(canvas-web-style-paste 工单 01):样式集/适用面常量 +
2159
+ * 复制快照/粘贴落地的纯函数面。
2160
+ *
2161
+ * - 样式 = 三族字段子集(spec 决策 2):盒内对齐(align.horizontal/vertical)、
2162
+ * 形状皮肤(shape.backgroundColor/shape.border/shape.padding)、文本族
2163
+ * (font/fontSize/fontColor/autowrap/angle/shape.lineHeight,仅文本层适用;
2164
+ * lineHeight 虽住 shape 段但随文本族走跨型规则)。明确排除:位置/尺寸/锚点
2165
+ * (几何不是样式)、name/visible/priority、数据字段 text/src/value(内容走
2166
+ * 复制/粘贴本体)、expression、表格结构字段(template/rows/rowsPath)。
2167
+ * - 适用面清单(spec 决策 3)按 LayerType 由内核 editing 域自持——对齐
2168
+ * editor-vue fieldSchema 的分组事实但**不引它**(依赖方向红线):fieldSchema
2169
+ * 给行模板替身只暴露高/高自适应(尺寸族不在样式集),故其适用面为空。
2170
+ * - 快照 = {sourceType, values}:逐字段 JSON 深拷贝(与源层断开引用,后续编辑
2171
+ * 源层不影响);拷贝不进历史(剪贴板态不是文档态,session 槽同门)。
2172
+ * - 粘贴 = 目标类型适用面 ∩ 快照字段逐字段 verbatim 覆盖:null 即值(无填充/
2173
+ * 无边框是样式事实)、Border 整对象覆盖;源不适型字段静默跳过;全等字段
2174
+ * 跳写——结构等值的新引用 immer 仍记 patch,跳写保「零变化 = 空 patch =
2175
+ * 不进历史」真短路。autoHeight⟹height=0 断言与 canonicalizeTableSync 重断言
2176
+ * 在 session 事务内一次收口(updateSpec 同门),本模块只做字段读写与判定。
2177
+ */
2178
+
2179
+ /**
2180
+ * 按 LayerType 的样式字段适用面(spec 决策 3):键为图层内字段路径。行模板替身
2181
+ * 适用面为空——面板只暴露高/高自适应(尺寸族不在样式集),无样式可复制可贴。
2182
+ */
2183
+ declare const STYLE_FIELD_KEYS_BY_TYPE: Readonly<Record<LayerType, readonly (readonly string[])[]>>;
2184
+ /** 样式快照(样式剪贴板条目):源类型 + 适用面逐字段值 */
2185
+ interface StyleSnapshot {
2186
+ readonly sourceType: LayerType;
2187
+ /** 字段值:键 = 字段路径点串(如 'align.horizontal'),值 = JSON 深拷贝 */
2188
+ readonly values: Readonly<Record<string, unknown>>;
2189
+ }
2190
+ /**
2191
+ * 按源类型适用面抓逐字段深拷贝快照;适用面为空(行模板替身)返回 null——
2192
+ * 无样式可复制,调用方空转(空快照不占槽,已拷贝的样式不被无样式源清掉)。
2193
+ */
2194
+ declare function captureStyleSnapshot(layer: Layer): StyleSnapshot | null;
2195
+ /** 逐字段应用快照到图层 draft(目标适用面 ∩ 快照):verbatim 覆盖、null 即值、
2196
+ * Border 整对象;快照没有的字段(源不适型)静默跳过;全等字段跳写。返回实际
2197
+ * 写入的字段键(空数组 = 零变化,调用方经 store 空 patch 空转)。
2198
+ */
2199
+ declare function applyStyleFieldsInDraft(layer: Draft<Layer>, snapshot: StyleSnapshot): (readonly string[])[];
2200
+
2201
+ /**
2202
+ * 表格容器结构编辑的内核语义(工单 12)。
2203
+ *
2204
+ * 重建路径与 graph 解码同源:decode.ts 镜像的 addRow/addCell/addContentLayer
2205
+ * 容器副作用在这里以 draft 变换原语复现,编辑器的行/格增删移动一律经这些原语
2206
+ * 落库——「add 同步语义」与「打开 graph 的强制同步」由同一份规则描述:
2207
+ *
2208
+ * - addRow:行宽 := 表宽、autoWidth 关;
2209
+ * - addCell:行高取最高单元格(只增长不收缩),增长时固化(autoHeight 关);
2210
+ * - addContentLayer:内容宽 := 格宽、autoWidth 关;格 auto 高 → 采纳内容动态高
2211
+ * 并固化(autoHeight 关),格固定高 → 压平内容高并关内容 autoHeight。
2212
+ *
2213
+ * 解码在每次打开时重断言这些耦合(行宽=表宽、内容宽=格宽、固定格内容高=格高、
2214
+ * 行高 ≥ 最高格),因此编辑器的字段写入(updateSpec)在表格子树按同一组规则
2215
+ * 收口(canonicalizeTableSyncInDraft)——凡解码会强同步的耦合,写入后立即重断言,
2216
+ * encode→decode 往返恒等在任何编辑序列后不破;解码不强求的(行高收缩)同样
2217
+ * 不重算,「先 add 后改尺寸不重算」的既有语义在重建路径下成立。
2218
+ *
2219
+ * 带内容的格在解码后恒为固定高(auto 标志被采纳语义固化),因此格的 autoHeight
2220
+ * 切换不是裸标志写:开 = 采纳内容动态高(autowrap 文本即「行数×行高+padding」)
2221
+ * 并固化,空格 = 置标志(解码对无内容的格保留该标志,往返恒等不受影响)。
2222
+ *
2223
+ * 模板态(TableLayer V2):行模板与 rows XOR,行级结构编辑在模板态拒绝(镜像
2224
+ * PHP addRow 抛 InvalidArgumentException,编辑器取 no-op 返回 null);表宽写入
2225
+ * 对行模板做同款宽度重同步(镜像解码 setTemplate→setWidth)。模板创作 UX(spec
2226
+ * `.scratch/canvas-web-template-authoring/`):模板格的增删重排开放——加格镜像
2227
+ * PHP TableRowTemplate::addCell 的零高度耦合(ADR 0006),行模板本身不可增删,
2228
+ * 双向转换原语是唯一合法的模板态进出门;模板子树进大纲、替身路径可选中(推翻
2229
+ * 决策 2026-09,勘误见 canvas-web-table-v2 issues/02 Comments)。
2230
+ *
2231
+ * 全模块纯 draft 变换、无 DOM;与 layerPanel.ts(工单 10 根层/行重排)分立。
2232
+ */
2233
+
2234
+ type DraftRow = Draft<TableRowLayer>;
2235
+ type DraftCell = Draft<TableCellLayer>;
2236
+ type DraftContent = Draft<Layer>;
2237
+ /** 行模板/具体行的 shape 写入面:syncRowWidth 只触 shape,两型结构兼容(工票 02) */
2238
+ type DraftRowOrTemplate = Draft<TableRowLayer | TableRowTemplateLayer>;
2239
+ /** addRow 副作用:行宽同步表宽并关 autoWidth(行模板重断言复用同款同步) */
2240
+ declare function syncRowWidthInDraft(row: DraftRowOrTemplate, tableWidth: number): void;
2241
+ /** addCell 副作用:行高取最高单元格(只增长不收缩),增长时固化(关 autoHeight) */
2242
+ declare function growRowToCellInDraft(row: DraftRow, cellHeight: number): void;
2243
+ /**
2244
+ * addContentLayer 副作用:内容宽同步格宽并关 autoWidth;格 auto 高 → 采纳内容
2245
+ * 动态高并固化(关 autoHeight),格固定高 → 压平内容高并关内容 autoHeight。
2246
+ * layerHeight 消费文本策略注入缝(与绘制/命中同一套度量)。
2247
+ */
2248
+ declare function syncContentIntoCellInDraft(content: DraftContent, cell: DraftCell, policies?: TextLayoutPolicies): void;
2249
+ /**
2250
+ * 新增行(draft 原位变换):缺省行 + 缺省格(带缺省文本内容)走完整重建路径——
2251
+ * 行宽=表宽(addRow)→ 内容宽=格宽、固定格压平内容高(addContentLayer)→
2252
+ * 行高取最高格(addCell)。首格宽取行宽(单格行铺满),后续格宽随末格(均齐
2253
+ * 直觉)。返回新行下标;路径无法解析到表返回 null。
2254
+ *
2255
+ * 模板态守卫(工票 02):目标表 template !== null 返回 null、文档零变化——
2256
+ * 镜像 PHP addRow 抛 InvalidArgumentException 的 API 误用语义,编辑器取 no-op
2257
+ * (防 template+rows 双键落 wire)。
2258
+ */
2259
+ declare function addTableRowInDraft(draft: Draft<Canvas>, tablePath: LayerPath): number | null;
2260
+ /**
2261
+ * 新增单元格(draft 原位变换):缺省格 + 缺省文本内容走重建路径(内容同步 + 行高
2262
+ * 取最高格),格宽取末格宽(首格取行宽)。返回新格下标;路径无法解析到行返回 null。
2263
+ */
2264
+ declare function addTableCellInDraft(draft: Draft<Canvas>, rowPath: LayerPath): number | null;
2265
+ /**
2266
+ * 模板行加格(draft 原位变换,spec §3.1):缺省格 + 缺省文本内容——格宽取末格
2267
+ * 宽(无格取模板行宽)、内容只做宽度同步(addTemplateContentLayer 镜像);格与
2268
+ * 内容的声明高/autoHeight 原样、模板行 shape 零触碰(ADR 0006 高度耦合全豁免,
2269
+ * 无任何行高耦合)。返回新格下标;路径非表/非模板态返回 null。
2270
+ */
2271
+ declare function addTemplateCellInDraft(draft: Draft<Canvas>, tablePath: LayerPath): number | null;
2272
+ /**
2273
+ * V1→V2 转换(draft 原位变换,spec §2.3):末行深拷贝重标定为行模板——搬运规则
2274
+ * 为行宽 := 表宽关 autoWidth(解码 setTemplate 同门重断言,V1 行宽恒等表宽,数值
2275
+ * 不变),格宽/格高/内容 data 全部原样(V1 带内容格恒固定高,无 auto 标志残留,
2276
+ * 零特判);rows 清空(XOR)、rowsPath 落 data 域。rowsPath 空串拒绝(解码
2277
+ * rows_path_missing 硬约束的对齐兜底——创建表单校验前置,此处双保险)。返回
2278
+ * true;no-op(路径非表/已是模板态/空表/末行零格/rowsPath 空)返回 null。
2279
+ */
2280
+ declare function convertTableToTemplateInDraft(draft: Draft<Canvas>, tablePath: LayerPath, rowsPath: string): boolean | null;
2281
+ /**
2282
+ * V2→V1 逆向转换(draft 原位变换,spec §2.3):模板深拷贝重标定为单行——内容
2283
+ * data 原样(expression 标记保留,V1 字面渲染既有行为),随后按解码副作用重断言
2284
+ * V1 归一(decodeTableRowLayer/decodeTableCellLayer 同门,往返恒等要求):带内容
2285
+ * 格 auto → 采纳内容动态高并固化、固定格压平内容高(addContentLayer 语义);空
2286
+ * auto 格声明高归零(auto 标志解码归一);行高取最高格增长固化(addCell 语义,
2287
+ * 只增长不收缩)。template 置 null、rowsPath 清空(V1 不消费)。返回 true;
2288
+ * no-op(路径非表/非模板态)返回 null。
2289
+ */
2290
+ declare function convertTableToRowsInDraft(draft: Draft<Canvas>, tablePath: LayerPath, policies?: TextLayoutPolicies): boolean | null;
2291
+ /** 跨容器移动的重映射凭据:源容器纯删除 + 目标容器纯插入(from = 原长、to = 落点) */
2292
+ interface MovedSubtreeRef {
2293
+ /** 移动前子树根路径(选择/悬停重挂基准) */
2294
+ fromPath: LayerPath;
2295
+ /** 移动后子树根路径 */
2296
+ toPath: LayerPath;
2297
+ source: {
2298
+ containerPath: LayerPath;
2299
+ key: 'rows' | 'cells';
2300
+ from: number;
2301
+ to: number;
2302
+ };
2303
+ target: {
2304
+ containerPath: LayerPath;
2305
+ key: 'rows' | 'cells';
2306
+ from: number;
2307
+ to: number;
2308
+ };
2309
+ }
2310
+ /**
2311
+ * 同行格重排(draft 原位变换):直接改 cells 数组序——容器内嵌套数组序语义,格的
2312
+ * priority 不参与(与 moveTableRow 同款 moveGuard 语义)。from/to 为格序号(格 0
2313
+ * 在视觉左端),to 为「insert-before 原始序号」落点 ∈ [0, cells.length]。返回
2314
+ * (from, to)(to 为插入后最终下标,供路径重映射);null = 无变化/入参不合法。
2315
+ */
2316
+ declare function moveTableCellInDraft(draft: Draft<Canvas>, rowPath: LayerPath, fromCell: number, toCell: number): {
2317
+ from: number;
2318
+ to: number;
2319
+ } | null;
2320
+ /**
2321
+ * 行跨表移动(draft 原位变换):从源表摘除、插入目标表 toIndex 处,addRow 副作用
2322
+ * 复现(行宽同步目标表宽)。toIndex ∈ [0, 目标 rows.length](insert-before 落点,
2323
+ * 不需摘除调整——目标容器不含被移行)。同表重排返回 null(moveTableRow 的
2324
+ * moveGuard 语义,调用方分流)。
2325
+ */
2326
+ declare function moveTableRowToTableInDraft(draft: Draft<Canvas>, rowPath: LayerPath, targetTablePath: LayerPath, toIndex: number): MovedSubtreeRef | null;
2327
+ /**
2328
+ * 格跨行移动(draft 原位变换):从源行摘除、插入目标行 toIndex 处,addCell 副作用
2329
+ * 复现(目标行高取最高格)+ addContentLayer 副作用重断言(内容层归属随格,宽/高
2330
+ * 同步幂等)。toIndex ∈ [0, 目标 cells.length](insert-before 落点,不需摘除调整)。
2331
+ * 同行重排返回 null(moveTableCellInDraft 的 moveGuard 语义,调用方分流)。
2332
+ */
2333
+ declare function moveTableCellToRowInDraft(draft: Draft<Canvas>, cellPath: LayerPath, targetRowPath: LayerPath, toIndex: number, policies?: TextLayoutPolicies): MovedSubtreeRef | null;
2334
+ /**
2335
+ * 格 autoHeight 切换(draft 原位变换):开且带内容 = 采纳内容动态高并固化
2336
+ * (解码对带内容的格恒归一为固定高,标志保留会破坏往返恒等);开且空格 = 置
2337
+ * 标志并把声明高归零(解码对 auto 标志恒清零声明高,PHP setHeight('auto') 同门);
2338
+ * 关 = 清标志。采纳可能增高,行高取最高格的解码不变量随之维持(只增长不收缩)。
2339
+ * 路径无法解析到格返回 false。
2340
+ */
2341
+ declare function setCellAutoHeightInDraft(draft: Draft<Canvas>, cellPath: LayerPath, on: boolean, policies?: TextLayoutPolicies): boolean;
2342
+ /**
2343
+ * 字段写入后的解码不变量重断言:解码在每次打开时强同步表格耦合,编辑器写入后
2344
+ * 立即重断言同一组关系(往返恒等的编辑器侧镜像)——
2345
+ * - 表:shape.width/autoWidth 写入 → 全部行宽重同步(行宽=表宽);模板态对模板
2346
+ * 行做同款重同步(镜像解码 setTemplate→setWidth,工票 02);
2347
+ * - 行:行宽恒归表宽(强同步字段的写入被覆写)、行高只增长到最高格;
2348
+ * - 格:内容宽/高按 addContentLayer 重同步(auto 采纳 / 固定压平)、行高取最高格;
2349
+ * - 内容:归属格的同步重断言(隐藏字段的直写被压回)。
2350
+ * key = updateSpec 的图层内字段路径;非表格子树路径原样返回(零 patch)。
2351
+ */
2352
+ declare function canonicalizeTableSyncInDraft(draft: Draft<Canvas>, path: LayerPath, key: readonly string[], policies?: TextLayoutPolicies): void;
2353
+
2354
+ /**
2355
+ * spec 字段路径读写原语(shared):沿图层内字段路径(如 ['shape','backgroundColor']、
2356
+ * 单段 ['fontSize'])下降读取/原位写入。updateSpec(属性面板权威写入口)与样式
2357
+ * 粘贴(editing/styleClipboard 的快照抓取与逐字段落地)共用同一套下降语义。
2358
+ *
2359
+ * 中间段悬空(null/非对象)即放弃:字段形态由上层(领域类型/注册表)把关,
2360
+ * 原语只做防御性下降。写入侧尾段原位赋值,immer draft 语义下生效。
2361
+ */
2362
+ /** 沿字段路径下降读取;目标不是对象或中间段悬空返回 undefined */
2363
+ declare function readSpecField(target: unknown, key: readonly string[]): unknown;
2364
+ /**
2365
+ * 沿字段路径下降写入(中间段悬空即放弃;尾段原位赋值,immer draft 语义下生效)。
2366
+ * 返回是否真写入——调用方据此统计落地字段(悬空放弃不计)。
2367
+ */
2368
+ declare function writeSpecField(target: unknown, key: readonly string[], value: unknown): boolean;
2369
+
2370
+ /**
2371
+ * 路径段解析纯函数(content-completion 工单 01,spec §4 纯函数 ②)。
2372
+ *
2373
+ * 语义权威 = php-canvas-next `InterpolationEvaluator::resolveFragment`(只读对齐,
2374
+ * 不改求值器):head 三分类——`row`(行命名空间)/ `$root`(显式取根,与裸名等价)/
2375
+ * 裸键(含 `$index` 等 `$` 保留名)——+ 余段原序下钻;空片段、路径含空段
2376
+ * (`row..x` / `.x` / `x.`)= `expression_syntax_error` 信号(与求值器同码)。
2377
+ *
2378
+ * 契约:入参是**已 trim** 的片段表达式(trim 是片段扫描的职责,对齐求值器
2379
+ * 先 trim 后 resolve 的次序;本函数不再 trim,空白字符属于键名)。
2380
+ * 补全消费侧(schema walker,工单 02)处理「片段内输入 `.` 刷新候选」时,
2381
+ * 先剥掉单个尾点(补全点标记)再走本函数——`row.` 按求值器语义是空段错误,
2382
+ * 剥点后的 `row` 才是可下钻前缀。
2383
+ *
2384
+ * 全模块纯函数、无 DOM、零内部依赖(editor-shared-isolation)。
2385
+ */
2386
+ /** 与 PHP 侧 HydrateException 稳定错误码对齐(spec §5.2,三端一致性抓手) */
2387
+ type ExpressionSyntaxError = 'expression_syntax_error';
2388
+ /** head 三分类(对齐 resolveFragment 的 match 分支;大小写敏感,Row/$ROOT 是裸键) */
2389
+ type ExpressionPathHead = {
2390
+ kind: 'row';
2391
+ } | {
2392
+ kind: 'root';
2393
+ } | {
2394
+ kind: 'bare';
2395
+ name: string;
2396
+ };
2397
+ type ExpressionPathParse = {
2398
+ ok: true;
2399
+ head: ExpressionPathHead;
2400
+ rest: readonly string[];
2401
+ } | {
2402
+ ok: false;
2403
+ error: ExpressionSyntaxError;
2404
+ };
2405
+ /** 解析点路径:head 分类 + 余段;语法不合法(空片段/空段)返回错误信号 */
2406
+ declare function parseExpressionPath(expr: string): ExpressionPathParse;
2407
+
2408
+ /**
2409
+ * 片段扫描纯函数(content-completion 工单 01,spec §4 纯函数 ①)。
2410
+ *
2411
+ * 语义逐条对齐 php-canvas-next `InterpolationEvaluator::evaluate` 扫描循环(只读
2412
+ * 对齐,不改求值器):首个未闭合 `{{` 起剩余全字面、片段内容 trim、`{{}}` 不嵌套
2413
+ * (首个 `}}` 收口,内容含 `{{`)、片段外字面直通;空片段/路径空段 =
2414
+ * `expression_syntax_error` 信号(与求值器同码,经 parseExpressionPath 判定)。
2415
+ *
2416
+ * 与求值器的一处表示分叉(编辑器侧补全所需):未闭合 `{{` 在求值期按字面直通,
2417
+ * 在扫描结果里另立 **open 片段**(kind: 'open')——这正是「输入 `{{` 自动弹出」的
2418
+ * 触发态(spec §3),其 expr 是待输入路径;closed 片段仍按求值语义带错误信号。
2419
+ *
2420
+ * 位置字段是 JS 码元索引(end 独占)——与 PHP 字节索引不同空间,跨端共享 fixture
2421
+ * (expression-scan v1)只钉切分内容不钉位置,位置由本端派生并由单测锁死。
2422
+ * trim 字符集对齐 PHP `trim()` 默认六字符(" \t\n\r\0\x0B"),**不是** JS 的
2423
+ * `String.trim()`(后者含全角空格等 Unicode 空白,会造成双端漂移)。
2424
+ *
2425
+ * 全模块纯函数、无 DOM(editor-shared-isolation)。
2426
+ */
2427
+
2428
+ /** 字面直通段(不含任何 `{{`) */
2429
+ interface ExpressionScanLiteral {
2430
+ kind: 'literal';
2431
+ start: number;
2432
+ end: number;
2433
+ text: string;
2434
+ }
2435
+ /** 闭合片段(含 `{{ }}` 外壳区域;raw 为外壳内原文,expr 为 trim 后路径) */
2436
+ interface ExpressionScanFragment {
2437
+ kind: 'fragment';
2438
+ start: number;
2439
+ end: number;
2440
+ raw: string;
2441
+ expr: string;
2442
+ /** 空片段/路径空段信号(与求值器 expression_syntax_error 同码);合法为 null */
2443
+ error: ExpressionSyntaxError | null;
2444
+ }
2445
+ /** 未闭合 `{{` 起的输入中片段(求值期按字面;编辑器侧补全触发态) */
2446
+ interface ExpressionScanOpen {
2447
+ kind: 'open';
2448
+ start: number;
2449
+ end: number;
2450
+ /** 含 `{{` 外壳的原文(到串尾) */
2451
+ text: string;
2452
+ /** `{{` 之后内容的 trim 结果(待输入路径,可能为空串) */
2453
+ expr: string;
2454
+ }
2455
+ type ExpressionScanPart = ExpressionScanLiteral | ExpressionScanFragment | ExpressionScanOpen;
2456
+ interface ExpressionScan {
2457
+ template: string;
2458
+ /** 有序切分;parts 拼回恒等于 template(空字面一律省略) */
2459
+ parts: readonly ExpressionScanPart[];
2460
+ }
2461
+ /** PHP trim() 默认字符集(JS String.trim 的 Unicode 空白集与之不同,勿用);
2462
+ * 编辑器侧补全的接受手术同用此集(导出防按值复刻漂移,工单 04) */
2463
+ declare const PHP_TRIM_CHARS = " \t\n\r\0\v";
2464
+ /** 扫描模板串的片段切分:闭合/未闭合/多片段/错误信号,语义对齐求值器扫描循环 */
2465
+ declare function scanExpressionFragments(template: string): ExpressionScan;
2466
+ /**
2467
+ * 光标所在片段判定(编辑器侧语义,PHP 无光标概念故不入跨端 fixture):
2468
+ * closed 片段含光标 ⟺ start < cursor < end(`}}` 之后即离开——输入 `}}` 关闭浮层);
2469
+ * open 片段含光标 ⟺ start < cursor <= end(串尾输入位恒在内)。
2470
+ */
2471
+ declare function expressionFragmentAtCursor(scan: ExpressionScan, cursor: number): ExpressionScanFragment | ExpressionScanOpen | null;
2472
+
2473
+ /**
2474
+ * 候选枚举器(content-completion 工单 02,spec §2 候选枚举规则表)。
2475
+ *
2476
+ * 规则逐条导出自 php-canvas-next 求值/填充语义(InterpolationEvaluator +
2477
+ * CanvasHydrator,只读对齐),非新发明:
2478
+ * - 根上下文(根层三字段)头部候选 = 载荷顶层键(D1:schema 声明即 data 载荷
2479
+ * 形状)+ `$root`;行上下文(格内容层)追加 `row` + `$index`。`$index` 仅行
2480
+ * 上下文给——行外求值为空串非报错,但不给写得出来而无效的表达式;`row.` 行外
2481
+ * 报 `expression_row_outside_loop`,同样不给。
2482
+ * - 裸键在两种上下文都走根 schema:行上下文求值上下文 = `$rowNamespace + $root`
2483
+ * 合并(CanvasHydrator::evaluateNode),行命名空间只有 row/$index 且
2484
+ * reserved_root_key 保证根级键不遮蔽——裸键即根键。
2485
+ * - `.` 下钻走 properties 树;无 properties 节点终止该分支;数组节点收敛为叶子
2486
+ * 无具名子候选(D9 修订 D3:具名段下钻数组在求值器是静默空串——缺字段信号,
2487
+ * 不给写得出来而渲染为空白的表达式;下钻层 items 链保留供 rowsPath/`row.*`
2488
+ * 机制,类型徽标仍显示 array 本身)。开放映射(open)节点的枚举结果携带 open
2489
+ * 信号(结果面扩展,不污染候选条目契约),供工单 10 浮层占位提示(D10)。
2490
+ *
2491
+ * 消费契约(工单 04 浮层):
2492
+ * - 入参 expr 是片段表达式(trim 后,来自 expressionScan);**单个尾点 = 补全点
2493
+ * 标记**,先剥再解析(对齐工单 01 备注:`row.` 按求值语义是空段错误,剥点后
2494
+ * `row` 才是可下钻前缀);剥点后为空且原 expr 非空 = 孤点前缀,无合法续写 →
2495
+ * expression_syntax_error 信号。
2496
+ * - 结果 `partial` 恒为 expr 的后缀(尾点态 = 空后缀):接受 = 在 expr 末尾把
2497
+ * partial 替换为候选 segment;候选给**全量合法子项**、不按 partial 预过滤,
2498
+ * 前缀过滤/高亮归浮层(Ctrl+Space 全量展示语义亦归浮层)。
2499
+ * - 元信息:path(补全后的完整点路径)+ schema description/title(D8 两字段分离
2500
+ * 透传,展示回落 description ?? title 归浮层)+ 类型徽标数据(type 归一)。
2501
+ * `$root`/`row`/`$index` 结构头是下钻踏脚石(`{{$root}}`/
2502
+ * `{{row}}` 单独求值无意义),元信息仅有行 schema 声明时的 row 携带。
2503
+ *
2504
+ * 降级语义:schema null(声明被拒/形态非法,经 normalizeExpressionSchemaSource
2505
+ * 收口)→ 全部无候选;schema 合法而行 schema 未解析(rowsPath 无 items 声明)→
2506
+ * 结构头 row 仍在、行子树无候选——辅助声明不作权威,全程不抛错。
2507
+ *
2508
+ * 全模块纯函数、无 DOM;层内依赖仅 expressionSchema / expressionPath。
2509
+ */
2510
+
2511
+ /** 'root' = 根层三字段(独立图层);'row' = 格内容层(模板行内) */
2512
+ type ExpressionContextKind = 'root' | 'row';
2513
+ /** 一条补全候选:路径 + schema 元信息(description/title/类型徽标,展示回落归浮层) */
2514
+ interface ExpressionCandidate {
2515
+ /** 补全后的完整点路径(前缀 + 本段;头部候选即头部名),如 row.user.city */
2516
+ path: string;
2517
+ /** 本段名——接受时替换 partial / 追加到前缀后的文本 */
2518
+ segment: string;
2519
+ /** schema description(未声明为 undefined) */
2520
+ description?: string;
2521
+ /** schema title(D8 形状树两字段分离透传,未声明为 undefined)——浮层展示取 description ?? title */
2522
+ title?: string;
2523
+ /** 类型徽标数据(schema type 归一;未声明为 undefined) */
2524
+ type?: string;
2525
+ }
2526
+ interface ExpressionCandidateQuery {
2527
+ context: ExpressionContextKind;
2528
+ /** 当前片段表达式(trim 后;'' = 刚输入 {{;允许单个补全尾点) */
2529
+ expr: string;
2530
+ /**
2531
+ * 行上下文的行键树 schema(rowsPath 数组 items,resolveRowSchema 产物)。
2532
+ * 嵌套模板表的行相对 rowsPath 由调用方按当前行 schema 解析后传入。
2533
+ * 未传/解析不出 = 行子树无候选,结构头 row 仍给。
2534
+ */
2535
+ rowSchema?: ExpressionSchemaNode | null;
2536
+ }
2537
+ type ExpressionCandidateResult = {
2538
+ ok: true;
2539
+ /** 候选所属前缀路径('' = 上下文头部层) */
2540
+ prefix: string;
2541
+ /** 正在输入的未完整段('' = 尾点/空表达式态);恒为 expr 的后缀 */
2542
+ partial: string;
2543
+ candidates: readonly ExpressionCandidate[];
2544
+ /**
2545
+ * 开放映射信号(D10):候选来源节点(头部层 = 根 schema,下钻层 = 前缀
2546
+ * 节点)标 open 时为 true,非 open 恒缺省——供浮层渲染「动态字段,键由
2547
+ * 模板定义」占位提示(工单 10),不污染候选条目契约。
2548
+ */
2549
+ open?: boolean;
2550
+ } | {
2551
+ ok: false;
2552
+ reason: ExpressionSyntaxError;
2553
+ };
2554
+ /**
2555
+ * 枚举当前片段表达式的合法候选。schema null(声明降级态)直接无候选;
2556
+ * 其余按 spec §2 规则表分上下文路由(见模块头注),分支不可达/节点为叶子
2557
+ * 一律空候选,不抛错。
2558
+ */
2559
+ declare function enumerateExpressionCandidates(schema: ExpressionSchemaNode | null, query: ExpressionCandidateQuery): ExpressionCandidateResult;
2560
+
2561
+ /**
2562
+ * rowsPath(取行路径)候选枚举器 + 起点三分流判别(rows-path-completion 工单 01,
2563
+ * spec §2 候选规则 + §4.1 落位 D9)。
2564
+ *
2565
+ * 候选规则逐条导出自水合语义(CanvasHydrator::hydrateTemplateNode,只读对齐):
2566
+ * rowsPath 点路径自起点 schema 逐段下钻,终点必须是行数组——
2567
+ * - object 子段 = 可继续下钻的中间站候选(无徽标数据);array 子段(type 声明为
2568
+ * array 或声明了 items)= 合法终点候选,type 徽标数据恒 'array'(「行数组」展示
2569
+ * 文案归后续组装面,浮层零改动);标量子段不出候选(怂恿无效路径即怂恿
2570
+ * rows_path_invalid)。
2571
+ * - 数组内不下钻:具名段穿数组在水合端无意义(navigateRows 对列表键不命中),
2572
+ * 完成段任一落点为数组/标量 → null——数组之下不再产出任何候选(「枚举为空」
2573
+ * 以走不通同款收口:无补全态、浮层关闭、手输不受阻不阻断),与声明漂移/缺失
2574
+ * 同款,全程不抛错。
2575
+ * - open 节点(additionalProperties: true):open 信号随结果透出(信号面与候选
2576
+ * 空否无关,对齐 schemaChildEntries 的 D10 面),供浮层渲染占位提示行。
2577
+ * - 候选给当前层全量合法子项、不按 partial 预过滤(前缀过滤归工单 02 浮层面,与
2578
+ * 表达式枚举器分工同款);单尾点 = 剥点列全量子段;partial 恒为输入串后缀。
2579
+ * - 元信息:path(起点相对完整点路径)+ description/title 两字段分离透传(展示
2580
+ * 回落 description ?? title 归浮层)+ type 徽标数据。
2581
+ *
2582
+ * 与 expressionCandidates(content-completion D9)的分叉点:那里数组节点收敛为
2583
+ * 叶子(具名段下钻数组在求值器是静默空串——缺字段信号),本题以数组为合法终点
2584
+ * ——语义相反,故自实现逐层枚举、**不复用 schemaChildEntries**;下钻组合现成件
2585
+ * schemaNodeAtPath + 节点判别(isArrayNode/isScalarNode)。
2586
+ *
2587
+ * 起点三分流判别(D3):按选中路径位置定枚举起点 schema——无 template 祖先
2588
+ * (根层表;建表表单同款恒根起点,不经此判别件)= 载荷根 schema;有 template
2589
+ * 祖先 = 沿路径每个 template 段行相对递归 resolveRowSchema(嵌套表自外层行取数,
2590
+ * hydrateTemplateNode 同门;判别思路对齐 editor-vue expressionContext 的
2591
+ * rowSchemaAlongPath,缺省面相反:表达式面无祖先给 null 表根上下文,本面无祖先
2592
+ * 给根 schema 表根起点,故独立落件不改 expressionContext);任一层解析不出
2593
+ * (rowsPath 无 items 声明/漂移/途经非表)→ null 降级无候选。
2594
+ *
2595
+ * 全模块纯函数、零 DOM;层内依赖仅 expressionSchema / expressionCandidates
2596
+ * (候选类型面)/ layerPath。
2597
+ */
2598
+
2599
+ /**
2600
+ * 一条 rowsPath 候选(面同 ExpressionCandidate → 结构兼容 editor-vue 的
2601
+ * CompletionItem,浮层零改动复用)。
2602
+ */
2603
+ type RowsPathCandidate = ExpressionCandidate;
2604
+ /** 一次枚举结果(partial/candidates 契约同表达式面 + open 信号);null = 无补全态 */
2605
+ interface RowsPathCandidateResult {
2606
+ /** 正在输入的未完整段('' = 空输入/单尾点态);恒为输入串后缀 */
2607
+ partial: string;
2608
+ /** 当前层全量合法子项(不按 partial 预过滤——前缀过滤归浮层) */
2609
+ candidates: readonly RowsPathCandidate[];
2610
+ /**
2611
+ * 开放映射信号(D10 同款):枚举落点标 open(additionalProperties: true)时为
2612
+ * true,非 open 恒缺省——浮层渲染「动态字段,键由模板定义」占位提示行,信号
2613
+ * 在场时候选空也不关浮层。
2614
+ */
2615
+ open?: boolean;
2616
+ }
2617
+ /**
2618
+ * 枚举当前输入串的合法 rowsPath 候选。起点 schema null(判别件降级态)直接无
2619
+ * 候选;完成段任一落点为数组/标量/缺失 → null(无补全态,不抛错——手输不受
2620
+ * 阻);枚举落点为对象层 → 该层全量合法子项。
2621
+ */
2622
+ declare function enumerateRowsPathCandidates(startSchema: ExpressionSchemaNode | null, input: string): RowsPathCandidateResult | null;
2623
+ /**
2624
+ * rowsPath 编辑起点的三分流判别(D3):无 template 祖先 → 载荷根 schema(根层
2625
+ * 表;建表表单恒根起点同款);有 template 祖先 → 沿选中路径每个 template 段行
2626
+ * 相对递归 resolveRowSchema(首个对根 schema、其后对当前行 schema),返回最终
2627
+ * 行 schema;任一层解析不出(rowsPath 无 items 声明/漂移/途经非 TableLayer)→
2628
+ * null 降级无候选。rootSchema null(声明未注入/被拒)→ null;doc/path 缺席 →
2629
+ * 根起点兜底(画布级判定兜底一致)。
2630
+ */
2631
+ declare function resolveRowsPathStartSchema(doc: Canvas | null, path: LayerPath | null, rootSchema: ExpressionSchemaNode | null): ExpressionSchemaNode | null;
2632
+
2633
+ /**
2634
+ * 文本布局策略的 memo 包装(canvas-web-render-perf 工单 02):把注入缝的
2635
+ * TextLayoutPolicies(断行器 + 度量器工厂)包上引用级缓存——全画布重绘对未变图层
2636
+ * 反复求布局(textLines 断行 / layerWidth 自然宽逐段度量)是逐键开销链的大头,
2637
+ * immer 结构共享保证未变图层的 (text, width, font, fontSize) 键稳定命中,逐键只
2638
+ * 重算被编辑的那一层。
2639
+ *
2640
+ * 键域与缓存:
2641
+ * - 断行:font \0 fontSize \0 width \0 text → 行数组(同键同引用返回);
2642
+ * - 度量:font \0 fontSize \u0000 text → 宽(measure 纯函数,键即输入)。
2643
+ * 两缓存均 LRU(命中重插刷新位次),容量有界(缺省 512 / 2048,可配)——被编辑层
2644
+ * 的历史键随容量上限自然淘汰,长会话不无界增长。
2645
+ *
2646
+ * 缺省分量回落 core 缺省实现并同样被 memo(断行 = createUax14LineBreaker()、度量 =
2647
+ * heuristicMeasurerFactory)——宿主只注入 measureText 工厂时(playground 先例)断行
2648
+ * 缓存照样生效。核心包(canvas)零改动:本模块纯包装注入缝,语义与未包装完全
2649
+ * 一致(wire 零变化,Go/PHP 不跟进)。
2650
+ *
2651
+ * 上下文传递:断行器签名 (text, width, measurer) 不见 font/fontSize——本包装的
2652
+ * wrappedFactory 求值时把 (font, fontSize) 记进闭包上下文,wrappedBreaker 用它组键;
2653
+ * 布局消费恒为「先 factory 后 breaker」的同同步序列(layout.ts textLines/layerWidth),
2654
+ * 上下文读取因此总对应当前图层。
2655
+ */
2656
+
2657
+ interface MemoTextPoliciesOptions {
2658
+ /** 断行缓存容量(键数),缺省 512 */
2659
+ readonly maxLineEntries?: number;
2660
+ /** 度量缓存容量(键数),缺省 2048 */
2661
+ readonly maxMeasureEntries?: number;
2662
+ }
2663
+ declare function memoizeTextPolicies(policies?: TextLayoutPolicies, options?: MemoTextPoliciesOptions): TextLayoutPolicies;
2664
+
2665
+ /**
2666
+ * @hankchen/canvas-editor headless 内核公共出口。
2667
+ *
2668
+ * src 按 session(会话门面/store/快捷键)→ spatial(视口几何/拾取/滚轮)→
2669
+ * editing(剪贴板/图层/表格/字体/上传)→ shared(layerPath 寻址原语)分层,
2670
+ * 下层禁止反向引用上层(dependency-cruiser 域隔离规则 editor-*-isolation 锁定)。
2671
+ * 内核只保留根 barrel 单出口:EditorSession 门面是宿主唯一 API,域目录是
2672
+ * 内部组织,不开子路径导出,避免宿主绕过门面拼装内核。
2673
+ * 红线:内核不依赖任何 UI 绑定层(Vue/React),无 DOM lib,测试全部在
2674
+ * Node 无 DOM 环境运行。
2675
+ */
2676
+ declare const PACKAGE_NAME: "@hankchen/canvas-editor";
2677
+
2678
+ export { ALIGN_CORNER_MARGIN_PX, ALT_DRAG_DEAD_ZONE_SCREEN_PX, ARM_CREATE_LAYER_TYPES, type AlignBoxGeometry, type AlignToCanvasMode, type AlignToCanvasOptions, type ArmCreateShortcutAction, CREATE_DEAD_ZONE_SCREEN_PX, type CreateGesture, type CreateRectResult, DEFAULT_EDITOR_SHORTCUTS, DEFAULT_ZOOM_BOUNDS, type DeletedLayerRef, type DocRecipe, type DragGesture, type EditorChange, EditorSession, type EditorSessionOptions, type EditorShortcutAction, type EditorShortcutBinding, type EditorShortcutGroup, type EditorShortcutInput, EditorStore, type EditorUi, type ExpressionCandidate, type ExpressionCandidateQuery, type ExpressionCandidateResult, type ExpressionContextKind, type ExpressionPathHead, type ExpressionPathParse, type ExpressionScan, type ExpressionScanFragment, type ExpressionScanLiteral, type ExpressionScanOpen, type ExpressionScanPart, type ExpressionSchemaChildEntry, type ExpressionSchemaChildView, type ExpressionSchemaDiagnostic, type ExpressionSchemaDiagnosticCode, type ExpressionSchemaNode, type ExpressionSchemaParse, type ExpressionSchemaRejectReason, type ExpressionSyntaxError, type FindSession, type FindTextHit, FontCatalog, type FontCatalogEntry, type FrameScheduler, type Guide, type GuideOrientation, type HistoryStep, type HitTestOptions, type InvalidateTarget, type LayerOutlineNode, type LayerOutlineRole, type LayerPath, MAX_HISTORY_STEPS, type MemoTextPoliciesOptions, type MovedSubtreeRef, type OverlayPaintArgs, type OverlayPainter, PACKAGE_NAME, PAN_TO_BOX_MARGIN_PX, PASTE_OFFSET_PX, PHP_TRIM_CHARS, type Point, RESIZE_HANDLES, RESIZE_HANDLE_HIT_PX, RESIZE_MIN_SIZE_PX, type Rect, type ResizeAxes, type ResizeBoxGeometry, type ResizeBoxResult, type ResizeHandle, type ResourceStatus, type ResourceStatusMap, type RowsPathCandidate, type RowsPathCandidateResult, SNAP_THRESHOLD_SCREEN_PX, STYLE_FIELD_KEYS_BY_TYPE, type ShortcutCombo, type Size, type SnapAxis, type SnapAxisSource, type SnapBoxGeometry, type SnapPoints, type SnapResolution, type StyleSnapshot, type TextEditLayout, type TextEditingSession, type TransactOptions, type UploadFile, type UploadHandler, UploadHandlerMissingError, type UploadImagePlacement, type Viewport, WHEEL_ZOOM_STEP, type WheelInput, type WheelIntent, type ZoomBounds, addRootLayerInDraft, addTableCellInDraft, addTableRowInDraft, addTemplateCellInDraft, alignToCanvasTarget, applyStyleFieldsInDraft, buildLayerOutline, canCopyLayerAt, canonicalizeTableSyncInDraft, captureStyleSnapshot, clampZoom, classifyEditorShortcut, classifyWheel, cloneLayerSubtree, convertTableToRowsInDraft, convertTableToTemplateInDraft, createDefaultLayer, createRubberBandRect, createTemplateTable, deleteLayerInDraft, enumerateExpressionCandidates, enumerateRowsPathCandidates, expressionFragmentAtCursor, fitRect, fitViewport, growRowToCellInDraft, handleResizesHeight, handleResizesWidth, hitTest, insertRootLayerAdjacentInDraft, insertRootLayerInDraft, isImageContentUndrawn, isLayerPath, isLockedPath, isRootLayerPath, isRootPasteableType, isTemplateSubtreePath, layerBoxByPath, memoizeTextPolicies, moveGuard, moveRootLayerInDraft, moveTableCellInDraft, moveTableCellToRowInDraft, moveTableRowInDraft, moveTableRowToTableInDraft, nextZoomByWheel, normalizeExpressionSchemaSource, panBy, panToBox, parseExpressionPath, parseExpressionSchema, pastePosition, pathStartsWith, pathsEqual, prepareRootPaste, readSpecField, remapPathAfterSplice, replaceHitsInDraft, resizableAxesAt, resizeBox, resizeHandleAt, resizeHandlePoint, resizeHandlesAt, resizeSnapPoints, resolveLayer, resolveRowSchema, resolveRowsPathStartSchema, resolveSnap, resolveSnapPoints, rootLayerOf, scanExpressionFragments, scanTextMatches, sceneToScreen, schemaChildEntries, schemaNodeAtPath, screenToScene, selectionParentPath, setCellAutoHeightInDraft, snapAxesFromBoxes, snapThresholdScene, snapViewportToPhysicalPixels, syncContentIntoCellInDraft, syncRowWidthInDraft, uploadDisplayName, visibleRootBoxes, writeSpecField, zoomAtPoint };