drawui-core 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -80,6 +80,13 @@ interface HexagonShape extends BaseShape {
80
80
  type: 'hexagon';
81
81
  width: number;
82
82
  height: number;
83
+ /**
84
+ * 六边形朝向(缺省 `'pointy'`,老文档行为不变):
85
+ * - `'pointy'`:上下为尖、左右为竖边(原行为)
86
+ * - `'flat'`:上下为平边、左右为尖 —— 蜂窝图要用这个,
87
+ * 它的六个邻居正好落在 30°/90°/150°/210°/270°/330°,上下左右都能拼上。
88
+ */
89
+ orientation?: 'pointy' | 'flat';
83
90
  }
84
91
  interface ParallelogramShape extends BaseShape {
85
92
  type: 'parallelogram';
@@ -90,6 +97,13 @@ interface TrapezoidShape extends BaseShape {
90
97
  type: 'trapezoid';
91
98
  width: number;
92
99
  height: number;
100
+ /**
101
+ * 上边每侧内缩的距离(页面单位)。缺省 = `width * 0.15`(老文档行为不变)。
102
+ *
103
+ * 用来堆金字塔:每层给出精确的缩进量,侧边才能连成**同一条斜线**;
104
+ * 靠默认的 15% 比例会让每层斜率各不相同,堆起来像歪的楼梯。
105
+ */
106
+ topInset?: number;
93
107
  }
94
108
  interface PentagonShape extends BaseShape {
95
109
  type: 'pentagon';
@@ -239,6 +253,10 @@ interface LineShape extends BaseShape {
239
253
  start: Vec2;
240
254
  end: Vec2;
241
255
  };
256
+ /** 手工艺状态的自动走线基线(见 ArrowShape.userPointsBase) */
257
+ userPointsBase?: Vec2[];
258
+ /** 纯装饰线(见 ArrowShape.freeform) */
259
+ freeform?: boolean;
242
260
  }
243
261
  type ConnectionType = 'straight' | 'polyline' | 'orthogonal' | 'curve';
244
262
  /** 生成点位创建上下文:from 为源点(生成点位),to 为目标落点 */
@@ -328,6 +346,22 @@ interface ArrowShape extends BaseShape {
328
346
  start: Vec2;
329
347
  end: Vec2;
330
348
  };
349
+ /**
350
+ * 写下手工艺状态那一刻的**自动走线**(世界坐标,含两端)。
351
+ *
352
+ * 只用来判断手工折点是否已经过期:自动走线的形状签名(折点数 + 首末段轴向)只由布局决定,
353
+ * 一旦当前自动走线的签名和这里记的不一样,说明「当初为了绕开什么而折」的前提没了 ——
354
+ * 这时折点只会让线凭空多绕一圈(见 connectorEdit.userPointsAreStale)。
355
+ */
356
+ userPointsBase?: Vec2[];
357
+ /**
358
+ * 纯装饰线/箭头:**不把端点吸附到附近图形的轮廓上**。
359
+ *
360
+ * 模板里的分隔线、网格线、肋骨、标注线往往是**故意画在容器图形内部**的;
361
+ * 默认的「端点落进图形就吸到轮廓」会把它们拽到容器边上(画布上看是"线自己跑了"),
362
+ * 而且导出侧从不做这个吸附 → 画布与导出还会对不上。
363
+ */
364
+ freeform?: boolean;
331
365
  }
