@8btc/whiteboard 0.0.1

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.js ADDED
@@ -0,0 +1,1706 @@
1
+ var Dt = Object.defineProperty;
2
+ var vt = (o) => {
3
+ throw TypeError(o);
4
+ };
5
+ var Zt = (o, s, t) => s in o ? Dt(o, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[s] = t;
6
+ var z = (o, s, t) => Zt(o, typeof s != "symbol" ? s + "" : s, t), mt = (o, s, t) => s.has(o) || vt("Cannot " + t);
7
+ var i = (o, s, t) => (mt(o, s, "read from private field"), t ? t.call(o) : s.get(o)), d = (o, s, t) => s.has(o) ? vt("Cannot add the same private member more than once") : s instanceof WeakSet ? s.add(o) : s.set(o, t), N = (o, s, t, e) => (mt(o, s, "write to private field"), e ? e.call(o, t) : s.set(o, t), t), S = (o, s, t) => (mt(o, s, "access private method"), t);
8
+ import { jsxs as U, jsx as w, Fragment as zt } from "react/jsx-runtime";
9
+ import { useState as O, useEffect as xt, useRef as At } from "react";
10
+ import R from "konva";
11
+ import Yt from "mitt";
12
+ import { v4 as $ } from "uuid";
13
+ import { Slot as Xt } from "@radix-ui/react-slot";
14
+ import { cva as Vt } from "class-variance-authority";
15
+ import { clsx as Ut } from "clsx";
16
+ import { twMerge as Ht } from "tailwind-merge";
17
+ import { Minus as Ot, Plus as Ft, Undo2 as Lt, Redo2 as Wt } from "lucide-react";
18
+ var f, l, P, K, q, j, B, J, Q, tt, et, bt;
19
+ class Gt {
20
+ constructor(s, t) {
21
+ d(this, et);
22
+ d(this, f);
23
+ d(this, l);
24
+ d(this, P, { x: 0, y: 0, scale: 1 });
25
+ /**
26
+ * 处理滚轮缩放和平移
27
+ */
28
+ d(this, K, (s) => {
29
+ s.evt.preventDefault();
30
+ const e = i(this, l).getPointerPosition();
31
+ if (e)
32
+ if (s.evt.ctrlKey) {
33
+ const r = i(this, P).scale, n = {
34
+ x: (e.x - i(this, P).x) / r,
35
+ y: (e.y - i(this, P).y) / r
36
+ }, a = 1.01, h = s.evt.deltaY > 0 ? -1 : 1, c = Math.min(Math.abs(s.evt.deltaY), 10);
37
+ let m = r;
38
+ for (let p = 0; p < c; p++)
39
+ m = h > 0 ? m * a : m / a;
40
+ const g = Math.max(0.1, Math.min(5, m)), I = {
41
+ x: e.x - n.x * g,
42
+ y: e.y - n.y * g
43
+ };
44
+ i(this, f).updateViewport({ x: I.x, y: I.y, scale: g });
45
+ } else {
46
+ const r = s.evt.shiftKey ? s.evt.deltaY : s.evt.deltaX, n = s.evt.shiftKey ? 0 : s.evt.deltaY;
47
+ i(this, f).updateViewport({
48
+ x: i(this, P).x - r,
49
+ y: i(this, P).y - n
50
+ });
51
+ }
52
+ });
53
+ d(this, q, (s) => {
54
+ const t = i(this, f).getState().toolType;
55
+ if (s.evt.button !== 0 || t === "hand")
56
+ return;
57
+ const e = s.target === i(this, l), r = i(this, l).getRelativePointerPosition();
58
+ if (t === "select" && !e) {
59
+ const n = s.target.id();
60
+ n && i(this, f).selectNode(n, s.evt.shiftKey);
61
+ return;
62
+ }
63
+ if (t === "rectangle" && r && i(this, f).createDraftNode(t, r), t === "image-marker" && r) {
64
+ const n = i(this, f).findImageAtPosition(r);
65
+ if (console.log(n, "imageShape"), n) {
66
+ const a = n.width(), h = n.height();
67
+ if (a && h) {
68
+ const c = {
69
+ x: n.x(),
70
+ y: n.y(),
71
+ width: a,
72
+ height: h
73
+ };
74
+ i(this, f).createDraftNode(t, r, {
75
+ parent: n.id(),
76
+ bounds: c,
77
+ startPosition: r
78
+ });
79
+ }
80
+ }
81
+ }
82
+ i(this, f).selectNode();
83
+ });
84
+ d(this, j, () => {
85
+ const s = i(this, f).getState().toolType;
86
+ if (s === "hand")
87
+ return;
88
+ const t = i(this, l).getRelativePointerPosition();
89
+ (s === "rectangle" || s === "image-marker") && t && i(this, f).updateDraftNode(t);
90
+ });
91
+ d(this, B, () => {
92
+ const s = i(this, f).getState().toolType;
93
+ s !== "hand" && (s === "rectangle" || s === "image-marker") && i(this, f).finalizeDraftNode();
94
+ });
95
+ d(this, J, (s) => {
96
+ if (s.target !== i(this, l))
97
+ return;
98
+ const t = i(this, f).getState().toolType;
99
+ t === "hand" ? this.setCursor("grabbing") : t === "select" && this.setCursor("all-scroll");
100
+ });
101
+ d(this, Q, (s) => {
102
+ s.target === i(this, l) && i(this, f).updateViewport({
103
+ x: i(this, l).x(),
104
+ y: i(this, l).y()
105
+ });
106
+ });
107
+ d(this, tt, (s) => {
108
+ s.target === i(this, l) && (i(this, f).updateViewport({
109
+ x: i(this, l).x(),
110
+ y: i(this, l).y()
111
+ }), this.resetCursor());
112
+ });
113
+ N(this, f, s), N(this, l, new R.Stage({
114
+ container: t.container,
115
+ width: t.width,
116
+ height: t.height,
117
+ x: 0,
118
+ y: 0,
119
+ scaleX: 1,
120
+ scaleY: 1,
121
+ draggable: t.draggable ?? !1,
122
+ className: t.className
123
+ })), S(this, et, bt).call(this);
124
+ }
125
+ /**
126
+ * 获取原生 Konva.Stage 实例
127
+ */
128
+ getStage() {
129
+ return i(this, l);
130
+ }
131
+ /**
132
+ * 获取当前视口状态
133
+ */
134
+ getViewport() {
135
+ return { ...i(this, P) };
136
+ }
137
+ /**
138
+ * 设置视口(包括位置、缩放和尺寸)
139
+ */
140
+ setViewport(s) {
141
+ const t = { ...i(this, P), ...s };
142
+ N(this, P, t), s.x !== void 0 && i(this, l).x(s.x), s.y !== void 0 && i(this, l).y(s.y), s.scale !== void 0 && (i(this, l).scaleX(s.scale), i(this, l).scaleY(s.scale)), s.width !== void 0 && i(this, l).width(s.width), s.height !== void 0 && i(this, l).height(s.height);
143
+ }
144
+ /**
145
+ * 设置是否可拖拽
146
+ */
147
+ setDraggable(s) {
148
+ i(this, l).draggable(s);
149
+ }
150
+ /**
151
+ * 设置光标样式
152
+ */
153
+ setCursor(s) {
154
+ const t = i(this, l).container();
155
+ t.style.cursor = s;
156
+ }
157
+ /**
158
+ * 重置光标样式
159
+ */
160
+ resetCursor() {
161
+ const s = i(this, l).container();
162
+ if (i(this, f).getState().toolType === "hand") {
163
+ s.style.cursor = "grab";
164
+ return;
165
+ }
166
+ s.style.cursor = "default";
167
+ }
168
+ /**
169
+ * 销毁 Stage
170
+ */
171
+ destroy() {
172
+ i(this, l).destroy();
173
+ }
174
+ }
175
+ f = new WeakMap(), l = new WeakMap(), P = new WeakMap(), K = new WeakMap(), q = new WeakMap(), j = new WeakMap(), B = new WeakMap(), J = new WeakMap(), Q = new WeakMap(), tt = new WeakMap(), et = new WeakSet(), /**
176
+ * 设置事件监听器
177
+ */
178
+ bt = function() {
179
+ i(this, l).on("wheel", i(this, K)), i(this, l).on("pointerdown", i(this, q)), i(this, l).on("pointermove", i(this, j)), i(this, l).on("pointerup", i(this, B)), i(this, l).on("dragstart", i(this, J)), i(this, l).on("dragmove", i(this, Q)), i(this, l).on("dragend", i(this, tt));
180
+ };
181
+ var F, x, st, it, rt, nt, ot, at, ht, Et;
182
+ class $t {
183
+ constructor(s, t) {
184
+ d(this, ht);
185
+ d(this, F);
186
+ d(this, x);
187
+ /**
188
+ * 处理 transformstart 事件
189
+ */
190
+ d(this, st, () => {
191
+ this.emitPositionChange();
192
+ });
193
+ /**
194
+ * 处理 transform 事件
195
+ */
196
+ d(this, it, () => {
197
+ console.log("transforming..."), this.emitPositionChange();
198
+ });
199
+ /**
200
+ * 处理 transformend 事件
201
+ */
202
+ d(this, rt, () => {
203
+ this.emitPositionChange();
204
+ });
205
+ /**
206
+ * 处理 dragstart 事件
207
+ */
208
+ d(this, nt, () => {
209
+ this.emitPositionChange();
210
+ });
211
+ /**
212
+ * 处理 dragmove 事件
213
+ */
214
+ d(this, ot, () => {
215
+ this.emitPositionChange();
216
+ });
217
+ /**
218
+ * 处理 dragend 事件
219
+ */
220
+ d(this, at, () => {
221
+ this.emitPositionChange();
222
+ });
223
+ N(this, F, s), N(this, x, new R.Transformer({
224
+ rotateEnabled: t?.rotateEnabled ?? !0,
225
+ ignoreStroke: t?.ignoreStroke ?? !0,
226
+ anchorSize: t?.anchorSize ?? 8,
227
+ borderDash: t?.borderDash ?? [4, 4],
228
+ anchorCornerRadius: t?.anchorCornerRadius ?? 4,
229
+ padding: t?.padding ?? 6
230
+ })), S(this, ht, Et).call(this);
231
+ }
232
+ /**
233
+ * 获取原生 Konva.Transformer 实例
234
+ */
235
+ getTransformer() {
236
+ return i(this, x);
237
+ }
238
+ /**
239
+ * 获取 Transformer 的位置信息
240
+ */
241
+ getPosition() {
242
+ if (i(this, x).nodes().length === 0)
243
+ return null;
244
+ const t = i(this, x).getClientRect();
245
+ return {
246
+ x: t.x,
247
+ y: t.y,
248
+ width: t.width,
249
+ height: t.height,
250
+ rotation: i(this, x).rotation()
251
+ };
252
+ }
253
+ /**
254
+ * 设置要变换的节点
255
+ */
256
+ setNodes(s) {
257
+ if (s.length === 0) {
258
+ this.clearNodes();
259
+ return;
260
+ }
261
+ i(this, x).nodes(s), i(this, x).moveToTop(), this.emitPositionChange();
262
+ }
263
+ /**
264
+ * 获取当前变换的节点
265
+ */
266
+ getNodes() {
267
+ return i(this, x).nodes();
268
+ }
269
+ /**
270
+ * 清除所有节点
271
+ */
272
+ clearNodes() {
273
+ i(this, x).nodes([]), i(this, x).moveToBottom(), this.emitPositionChange();
274
+ }
275
+ /**
276
+ * emit Transformer 位置
277
+ */
278
+ emitPositionChange() {
279
+ const s = this.getPosition();
280
+ i(this, F).emitEvent("transformer:positionChange", s);
281
+ }
282
+ /**
283
+ * 销毁 Transformer
284
+ */
285
+ destroy() {
286
+ i(this, x).destroy();
287
+ }
288
+ }
289
+ F = new WeakMap(), x = new WeakMap(), st = new WeakMap(), it = new WeakMap(), rt = new WeakMap(), nt = new WeakMap(), ot = new WeakMap(), at = new WeakMap(), ht = new WeakSet(), /**
290
+ * 设置事件监听器
291
+ */
292
+ Et = function() {
293
+ i(this, x).on("transformstart", i(this, st)), i(this, x).on("transform", i(this, it)), i(this, x).on("transformend", i(this, rt)), i(this, x).on("dragstart", i(this, nt)), i(this, x).on("dragmove", i(this, ot)), i(this, x).on("dragend", i(this, at));
294
+ };
295
+ class Kt {
296
+ constructor(s) {
297
+ z(this, "_past", []);
298
+ z(this, "_present");
299
+ z(this, "_future", []);
300
+ z(this, "_emitter");
301
+ this._present = s, this._emitter = Yt();
302
+ }
303
+ /**
304
+ * 获取当前状态
305
+ */
306
+ getState() {
307
+ return { ...this._present };
308
+ }
309
+ /**
310
+ * 获取完整历史状态
311
+ */
312
+ getHistory() {
313
+ return {
314
+ past: [...this._past],
315
+ present: { ...this._present },
316
+ future: [...this._future]
317
+ };
318
+ }
319
+ /**
320
+ * 是否可以撤销
321
+ */
322
+ canUndo() {
323
+ return this._past.length > 0;
324
+ }
325
+ /**
326
+ * 是否可以重做
327
+ */
328
+ canRedo() {
329
+ return this._future.length > 0;
330
+ }
331
+ /**
332
+ * 订阅状态事件
333
+ */
334
+ on(s, t) {
335
+ this._emitter.on(s, t);
336
+ }
337
+ /**
338
+ * 取消订阅状态事件
339
+ */
340
+ off(s, t) {
341
+ this._emitter.off(s, t);
342
+ }
343
+ /**
344
+ * 发送事件
345
+ */
346
+ emit(s, t) {
347
+ this._emitter.emit(s, t);
348
+ }
349
+ /**
350
+ * 撤销操作
351
+ */
352
+ undo() {
353
+ if (this._past.length === 0) return;
354
+ const s = this._past[this._past.length - 1], t = this._past.slice(0, this._past.length - 1);
355
+ this._past = t, this._future = [this._present, ...this._future], this._present = s, this._syncState(s), this._emitter.emit("state:undo", s), this._emitter.emit("state:change", s);
356
+ }
357
+ /**
358
+ * 重做操作
359
+ */
360
+ redo() {
361
+ if (this._future.length === 0) return;
362
+ const s = this._future[0], t = this._future.slice(1);
363
+ this._past = [...this._past, this._present], this._future = t, this._present = s, this._syncState(s), this._emitter.emit("state:redo", s), this._emitter.emit("state:change", s);
364
+ }
365
+ /**
366
+ * 重置历史记录
367
+ */
368
+ resetHistory() {
369
+ this._past = [], this._future = [], this._emitter.emit("state:reset", this._present), this._emitter.emit("state:change", this._present);
370
+ }
371
+ /**
372
+ * 更新状态
373
+ * @param partial - 部分状态更新
374
+ * @param addToHistory - 是否添加到历史记录
375
+ */
376
+ _updateState(s, t = !0) {
377
+ const e = { ...this._present, ...s };
378
+ t && (this._past = [...this._past, this._present], this._future = []), this._present = e, this._emitter.emit("state:change", e);
379
+ }
380
+ /**
381
+ * 同步状态到外部系统(由子类实现)
382
+ * @param _state - 要同步的状态
383
+ */
384
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
385
+ _syncState(s) {
386
+ }
387
+ /**
388
+ * 清理资源
389
+ */
390
+ _dispose() {
391
+ this._emitter.all.clear();
392
+ }
393
+ }
394
+ const wt = "shapeNameForSelect", y = {
395
+ CORNER_RADIUS: 6,
396
+ MIN_SIZE: 10
397
+ }, V = {
398
+ MIN_SIZE: 10
399
+ }, St = {
400
+ MIN_SIZE: 10
401
+ };
402
+ function qt(o, s, t) {
403
+ return 2 * (s + o - t * (4 - Math.PI));
404
+ }
405
+ function jt(o, s, t) {
406
+ let e = 1, r = 0, n = 0;
407
+ switch (t) {
408
+ case "dashed":
409
+ r = Math.min(s * 2, o / 4);
410
+ break;
411
+ case "dotted":
412
+ e = 8, r = s / e;
413
+ break;
414
+ default:
415
+ return [];
416
+ }
417
+ let a = Math.floor(o / r / (2 * e));
418
+ return a = Math.max(a, 3), r = o / a / (2 * e), n = (o - a * r) / a, [r, n];
419
+ }
420
+ function Bt(o) {
421
+ switch (o) {
422
+ case "small":
423
+ return 2;
424
+ case "medium":
425
+ return 3;
426
+ case "large":
427
+ return 5;
428
+ case "extra-large":
429
+ return 8;
430
+ default:
431
+ return 3;
432
+ }
433
+ }
434
+ function Jt(o) {
435
+ return Array.isArray(o) && o.length > 1 ? o[0] + o[1] : 0;
436
+ }
437
+ function _t(o) {
438
+ return {
439
+ width: Math.max(St.MIN_SIZE, o.width() * o.scaleX()),
440
+ height: Math.max(St.MIN_SIZE, o.height() * o.scaleY())
441
+ };
442
+ }
443
+ class Nt {
444
+ constructor(s, t) {
445
+ z(this, "core");
446
+ z(this, "node");
447
+ z(this, "element");
448
+ z(this, "toolTypeChangeHandler");
449
+ this.core = s, this.node = t, this.element = this.createElement(), this.toolTypeChangeHandler = (e) => {
450
+ const r = e === "select";
451
+ this.element.listening(r);
452
+ }, this.toolTypeChangeHandler(this.core.getToolType()), this.core.on("toolType:change", this.toolTypeChangeHandler);
453
+ }
454
+ /**
455
+ * 获取 Konva 元素
456
+ */
457
+ getElement() {
458
+ return this.element;
459
+ }
460
+ /**
461
+ * 获取节点数据
462
+ */
463
+ getNode() {
464
+ return this.node;
465
+ }
466
+ /**
467
+ * 销毁节点
468
+ */
469
+ destroy() {
470
+ this.core.off("toolType:change", this.toolTypeChangeHandler), this.element.destroy();
471
+ }
472
+ }
473
+ function Qt(o, s) {
474
+ if (!s || s === 0)
475
+ return null;
476
+ const t = new R.Animation((e) => {
477
+ if (!e) return;
478
+ const r = -e.time / 10 % s;
479
+ o.dashOffset(r);
480
+ }, o.getLayer());
481
+ return {
482
+ start: () => t.start(),
483
+ stop: () => t.stop(),
484
+ isRunning: () => t.isRunning()
485
+ };
486
+ }
487
+ var _, D, gt, pt, kt;
488
+ class te extends Nt {
489
+ constructor(t, e) {
490
+ super(t, e);
491
+ d(this, D);
492
+ d(this, _, null);
493
+ e.style.animated && S(this, D, gt).call(this), S(this, D, kt).call(this, this.getElement());
494
+ }
495
+ createElement() {
496
+ const t = Math.max(
497
+ this.node.props.width ?? y.MIN_SIZE,
498
+ y.MIN_SIZE
499
+ ), e = Math.max(
500
+ this.node.props.height ?? y.MIN_SIZE,
501
+ y.MIN_SIZE
502
+ ), r = {
503
+ id: this.node.id,
504
+ ...this.node.props,
505
+ ...this.node.style,
506
+ width: t,
507
+ height: e,
508
+ cornerRadius: y.CORNER_RADIUS,
509
+ name: wt,
510
+ draggable: !0,
511
+ stroke: "black",
512
+ strokeWidth: 2
513
+ }, n = new R.Rect(r);
514
+ return n.setAttrs({
515
+ width: t,
516
+ height: e
517
+ }), n;
518
+ }
519
+ /**
520
+ * 获取 Konva.Rect 实例
521
+ */
522
+ getElement() {
523
+ return this.element;
524
+ }
525
+ /**
526
+ * 更新节点数据
527
+ */
528
+ updateNode(t) {
529
+ this.node = {
530
+ ...this.node,
531
+ ...t,
532
+ props: {
533
+ ...this.node.props,
534
+ ...t.props
535
+ },
536
+ style: {
537
+ ...this.node.style,
538
+ ...t.style
539
+ }
540
+ };
541
+ const e = this.getElement();
542
+ e.x(this.node.props.x), e.y(this.node.props.y);
543
+ const r = Math.max(
544
+ this.node.props.width ?? y.MIN_SIZE,
545
+ y.MIN_SIZE
546
+ ), n = Math.max(
547
+ this.node.props.height ?? y.MIN_SIZE,
548
+ y.MIN_SIZE
549
+ );
550
+ e.width(r), e.height(n), this.node.style.animated && !i(this, _) ? S(this, D, gt).call(this) : !this.node.style.animated && i(this, _) && S(this, D, pt).call(this);
551
+ }
552
+ /**
553
+ * 销毁
554
+ */
555
+ destroy() {
556
+ S(this, D, pt).call(this), super.destroy();
557
+ }
558
+ }
559
+ _ = new WeakMap(), D = new WeakSet(), /**
560
+ * 初始化动画
561
+ */
562
+ gt = function() {
563
+ const t = this.getElement(), e = t.dash();
564
+ if (!e || e.length === 0) return;
565
+ const r = Jt(e);
566
+ N(this, _, Qt(t, r)), i(this, _) && i(this, _).start();
567
+ }, /**
568
+ * 销毁动画
569
+ */
570
+ pt = function() {
571
+ i(this, _) && (i(this, _).stop(), N(this, _, null));
572
+ }, /**
573
+ * 设置事件处理器
574
+ */
575
+ kt = function(t = void 0) {
576
+ const e = t ?? this.getElement();
577
+ e.on("transformstart", () => {
578
+ this.node.style.animated && i(this, _) && i(this, _).stop();
579
+ }), e.on("transform", (r) => {
580
+ const n = r.target, { width: a, height: h } = _t(n), c = qt(a, h, y.CORNER_RADIUS), m = jt(
581
+ c,
582
+ Bt(this.node.style.size),
583
+ this.node.style.line
584
+ );
585
+ n.scale({ x: 1, y: 1 }), n.width(a), n.height(h), n.dash(m.map((g) => g * this.core.getStageScale()));
586
+ }), e.on("transformend", (r) => {
587
+ const n = r.target, { width: a, height: h } = _t(n), c = {
588
+ ...this.node.props,
589
+ x: n.x(),
590
+ y: n.y(),
591
+ width: a,
592
+ height: h,
593
+ rotation: n.rotation()
594
+ };
595
+ this.node.props = c, this.core._syncNodeFromElement(this.node.id, {
596
+ props: c
597
+ }), this.node.style.animated && i(this, _)?.isRunning() === !1 && i(this, _).start();
598
+ }), e.on("dragend", (r) => {
599
+ const n = r.target, a = {
600
+ ...this.node.props,
601
+ x: n.x(),
602
+ y: n.y()
603
+ };
604
+ this.node.props = a, this.core._syncNodeFromElement(this.node.id, {
605
+ props: a
606
+ });
607
+ });
608
+ };
609
+ var E, ut, Ct, yt, ft;
610
+ class ee extends Nt {
611
+ constructor(t, e) {
612
+ super(t, e);
613
+ d(this, E);
614
+ S(this, E, ut).call(this), S(this, E, Ct).call(this, this.getElement());
615
+ }
616
+ createElement() {
617
+ const t = document.createElement("canvas");
618
+ return t.width = 1, t.height = 1, new R.Image({
619
+ id: this.node.id,
620
+ x: this.node.props.x,
621
+ y: this.node.props.y,
622
+ name: wt,
623
+ draggable: !0,
624
+ image: t
625
+ });
626
+ }
627
+ /**
628
+ * 获取 Konva.Image 实例
629
+ */
630
+ getElement() {
631
+ return this.element;
632
+ }
633
+ /**
634
+ * 更新节点数据
635
+ */
636
+ updateNode(t) {
637
+ this.node = {
638
+ ...this.node,
639
+ ...t,
640
+ props: {
641
+ ...this.node.props,
642
+ ...t.props
643
+ },
644
+ style: {
645
+ ...this.node.style,
646
+ ...t.style
647
+ },
648
+ meta: {
649
+ ...this.node.meta,
650
+ ...t.meta
651
+ }
652
+ };
653
+ const e = this.getElement();
654
+ if (e.x(this.node.props.x), e.y(this.node.props.y), this.node.props.width && this.node.props.height) {
655
+ const r = Math.max(this.node.props.width, V.MIN_SIZE), n = Math.max(this.node.props.height, V.MIN_SIZE);
656
+ e.width(r), e.height(n);
657
+ }
658
+ this.node.props.rotation !== void 0 && e.rotation(this.node.props.rotation), t.meta?.imageUrl && t.meta.imageUrl !== this.node.meta.imageUrl && S(this, E, ut).call(this);
659
+ }
660
+ /**
661
+ * 销毁
662
+ */
663
+ destroy() {
664
+ super.destroy();
665
+ }
666
+ }
667
+ E = new WeakSet(), /**
668
+ * 加载图片
669
+ */
670
+ ut = function() {
671
+ const t = this.node.meta.imageUrl;
672
+ if (!t) {
673
+ console.warn("Image URL is missing");
674
+ return;
675
+ }
676
+ const e = new window.Image();
677
+ e.crossOrigin = "anonymous", e.src = t, e.onload = () => {
678
+ this.getElement().image(e);
679
+ const r = this.node.props.width ?? e.width, n = this.node.props.height ?? e.height;
680
+ this.getElement().width(Math.max(r, V.MIN_SIZE)), this.getElement().height(Math.max(n, V.MIN_SIZE));
681
+ }, e.onerror = () => {
682
+ console.error("Failed to load image:", t);
683
+ };
684
+ }, /**
685
+ * 设置事件处理器
686
+ */
687
+ Ct = function(t) {
688
+ t.on("transform", (e) => {
689
+ const r = e.target, n = Math.max(V.MIN_SIZE, r.width() * r.scaleX()), a = Math.max(V.MIN_SIZE, r.height() * r.scaleY());
690
+ r.scale({ x: 1, y: 1 }), r.width(n), r.height(a), S(this, E, yt).call(this);
691
+ }), t.on("transformend", (e) => {
692
+ const r = e.target, n = {
693
+ ...this.node.props,
694
+ x: r.x(),
695
+ y: r.y(),
696
+ width: r.width(),
697
+ height: r.height(),
698
+ rotation: r.rotation()
699
+ };
700
+ this.node.props = n, this.core._syncNodeFromElement(this.node.id, {
701
+ props: n
702
+ }), S(this, E, ft).call(this);
703
+ }), t.on("dragmove", () => {
704
+ S(this, E, yt).call(this);
705
+ }), t.on("dragend", (e) => {
706
+ const r = e.target, n = {
707
+ ...this.node.props,
708
+ x: r.x(),
709
+ y: r.y()
710
+ };
711
+ this.node.props = n, this.core._syncNodeFromElement(this.node.id, {
712
+ props: n
713
+ }), S(this, E, ft).call(this);
714
+ });
715
+ }, /**
716
+ * 同步 image-marker 节点的位置(实时更新 Konva 元素)
717
+ */
718
+ yt = function() {
719
+ const t = this.getElement(), e = t.getLayer();
720
+ if (!e) return;
721
+ const r = t.x(), n = t.y(), a = t.width(), h = t.height(), c = e.find(
722
+ (g) => g.hasName(this.node.id)
723
+ ), m = this.core.getState().nodes || [];
724
+ c.forEach((g) => {
725
+ const I = m.find((p) => p.id === g.id());
726
+ if (I?.type === "image-marker" && I.meta.relativePosition) {
727
+ const { start: p, end: k } = I.meta.relativePosition, C = r + p.percentX / 100 * a, T = n + p.percentY / 100 * h, X = r + k.percentX / 100 * a, Z = n + k.percentY / 100 * h, Pt = Math.min(C, X), Rt = Math.min(T, Z), dt = Math.abs(X - C), lt = Math.abs(Z - T);
728
+ g.position({ x: Pt, y: Rt }), g.setAttrs({ width: dt, height: lt }), g.getChildren().forEach((G) => {
729
+ G.getClassName() === "Rect" ? G.setAttrs({ width: dt, height: lt }) : G.getClassName() === "Group" && G.setAttrs({ x: dt, y: lt });
730
+ });
731
+ }
732
+ });
733
+ }, /**
734
+ * 同步 image-marker 节点到状态
735
+ */
736
+ ft = function() {
737
+ const e = this.getElement().getLayer();
738
+ if (!e) return;
739
+ e.find(
740
+ (n) => n.hasName(this.node.id)
741
+ ).forEach((n) => {
742
+ this.core._syncNodeFromElement(n.id(), {
743
+ props: {
744
+ x: n.x(),
745
+ y: n.y(),
746
+ width: n.width(),
747
+ height: n.height()
748
+ }
749
+ });
750
+ });
751
+ };
752
+ var Y, b, L, W, ct, Tt;
753
+ class se extends Nt {
754
+ constructor(t, e) {
755
+ super(t, e);
756
+ d(this, ct);
757
+ d(this, Y);
758
+ d(this, b);
759
+ d(this, L);
760
+ d(this, W);
761
+ const r = this.getElement();
762
+ N(this, Y, r.findOne(".rect")), N(this, b, r.findOne(".marker-group")), N(this, L, i(this, b).findOne("Circle")), N(this, W, i(this, b).findOne("Text")), S(this, ct, Tt).call(this);
763
+ }
764
+ createElement() {
765
+ const t = Math.max(
766
+ this.node.props.width ?? y.MIN_SIZE,
767
+ y.MIN_SIZE
768
+ ), e = Math.max(
769
+ this.node.props.height ?? y.MIN_SIZE,
770
+ y.MIN_SIZE
771
+ ), r = new R.Group({
772
+ id: this.node.id,
773
+ name: `static ${this.node.meta.parent}`,
774
+ x: this.node.props.x,
775
+ y: this.node.props.y,
776
+ width: t,
777
+ height: e
778
+ }), n = new R.Rect({
779
+ name: "rect",
780
+ x: 0,
781
+ y: 0,
782
+ width: t,
783
+ height: e,
784
+ stroke: this.node.style.color,
785
+ strokeWidth: 2,
786
+ dash: [5, 5],
787
+ fill: "transparent",
788
+ cornerRadius: y.CORNER_RADIUS
789
+ }), a = new R.Group({
790
+ name: "marker-group",
791
+ x: t,
792
+ y: e
793
+ }), c = 16 / this.core.getStageScale(), m = new R.Circle({
794
+ radius: c,
795
+ fill: "red",
796
+ stroke: "black",
797
+ strokeWidth: 2
798
+ }), g = new R.Text({
799
+ x: -c,
800
+ y: -c,
801
+ width: c * 2,
802
+ height: c * 2,
803
+ text: String(this.node.meta.markerNumber || ""),
804
+ align: "center",
805
+ verticalAlign: "middle",
806
+ fontSize: 16,
807
+ fill: "white"
808
+ });
809
+ return a.add(m), a.add(g), r.add(n), r.add(a), r;
810
+ }
811
+ /**
812
+ * 获取 Konva.Group 实例
813
+ */
814
+ getElement() {
815
+ return this.element;
816
+ }
817
+ /**
818
+ * 更新节点数据
819
+ */
820
+ updateNode(t) {
821
+ this.node = {
822
+ ...this.node,
823
+ ...t,
824
+ props: {
825
+ ...this.node.props,
826
+ ...t.props
827
+ },
828
+ style: {
829
+ ...this.node.style,
830
+ ...t.style
831
+ },
832
+ meta: {
833
+ ...this.node.meta,
834
+ ...t.meta
835
+ }
836
+ };
837
+ const e = this.getElement();
838
+ e.x(this.node.props.x), e.y(this.node.props.y);
839
+ const r = Math.max(
840
+ this.node.props.width ?? y.MIN_SIZE,
841
+ y.MIN_SIZE
842
+ ), n = Math.max(
843
+ this.node.props.height ?? y.MIN_SIZE,
844
+ y.MIN_SIZE
845
+ );
846
+ e.width(r), e.height(n), i(this, Y).width(r), i(this, Y).height(n), i(this, b).x(r), i(this, b).y(n), t.style?.color && i(this, Y).stroke(t.style.color), t.meta?.markerNumber !== void 0 && i(this, W).text(String(t.meta.markerNumber));
847
+ }
848
+ /**
849
+ * 销毁
850
+ */
851
+ destroy() {
852
+ super.destroy();
853
+ }
854
+ /**
855
+ * 更新焦点状态(hover 或 selected)
856
+ */
857
+ setFocusState(t) {
858
+ const e = t ? 3 : 2, r = t ? 1.2 : 1;
859
+ i(this, Y).strokeWidth(e), i(this, L).strokeWidth(e), i(this, b).scaleX(r), i(this, b).scaleY(r);
860
+ }
861
+ }
862
+ Y = new WeakMap(), b = new WeakMap(), L = new WeakMap(), W = new WeakMap(), ct = new WeakSet(), /**
863
+ * 设置事件处理器
864
+ */
865
+ Tt = function() {
866
+ i(this, b).on("pointerover", () => {
867
+ this.setFocusState(!0), this.core.setCursor("pointer");
868
+ }), i(this, b).on("pointerout", () => {
869
+ const e = (this.core.getState().selectedNodeIds || []).includes(this.node.id);
870
+ this.setFocusState(e), this.core.resetCursor();
871
+ }), i(this, b).on("pointerdown", () => {
872
+ this.core.selectNode(this.node.id);
873
+ });
874
+ };
875
+ function Mt(o, s, t) {
876
+ switch (s) {
877
+ case "rectangle":
878
+ return new te(o, t);
879
+ case "image":
880
+ return new ee(o, t);
881
+ case "image-marker":
882
+ return new se(o, t);
883
+ default:
884
+ return null;
885
+ }
886
+ }
887
+ const ie = (o, s, t, e) => {
888
+ const r = {
889
+ type: o,
890
+ id: $(),
891
+ text: null,
892
+ style: {
893
+ opacity: 1,
894
+ line: "solid",
895
+ color: "black",
896
+ size: "medium",
897
+ animated: !1
898
+ },
899
+ props: {
900
+ x: s.x,
901
+ y: s.y,
902
+ rotation: 0,
903
+ visible: !0
904
+ },
905
+ meta: e ?? {}
906
+ };
907
+ return o === "image-marker" ? {
908
+ ...r,
909
+ style: {
910
+ ...r.style,
911
+ color: "#ff0000",
912
+ line: "dashed"
913
+ }
914
+ } : r;
915
+ };
916
+ function re(o, s, t) {
917
+ let e = s;
918
+ o.type === "image-marker" && t && (e = {
919
+ x: Math.max(t.x, Math.min(t.x + t.width, s.x)),
920
+ y: Math.max(t.y, Math.min(t.y + t.height, s.y))
921
+ });
922
+ const [r, n] = ne(
923
+ { x: o.props.x, y: o.props.y },
924
+ e
925
+ );
926
+ return o.type === "rectangle" || o.type === "image-marker" ? {
927
+ ...o,
928
+ props: {
929
+ ...o.props,
930
+ x: r.x,
931
+ y: r.y,
932
+ width: Math.max(n.x - r.x, y.MIN_SIZE),
933
+ height: Math.max(n.y - r.y, y.MIN_SIZE)
934
+ }
935
+ } : o;
936
+ }
937
+ function ne(o, s) {
938
+ let t = o.x, e = o.y, r = s.x, n = s.y, a;
939
+ return t > r && (a = Math.abs(t - r), t = r, r = t + a), e > n && (a = Math.abs(e - n), e = n, n = e + a), [
940
+ { x: t, y: e },
941
+ { x: r, y: n }
942
+ ];
943
+ }
944
+ var u, A, M, v;
945
+ class oe extends Kt {
946
+ constructor(t) {
947
+ super({
948
+ viewport: {
949
+ x: 0,
950
+ y: 0,
951
+ width: t.clientWidth,
952
+ height: t.clientHeight,
953
+ scale: 1
954
+ },
955
+ toolType: "select",
956
+ nodes: []
957
+ });
958
+ d(this, u);
959
+ d(this, A);
960
+ d(this, M);
961
+ d(this, v, null);
962
+ N(this, u, new Gt(this, {
963
+ container: t,
964
+ width: t.clientWidth,
965
+ height: t.clientHeight,
966
+ draggable: !1,
967
+ className: "touch-none"
968
+ })), N(this, A, new R.Layer()), N(this, M, new $t(this)), i(this, u).getStage().add(i(this, A)), i(this, A).add(i(this, M).getTransformer()), this.updateViewport(this.getState().viewport, !1);
969
+ }
970
+ /**
971
+ * 获取 CanvasStage 实例
972
+ */
973
+ getCanvasStage() {
974
+ return i(this, u);
975
+ }
976
+ /**
977
+ * 获取 CanvasTransformer 实例
978
+ */
979
+ getCanvasTransformer() {
980
+ return i(this, M);
981
+ }
982
+ /**
983
+ * 发射事件(供内部类使用)
984
+ */
985
+ emitEvent(t, e) {
986
+ this.emit(t, e);
987
+ }
988
+ /**
989
+ * 获取 Konva.Stage 实例
990
+ */
991
+ getStage() {
992
+ return i(this, u).getStage();
993
+ }
994
+ /**
995
+ * 获取 Stage 容器元素
996
+ */
997
+ getContainer() {
998
+ return i(this, u).getStage().container();
999
+ }
1000
+ /**
1001
+ * 获取主图层
1002
+ */
1003
+ getMainLayer() {
1004
+ return i(this, A);
1005
+ }
1006
+ /**
1007
+ * 获取当前工具类型
1008
+ */
1009
+ getToolType() {
1010
+ return this.getState().toolType;
1011
+ }
1012
+ /**
1013
+ * 设置当前工具类型(内部使用)
1014
+ */
1015
+ setToolType(t) {
1016
+ this.selectNode(), this._updateState(
1017
+ {
1018
+ toolType: t
1019
+ },
1020
+ !1
1021
+ ), this.emit("toolType:change", t), t === "hand" ? (i(this, u).setDraggable(!0), i(this, u).setCursor("grab")) : (i(this, u).setDraggable(!1), i(this, u).resetCursor());
1022
+ }
1023
+ /**
1024
+ * 设置是否可拖拽(内部使用)
1025
+ */
1026
+ setDraggable(t) {
1027
+ i(this, u).setDraggable(t);
1028
+ }
1029
+ /**
1030
+ * 设置光标
1031
+ * @internal 仅供内部使用
1032
+ */
1033
+ setCursor(t) {
1034
+ i(this, u).setCursor(t);
1035
+ }
1036
+ /**
1037
+ * 重置光标
1038
+ * @internal 仅供内部使用
1039
+ */
1040
+ resetCursor() {
1041
+ i(this, u).resetCursor();
1042
+ }
1043
+ /**
1044
+ * 获取当前 Stage 缩放比例
1045
+ */
1046
+ getStageScale() {
1047
+ return i(this, u).getStage().scaleX();
1048
+ }
1049
+ /**
1050
+ * 更新视口位置
1051
+ * @internal 仅供内部使用,外部请使用 CanvasApi
1052
+ */
1053
+ updateViewport(t, e = !1) {
1054
+ i(this, u).setViewport(t);
1055
+ const r = {
1056
+ ...this.getState().viewport,
1057
+ ...t
1058
+ };
1059
+ this._updateState(
1060
+ {
1061
+ viewport: r
1062
+ },
1063
+ e
1064
+ ), this.emit("viewport:change", r), i(this, M).emitPositionChange();
1065
+ }
1066
+ createNodes(t) {
1067
+ t.map((n) => Mt(this, n.type, n)).filter((n) => n !== null).forEach((n) => {
1068
+ i(this, A).add(n.getElement());
1069
+ });
1070
+ const r = [...this.getState().nodes || [], ...t];
1071
+ this._updateState(
1072
+ {
1073
+ nodes: r
1074
+ },
1075
+ !0
1076
+ );
1077
+ }
1078
+ /**
1079
+ * 创建图片节点(内部使用)
1080
+ */
1081
+ _createImageNode(t, e) {
1082
+ const r = e ?? { x: 100, y: 100 }, n = {
1083
+ id: $(),
1084
+ type: "image",
1085
+ props: {
1086
+ x: r.x,
1087
+ y: r.y,
1088
+ width: void 0,
1089
+ // 将在图片加载后设置
1090
+ height: void 0,
1091
+ rotation: 0,
1092
+ visible: !0
1093
+ },
1094
+ style: {
1095
+ color: "#000000",
1096
+ line: "solid",
1097
+ size: "medium",
1098
+ opacity: 1
1099
+ },
1100
+ meta: {
1101
+ imageUrl: t
1102
+ }
1103
+ };
1104
+ this.createNodes([n]);
1105
+ }
1106
+ /**
1107
+ * 创建图片标注节点(内部使用)
1108
+ */
1109
+ createImageMarkerNode(t, e, r, n) {
1110
+ const a = this.getState().nodes || [];
1111
+ let h = 0;
1112
+ a.forEach((Z) => {
1113
+ Z.type === "image-marker" && Z.meta.parent === t && typeof Z.meta.markerNumber == "number" && (h = Math.max(h, Z.meta.markerNumber));
1114
+ });
1115
+ const c = (e.x - n.x) / n.width * 100, m = (e.y - n.y) / n.height * 100, g = (r.x - n.x) / n.width * 100, I = (r.y - n.y) / n.height * 100, p = Math.min(e.x, r.x), k = Math.min(e.y, r.y), C = Math.abs(r.x - e.x), T = Math.abs(r.y - e.y), X = {
1116
+ id: $(),
1117
+ type: "image-marker",
1118
+ props: {
1119
+ x: p,
1120
+ y: k,
1121
+ width: C,
1122
+ height: T,
1123
+ rotation: 0,
1124
+ visible: !0
1125
+ },
1126
+ style: {
1127
+ color: "#ff0000",
1128
+ line: "dashed",
1129
+ size: "medium",
1130
+ opacity: 1
1131
+ },
1132
+ meta: {
1133
+ parent: t,
1134
+ markerNumber: h + 1,
1135
+ relativePosition: {
1136
+ start: {
1137
+ percentX: Math.max(0, Math.min(100, c)),
1138
+ percentY: Math.max(0, Math.min(100, m))
1139
+ },
1140
+ end: {
1141
+ percentX: Math.max(0, Math.min(100, g)),
1142
+ percentY: Math.max(0, Math.min(100, I))
1143
+ }
1144
+ }
1145
+ }
1146
+ };
1147
+ this.createNodes([X]);
1148
+ }
1149
+ /**
1150
+ * 在指定位置查找图片节点
1151
+ * @internal 仅供内部使用
1152
+ */
1153
+ findImageAtPosition(t) {
1154
+ const e = i(this, u).getStage(), r = e.find(
1155
+ (a) => a.getClassName() === "Image"
1156
+ ), n = r.map(
1157
+ (a) => a.listening()
1158
+ );
1159
+ r.forEach((a) => a.listening(!0));
1160
+ try {
1161
+ const h = e.getAllIntersections(t).filter(
1162
+ (c) => c.getClassName() === "Image"
1163
+ );
1164
+ return h.length === 0 ? null : h[h.length - 1];
1165
+ } finally {
1166
+ r.forEach((a, h) => {
1167
+ a.listening(n[h]);
1168
+ });
1169
+ }
1170
+ }
1171
+ /**
1172
+ * @internal 仅供内部使用
1173
+ */
1174
+ createDraftNode(t, e, r) {
1175
+ i(this, v) && i(this, v).destroy();
1176
+ const n = ie(t, e, void 0, r);
1177
+ N(this, v, Mt(this, t, n)), console.log(i(this, v)), i(this, v) && i(this, A).add(i(this, v).getElement());
1178
+ }
1179
+ /**
1180
+ * @internal 仅供内部使用
1181
+ */
1182
+ updateDraftNode(t, e) {
1183
+ if (!i(this, v)) return;
1184
+ const r = i(this, v).getNode(), n = re(r, t, e);
1185
+ i(this, v).updateNode(n);
1186
+ }
1187
+ /**
1188
+ * @internal 仅供内部使用
1189
+ */
1190
+ finalizeDraftNode() {
1191
+ if (!i(this, v)) return;
1192
+ const t = $(), e = i(this, v).getNode();
1193
+ if (e.type === "image-marker" && e.meta.parent) {
1194
+ const n = e.meta.bounds, a = e.meta.startPosition, h = {
1195
+ x: e.props.x + (e.props.width || 0),
1196
+ y: e.props.y + (e.props.height || 0)
1197
+ }, c = this.getState().nodes || [];
1198
+ let m = 0;
1199
+ c.forEach((T) => {
1200
+ T.type === "image-marker" && T.meta.parent === e.meta.parent && typeof T.meta.markerNumber == "number" && (m = Math.max(m, T.meta.markerNumber));
1201
+ });
1202
+ const g = (a.x - n.x) / n.width * 100, I = (a.y - n.y) / n.height * 100, p = (h.x - n.x) / n.width * 100, k = (h.y - n.y) / n.height * 100, C = {
1203
+ ...e,
1204
+ props: {
1205
+ ...e.props
1206
+ },
1207
+ style: {
1208
+ ...e.style
1209
+ },
1210
+ meta: {
1211
+ parent: e.meta.parent,
1212
+ markerNumber: m + 1,
1213
+ relativePosition: {
1214
+ start: {
1215
+ percentX: Math.max(0, Math.min(100, g)),
1216
+ percentY: Math.max(0, Math.min(100, I))
1217
+ },
1218
+ end: {
1219
+ percentX: Math.max(0, Math.min(100, p)),
1220
+ percentY: Math.max(0, Math.min(100, k))
1221
+ }
1222
+ }
1223
+ },
1224
+ id: t,
1225
+ type: "image-marker"
1226
+ };
1227
+ this.createNodes([C]), i(this, v).destroy(), N(this, v, null), this.setToolType("select");
1228
+ return;
1229
+ }
1230
+ const r = {
1231
+ ...e,
1232
+ props: {
1233
+ ...e.props
1234
+ },
1235
+ style: {
1236
+ ...e.style
1237
+ },
1238
+ meta: {
1239
+ ...e.meta
1240
+ },
1241
+ id: t
1242
+ };
1243
+ this.createNodes([r]), i(this, v).destroy(), N(this, v, null), this.setToolType("select");
1244
+ }
1245
+ /**
1246
+ * 选择节点
1247
+ * @internal 仅供内部使用,外部请使用 CanvasApi
1248
+ */
1249
+ selectNode(t, e = !1) {
1250
+ const r = this.getState().selectedNodeIds ?? [];
1251
+ if (r.length === 0 && !t)
1252
+ return;
1253
+ if (!t) {
1254
+ i(this, M).clearNodes(), this._updateState(
1255
+ {
1256
+ selectedNodeIds: []
1257
+ },
1258
+ !1
1259
+ );
1260
+ return;
1261
+ }
1262
+ let n = [];
1263
+ e ? n = r.length ? [...r, t] : [t] : n = [t];
1264
+ const a = this.getStage().find(`.${wt}`).filter((h) => {
1265
+ const c = h.id();
1266
+ return n.includes(c);
1267
+ });
1268
+ i(this, M).setNodes(a), this._updateState(
1269
+ {
1270
+ selectedNodeIds: n
1271
+ },
1272
+ !1
1273
+ );
1274
+ }
1275
+ /**
1276
+ * 从元素同步节点数据(供节点类内部使用)
1277
+ */
1278
+ _syncNodeFromElement(t, e) {
1279
+ const r = this.getState().nodes || [], n = r.findIndex((c) => c.id === t);
1280
+ if (n === -1) return;
1281
+ const a = {
1282
+ ...r[n],
1283
+ ...e,
1284
+ props: {
1285
+ ...r[n].props,
1286
+ ...e.props
1287
+ },
1288
+ style: {
1289
+ ...r[n].style,
1290
+ ...e.style
1291
+ },
1292
+ meta: {
1293
+ ...r[n].meta,
1294
+ ...e.meta
1295
+ }
1296
+ }, h = [...r];
1297
+ h[n] = a, this._updateState(
1298
+ {
1299
+ nodes: h
1300
+ },
1301
+ !0
1302
+ );
1303
+ }
1304
+ /**
1305
+ * 删除节点(内部使用)
1306
+ */
1307
+ _deleteNodes(t) {
1308
+ if (t.length === 0) return;
1309
+ const e = this.getState().nodes || [], r = new Set(t);
1310
+ t.forEach((a) => {
1311
+ e.find((c) => c.id === a)?.type === "image" && e.forEach((c) => {
1312
+ c.type === "image-marker" && c.meta.parent === a && r.add(c.id);
1313
+ });
1314
+ }), r.forEach((a) => {
1315
+ const h = this.getStage().findOne(`#${a}`);
1316
+ h && h.destroy();
1317
+ });
1318
+ const n = e.filter((a) => !r.has(a.id));
1319
+ i(this, M).clearNodes(), this._updateState(
1320
+ {
1321
+ nodes: n,
1322
+ selectedNodeIds: []
1323
+ },
1324
+ !0
1325
+ );
1326
+ }
1327
+ /**
1328
+ * 删除当前选中的节点(内部使用)
1329
+ */
1330
+ _deleteSelectedNodes() {
1331
+ const t = this.getState().selectedNodeIds || [];
1332
+ t.length !== 0 && this._deleteNodes(t);
1333
+ }
1334
+ /**
1335
+ * 获取 Transformer 的位置信息(内部使用)
1336
+ */
1337
+ getTransformerPosition() {
1338
+ return i(this, M).getPosition();
1339
+ }
1340
+ /**
1341
+ * 导出全部图形为图片(内部使用)
1342
+ * @param options - 导出配置
1343
+ * @returns DataURL 格式的图片数据
1344
+ */
1345
+ _exportAsImage(t) {
1346
+ const e = i(this, u).getStage(), r = i(this, M).getTransformer(), n = r.visible();
1347
+ r.visible(!1);
1348
+ try {
1349
+ return e.toDataURL({
1350
+ pixelRatio: t?.pixelRatio ?? 2,
1351
+ mimeType: t?.mimeType ?? "image/png",
1352
+ quality: t?.quality ?? 1
1353
+ });
1354
+ } finally {
1355
+ r.visible(n);
1356
+ }
1357
+ }
1358
+ /**
1359
+ * 导出当前选区为图片(内部使用)
1360
+ * @param options - 导出配置
1361
+ * @returns DataURL 格式的图片数据,如果没有选区则返回 null
1362
+ */
1363
+ _exportSelectionAsImage(t) {
1364
+ const e = i(this, M).getPosition();
1365
+ if (!e)
1366
+ return console.warn("No selection to export"), null;
1367
+ const r = i(this, u).getStage(), n = t?.padding ?? 10, a = i(this, M).getTransformer(), h = a.visible();
1368
+ a.visible(!1);
1369
+ try {
1370
+ return r.toDataURL({
1371
+ x: e.x - n,
1372
+ y: e.y - n,
1373
+ width: e.width + n * 2,
1374
+ height: e.height + n * 2,
1375
+ pixelRatio: t?.pixelRatio ?? 2,
1376
+ mimeType: t?.mimeType ?? "image/png",
1377
+ quality: t?.quality ?? 1
1378
+ });
1379
+ } finally {
1380
+ a.visible(h);
1381
+ }
1382
+ }
1383
+ /**
1384
+ * 销毁 core(内部使用)
1385
+ */
1386
+ _dispose() {
1387
+ i(this, M).destroy(), i(this, u).destroy(), super._dispose();
1388
+ }
1389
+ /**
1390
+ * 实现父类的状态同步方法
1391
+ * 当 undo/redo 时被调用
1392
+ */
1393
+ _syncState(t) {
1394
+ i(this, u).setViewport({
1395
+ x: t.viewport.x,
1396
+ y: t.viewport.y,
1397
+ scale: t.viewport.scale,
1398
+ width: t.viewport.width,
1399
+ height: t.viewport.height
1400
+ });
1401
+ }
1402
+ }
1403
+ u = new WeakMap(), A = new WeakMap(), M = new WeakMap(), v = new WeakMap();
1404
+ class ae extends oe {
1405
+ /**
1406
+ * 获取所有可用的工具类型
1407
+ */
1408
+ getAvailableTools() {
1409
+ return ["select", "hand", "rectangle", "image-marker"];
1410
+ }
1411
+ /**
1412
+ * 设置当前工具类型
1413
+ */
1414
+ setToolType(s) {
1415
+ super.setToolType(s);
1416
+ }
1417
+ /**
1418
+ * 更新视口位置
1419
+ */
1420
+ updateViewport(s, t = !1) {
1421
+ super.updateViewport(s, t);
1422
+ }
1423
+ /**
1424
+ * 创建图片节点
1425
+ */
1426
+ createImageNode(s, t) {
1427
+ this._createImageNode(s, t);
1428
+ }
1429
+ /**
1430
+ * 导出全部图形为图片
1431
+ * @param options - 导出配置
1432
+ * @returns DataURL 格式的图片数据
1433
+ */
1434
+ exportAsImage(s) {
1435
+ return this._exportAsImage(s);
1436
+ }
1437
+ /**
1438
+ * 导出当前选区为图片
1439
+ * @param options - 导出配置
1440
+ * @returns DataURL 格式的图片数据,如果没有选区则返回 null
1441
+ */
1442
+ exportSelectionAsImage(s) {
1443
+ return this._exportSelectionAsImage(s);
1444
+ }
1445
+ /**
1446
+ * 删除当前选中的节点
1447
+ * 如果删除的是 image 节点,会同步删除所有关联的 image-marker
1448
+ */
1449
+ deleteSelectedNodes() {
1450
+ this._deleteSelectedNodes();
1451
+ }
1452
+ /**
1453
+ * 删除指定的节点
1454
+ * 如果删除的是 image 节点,会同步删除所有关联的 image-marker
1455
+ * @param nodeIds - 要删除的节点 ID 数组
1456
+ */
1457
+ deleteNodes(s) {
1458
+ this._deleteNodes(s);
1459
+ }
1460
+ /**
1461
+ * 销毁 canvas
1462
+ */
1463
+ dispose() {
1464
+ super._dispose();
1465
+ }
1466
+ }
1467
+ function he(o, s, t, e = !1) {
1468
+ const [r, n] = s, [a, h] = t, c = a + (o - r) / (n - r) * (h - a);
1469
+ return e ? a < h ? Math.max(Math.min(c, h), a) : Math.max(Math.min(c, a), h) : c;
1470
+ }
1471
+ const It = [
1472
+ {
1473
+ min: -1,
1474
+ mid: 0.15,
1475
+ step: 64
1476
+ },
1477
+ {
1478
+ min: 0.05,
1479
+ mid: 0.375,
1480
+ step: 16
1481
+ },
1482
+ {
1483
+ min: 0.15,
1484
+ mid: 1,
1485
+ step: 4
1486
+ },
1487
+ {
1488
+ min: 0.7,
1489
+ mid: 2.5,
1490
+ step: 1
1491
+ }
1492
+ ];
1493
+ function ce({
1494
+ viewportX: o,
1495
+ viewportY: s,
1496
+ scale: t,
1497
+ size: e = 20,
1498
+ showGrid: r = !0
1499
+ }) {
1500
+ const n = o / t, a = s / t, h = t;
1501
+ return r ? /* @__PURE__ */ U(
1502
+ "svg",
1503
+ {
1504
+ className: "canvas-grid w-full h-full absolute top-0 left-0",
1505
+ version: "1.1",
1506
+ xmlns: "http://www.w3.org/2000/svg",
1507
+ "aria-hidden": "true",
1508
+ children: [
1509
+ /* @__PURE__ */ w("defs", { children: It.map(({ min: c, mid: m, step: g }, I) => {
1510
+ const p = g * e * h, k = 0.5 + n * h, C = 0.5 + a * h, T = k > 0 ? k % p : p + k % p, X = C > 0 ? C % p : p + C % p, Z = h < m ? he(h, [c, m], [0, 1]) : 1;
1511
+ return /* @__PURE__ */ w(
1512
+ "pattern",
1513
+ {
1514
+ id: `grid_${g}`,
1515
+ width: p,
1516
+ height: p,
1517
+ patternUnits: "userSpaceOnUse",
1518
+ children: /* @__PURE__ */ w(
1519
+ "circle",
1520
+ {
1521
+ className: "tl-grid-dot",
1522
+ cx: T,
1523
+ cy: X,
1524
+ r: 1,
1525
+ opacity: Z
1526
+ }
1527
+ )
1528
+ },
1529
+ I
1530
+ );
1531
+ }) }),
1532
+ It.map(({ step: c }, m) => /* @__PURE__ */ w("rect", { width: "100%", height: "100%", fill: `url(#grid_${c})` }, m))
1533
+ ]
1534
+ }
1535
+ ) : null;
1536
+ }
1537
+ function de(...o) {
1538
+ return Ht(Ut(o));
1539
+ }
1540
+ const le = Vt(
1541
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1542
+ {
1543
+ variants: {
1544
+ variant: {
1545
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
1546
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
1547
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
1548
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1549
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1550
+ link: "text-primary underline-offset-4 hover:underline"
1551
+ },
1552
+ size: {
1553
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
1554
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
1555
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1556
+ icon: "size-9",
1557
+ "icon-sm": "size-8",
1558
+ "icon-lg": "size-10"
1559
+ }
1560
+ },
1561
+ defaultVariants: {
1562
+ variant: "default",
1563
+ size: "default"
1564
+ }
1565
+ }
1566
+ );
1567
+ function H({
1568
+ className: o,
1569
+ variant: s = "default",
1570
+ size: t = "default",
1571
+ asChild: e = !1,
1572
+ ...r
1573
+ }) {
1574
+ return /* @__PURE__ */ w(
1575
+ e ? Xt : "button",
1576
+ {
1577
+ "data-slot": "button",
1578
+ "data-variant": s,
1579
+ "data-size": t,
1580
+ className: de(le({ variant: s, size: t, className: o })),
1581
+ ...r
1582
+ }
1583
+ );
1584
+ }
1585
+ function me({ api: o }) {
1586
+ const [s, t] = O(o.getState().viewport);
1587
+ xt(() => {
1588
+ o.on("viewport:change", (c) => {
1589
+ t(c);
1590
+ });
1591
+ }, [t, o]);
1592
+ const e = (c) => {
1593
+ const m = s.width / 2, g = s.height / 2, I = (m - s.x) / s.scale, p = (g - s.y) / s.scale, k = m - I * c, C = g - p * c;
1594
+ o.updateViewport({ x: k, y: C, scale: c });
1595
+ }, r = () => {
1596
+ const c = Math.min(s.scale * 1.2, 5);
1597
+ e(c);
1598
+ }, n = () => {
1599
+ const c = Math.max(s.scale / 1.2, 0.1);
1600
+ e(c);
1601
+ }, a = () => {
1602
+ e(1);
1603
+ }, h = Math.round(s.scale * 100);
1604
+ return /* @__PURE__ */ U("div", { className: "zoom-panel flex items-center gap-2", children: [
1605
+ /* @__PURE__ */ w(
1606
+ H,
1607
+ {
1608
+ size: "sm",
1609
+ variant: "secondary",
1610
+ onClick: n,
1611
+ title: "缩小",
1612
+ children: /* @__PURE__ */ w(Ot, {})
1613
+ }
1614
+ ),
1615
+ /* @__PURE__ */ U(
1616
+ H,
1617
+ {
1618
+ size: "sm",
1619
+ variant: "secondary",
1620
+ onClick: a,
1621
+ title: `${h}%`,
1622
+ className: "min-w-16",
1623
+ children: [
1624
+ h,
1625
+ "%"
1626
+ ]
1627
+ }
1628
+ ),
1629
+ /* @__PURE__ */ w(
1630
+ H,
1631
+ {
1632
+ size: "sm",
1633
+ variant: "secondary",
1634
+ onClick: r,
1635
+ title: "放大",
1636
+ children: /* @__PURE__ */ w(Ft, {})
1637
+ }
1638
+ )
1639
+ ] });
1640
+ }
1641
+ function ge({ api: o }) {
1642
+ const [s, t] = O(o.canUndo()), [e, r] = O(o.canRedo());
1643
+ return xt(() => {
1644
+ const n = () => {
1645
+ t(o.canUndo()), r(o.canRedo());
1646
+ };
1647
+ return o.on("state:change", n), () => {
1648
+ o.off("state:change", n);
1649
+ };
1650
+ }, [o]), /* @__PURE__ */ U("div", { className: "history-panel flex items-center gap-2", children: [
1651
+ /* @__PURE__ */ w(
1652
+ H,
1653
+ {
1654
+ size: "sm",
1655
+ variant: "secondary",
1656
+ disabled: !s,
1657
+ onClick: () => o.undo(),
1658
+ title: "撤销",
1659
+ children: /* @__PURE__ */ w(Lt, {})
1660
+ }
1661
+ ),
1662
+ /* @__PURE__ */ w(
1663
+ H,
1664
+ {
1665
+ size: "sm",
1666
+ variant: "secondary",
1667
+ disabled: !e,
1668
+ onClick: () => o.redo(),
1669
+ title: "重做",
1670
+ children: /* @__PURE__ */ w(Wt, {})
1671
+ }
1672
+ )
1673
+ ] });
1674
+ }
1675
+ function be({ setApi: o }) {
1676
+ const s = At(null), [t, e] = O(null), [r, n] = O({ x: 0, y: 0, scale: 1 });
1677
+ return xt(() => {
1678
+ if (!s.current) return;
1679
+ const a = new ae(s.current);
1680
+ return e(a), o?.(a), a.on("viewport:change", (h) => {
1681
+ n(h);
1682
+ }), a.on("transformer:positionChange", (h) => {
1683
+ console.log("Transformer position changed:", h);
1684
+ }), () => {
1685
+ a.dispose();
1686
+ };
1687
+ }, [o]), /* @__PURE__ */ U("div", { className: "pure-canvas relative size-full", children: [
1688
+ /* @__PURE__ */ w(
1689
+ ce,
1690
+ {
1691
+ viewportX: r.x,
1692
+ viewportY: r.y,
1693
+ scale: r.scale
1694
+ }
1695
+ ),
1696
+ /* @__PURE__ */ w("div", { ref: s, className: "size-full" }),
1697
+ t && /* @__PURE__ */ U(zt, { children: [
1698
+ /* @__PURE__ */ w("div", { className: "history-panel-wrapper absolute bottom-4 left-4 z-10", children: /* @__PURE__ */ w(ge, { api: t }) }),
1699
+ /* @__PURE__ */ w("div", { className: "zoom-panel-wrapper absolute bottom-4 right-4 z-10", children: /* @__PURE__ */ w(me, { api: t }) })
1700
+ ] })
1701
+ ] });
1702
+ }
1703
+ export {
1704
+ ae as CanvasApi,
1705
+ be as PureCanvas
1706
+ };