332
366
  interface TextShape extends BaseShape {
333
367
  type: 'text';
@@ -673,11 +707,11 @@ declare const zhCN: {
673
707
  readonly 'toolbar.grid': "网格";
674
708
  readonly 'toolbar.undo': "撤销 (Ctrl+Z)";
675
709
  readonly 'toolbar.redo': "重做 (Ctrl+Shift+Z)";
676
- readonly 'toolbar.arrow': "箭头";
677
- readonly 'toolbar.text': "文本";
710
+ readonly 'toolbar.arrow': "箭头 (A)";
711
+ readonly 'toolbar.text': "文本 (T)";
678
712
  readonly 'toolbar.pen': "画笔";
679
713
  readonly 'toolbar.highlighter': "荧光笔";
680
- readonly 'toolbar.eraser': "橡皮擦";
714
+ readonly 'toolbar.eraser': "橡皮擦 (E)";
681
715
  readonly 'toolbar.uml-class': "UML 类图";
682
716
  readonly 'toolbar.uml-interface': "UML 接口";
683
717
  readonly 'zoom.in': "放大";
@@ -698,7 +732,7 @@ declare const zhCN: {
698
732
  readonly 'style.solid': "实线";
699
733
  readonly 'style.opacity': "不透明度";
700
734
  readonly 'style.connection.straight': "直线";
701
- readonly 'style.connection.polyline': "折线";
735
+ readonly 'style.connection.polyline': "圆角折线";
702
736
  readonly 'style.connection.orthogonal': "直角折线";
703
737
  readonly 'style.connection.curve': "曲线";
704
738
  readonly 'style.arrowhead.none': "无箭头";
@@ -788,7 +822,6 @@ declare const zhCN: {
788
822
  readonly 'table.insertColRight': "在右侧插入列";
789
823
  readonly 'table.pickHint': "拖选行列数";
790
824
  readonly 'ctx.copy': "复制";
791
- readonly 'ctx.copyAsImage': "复制为图片";
792
825
  readonly 'ctx.paste': "粘贴";
793
826
  readonly 'ctx.duplicate': "创建副本";
794
827
  readonly 'ctx.layer': "层级";
@@ -806,8 +839,13 @@ declare const zhCN: {
806
839
  readonly 'ctx.ungroup': "解组";
807
840
  readonly 'ctx.lock': "锁定";
808
841
  readonly 'ctx.unlock': "解锁";
842
+ readonly 'ctx.unlockAll': "全部解锁";
809
843
  readonly 'ctx.delete': "删除";
810
- readonly 'toolbar.icon': "图标";
844
+ readonly 'toolbar.icon': "图标 (S)";
845
+ readonly 'toolbar.template': "模板";
846
+ readonly 'template.search': "搜索模板";
847
+ readonly 'template.all': "全部";
848
+ readonly 'template.empty': "没有匹配的模板";
811
849
  readonly 'toolbar.image': "图片";
812
850
  readonly 'toolbar.more': "更多";
813
851
  readonly 'toolbar.pageTheme': "页面主题";
@@ -835,6 +873,7 @@ declare const zhCN: {
835
873
  readonly 'iconCat.arrow': "箭头方向";
836
874
  readonly 'iconCat.misc': "常用";
837
875
  readonly 'iconCat.emoji': "表情";
876
+ readonly 'iconCat.sticker': "贴纸";
838
877
  readonly 'iconCat.device': "电脑设备";
839
878
  readonly 'iconCat.weather': "天气";
840
879
  readonly 'iconCat.transport': "交通";
@@ -859,12 +898,24 @@ declare function t(key: MessageKey, params?: Record<string, string | number>): s
859
898
 
860
899
  type ExportFormat = 'png' | 'svg' | 'pdf';
861
900
  interface ExportOptions {
862
- /** 输出缩放倍数(仅位图 PNG 生效,默认 2,矢量 SVG 始终无损) */
901
+ /**
902
+ * 输出缩放倍数(仅位图 PNG 生效,默认 2,矢量 SVG 始终无损)。
903
+ * 语义:内容 1 个页面单位 → 输出 scale 个设备像素(就是最终图与内容的尺寸比)。
904
+ */
863
905
  scale?: number;
864
906
  /** 内容四周留白(页面坐标,默认 24) */
865
907
  padding?: number;
866
908
  /** 背景色,null = 透明;不传默认白色('#ffffff') */
867
909
  background?: string | null;
910
+ /**
911
+ * 输出最长边的像素上限(默认不限制)。超过时按比例压低 scale,**保证整幅内容完整、不裁切**。
912
+ *
913
+ * 缩略图 / 预览图这类"显示尺寸远小于内容尺寸"的场景务必传:
914
+ * 内容跨度大时(把一个图形拖到几千像素外)输出画布面积会平方增长,
915
+ * 不仅编码要几百毫秒,还会触及浏览器 canvas 面积上限(Chrome 约 16384×16384,
916
+ * 约 2.68 亿像素 / 1GB),超限后 toDataURL 直接返回 `data:,` —— 图变空白。
917
+ */
918
+ maxSize?: number;
868
919
  }
869
920
  /** 计算所有图形的并集包围盒(含留白),用于导出画布尺寸与视口 */
870
921
  declare function getContentBounds(shapes: Shape[], padding?: number): {
@@ -1066,7 +1117,20 @@ declare class Editor {
1066
1117
  private genPreview;
1067
1118
  private hoverAnchorShapeId;
1068
1119
  private hoverConnectorId;
1120
+ /**
1121
+ * 悬停中的**锁定**图形:锁标记不再常驻(一屏三把锁比锁本身更吵),
1122
+ * 只在指针落在这个锁定图形上时才亮出来 —— 顺带告诉用户「它为什么点不动」,
1123
+ * 而且那枚锁是可以直接点开解锁的(见 pointerdown 里的锁徽标命中)。
1124
+ */
1125
+ private hoverLockedShapeId;
1069
1126
  private eraserPoint;
1127
+ /**
1128
+ * 橡皮擦「渐隐」中的图形:id → 开始时间戳。
1129
+ * 命中后不立刻从 shapes 里删掉,而是先让不透明度在 ERASE_FADE_MS 内降到 0,
1130
+ * 到点再真正移除(这样擦除是「变浅 → 消失」而不是「啪一下不见」)。
1131
+ */
1132
+ private eraseFades;
1133
+ private eraseFadeRaf;
1070
1134
  private mindMapEditingShapeId;
1071
1135
  private mindMapSelectedNode;
1072
1136
  private mindMapHoverNode;
@@ -1147,6 +1211,17 @@ declare class Editor {
1147
1211
  private handlePointerUp;
1148
1212
  /** 根据当前工具的命中结果更新鼠标样式(伸缩手柄↔/↕、拖拽 grabbing 等) */
1149
1213
  private updateCursor;
1214
+ /**
1215
+ * 锁定图形的悬停跟踪:只有悬停中的锁定图形才亮出锁徽标(默认不画,见 Renderer.visibleLockBadges)。
1216
+ *
1217
+ * 除了"指针在图形上",还要认"指针在锁徽标上" —— 徽标压在包围盒外侧,
1218
+ * 指针移过去的瞬间已经离开了图形本身,不认就会闪掉、点不到。
1219
+ */
1220
+ private updateLockHover;
1221
+ /** 命中「锁徽标」(世界坐标点);返回要解锁的目标。命中范围比视觉上略大一圈,好点 */
1222
+ private lockBadgeAtPoint;
1223
+ /** 点锁徽标 → 解锁(组锁整组解),一步撤销 */
1224
+ private unlockFromBadge;
1150
1225
  /**
1151
1226
  * 滚轮:普通滚动平移视口(内容跟随常规滚动方向),
1152
1227
  * Ctrl/Cmd + 滚轮以光标为中心缩放,Shift + 滚轮水平平移。
@@ -1156,7 +1231,24 @@ declare class Editor {
1156
1231
  private handleKeyUp;
1157
1232
  private handleResize;
1158
1233
  private screenToPage;
1234
+ /**
1235
+ * 插入一组图形(模板库的落地入口)。
1236
+ *
1237
+ * - 模板坐标是「相对自己的中心」,这里整体平移到**当前视口中心**,插到用户正在看的地方;
1238
+ * - 整批插入只打**一个**历史快照,撤销一次就整块拿掉;
1239
+ * - 插入后自动选中这批图形,用户可以直接拖动/改样式;
1240
+ * - 连线要平移 `points`(`shape.x/y` 对连线只是渲染原点,平移它视觉上不动)。
1241
+ */
1242
+ insertTemplate(shapes: Shape[]): void;
1159
1243
  private addShape;
1244
+ /**
1245
+ * 文档里出现了「大目录」里的图标(表情/贴纸/补充图标)就把它拉回来,回来后重绘一次。
1246
+ *
1247
+ * 图标目录是懒加载的:首包只带手绘线稿那部分。打开一份含表情的文档时,
1248
+ * 第一次渲染会短暂缺图 —— 这里补一次加载,加载完 requestRender 补画上。
1249
+ * 全部图标都在首包时 `ensureIcons` 是纯同步空转,不会产生任何网络请求。
1250
+ */
1251
+ private prefetchShapeIcons;
1160
1252
  private updateShape;
1161
1253
  /**
1162
1254
  * 表格尺寸变化时保持「左上角」不动:行高变化向下伸缩、列宽变化向右伸缩,
@@ -1170,6 +1262,20 @@ declare class Editor {
1170
1262
  private updateBoundArrows;
1171
1263
  private getBindingPosition;
1172
1264
  private deleteShape;
1265
+ /**
1266
+ * 橡皮擦命中:图形先进入「渐隐」再被真正删除(见 eraseFades)。
1267
+ * 历史快照在拖拽开始时已经打过,所以整段擦除仍然只占一个撤销点。
1268
+ */
1269
+ private eraseShape;
1270
+ /** 渐隐时钟:每帧推进,到点的图形才真正删除;整批结束后停表 */
1271
+ private tickEraseFade;
1272
+ /** 当前帧各渐隐图形的不透明度(0~1);没有渐隐时返回 undefined,避免每帧建 Map */
1273
+ private eraseFadeMap;
1274
+ /**
1275
+ * 丢弃所有未完成的渐隐(撤销 / 重做 / 整体换数据时调用)。
1276
+ * 否则「撤销把图形救回来 → 渐隐计时器到点又把它删掉」会凭空吃掉一次撤销。
1277
+ */
1278
+ private clearEraseFades;
1173
1279
  /**
1174
1280
  * 选中图形(additive=true 时切换选中态);id 为空串清空选中。右键菜单/外部控制用。
1175
1281
  * ⚠️ 组感知:点任一成员都会选中它所属的整组(同组的绑行为一致),additive 时整组一起加减。
@@ -1177,6 +1283,15 @@ declare class Editor {
1177
1283
  selectShape(id: string, additive: boolean): void;
1178
1284
  /** 整体替换选中集(右键菜单/外部控制用);框选结果同样按组扩张 */
1179
1285
  selectShapes(ids: string[]): void;
1286
+ /**
1287
+ * 全选(Ctrl/Cmd + A):选中画布上所有图形。
1288
+ *
1289
+ * 锁定图形**跳过** —— 它们本来就不接受选中(点不中、拖不动),
1290
+ * 混进选区只会让「删除/移动/改样式」这些操作对它们做无用功,
1291
+ * 还会让状态栏的「已选 N 个」数字对不上能操作的图形数。
1292
+ * 要处理锁定的图形,先解锁(悬停点锁 / 右键解锁 / 空白右键「全部解锁」)。
1293
+ */
1294
+ selectAll(): boolean;
1180
1295
  /** 某图形所在的整组 id 列表(不在组里就只有它自己) */
1181
1296
  private getGroupMembers;
1182
1297
  /** 选中集是否恰好是完整的一个组(返回 groupId,否则 null) */
@@ -1312,6 +1427,14 @@ declare class Editor {
1312
1427
  setShapeLocked(id: string, locked: boolean): boolean;
1313
1428
  /** 某个图形是否被锁定 */
1314
1429
  isShapeLocked(id: string): boolean;
1430
+ /** 画布上是否存在锁定图形(右键菜单决定要不要显示「全部解锁」) */
1431
+ hasLockedShapes(): boolean;
1432
+ /**
1433
+ * 全部解锁(一步撤销)。
1434
+ * 兜底入口:锁定的图形点不中、框选也框不中,如果用户忘了自己锁过什么、
1435
+ * 或者锁的量比较多,挨个悬停点锁太麻烦 —— 空白处右键就能一次清掉。
1436
+ */
1437
+ unlockAll(): boolean;
1315
1438
  /** 置于顶层:选中的图形整体移到最上面(保持它们之间的相对顺序) */
1316
1439
  bringToFront(): boolean;
1317
1440
  /** 置于底层:选中的图形整体移到最下面 */
@@ -1774,6 +1897,8 @@ declare class Editor {
1774
1897
  private getShapeScreenBounds;
1775
1898
  getShapeAtScreenPoint(screenPoint: Vec2): Shape | null;
1776
1899
  getShapeAtPoint(point: Vec2, excludeId?: string): Shape | null;
1900
+ /** 点是否落在连线的**实际走线**上(与画面一致:含出/入桩、绕行、手工折点) */
1901
+ private isPointOnConnector;
1777
1902
  /** 橡皮擦:返回与圆形笔刷相交的所有图形(不含线条的命中由其线段距离判定) */
1778
1903
  getShapesInBrushRadius(point: Vec2, radius: number): Shape[];
1779
1904
  private shapeIntersectsBrush;
@@ -1877,6 +2002,19 @@ interface SnapGuide {
1877
2002
  start: number;
1878
2003
  end: number;
1879
2004
  }
2005
+ /** 一枚锁徽标(世界坐标矩形 + 点击它要解锁的目标)—— 绘制与命中共用 */
2006
+ interface LockBadgeTarget {
2007
+ x: number;
2008
+ y: number;
2009
+ /** 边长(屏幕 20px 换算到世界单位) */
2010
+ size: number;
2011
+ /** 单图形锁定:解锁它本身 */
2012
+ shapeId?: string;
2013
+ /** 组锁定:解锁整组 */
2014
+ groupId?: string;
2015
+ /** 组锁的所有成员 id(用于判断"悬停/选中是否在这个组上") */
2016
+ memberIds: string[];
2017
+ }
1880
2018
  declare class Renderer {
1881
2019
  private ctx;
1882
2020
  private width;
@@ -1901,6 +2039,11 @@ declare class Renderer {
1901
2039
  } | null): void;
1902
2040
  /** 当前悬停的图形 id(由每帧 render 写入),仅用于表格拖动把手等提示性绘制 */
1903
2041
  private hoverShapeId;
2042
+ /**
2043
+ * 本帧是否是「多选」(选中集 > 1)。多选时每个图形只描发丝轮廓、不再画手柄,
2044
+ * 手柄收敛到整体范围框上(见 drawMultiSelectionBounds)。
2045
+ */
2046
+ private multiSelection;
1904
2047
  private static readonly TEXT_CACHE_LIMIT;
1905
2048
  constructor(canvas: HTMLCanvasElement, dpr?: number);
1906
2049
  resize(width: number, height: number): void;
@@ -1930,7 +2073,9 @@ declare class Renderer {
1930
2073
  /** 悬停的图形 id:为其绘制四边连接锚点(飞书式悬浮提示),已选中图形由选择手柄负责 */
1931
2074
  hoverAnchorShapeId?: string | null,
1932
2075
  /** 悬停的连线 id:整条线描一层浅蓝高亮(提示「这条线可点、点开能调线段」) */
1933
- hoverConnectorId?: string | null): void;
2076
+ hoverConnectorId?: string | null,
2077
+ /** 橡皮擦渐隐:图形 id → 不透明度(0~1)。缺省时不改任何图形的透明度 */
2078
+ eraseFade?: Map<string, number> | null): void;
1934
2079
  /** 橡皮擦:半透明主色圆 + 主色描边,半径与笔刷命中范围一致(屏幕像素恒定) */
1935
2080
  private drawEraserBrush;
1936
2081
  /** 连接进行中,高亮目标图形四边中点的可连锚点 */
@@ -2052,6 +2197,14 @@ declare class Renderer {
2052
2197
  getConnectorRoute(shape: Shape, shapeMap: Map<string, Shape>, options?: {
2053
2198
  ignoreUserPoints?: boolean;
2054
2199
  }): Vec2[];
2200
+ /**
2201
+ * 连线最终走线(世界坐标)+ 生效的连线类型 —— **绘制、命中、导出判定都走这里**。
2202
+ *
2203
+ * 手工折点会先过一道「过期检查」(connectorEdit.userPointsAreStale):
2204
+ * 走线压到自己连的图形、或者布局已经变到自动走线的形状都不一样了,就丢弃手工折点。
2205
+ * 这样「旧布局下绕出来的大回环」不会跟着图形被带进新布局,默认永远是最少突兀的那条线。
2206
+ */
2207
+ private connectorWorldPath;
2055
2208
  /**
2056
2209
  * 选中连线时:在每段(够长的)线段中点画一个可拖动的小手柄(飞书式「拖线段调位置」)。
2057
2210
  * 拖动交互在 SelectTool 的 'dragging-segment' 状态里。
@@ -2194,16 +2347,22 @@ declare class Renderer {
2194
2347
  private drawText;
2195
2348
  private drawFreehand;
2196
2349
  /**
2197
- * 锁定标记:图形右上角画一枚小挂锁(尺寸按屏幕像素恒定),
2198
- * 独立于选中态 —— 锁定的图形通常选不中,得靠这枚锁告诉用户「它为什么点不动」。
2199
- */
2200
- private drawLockBadge;
2201
- /**
2202
- * 锁定组的统一标记:整组只画**一把锁**,挂在组包围盒顶部居中,
2203
- * 而不是组里每个成员各挂一把(一堆锁没法看)。只要组处于锁定状态就画,
2204
- * 与选中无关(锁定的组本来也选不中,这枚锁就是唯一的视觉提示)。
2205
- */
2206
- private drawGroupLockBadges;
2350
+ * 当前**可见**的锁标记(世界坐标矩形 + 解锁目标)。
2351
+ *
2352
+ * 绘制与「点锁解锁」的命中共用这一份 —— 看到哪就能点到哪,不会出现"看不见却能点"。
2353
+ *
2354
+ * 为什么不再给每个锁定图形常驻画一把锁:一条流程图里锁三个图形就冒出三把锁,
2355
+ * 满屏徽标比锁本身更干扰。现在只在**需要解释的时候**出现:
2356
+ * 指针悬停在这个锁定图形上,或它刚被右键选中(右键是选中锁定图形的入口)。
2357
+ * 组锁仍然整组一把(挂在组包围盒顶部居中),条件同上:悬停/选中任一成员。
2358
+ */
2359
+ visibleLockBadges(shapes: Shape[], selectedIds: Set<string>, zoom: number): LockBadgeTarget[];
2360
+ /** 所有「锁定图形」的徽标位置(不考虑是否显示)—— 命中测试与悬停判定用 */
2361
+ lockBadgeTargets(shapes: Shape[], zoom: number): LockBadgeTarget[];
2362
+ private allLockBadges;
2363
+ /** 悬停的锁定图形 id(由 Editor 在指针移动时写入):只有它才会亮出锁徽标 */
2364
+ private lockHoverId;
2365
+ setLockHover(id: string | null): void;
2207
2366
  /** 在指定位置(徽标左上角,世界坐标)画一枚挂锁徽标 */
2208
2367
  private drawLockBadgeAt;
2209
2368
  /**
@@ -2211,6 +2370,13 @@ declare class Renderer {
2211
2370
  * 提示「这些图形是绑在一起的」。整组锁定的成员画不到(成员不可选),不需要外框。
2212
2371
  */
2213
2372
  private drawGroupFrames;
2373
+ /**
2374
+ * 多选时每个图形的「发丝轮廓」:1px、不填充、不画任何手柄。
2375
+ * 目的是告诉用户「这个也在选区里」,而不是喧宾夺主。
2376
+ */
2377
+ private drawMultiSelectOutline;
2378
+ /** 多选的整体范围框:一个蓝框圈住全部选中图形(不带手柄) */
2379
+ private drawMultiSelectionBounds;
2214
2380
  private drawSelectionOutline;
2215
2381
  private drawSelectionHandles;
2216
2382
  /**
@@ -2286,7 +2452,7 @@ declare const ICON_CATEGORIES: {
2286
2452
  id: IconCategoryId;
2287
2453
  labelKey: MessageKey;
2288
2454
  }[];
2289
- type IconCategoryId = 'status' | 'number' | 'emoji' | 'device' | 'tech' | 'chart' | 'office' | 'media' | 'arrow' | 'misc' | 'weather' | 'transport' | 'nature' | 'building' | 'medical' | 'shopping' | 'science' | 'animal' | 'time' | 'edit' | 'food' | 'communication';
2455
+ type IconCategoryId = 'status' | 'number' | 'emoji' | 'sticker' | 'device' | 'tech' | 'chart' | 'office' | 'media' | 'arrow' | 'misc' | 'weather' | 'transport' | 'nature' | 'building' | 'medical' | 'shopping' | 'science' | 'animal' | 'time' | 'edit' | 'food' | 'communication';
2290
2456
  interface IconLayer {
2291
2457
  /** 24×24 网格内的路径(语法同 SVG path,统一大写指令) */
2292
2458
  d: string;
@@ -2323,6 +2489,20 @@ interface IconDef {
2323
2489
  };
2324
2490
  }
2325
2491
  declare const ICONS: IconDef[];
2492
+ /** 大目录是否已并入(同步查询) */
2493
+ declare function isIconCatalogLoaded(): boolean;
2494
+ /**
2495
+ * 加载大目录。可重复调用(共享同一个 Promise);失败会重置状态,允许下次重试。
2496
+ * 宿主一般不用直接调 —— 图标面板与 `Editor` 会自动触发。
2497
+ */
2498
+ declare function loadIconCatalog(): Promise<void>;
2499
+ /**
2500
+ * 预取一批图标(通常是文档里出现过的 iconId)。
2501
+ *
2502
+ * 全部已在首包里时**不会触发任何加载**,所以「没有表情/贴纸」的文档完全不受影响;
2503
+ * 只要有一个不认识,就说明文档用了大目录里的图标,此时整份目录拉一次(浏览器会缓存)。
2504
+ */
2505
+ declare function ensureIcons(ids: Iterable<string>): Promise<void>;
2326
2506
  declare function getIcon(id: string): IconDef | undefined;
2327
2507
  /** 存在该 id 的图标(写入图形/读文档时校验,避免引用到不存在的图标) */
2328
2508
  declare function hasIcon(id: string): boolean;
@@ -2349,6 +2529,39 @@ declare const ICON_CATEGORY_INK: Record<IconCategoryId, string>;
2349
2529
  /** 取某个图标的默认墨色:图标自带 ink 优先(全彩贴纸),否则用分类主色,最后退回全局默认 */
2350
2530
  declare function iconInk(icon: IconDef): string;
2351
2531
 
2532
+ /**
2533
+ * 内置模板库(数据层,不含 UI)。
2534
+ *
2535
+ * 设计约定(每一条都踩过坑):
2536
+ * - `build()` **现场生成**图形,不共享可变数组 —— 否则插两次会复用同一批 id。
2537
+ * - 坐标是**相对模板中心**的,插入时整体平移到视口中心;`x/y` 是**图形中心**。
2538
+ * - **连线段必须绑定到图形**(`startBinding`/`endBinding`)。不绑的话拖动盒子线不动,
2539
+ * 整个模板当场散架。走线交给路由算法(同一侧引出的多条线会自动共用主干)。
2540
+ * - 一个视觉整体由多个图形拼成时(表头+字段行、卡片+按钮),用 `groupId` 绑成组,
2541
+ * 拖动才会整块走。
2542
+ * - 想要「只有字、没有框」必须用 `type: 'text'`:`strokeWidth: 0` 并不会去掉矩形边框。
2543
+ * - 想加模板:往 `TEMPLATES` 里加一项,UI 与搜索会自动带上。
2544
+ */
2545
+ type TemplateCategoryId = 'flow' | 'org' | 'analysis' | 'plan' | 'uml' | 'tech';
2546
+ declare const TEMPLATE_CATEGORIES: {
2547
+ id: TemplateCategoryId;
2548
+ zh: string;
2549
+ en: string;
2550
+ }[];
2551
+ interface TemplateDef {
2552
+ id: string;
2553
+ cat: TemplateCategoryId;
2554
+ zh: string;
2555
+ en: string;
2556
+ /** 搜索关键词(中英混排,空格分词) */
2557
+ kw: string;
2558
+ build: () => Shape[];
2559
+ }
2560
+ declare const TEMPLATES: TemplateDef[];
2561
+ declare function templateById(id: string): TemplateDef | undefined;
2562
+ /** 搜索模板:中/英文名 + 关键词,空格分词全部命中 */
2563
+ declare function searchTemplates(query: string): TemplateDef[];
2564
+
2352
2565
  /**
2353
2566
  * 画板范围(Board Extent)—— 宿主用来决定「画板这块 DOM / 可滚区域该多大」。
2354
2567
  *
@@ -2423,8 +2636,16 @@ interface ToolContext {
2423
2636
  addShape(shape: Shape): void;
2424
2637
  updateShape(id: string, updates: Partial<Shape>): void;
2425
2638
  deleteShape(id: string): void;
2639
+ /** 橡皮擦专用:先渐隐再删除(普通删除请用 deleteShape) */
2640
+ eraseShape(id: string): void;
2426
2641
  selectShape(id: string, additive: boolean): void;
2427
2642
  selectShapes(ids: string[]): void;
2643
+ /**
2644
+ * 绕过「组扩张」直接只选这一个图形。
2645
+ * 用于「点第二次进入组」:组已经被整组选中时,再点某个成员就把它单独选出来
2646
+ * (否则组内成员永远只能整组操作,改一个标签得先解组)。
2647
+ */
2648
+ selectShapeRaw(id: string): void;
2428
2649
  getShapes(): Shape[];
2429
2650
  getSelectedIds(): string[];
2430
2651
  setActiveTool(tool: ToolType): void;
@@ -2560,6 +2781,11 @@ declare class SelectTool extends Tool {
2560
2781
  /** 把生成点位打包成创建上下文(from 为源点,to 为目标落点) */
2561
2782
  private genContext;
2562
2783
  onPointerDown(point: Vec2, event: PointerEvent): void;
2784
+ /**
2785
+ * 是否已经「进入」了 shape 所在的组:当前选区非空,且是这一组**严格子集**的成员。
2786
+ * 用于「组内逐成员切换」—— 进了组以后点同组其他成员不再整组扩张。
2787
+ */
2788
+ private isInsideGroup;
2563
2789
  /**
2564
2790
  * 命中「表格左上角拖动把手」的表格。把手画在表格包围盒之外、普通 hitTest 命中不到,
2565
2791
  * 所以这里单独判定;落点上若有别的图形则不抢(避免盖住别人的点击)。
@@ -2607,6 +2833,10 @@ declare class SelectTool extends Tool {
2607
2833
  *
2608
2834
  * 另外还要丢掉「其实属于自动走线的折点」(两端出/入桩的拐角,见 pruneStubBends):
2609
2835
  * 它们是自动生成的,冻进 userPoints 后会让之后移动图形时端点旁长出小钩子。
2836
+ *
2837
+ * 同时记下 `userPointsBase`(这一刻的自动走线):之后布局一变(自动走线的形状签名变了),
2838
+ * 这些折点就按「过期」处理,连线自动回到当前布局下最简单的走线 —— 否则旧布局下
2839
+ * 绕出来的大回环会被一路带到新布局里,看着就像凭空多出一圈。
2610
2840
  */
2611
2841
  private writeConnectorRoute;
2612
2842
  /**
@@ -2659,6 +2889,11 @@ declare class SelectTool extends Tool {
2659
2889
  /** 生成点位命中阈值 / 单击 vs 拖动判定阈值 */
2660
2890
  private static readonly GEN_HIT_THRESHOLD;
2661
2891
  private static readonly CLICK_MOVE_THRESHOLD;
2892
+ /**
2893
+ * 拖动起始阈值:小于这个距离的移动不生效。
2894
+ * 点击时鼠标/触控板的抖动(1~2px)以前会把图形真的挪动一点 —— 看着像"点一下就偏了"。
2895
+ */
2896
+ private static readonly DRAG_START_THRESHOLD;
2662
2897
  /**
2663
2898
  * 「点击锚点生成图形」时源图形与新图形之间的默认间距(页面单位,**边到边**)。
2664
2899
  * 旧实现是「中心沿法线外移 96」:对默认矩形(120×60)实际边距只有 36,看着太挤;
@@ -3098,4 +3333,4 @@ declare function imageDataUrl(src: string): string | null;
3098
3333
  /** 清空缓存(测试/卸载时用;会释放自己创建的 blob 地址) */
3099
3334
  declare function clearImageCache(): void;
3100
3335
 
3101
- export { type ArrowBinding, type ArrowHeadType, type ArrowShape, type ArrowStyle, ArrowTool, BoardExtentKeeper, type BoardExtentResult, type BoardExtentUpdate, Camera, type CameraState, type CircleShape, type ConnectionType, DEFAULT_HIGHLIGHTER_COLOR, DEFAULT_IMAGE_MAX_SIZE_BYTES, DEFAULT_PAGE_THEME, type DiamondShape, type DrawShape, type DrawuiTheme, type DrawuiThemeSelection, Editor, type EditorData, type EditorOptions, type EditorState, type EllipseShape, type ExportFormat, type ExportOptions, type FreehandMode, FreehandTool, type GenerationContext, HandTool, type HexagonShape, ICONS, ICON_CATEGORIES, ICON_CATEGORY_INK, ICON_DEFAULT_INK, ICON_DEFAULT_SIZE, ICON_GRID, type IconCategoryId, type IconDef, type IconLayer, type IconShape, type ImageShape, type ImageUploadConfig, type ImageUploadOption, type ImageUploader, LANGUAGES, type Language, type LineShape, LineTool, MINDMAP_COLORS, MINDMAP_DIRECTIONS, MINDMAP_FONT_SIZE, MINDMAP_HANDLE_HIT_RADIUS, MINDMAP_HANDLE_RADIUS, MINDMAP_MAX_WIDTH, MINDMAP_MIN_WIDTH, MINDMAP_NODE_HEIGHT, MIN_HANDLE_SEGMENT, MIN_JOG, Mat2d, type MessageKey, type MindMapDirection, type MindMapHandleKind, type MindMapHandlePos, type MindMapLayoutResult, type MindMapNode, type MindMapNodePos, type MindMapShape, type OctagonShape, PAGE_THEMES, type PageTheme, type PageThemePreset, type ParallelogramShape, type PentagonShape, type Rect, type RectangleShape, Renderer, type RoundedRectangleShape, STICKY_FOLD_RATIO, STICKY_NOTE_COLORS, STICKY_NOTE_DEFAULT_COLOR, STICKY_TEXT_SCALE, type SegmentHandle, SelectTool, type Shape, type ShapeStyle, ShapeTool, type ShapeType, type SnapGuide, type StarShape, type StickyNoteShape, TABLE_CELL_PADDING_X, TABLE_CELL_PADDING_Y, TABLE_DEFAULT_COL_WIDTH, TABLE_DEFAULT_ROW_HEIGHT, TABLE_FONT_SIZE, TABLE_HEADER_FILL, TABLE_LINE_HEIGHT_RATIO, TABLE_MAX_AUTO_LINES, TABLE_MIN_COL_WIDTH, TABLE_MIN_ROW_HEIGHT, TABLE_MOVE_HANDLE_GAP, TABLE_MOVE_HANDLE_HEIGHT, TABLE_MOVE_HANDLE_WIDTH, THEME, type TableCell, type TableShape, type TextShape, type ToolType, type TrapezoidShape, type TriangleShape, UML_FONT_SIZE, UML_HEADER_HEIGHT, UML_LINE_HEIGHT, UML_MIN_WIDTH, UML_PADDING_X, UML_PADDING_Y, type UiEvent, type UmlAttribute, type UmlClassShape, type UmlInterfaceShape, type UmlMethod, type UmlVisibility, Vec, type Vec2 as Vec2Type, addImageReadyListener, calcUmlClassSize, clearImageCache, cloneMindMapNode, cloneTable, cloneTableCells, collapseRoute, collectTemporaryImageSrcs, connectorSegmentHandles, countMindMapNodes, createTableCells, downloadBlob, effectiveDirection, ensureImagesLoaded, exportToBlob, findMindMapNode, getContentBounds, getIcon, getLanguage, getMindMapNodeDepth, getMindMapNodeSiblings, getMindMapParent, getTheme, hasIcon, hexToRgba, hitTableBody, hitTableMoveHandle, iconInk, iconsByCategory, imageDataUrl, isEmptyRect, isFlippableShapeType, isHorizontalDirection, isImageFailed, isImageLoading, isStubCorner, isUploaderFunction, layoutMindMap, loadImage, mindMapHandleAtLocalPoint, mindMapNodeColor, mindMapNodeHandles, mindMapNodeSize, moveMindMapNodeInTree, offsetConnectorSegment, parseUploadResponse, peekImage, pickSegmentHandle, pointToSegmentDistance, promoteMindMapNodeInTree, pruneStubBends, rectsIntersect, renderSVGString, renderToCanvas, reorderMindMapNodeInTree, routeToUserPoints, sameRoute, searchIcons, setImageCrossOrigin, setLanguage, setTheme, shadeColor, subscribeLanguage, subscribeTheme, subtractUnion, t, tableAnchorAt, tableCellAtLocalClamped, tableCellLineCount, tableCellLocalRect, tableCellTextStartOffset, tableCoveredMap, tableDeleteColumn, tableDeleteRow, tableExpandRangeToMerges, tableGrowRowHeights, tableInsertColumn, tableInsertRow, tableLocalPoint, tableMerge, tableMoveHandleLocalRect, tablePagePoint, tableRangeHasMerge, tableRangeLocalRect, tableRowAtPoint, tableSplit, tableSplitRange, tableTotalHeight, tableTotalWidth, umlAttrRowText, umlMethodRowText, unionRect, uploadImageViaConfig };
3336
+ export { type ArrowBinding, type ArrowHeadType, type ArrowShape, type ArrowStyle, ArrowTool, BoardExtentKeeper, type BoardExtentResult, type BoardExtentUpdate, Camera, type CameraState, type CircleShape, type ConnectionType, DEFAULT_HIGHLIGHTER_COLOR, DEFAULT_IMAGE_MAX_SIZE_BYTES, DEFAULT_PAGE_THEME, type DiamondShape, type DrawShape, type DrawuiTheme, type DrawuiThemeSelection, Editor, type EditorData, type EditorOptions, type EditorState, type EllipseShape, type ExportFormat, type ExportOptions, type FreehandMode, FreehandTool, type GenerationContext, HandTool, type HexagonShape, ICONS, ICON_CATEGORIES, ICON_CATEGORY_INK, ICON_DEFAULT_INK, ICON_DEFAULT_SIZE, ICON_GRID, type IconCategoryId, type IconDef, type IconLayer, type IconShape, type ImageShape, type ImageUploadConfig, type ImageUploadOption, type ImageUploader, LANGUAGES, type Language, type LineShape, LineTool, MINDMAP_COLORS, MINDMAP_DIRECTIONS, MINDMAP_FONT_SIZE, MINDMAP_HANDLE_HIT_RADIUS, MINDMAP_HANDLE_RADIUS, MINDMAP_MAX_WIDTH, MINDMAP_MIN_WIDTH, MINDMAP_NODE_HEIGHT, MIN_HANDLE_SEGMENT, MIN_JOG, Mat2d, type MessageKey, type MindMapDirection, type MindMapHandleKind, type MindMapHandlePos, type MindMapLayoutResult, type MindMapNode, type MindMapNodePos, type MindMapShape, type OctagonShape, PAGE_THEMES, type PageTheme, type PageThemePreset, type ParallelogramShape, type PentagonShape, type Rect, type RectangleShape, Renderer, type RoundedRectangleShape, STICKY_FOLD_RATIO, STICKY_NOTE_COLORS, STICKY_NOTE_DEFAULT_COLOR, STICKY_TEXT_SCALE, type SegmentHandle, SelectTool, type Shape, type ShapeStyle, ShapeTool, type ShapeType, type SnapGuide, type StarShape, type StickyNoteShape, TABLE_CELL_PADDING_X, TABLE_CELL_PADDING_Y, TABLE_DEFAULT_COL_WIDTH, TABLE_DEFAULT_ROW_HEIGHT, TABLE_FONT_SIZE, TABLE_HEADER_FILL, TABLE_LINE_HEIGHT_RATIO, TABLE_MAX_AUTO_LINES, TABLE_MIN_COL_WIDTH, TABLE_MIN_ROW_HEIGHT, TABLE_MOVE_HANDLE_GAP, TABLE_MOVE_HANDLE_HEIGHT, TABLE_MOVE_HANDLE_WIDTH, TEMPLATES, TEMPLATE_CATEGORIES, THEME, type TableCell, type TableShape, type TemplateCategoryId, type TemplateDef, type TextShape, type ToolType, type TrapezoidShape, type TriangleShape, UML_FONT_SIZE, UML_HEADER_HEIGHT, UML_LINE_HEIGHT, UML_MIN_WIDTH, UML_PADDING_X, UML_PADDING_Y, type UiEvent, type UmlAttribute, type UmlClassShape, type UmlInterfaceShape, type UmlMethod, type UmlVisibility, Vec, type Vec2 as Vec2Type, addImageReadyListener, calcUmlClassSize, clearImageCache, cloneMindMapNode, cloneTable, cloneTableCells, collapseRoute, collectTemporaryImageSrcs, connectorSegmentHandles, countMindMapNodes, createTableCells, downloadBlob, effectiveDirection, ensureIcons, ensureImagesLoaded, exportToBlob, findMindMapNode, getContentBounds, getIcon, getLanguage, getMindMapNodeDepth, getMindMapNodeSiblings, getMindMapParent, getTheme, hasIcon, hexToRgba, hitTableBody, hitTableMoveHandle, iconInk, iconsByCategory, imageDataUrl, isEmptyRect, isFlippableShapeType, isHorizontalDirection, isIconCatalogLoaded, isImageFailed, isImageLoading, isStubCorner, isUploaderFunction, layoutMindMap, loadIconCatalog, loadImage, mindMapHandleAtLocalPoint, mindMapNodeColor, mindMapNodeHandles, mindMapNodeSize, moveMindMapNodeInTree, offsetConnectorSegment, parseUploadResponse, peekImage, pickSegmentHandle, pointToSegmentDistance, promoteMindMapNodeInTree, pruneStubBends, rectsIntersect, renderSVGString, renderToCanvas, reorderMindMapNodeInTree, routeToUserPoints, sameRoute, searchIcons, searchTemplates, setImageCrossOrigin, setLanguage, setTheme, shadeColor, subscribeLanguage, subscribeTheme, subtractUnion, t, tableAnchorAt, tableCellAtLocalClamped, tableCellLineCount, tableCellLocalRect, tableCellTextStartOffset, tableCoveredMap, tableDeleteColumn, tableDeleteRow, tableExpandRangeToMerges, tableGrowRowHeights, tableInsertColumn, tableInsertRow, tableLocalPoint, tableMerge, tableMoveHandleLocalRect, tablePagePoint, tableRangeHasMerge, tableRangeLocalRect, tableRowAtPoint, tableSplit, tableSplitRange, tableTotalHeight, tableTotalWidth, templateById, umlAttrRowText, umlMethodRowText, unionRect, uploadImageViaConfig };