canvu-react 0.3.7 → 0.3.9
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/README.md +192 -0
- package/dist/chatbot.d.cts +1 -1
- package/dist/chatbot.d.ts +1 -1
- package/dist/index.cjs +32 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +32 -144
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +31 -109
- package/dist/native.cjs.map +1 -1
- package/dist/native.js +31 -109
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +1449 -1353
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +147 -4
- package/dist/react.d.ts +147 -4
- package/dist/react.js +1449 -1354
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs +244 -11
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +20 -7
- package/dist/realtime.d.ts +20 -7
- package/dist/realtime.js +244 -12
- package/dist/realtime.js.map +1 -1
- package/dist/tldraw.cjs +30 -142
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.js +30 -142
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-B_rv7p8b.d.cts → types-BtLGGw0r.d.cts} +126 -1
- package/dist/{types-BCtWx3zP.d.ts → types-ChnTSRSe.d.ts} +126 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -46,93 +46,6 @@ var init_custom_shape = __esm({
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
// src/scene/freehand-path.ts
|
|
50
|
-
function dedupeFreehandPoints(points, minDist) {
|
|
51
|
-
if (points.length <= 2) {
|
|
52
|
-
return points.map((p) => ({ ...p }));
|
|
53
|
-
}
|
|
54
|
-
const minSq = minDist * minDist;
|
|
55
|
-
const first = points[0];
|
|
56
|
-
if (!first) return [];
|
|
57
|
-
const out = [{ ...first }];
|
|
58
|
-
for (let i = 1; i < points.length - 1; i++) {
|
|
59
|
-
const p = points[i];
|
|
60
|
-
const last = out[out.length - 1];
|
|
61
|
-
if (!p || !last) continue;
|
|
62
|
-
const dx = p.x - last.x;
|
|
63
|
-
const dy = p.y - last.y;
|
|
64
|
-
if (dx * dx + dy * dy >= minSq) {
|
|
65
|
-
out.push({ ...p });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
const end = points[points.length - 1];
|
|
69
|
-
const lastKept = out[out.length - 1];
|
|
70
|
-
if (!end || !lastKept) return out;
|
|
71
|
-
if ((end.x - lastKept.x) ** 2 + (end.y - lastKept.y) ** 2 > 1e-12) {
|
|
72
|
-
out.push({ ...end });
|
|
73
|
-
}
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
76
|
-
function smoothFreehandPointsToPathD(points) {
|
|
77
|
-
const n = points.length;
|
|
78
|
-
if (n === 0) return "";
|
|
79
|
-
if (n === 1) {
|
|
80
|
-
const p = points[0];
|
|
81
|
-
if (!p) return "";
|
|
82
|
-
return `M ${p.x} ${p.y}`;
|
|
83
|
-
}
|
|
84
|
-
if (n === 2) {
|
|
85
|
-
const a = points[0];
|
|
86
|
-
const b = points[1];
|
|
87
|
-
if (!a || !b) return "";
|
|
88
|
-
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
89
|
-
}
|
|
90
|
-
const p0 = points[0];
|
|
91
|
-
if (!p0) return "";
|
|
92
|
-
let d = `M ${p0.x} ${p0.y}`;
|
|
93
|
-
let i = 1;
|
|
94
|
-
for (; i < n - 2; i++) {
|
|
95
|
-
const pi = points[i];
|
|
96
|
-
const pi1 = points[i + 1];
|
|
97
|
-
if (!pi || !pi1) continue;
|
|
98
|
-
const xc = (pi.x + pi1.x) / 2;
|
|
99
|
-
const yc = (pi.y + pi1.y) / 2;
|
|
100
|
-
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
101
|
-
}
|
|
102
|
-
const pLast = points[i];
|
|
103
|
-
const pEnd = points[i + 1];
|
|
104
|
-
if (!pLast || !pEnd) return d;
|
|
105
|
-
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
106
|
-
return d;
|
|
107
|
-
}
|
|
108
|
-
function outlineStrokeToClosedPathD(outline) {
|
|
109
|
-
const len = outline.length;
|
|
110
|
-
if (len === 0) return "";
|
|
111
|
-
const first = outline[0];
|
|
112
|
-
if (!first) return "";
|
|
113
|
-
if (len < 3) {
|
|
114
|
-
let d2 = `M ${first[0]} ${first[1]}`;
|
|
115
|
-
for (let i = 1; i < len; i++) {
|
|
116
|
-
const pt = outline[i];
|
|
117
|
-
if (!pt) continue;
|
|
118
|
-
d2 += ` L ${pt[0]} ${pt[1]}`;
|
|
119
|
-
}
|
|
120
|
-
return `${d2} Z`;
|
|
121
|
-
}
|
|
122
|
-
let d = `M ${first[0]} ${first[1]} Q`;
|
|
123
|
-
for (let i = 0; i < len; i++) {
|
|
124
|
-
const p0 = outline[i];
|
|
125
|
-
const p1 = outline[(i + 1) % len];
|
|
126
|
-
if (!p0 || !p1) continue;
|
|
127
|
-
d += ` ${p0[0]} ${p0[1]} ${(p0[0] + p1[0]) / 2} ${(p0[1] + p1[1]) / 2}`;
|
|
128
|
-
}
|
|
129
|
-
return `${d} Z`;
|
|
130
|
-
}
|
|
131
|
-
var init_freehand_path = __esm({
|
|
132
|
-
"src/scene/freehand-path.ts"() {
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
49
|
// src/scene/text-svg.ts
|
|
137
50
|
function escapeSvgTextContent(s) {
|
|
138
51
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -318,18 +231,18 @@ function perfectFreehandOptions(toolKind, style, strokeComplete, pressureAware =
|
|
|
318
231
|
...base2,
|
|
319
232
|
size: Math.max(2, sw * 1.05),
|
|
320
233
|
thinning: 0.42,
|
|
321
|
-
smoothing: 0.
|
|
322
|
-
streamline: 0.
|
|
323
|
-
simulatePressure:
|
|
234
|
+
smoothing: 0.78,
|
|
235
|
+
streamline: 0.62,
|
|
236
|
+
simulatePressure: true
|
|
324
237
|
};
|
|
325
238
|
}
|
|
326
239
|
return {
|
|
327
240
|
...base2,
|
|
328
241
|
size: Math.max(2, sw * 1.18),
|
|
329
242
|
thinning: 0.12,
|
|
330
|
-
smoothing: 0.
|
|
331
|
-
streamline: 0.
|
|
332
|
-
simulatePressure:
|
|
243
|
+
smoothing: 0.85,
|
|
244
|
+
streamline: 0.78,
|
|
245
|
+
simulatePressure: true
|
|
333
246
|
};
|
|
334
247
|
}
|
|
335
248
|
if (toolKind === "brush") {
|
|
@@ -347,7 +260,7 @@ function perfectFreehandOptions(toolKind, style, strokeComplete, pressureAware =
|
|
|
347
260
|
thinning: 0.08,
|
|
348
261
|
smoothing: 0.88,
|
|
349
262
|
streamline: 0.84,
|
|
350
|
-
simulatePressure:
|
|
263
|
+
simulatePressure: true
|
|
351
264
|
};
|
|
352
265
|
}
|
|
353
266
|
function resolveStrokeStyle(item) {
|
|
@@ -417,70 +330,41 @@ function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeCompl
|
|
|
417
330
|
if (pathPointsLocal.length === 1) {
|
|
418
331
|
const p = pathPointsLocal[0];
|
|
419
332
|
if (!p) return null;
|
|
420
|
-
const r = Math.max(0.5, style.strokeWidth / 2);
|
|
421
|
-
return {
|
|
422
|
-
kind: "circle",
|
|
423
|
-
cx: p.x,
|
|
424
|
-
cy: p.y,
|
|
425
|
-
r,
|
|
426
|
-
fill: style.stroke,
|
|
427
|
-
fillOpacity: style.strokeOpacity
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
const minDist = Math.min(0.25, Math.max(0.02, style.strokeWidth * 0.02));
|
|
431
|
-
const pts = dedupeFreehandPoints(pathPointsLocal, minDist);
|
|
432
|
-
if (pts.length === 0) return null;
|
|
433
|
-
if (pts.length === 1) {
|
|
434
|
-
const p = pts[0];
|
|
435
|
-
if (!p) return null;
|
|
436
|
-
const r = Math.max(0.5, style.strokeWidth / 2);
|
|
437
333
|
return {
|
|
438
334
|
kind: "circle",
|
|
439
335
|
cx: p.x,
|
|
440
336
|
cy: p.y,
|
|
441
|
-
r,
|
|
337
|
+
r: Math.max(0.5, style.strokeWidth / 2),
|
|
442
338
|
fill: style.stroke,
|
|
443
339
|
fillOpacity: style.strokeOpacity
|
|
444
340
|
};
|
|
445
341
|
}
|
|
446
|
-
const hasPressure =
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
kind: "strokePath",
|
|
451
|
-
d: d2,
|
|
452
|
-
stroke: style.stroke,
|
|
453
|
-
strokeWidth: style.strokeWidth,
|
|
454
|
-
strokeOpacity: style.strokeOpacity
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
const input = hasPressure ? pts.map(
|
|
342
|
+
const hasPressure = pathPointsLocal.some(
|
|
343
|
+
(p) => p.pressure != null && Number.isFinite(p.pressure)
|
|
344
|
+
);
|
|
345
|
+
const input = hasPressure ? pathPointsLocal.map(
|
|
458
346
|
(p) => [p.x, p.y, Math.min(1, Math.max(0, p.pressure ?? 0.5))]
|
|
459
|
-
) :
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
fill: style.stroke,
|
|
474
|
-
fillOpacity: style.strokeOpacity
|
|
475
|
-
};
|
|
347
|
+
) : pathPointsLocal.map((p) => [p.x, p.y]);
|
|
348
|
+
const stroke = getStroke(
|
|
349
|
+
input,
|
|
350
|
+
perfectFreehandOptions(toolKind, style, strokeComplete, hasPressure)
|
|
351
|
+
);
|
|
352
|
+
if (stroke.length < 3) return null;
|
|
353
|
+
const first = stroke[0];
|
|
354
|
+
if (!first) return null;
|
|
355
|
+
let d = `M ${first[0]} ${first[1]} Q`;
|
|
356
|
+
for (let i = 0; i < stroke.length; i++) {
|
|
357
|
+
const a = stroke[i];
|
|
358
|
+
const b = stroke[(i + 1) % stroke.length];
|
|
359
|
+
if (!a || !b) continue;
|
|
360
|
+
d += ` ${a[0]} ${a[1]} ${(a[0] + b[0]) / 2} ${(a[1] + b[1]) / 2}`;
|
|
476
361
|
}
|
|
477
|
-
|
|
362
|
+
d += " Z";
|
|
478
363
|
return {
|
|
479
|
-
kind: "
|
|
364
|
+
kind: "fillPath",
|
|
480
365
|
d,
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
strokeOpacity: style.strokeOpacity
|
|
366
|
+
fill: style.stroke,
|
|
367
|
+
fillOpacity: style.strokeOpacity
|
|
484
368
|
};
|
|
485
369
|
}
|
|
486
370
|
function freehandPayloadToSvgString(payload) {
|
|
@@ -810,1118 +694,1369 @@ var init_shape_builders = __esm({
|
|
|
810
694
|
"src/scene/shape-builders.ts"() {
|
|
811
695
|
init_rect();
|
|
812
696
|
init_custom_shape();
|
|
813
|
-
init_freehand_path();
|
|
814
697
|
init_text_svg();
|
|
815
698
|
DEFAULT_STROKE_STYLE = {
|
|
816
699
|
stroke: "#2563eb",
|
|
817
700
|
strokeWidth: 2
|
|
818
701
|
};
|
|
819
702
|
TOOL_FREEHAND_DEFAULTS = {
|
|
820
|
-
draw: { strokeWidth:
|
|
703
|
+
draw: { strokeWidth: 10 },
|
|
821
704
|
pencil: { strokeWidth: 3 },
|
|
822
705
|
brush: { strokeWidth: 10 },
|
|
823
706
|
marker: { stroke: "#fde047", strokeWidth: 16, strokeOpacity: 0.5 }
|
|
824
707
|
};
|
|
825
708
|
}
|
|
826
709
|
});
|
|
827
|
-
var CanvuChromeContext = createContext(
|
|
828
|
-
null
|
|
829
|
-
);
|
|
830
|
-
function useCanvuChromeContext() {
|
|
831
|
-
return useContext(CanvuChromeContext);
|
|
832
|
-
}
|
|
833
710
|
|
|
834
|
-
// src/
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
711
|
+
// src/image/indexed-db-image-store.ts
|
|
712
|
+
var DB_NAME = "canvu-image-store";
|
|
713
|
+
var DB_VERSION = 1;
|
|
714
|
+
function openDb() {
|
|
715
|
+
return new Promise((resolve, reject) => {
|
|
716
|
+
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
|
717
|
+
req.onupgradeneeded = () => {
|
|
718
|
+
const db = req.result;
|
|
719
|
+
if (!db.objectStoreNames.contains("images")) {
|
|
720
|
+
db.createObjectStore("images");
|
|
721
|
+
}
|
|
722
|
+
if (!db.objectStoreNames.contains("thumbnails")) {
|
|
723
|
+
db.createObjectStore("thumbnails");
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
req.onsuccess = () => resolve(req.result);
|
|
727
|
+
req.onerror = () => reject(req.error);
|
|
728
|
+
});
|
|
838
729
|
}
|
|
839
|
-
function
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
x: itemX + c.x + cos * dlx - sin * dly,
|
|
847
|
-
y: itemY + c.y + sin * dlx + cos * dly
|
|
848
|
-
};
|
|
730
|
+
function getFromStore(db, storeName, id) {
|
|
731
|
+
return new Promise((resolve, reject) => {
|
|
732
|
+
const tx = db.transaction(storeName, "readonly");
|
|
733
|
+
const req = tx.objectStore(storeName).get(id);
|
|
734
|
+
req.onsuccess = () => resolve(req.result ?? void 0);
|
|
735
|
+
req.onerror = () => reject(req.error);
|
|
736
|
+
});
|
|
849
737
|
}
|
|
850
|
-
function
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
const sin = Math.sin(-rotationRad);
|
|
858
|
-
const lx = cos * dx - sin * dy;
|
|
859
|
-
const ly = sin * dx + cos * dy;
|
|
860
|
-
return { x: c.x + lx, y: c.y + ly };
|
|
738
|
+
function putInStore(db, storeName, id, blob) {
|
|
739
|
+
return new Promise((resolve, reject) => {
|
|
740
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
741
|
+
const req = tx.objectStore(storeName).put(blob, id);
|
|
742
|
+
req.onsuccess = () => resolve();
|
|
743
|
+
req.onerror = () => reject(req.error);
|
|
744
|
+
});
|
|
861
745
|
}
|
|
862
|
-
function
|
|
863
|
-
|
|
864
|
-
|
|
746
|
+
function deleteFromStore(db, storeName, id) {
|
|
747
|
+
return new Promise((resolve, reject) => {
|
|
748
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
749
|
+
const req = tx.objectStore(storeName).delete(id);
|
|
750
|
+
req.onsuccess = () => resolve();
|
|
751
|
+
req.onerror = () => reject(req.error);
|
|
752
|
+
});
|
|
865
753
|
}
|
|
866
|
-
function
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
754
|
+
function generateBlobId() {
|
|
755
|
+
return crypto.randomUUID();
|
|
756
|
+
}
|
|
757
|
+
var IndexedDbImageStore = class {
|
|
758
|
+
dbPromise = null;
|
|
759
|
+
getDb() {
|
|
760
|
+
if (!this.dbPromise) {
|
|
761
|
+
this.dbPromise = openDb();
|
|
762
|
+
}
|
|
763
|
+
return this.dbPromise;
|
|
870
764
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
765
|
+
async storeOriginal(blob) {
|
|
766
|
+
const id = generateBlobId();
|
|
767
|
+
const db = await this.getDb();
|
|
768
|
+
await putInStore(db, "images", id, blob);
|
|
769
|
+
return id;
|
|
874
770
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
771
|
+
async getOriginal(id) {
|
|
772
|
+
const db = await this.getDb();
|
|
773
|
+
return getFromStore(db, "images", id);
|
|
774
|
+
}
|
|
775
|
+
async deleteOriginal(id) {
|
|
776
|
+
const db = await this.getDb();
|
|
777
|
+
await deleteFromStore(db, "images", id);
|
|
778
|
+
}
|
|
779
|
+
async storeThumbnail(blob) {
|
|
780
|
+
const id = generateBlobId();
|
|
781
|
+
const db = await this.getDb();
|
|
782
|
+
await putInStore(db, "thumbnails", id, blob);
|
|
783
|
+
return id;
|
|
784
|
+
}
|
|
785
|
+
async getThumbnail(id) {
|
|
786
|
+
const db = await this.getDb();
|
|
787
|
+
return getFromStore(db, "thumbnails", id);
|
|
788
|
+
}
|
|
789
|
+
async deleteThumbnail(id) {
|
|
790
|
+
const db = await this.getDb();
|
|
791
|
+
await deleteFromStore(db, "thumbnails", id);
|
|
891
792
|
}
|
|
892
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
// src/react/NavMenu.tsx
|
|
896
|
-
init_rect();
|
|
897
|
-
var shellLook = {
|
|
898
|
-
display: "flex",
|
|
899
|
-
flexDirection: "column",
|
|
900
|
-
gap: 8,
|
|
901
|
-
minWidth: 160,
|
|
902
|
-
padding: "10px 12px",
|
|
903
|
-
borderRadius: 8,
|
|
904
|
-
border: "1px solid rgba(0,0,0,0.12)",
|
|
905
|
-
background: "rgba(255,255,255,0.96)",
|
|
906
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
907
|
-
pointerEvents: "auto"
|
|
908
|
-
};
|
|
909
|
-
var labelStyle = {
|
|
910
|
-
display: "flex",
|
|
911
|
-
flexDirection: "column",
|
|
912
|
-
gap: 4,
|
|
913
|
-
fontSize: 11,
|
|
914
|
-
fontWeight: 600,
|
|
915
|
-
color: "#52525b"
|
|
916
793
|
};
|
|
917
|
-
function
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
const onChange = onChangeProp ?? ctx?.onSelectionStyleChange ?? null;
|
|
938
|
-
if (!onChange) return null;
|
|
939
|
-
const shell = {
|
|
940
|
-
...getBoardPositionStyle(position, inset, zIndex),
|
|
941
|
-
...shellLook,
|
|
942
|
-
...style
|
|
943
|
-
};
|
|
944
|
-
if (activeToolStyle) {
|
|
945
|
-
const stroke2 = activeToolStyle.stroke;
|
|
946
|
-
const strokeWidth2 = activeToolStyle.strokeWidth;
|
|
947
|
-
const hex2 = normalizeHex(stroke2);
|
|
948
|
-
const showMarkerOpacity2 = activeToolStyle.toolKind === "marker";
|
|
949
|
-
const opacityPct2 = Math.round((activeToolStyle.strokeOpacity ?? 1) * 100);
|
|
950
|
-
return /* @__PURE__ */ jsxs(
|
|
951
|
-
"section",
|
|
952
|
-
{
|
|
953
|
-
"data-slot": "vector-selection-inspector",
|
|
954
|
-
"data-position": position,
|
|
955
|
-
className,
|
|
956
|
-
"aria-label": activeToolStyle.label ?? "Estilo da ferramenta",
|
|
957
|
-
style: shell,
|
|
958
|
-
children: [
|
|
959
|
-
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
960
|
-
"Cor",
|
|
961
|
-
/* @__PURE__ */ jsx(
|
|
962
|
-
"input",
|
|
963
|
-
{
|
|
964
|
-
type: "color",
|
|
965
|
-
value: hex2,
|
|
966
|
-
onChange: (e) => onChange({
|
|
967
|
-
stroke: e.target.value,
|
|
968
|
-
strokeWidth: strokeWidth2,
|
|
969
|
-
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
970
|
-
}),
|
|
971
|
-
style: {
|
|
972
|
-
width: "100%",
|
|
973
|
-
height: 32,
|
|
974
|
-
padding: 0,
|
|
975
|
-
border: "none",
|
|
976
|
-
cursor: "pointer",
|
|
977
|
-
background: "transparent"
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
)
|
|
981
|
-
] }),
|
|
982
|
-
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
983
|
-
"Grossura",
|
|
984
|
-
/* @__PURE__ */ jsx(
|
|
985
|
-
"input",
|
|
986
|
-
{
|
|
987
|
-
type: "range",
|
|
988
|
-
min: 1,
|
|
989
|
-
max: 48,
|
|
990
|
-
value: strokeWidth2,
|
|
991
|
-
onChange: (e) => onChange({
|
|
992
|
-
stroke: hex2,
|
|
993
|
-
strokeWidth: Number(e.target.value),
|
|
994
|
-
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
995
|
-
})
|
|
996
|
-
}
|
|
997
|
-
),
|
|
998
|
-
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
999
|
-
strokeWidth2,
|
|
1000
|
-
"px"
|
|
1001
|
-
] })
|
|
1002
|
-
] }),
|
|
1003
|
-
showMarkerOpacity2 && /* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1004
|
-
"Opacidade (marcador)",
|
|
1005
|
-
/* @__PURE__ */ jsx(
|
|
1006
|
-
"input",
|
|
1007
|
-
{
|
|
1008
|
-
type: "range",
|
|
1009
|
-
min: 10,
|
|
1010
|
-
max: 100,
|
|
1011
|
-
value: opacityPct2,
|
|
1012
|
-
onChange: (e) => {
|
|
1013
|
-
const v = Number(e.target.value) / 100;
|
|
1014
|
-
onChange({
|
|
1015
|
-
stroke: hex2,
|
|
1016
|
-
strokeWidth: strokeWidth2,
|
|
1017
|
-
strokeOpacity: v
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
),
|
|
1022
|
-
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1023
|
-
opacityPct2,
|
|
1024
|
-
"%"
|
|
1025
|
-
] })
|
|
1026
|
-
] })
|
|
1027
|
-
]
|
|
794
|
+
function decodeImageToCanvas(blob, maxDimension) {
|
|
795
|
+
return new Promise((resolve, reject) => {
|
|
796
|
+
const img = new Image();
|
|
797
|
+
img.onload = () => {
|
|
798
|
+
const w0 = img.naturalWidth;
|
|
799
|
+
const h0 = img.naturalHeight;
|
|
800
|
+
if (w0 < 1 || h0 < 1) {
|
|
801
|
+
reject(new Error("Image has no dimensions"));
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const scale = 1;
|
|
805
|
+
const cw = Math.max(1, Math.round(w0 * scale));
|
|
806
|
+
const ch = Math.max(1, Math.round(h0 * scale));
|
|
807
|
+
const canvas = document.createElement("canvas");
|
|
808
|
+
canvas.width = cw;
|
|
809
|
+
canvas.height = ch;
|
|
810
|
+
const ctx = canvas.getContext("2d");
|
|
811
|
+
if (!ctx) {
|
|
812
|
+
reject(new Error("Canvas 2D context unavailable"));
|
|
813
|
+
return;
|
|
1028
814
|
}
|
|
815
|
+
ctx.imageSmoothingEnabled = true;
|
|
816
|
+
ctx.imageSmoothingQuality = "high";
|
|
817
|
+
ctx.drawImage(img, 0, 0, cw, ch);
|
|
818
|
+
URL.revokeObjectURL(img.src);
|
|
819
|
+
resolve({ canvas, width: cw, height: ch });
|
|
820
|
+
};
|
|
821
|
+
img.onerror = () => {
|
|
822
|
+
URL.revokeObjectURL(img.src);
|
|
823
|
+
reject(new Error("Could not decode image"));
|
|
824
|
+
};
|
|
825
|
+
img.src = URL.createObjectURL(blob);
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
function canvasToBlob(canvas, mime, quality) {
|
|
829
|
+
return new Promise((resolve, reject) => {
|
|
830
|
+
canvas.toBlob(
|
|
831
|
+
(blob) => {
|
|
832
|
+
if (!blob) {
|
|
833
|
+
reject(new Error("Could not encode blob"));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
resolve(blob);
|
|
837
|
+
},
|
|
838
|
+
mime,
|
|
839
|
+
quality
|
|
1029
840
|
);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
async function loadImageToStore(file, store) {
|
|
844
|
+
const originalBlob = file;
|
|
845
|
+
const blobId = await store.storeOriginal(originalBlob);
|
|
846
|
+
const { canvas, width, height } = await decodeImageToCanvas(originalBlob);
|
|
847
|
+
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
848
|
+
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
849
|
+
const th = Math.max(1, Math.round(height * thumbScale));
|
|
850
|
+
const thumbCanvas = document.createElement("canvas");
|
|
851
|
+
thumbCanvas.width = tw;
|
|
852
|
+
thumbCanvas.height = th;
|
|
853
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
854
|
+
if (tCtx) {
|
|
855
|
+
tCtx.imageSmoothingEnabled = true;
|
|
856
|
+
tCtx.imageSmoothingQuality = "high";
|
|
857
|
+
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
1030
858
|
}
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
const first = stylable[0];
|
|
1036
|
-
if (!first) return null;
|
|
1037
|
-
const allSameStroke = stylable.every(
|
|
1038
|
-
(it) => (it.stroke ?? "#2563eb") === (first.stroke ?? "#2563eb")
|
|
1039
|
-
);
|
|
1040
|
-
const allSameWidth = stylable.every(
|
|
1041
|
-
(it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
|
|
1042
|
-
);
|
|
1043
|
-
const stroke = first.stroke ?? "#2563eb";
|
|
1044
|
-
const strokeWidth = first.strokeWidth ?? 2;
|
|
1045
|
-
const hex = normalizeHex(stroke);
|
|
1046
|
-
const markers = stylable.filter((it) => it.toolKind === "marker");
|
|
1047
|
-
const showMarkerOpacity = markers.length > 0;
|
|
1048
|
-
const firstMarker = markers[0];
|
|
1049
|
-
const allSameMarkerOpacity = markers.length > 0 && markers.every(
|
|
1050
|
-
(it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
|
|
1051
|
-
);
|
|
1052
|
-
const opacityPct = firstMarker ? Math.round((firstMarker.strokeOpacity ?? 1) * 100) : 100;
|
|
1053
|
-
return /* @__PURE__ */ jsxs(
|
|
1054
|
-
"section",
|
|
1055
|
-
{
|
|
1056
|
-
"data-slot": "vector-selection-inspector",
|
|
1057
|
-
"data-position": position,
|
|
1058
|
-
className,
|
|
1059
|
-
"aria-label": "Estilo da sele\xE7\xE3o",
|
|
1060
|
-
style: shell,
|
|
1061
|
-
children: [
|
|
1062
|
-
stylable.length > 1 && /* @__PURE__ */ jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
|
|
1063
|
-
stylable.length,
|
|
1064
|
-
" objetos selecionados"
|
|
1065
|
-
] }),
|
|
1066
|
-
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1067
|
-
"Cor",
|
|
1068
|
-
!allSameStroke && /* @__PURE__ */ jsxs("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: [
|
|
1069
|
-
" ",
|
|
1070
|
-
"(valores misturados \u2014 novo valor aplica a todos)"
|
|
1071
|
-
] }),
|
|
1072
|
-
/* @__PURE__ */ jsx(
|
|
1073
|
-
"input",
|
|
1074
|
-
{
|
|
1075
|
-
type: "color",
|
|
1076
|
-
value: hex,
|
|
1077
|
-
onChange: (e) => onChange({
|
|
1078
|
-
stroke: e.target.value,
|
|
1079
|
-
strokeWidth
|
|
1080
|
-
}),
|
|
1081
|
-
style: {
|
|
1082
|
-
width: "100%",
|
|
1083
|
-
height: 32,
|
|
1084
|
-
padding: 0,
|
|
1085
|
-
border: "none",
|
|
1086
|
-
cursor: "pointer",
|
|
1087
|
-
background: "transparent"
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
)
|
|
1091
|
-
] }),
|
|
1092
|
-
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1093
|
-
"Grossura",
|
|
1094
|
-
!allSameWidth && /* @__PURE__ */ jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1095
|
-
/* @__PURE__ */ jsx(
|
|
1096
|
-
"input",
|
|
1097
|
-
{
|
|
1098
|
-
type: "range",
|
|
1099
|
-
min: 1,
|
|
1100
|
-
max: 48,
|
|
1101
|
-
value: strokeWidth,
|
|
1102
|
-
onChange: (e) => onChange({
|
|
1103
|
-
stroke: hex,
|
|
1104
|
-
strokeWidth: Number(e.target.value)
|
|
1105
|
-
})
|
|
1106
|
-
}
|
|
1107
|
-
),
|
|
1108
|
-
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1109
|
-
strokeWidth,
|
|
1110
|
-
"px"
|
|
1111
|
-
] })
|
|
1112
|
-
] }),
|
|
1113
|
-
showMarkerOpacity && firstMarker && /* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1114
|
-
"Opacidade (marcador)",
|
|
1115
|
-
!allSameMarkerOpacity && markers.length > 1 && /* @__PURE__ */ jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1116
|
-
/* @__PURE__ */ jsx(
|
|
1117
|
-
"input",
|
|
1118
|
-
{
|
|
1119
|
-
type: "range",
|
|
1120
|
-
min: 10,
|
|
1121
|
-
max: 100,
|
|
1122
|
-
value: opacityPct,
|
|
1123
|
-
onChange: (e) => {
|
|
1124
|
-
const v = Number(e.target.value) / 100;
|
|
1125
|
-
onChange({
|
|
1126
|
-
stroke: hex,
|
|
1127
|
-
strokeWidth,
|
|
1128
|
-
strokeOpacity: v
|
|
1129
|
-
});
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
),
|
|
1133
|
-
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1134
|
-
opacityPct,
|
|
1135
|
-
"%"
|
|
1136
|
-
] })
|
|
1137
|
-
] })
|
|
1138
|
-
]
|
|
1139
|
-
}
|
|
859
|
+
const thumbBlob = await canvasToBlob(
|
|
860
|
+
thumbCanvas,
|
|
861
|
+
file.type === "image/jpeg" || file.type === "image/jpg" ? "image/jpeg" : "image/png",
|
|
862
|
+
file.type.startsWith("image/jpeg") ? 0.85 : void 0
|
|
1140
863
|
);
|
|
864
|
+
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
865
|
+
return { blobId, thumbnailBlobId, width, height };
|
|
1141
866
|
}
|
|
1142
|
-
function
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
...base2,
|
|
1166
|
-
bottom: inset,
|
|
1167
|
-
left: "50%",
|
|
1168
|
-
transform: "translateX(-50%)"
|
|
1169
|
-
};
|
|
1170
|
-
case "bottom-right":
|
|
1171
|
-
case "right-bottom":
|
|
1172
|
-
return { ...base2, bottom: inset, right: inset };
|
|
1173
|
-
case "left-center":
|
|
1174
|
-
return {
|
|
1175
|
-
...base2,
|
|
1176
|
-
left: inset,
|
|
1177
|
-
top: "50%",
|
|
1178
|
-
transform: "translateY(-50%)"
|
|
1179
|
-
};
|
|
1180
|
-
case "right-center":
|
|
1181
|
-
return {
|
|
1182
|
-
...base2,
|
|
1183
|
-
right: inset,
|
|
1184
|
-
top: "50%",
|
|
1185
|
-
transform: "translateY(-50%)"
|
|
1186
|
-
};
|
|
1187
|
-
case "center":
|
|
1188
|
-
return {
|
|
1189
|
-
...base2,
|
|
1190
|
-
top: "50%",
|
|
1191
|
-
left: "50%",
|
|
1192
|
-
transform: "translate(-50%, -50%)"
|
|
1193
|
-
};
|
|
1194
|
-
default:
|
|
1195
|
-
return base2;
|
|
867
|
+
async function createBlobUrlFromStore(store, blobId) {
|
|
868
|
+
const blob = await store.getOriginal(blobId);
|
|
869
|
+
if (!blob) return null;
|
|
870
|
+
return URL.createObjectURL(blob);
|
|
871
|
+
}
|
|
872
|
+
async function createThumbnailBlobUrlFromStore(store, thumbnailBlobId) {
|
|
873
|
+
const blob = await store.getThumbnail(thumbnailBlobId);
|
|
874
|
+
if (!blob) return null;
|
|
875
|
+
return URL.createObjectURL(blob);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// src/image/pdf-loader.ts
|
|
879
|
+
var pdfjsPromise = null;
|
|
880
|
+
function getPdfJs() {
|
|
881
|
+
if (!pdfjsPromise) {
|
|
882
|
+
pdfjsPromise = import('pdfjs-dist').then((mod) => {
|
|
883
|
+
const workerSrc = new URL(
|
|
884
|
+
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
885
|
+
import.meta.url
|
|
886
|
+
).toString();
|
|
887
|
+
mod.GlobalWorkerOptions.workerSrc = workerSrc;
|
|
888
|
+
return mod;
|
|
889
|
+
});
|
|
1196
890
|
}
|
|
891
|
+
return pdfjsPromise;
|
|
1197
892
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
background: "#fff",
|
|
1214
|
-
fontSize: "0.875rem"
|
|
1215
|
-
};
|
|
1216
|
-
var bodyStyle = {
|
|
1217
|
-
flex: 1,
|
|
1218
|
-
minHeight: 0,
|
|
1219
|
-
display: "flex",
|
|
1220
|
-
flexDirection: "column"
|
|
1221
|
-
};
|
|
1222
|
-
var mainStyle = {
|
|
1223
|
-
flex: 1,
|
|
1224
|
-
minHeight: 0,
|
|
1225
|
-
position: "relative",
|
|
1226
|
-
display: "flex",
|
|
1227
|
-
flexDirection: "column"
|
|
1228
|
-
};
|
|
1229
|
-
var viewportSurfaceStyle = {
|
|
1230
|
-
flex: 1,
|
|
1231
|
-
minHeight: 0,
|
|
1232
|
-
position: "relative",
|
|
1233
|
-
width: "100%",
|
|
1234
|
-
alignSelf: "stretch",
|
|
1235
|
-
background: "#fff",
|
|
1236
|
-
touchAction: "none"
|
|
1237
|
-
};
|
|
1238
|
-
function mergeStyle(base2, style) {
|
|
1239
|
-
return style ? { ...base2, ...style } : base2;
|
|
893
|
+
async function renderPageToCanvas(page, scale) {
|
|
894
|
+
const raw = page.getViewport({ scale: 1 });
|
|
895
|
+
const adjustedScale = Math.round(raw.width * scale) / raw.width;
|
|
896
|
+
const viewport = page.getViewport({ scale: adjustedScale });
|
|
897
|
+
const w = Math.round(viewport.width);
|
|
898
|
+
const h = Math.round(viewport.height);
|
|
899
|
+
const canvas = document.createElement("canvas");
|
|
900
|
+
canvas.width = w;
|
|
901
|
+
canvas.height = h;
|
|
902
|
+
const ctx = canvas.getContext("2d");
|
|
903
|
+
if (!ctx) throw new Error("Canvas 2D context unavailable");
|
|
904
|
+
ctx.imageSmoothingEnabled = true;
|
|
905
|
+
ctx.imageSmoothingQuality = "high";
|
|
906
|
+
await page.render({ canvas, viewport }).promise;
|
|
907
|
+
return { canvas, width: w, height: h };
|
|
1240
908
|
}
|
|
1241
|
-
function
|
|
1242
|
-
return {
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
909
|
+
function canvasToBlob2(canvas, mime, quality) {
|
|
910
|
+
return new Promise((resolve, reject) => {
|
|
911
|
+
canvas.toBlob(
|
|
912
|
+
(blob) => {
|
|
913
|
+
if (!blob) {
|
|
914
|
+
reject(new Error("Could not encode blob"));
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
resolve(blob);
|
|
918
|
+
},
|
|
919
|
+
mime,
|
|
920
|
+
quality
|
|
921
|
+
);
|
|
922
|
+
});
|
|
1246
923
|
}
|
|
1247
|
-
function
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
})
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
924
|
+
async function loadPdfToStore(file, store, options) {
|
|
925
|
+
const scale = options?.scale ?? 1.5;
|
|
926
|
+
const pdfjs = await getPdfJs();
|
|
927
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
928
|
+
const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
|
|
929
|
+
const results = [];
|
|
930
|
+
for (let i = 1; i <= pdf.numPages; i++) {
|
|
931
|
+
const page = await pdf.getPage(i);
|
|
932
|
+
const { canvas, width, height } = await renderPageToCanvas(page, scale);
|
|
933
|
+
const mime = "image/png";
|
|
934
|
+
const pageBlob = await canvasToBlob2(canvas, mime);
|
|
935
|
+
const blobId = await store.storeOriginal(pageBlob);
|
|
936
|
+
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
937
|
+
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
938
|
+
const th = Math.max(1, Math.round(height * thumbScale));
|
|
939
|
+
const thumbCanvas = document.createElement("canvas");
|
|
940
|
+
thumbCanvas.width = tw;
|
|
941
|
+
thumbCanvas.height = th;
|
|
942
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
943
|
+
if (tCtx) {
|
|
944
|
+
tCtx.imageSmoothingEnabled = true;
|
|
945
|
+
tCtx.imageSmoothingQuality = "high";
|
|
946
|
+
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
1259
947
|
}
|
|
1260
|
-
|
|
948
|
+
const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
|
|
949
|
+
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
950
|
+
results.push({ blobId, thumbnailBlobId, width, height, pageNumber: i });
|
|
951
|
+
}
|
|
952
|
+
return results;
|
|
1261
953
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
954
|
+
|
|
955
|
+
// src/react/asset-ingestion.ts
|
|
956
|
+
init_shape_builders();
|
|
957
|
+
|
|
958
|
+
// src/react/asset-store.ts
|
|
959
|
+
function applyAssetUploadResultToItem(item, result) {
|
|
960
|
+
if (!result?.pluginData) return item;
|
|
961
|
+
return {
|
|
962
|
+
...item,
|
|
963
|
+
pluginData: {
|
|
964
|
+
...item.pluginData ?? {},
|
|
965
|
+
...result.pluginData
|
|
1274
966
|
}
|
|
1275
|
-
|
|
967
|
+
};
|
|
1276
968
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
return
|
|
1283
|
-
"div",
|
|
1284
|
-
{
|
|
1285
|
-
"data-slot": "vector-canvas-body",
|
|
1286
|
-
className,
|
|
1287
|
-
style: mergeStyle(bodyStyle, style),
|
|
1288
|
-
children
|
|
1289
|
-
}
|
|
1290
|
-
);
|
|
969
|
+
|
|
970
|
+
// src/react/asset-ingestion.ts
|
|
971
|
+
function getAssetKindForFile(file) {
|
|
972
|
+
if (file.type === "application/pdf") return "pdf";
|
|
973
|
+
if (file.type.startsWith("image/")) return "image";
|
|
974
|
+
return null;
|
|
1291
975
|
}
|
|
1292
|
-
function
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
style
|
|
1296
|
-
}) {
|
|
1297
|
-
return /* @__PURE__ */ jsx(
|
|
1298
|
-
"div",
|
|
1299
|
-
{
|
|
1300
|
-
"data-slot": "vector-canvas-main",
|
|
1301
|
-
className,
|
|
1302
|
-
style: mergeStyle(mainStyle, style),
|
|
1303
|
-
children
|
|
1304
|
-
}
|
|
1305
|
-
);
|
|
976
|
+
function finalizeIngestedItem(item, context, uploadResult, decorateItem) {
|
|
977
|
+
const itemWithAssetData = applyAssetUploadResultToItem(item, uploadResult);
|
|
978
|
+
return decorateItem ? decorateItem(itemWithAssetData, context) : itemWithAssetData;
|
|
1306
979
|
}
|
|
1307
|
-
function
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
980
|
+
async function uploadAssetIfNeeded(assetStore, file, kind) {
|
|
981
|
+
if (!assetStore) return null;
|
|
982
|
+
const result = await assetStore.upload({ file, kind });
|
|
983
|
+
return result ?? null;
|
|
984
|
+
}
|
|
985
|
+
async function ingestAssetFilesToSceneItems(options) {
|
|
986
|
+
const {
|
|
987
|
+
files,
|
|
988
|
+
worldCenter,
|
|
989
|
+
assetStore,
|
|
990
|
+
imageStore = new IndexedDbImageStore(),
|
|
991
|
+
createId = createShapeId,
|
|
992
|
+
gapWorld = 16,
|
|
993
|
+
stepWorld = 48,
|
|
994
|
+
pdfScale = 1.5,
|
|
995
|
+
decorateItem,
|
|
996
|
+
onError
|
|
997
|
+
} = options;
|
|
998
|
+
const items = [];
|
|
999
|
+
const errors = [];
|
|
1000
|
+
let imagePlacementIndex = 0;
|
|
1001
|
+
let occupiedBottomY = null;
|
|
1002
|
+
let imageYOffsetAdjustment = 0;
|
|
1003
|
+
let hasImagePlacementBase = false;
|
|
1004
|
+
for (const file of files) {
|
|
1005
|
+
const kind = getAssetKindForFile(file);
|
|
1006
|
+
if (!kind) {
|
|
1007
|
+
const error = {
|
|
1008
|
+
file,
|
|
1009
|
+
error: new Error(`Unsupported asset type: ${file.type || "unknown"}`)
|
|
1010
|
+
};
|
|
1011
|
+
errors.push(error);
|
|
1012
|
+
onError?.(error);
|
|
1013
|
+
continue;
|
|
1319
1014
|
}
|
|
1320
|
-
|
|
1015
|
+
try {
|
|
1016
|
+
const uploadResult = await uploadAssetIfNeeded(assetStore, file, kind);
|
|
1017
|
+
if (kind === "pdf") {
|
|
1018
|
+
const pages = await loadPdfToStore(file, imageStore, { scale: pdfScale });
|
|
1019
|
+
for (const page of pages) {
|
|
1020
|
+
const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
|
|
1021
|
+
const naturalTopY2 = worldCenter.y - page.height / 2;
|
|
1022
|
+
const stackedTopY = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
|
|
1023
|
+
const bounds2 = {
|
|
1024
|
+
x: worldCenter.x - page.width / 2,
|
|
1025
|
+
y: Math.max(naturalTopY2, stackedTopY),
|
|
1026
|
+
width: page.width,
|
|
1027
|
+
height: page.height
|
|
1028
|
+
};
|
|
1029
|
+
const itemContext2 = {
|
|
1030
|
+
file,
|
|
1031
|
+
kind,
|
|
1032
|
+
itemIndex: items.length,
|
|
1033
|
+
pageNumber: page.pageNumber
|
|
1034
|
+
};
|
|
1035
|
+
const item2 = finalizeIngestedItem(
|
|
1036
|
+
{
|
|
1037
|
+
id: createId(),
|
|
1038
|
+
x: bounds2.x,
|
|
1039
|
+
y: bounds2.y,
|
|
1040
|
+
bounds: { ...bounds2 },
|
|
1041
|
+
toolKind: "image",
|
|
1042
|
+
imageBlobId: page.blobId,
|
|
1043
|
+
imageRasterHref: fullUrl2 ?? void 0,
|
|
1044
|
+
imageIntrinsicSize: {
|
|
1045
|
+
width: page.width,
|
|
1046
|
+
height: page.height
|
|
1047
|
+
},
|
|
1048
|
+
childrenSvg: fullUrl2 ? buildRasterImageChildrenSvg(
|
|
1049
|
+
fullUrl2,
|
|
1050
|
+
{ width: page.width, height: page.height },
|
|
1051
|
+
bounds2
|
|
1052
|
+
) : ""
|
|
1053
|
+
},
|
|
1054
|
+
itemContext2,
|
|
1055
|
+
uploadResult,
|
|
1056
|
+
decorateItem
|
|
1057
|
+
);
|
|
1058
|
+
items.push(item2);
|
|
1059
|
+
occupiedBottomY = bounds2.y + page.height;
|
|
1060
|
+
}
|
|
1061
|
+
hasImagePlacementBase = false;
|
|
1062
|
+
imagePlacementIndex = 0;
|
|
1063
|
+
imageYOffsetAdjustment = 0;
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
const { blobId, thumbnailBlobId, width, height } = await loadImageToStore(
|
|
1067
|
+
file,
|
|
1068
|
+
imageStore
|
|
1069
|
+
);
|
|
1070
|
+
const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
|
|
1071
|
+
const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
|
|
1072
|
+
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
1073
|
+
const ox = imagePlacementIndex % 8 * stepWorld;
|
|
1074
|
+
const oy = Math.floor(imagePlacementIndex / 8) * stepWorld;
|
|
1075
|
+
const naturalTopY = worldCenter.y - height / 2 + oy;
|
|
1076
|
+
if (!hasImagePlacementBase) {
|
|
1077
|
+
const minimumTopY = occupiedBottomY == null ? naturalTopY : occupiedBottomY + gapWorld;
|
|
1078
|
+
imageYOffsetAdjustment = Math.max(0, minimumTopY - naturalTopY);
|
|
1079
|
+
hasImagePlacementBase = true;
|
|
1080
|
+
}
|
|
1081
|
+
const bounds = {
|
|
1082
|
+
x: worldCenter.x - width / 2 + ox,
|
|
1083
|
+
y: naturalTopY + imageYOffsetAdjustment,
|
|
1084
|
+
width,
|
|
1085
|
+
height
|
|
1086
|
+
};
|
|
1087
|
+
const href = thumbnailHref ?? fullUrl ?? "";
|
|
1088
|
+
const itemContext = {
|
|
1089
|
+
file,
|
|
1090
|
+
kind,
|
|
1091
|
+
itemIndex: items.length
|
|
1092
|
+
};
|
|
1093
|
+
const item = finalizeIngestedItem(
|
|
1094
|
+
{
|
|
1095
|
+
id: createId(),
|
|
1096
|
+
x: bounds.x,
|
|
1097
|
+
y: bounds.y,
|
|
1098
|
+
bounds: { ...bounds },
|
|
1099
|
+
toolKind: "image",
|
|
1100
|
+
imageBlobId: blobId,
|
|
1101
|
+
imageThumbnailBlobId: thumbnailBlobId,
|
|
1102
|
+
imageRasterHref: fullUrl ?? void 0,
|
|
1103
|
+
imageThumbnailHref: thumbnailHref ?? void 0,
|
|
1104
|
+
imageIntrinsicSize: { width, height },
|
|
1105
|
+
childrenSvg: buildRasterImageChildrenSvg(href, { width, height }, bounds)
|
|
1106
|
+
},
|
|
1107
|
+
itemContext,
|
|
1108
|
+
uploadResult,
|
|
1109
|
+
decorateItem
|
|
1110
|
+
);
|
|
1111
|
+
items.push(item);
|
|
1112
|
+
imagePlacementIndex++;
|
|
1113
|
+
occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
const fileError = {
|
|
1116
|
+
file,
|
|
1117
|
+
kind,
|
|
1118
|
+
error
|
|
1119
|
+
};
|
|
1120
|
+
errors.push(fileError);
|
|
1121
|
+
onError?.(fileError);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return {
|
|
1125
|
+
items,
|
|
1126
|
+
errors
|
|
1127
|
+
};
|
|
1321
1128
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1129
|
+
var CanvuChromeContext = createContext(
|
|
1130
|
+
null
|
|
1131
|
+
);
|
|
1132
|
+
function useCanvuChromeContext() {
|
|
1133
|
+
return useContext(CanvuChromeContext);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// src/math/item-transform.ts
|
|
1137
|
+
init_rect();
|
|
1138
|
+
function getItemRotationRad(item) {
|
|
1139
|
+
return item.rotation ?? 0;
|
|
1140
|
+
}
|
|
1141
|
+
function itemLocalToWorld(lx, ly, itemX, itemY, w, h, rotationRad) {
|
|
1142
|
+
const c = { x: w / 2, y: h / 2 };
|
|
1143
|
+
const dlx = lx - c.x;
|
|
1144
|
+
const dly = ly - c.y;
|
|
1145
|
+
const cos = Math.cos(rotationRad);
|
|
1146
|
+
const sin = Math.sin(rotationRad);
|
|
1147
|
+
return {
|
|
1148
|
+
x: itemX + c.x + cos * dlx - sin * dly,
|
|
1149
|
+
y: itemY + c.y + sin * dlx + cos * dly
|
|
1337
1150
|
};
|
|
1338
|
-
return /* @__PURE__ */ jsx(
|
|
1339
|
-
"div",
|
|
1340
|
-
{
|
|
1341
|
-
"data-slot": "vector-canvas-toolbar",
|
|
1342
|
-
"data-position": position,
|
|
1343
|
-
className,
|
|
1344
|
-
style: mergeStyle(base2, style),
|
|
1345
|
-
children: /* @__PURE__ */ jsx("div", { style: { pointerEvents: "auto" }, children })
|
|
1346
|
-
}
|
|
1347
|
-
);
|
|
1348
1151
|
}
|
|
1349
|
-
function
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
{
|
|
1361
|
-
"data-slot": `vector-canvas-space-${position}`,
|
|
1362
|
-
className,
|
|
1363
|
-
style: mergeStyle(vectorCanvasSpaceStyle(position, inset, zIndex), style),
|
|
1364
|
-
children: /* @__PURE__ */ jsx("div", { style: { pointerEvents: contentPointerEvents }, children })
|
|
1365
|
-
}
|
|
1366
|
-
);
|
|
1152
|
+
function worldToItemLocal(wx, wy, itemX, itemY, w, h, rotationRad) {
|
|
1153
|
+
const c = { x: w / 2, y: h / 2 };
|
|
1154
|
+
const vx = wx - itemX;
|
|
1155
|
+
const vy = wy - itemY;
|
|
1156
|
+
const dx = vx - c.x;
|
|
1157
|
+
const dy = vy - c.y;
|
|
1158
|
+
const cos = Math.cos(-rotationRad);
|
|
1159
|
+
const sin = Math.sin(-rotationRad);
|
|
1160
|
+
const lx = cos * dx - sin * dy;
|
|
1161
|
+
const ly = sin * dx + cos * dy;
|
|
1162
|
+
return { x: c.x + lx, y: c.y + ly };
|
|
1367
1163
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
WebkitTapHighlightColor: "transparent"
|
|
1405
|
-
};
|
|
1406
|
-
var chevronBtnStyle = {
|
|
1407
|
-
pointerEvents: "auto",
|
|
1408
|
-
width: 24,
|
|
1409
|
-
height: 36,
|
|
1410
|
-
display: "inline-flex",
|
|
1411
|
-
alignItems: "center",
|
|
1412
|
-
justifyContent: "center",
|
|
1413
|
-
cursor: "pointer",
|
|
1414
|
-
fontSize: 12,
|
|
1415
|
-
lineHeight: 1,
|
|
1416
|
-
color: "#52525b",
|
|
1417
|
-
padding: 0,
|
|
1418
|
-
border: "none",
|
|
1419
|
-
outline: "none",
|
|
1420
|
-
background: "none",
|
|
1421
|
-
WebkitTapHighlightColor: "transparent"
|
|
1422
|
-
};
|
|
1423
|
-
var undoBtnStyle = {
|
|
1424
|
-
...chevronBtnStyle,
|
|
1425
|
-
width: 30,
|
|
1426
|
-
opacity: 1,
|
|
1427
|
-
color: "#18181b"
|
|
1428
|
-
};
|
|
1429
|
-
var labelStyle2 = {
|
|
1430
|
-
fontSize: 10,
|
|
1431
|
-
fontWeight: 600,
|
|
1432
|
-
color: "#52525b",
|
|
1433
|
-
textAlign: "center",
|
|
1434
|
-
userSelect: "none",
|
|
1435
|
-
padding: "2px 0"
|
|
1436
|
-
};
|
|
1437
|
-
var panelLayoutStyle = {
|
|
1164
|
+
function itemPivotWorld(item) {
|
|
1165
|
+
const r = normalizeRect(item.bounds);
|
|
1166
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
1167
|
+
}
|
|
1168
|
+
function boundsAabbForRotatedItem(item) {
|
|
1169
|
+
const rot = getItemRotationRad(item);
|
|
1170
|
+
if (Math.abs(rot) < 1e-12 && item.bounds.width >= 0 && item.bounds.height >= 0) {
|
|
1171
|
+
return item.bounds;
|
|
1172
|
+
}
|
|
1173
|
+
const r = normalizeRect(item.bounds);
|
|
1174
|
+
if (Math.abs(rot) < 1e-12) {
|
|
1175
|
+
return r;
|
|
1176
|
+
}
|
|
1177
|
+
const corners = [
|
|
1178
|
+
[0, 0],
|
|
1179
|
+
[r.width, 0],
|
|
1180
|
+
[r.width, r.height],
|
|
1181
|
+
[0, r.height]
|
|
1182
|
+
];
|
|
1183
|
+
let minX = Infinity;
|
|
1184
|
+
let minY = Infinity;
|
|
1185
|
+
let maxX = -Infinity;
|
|
1186
|
+
let maxY = -Infinity;
|
|
1187
|
+
for (const [lx, ly] of corners) {
|
|
1188
|
+
const p = itemLocalToWorld(lx, ly, item.x, item.y, r.width, r.height, rot);
|
|
1189
|
+
minX = Math.min(minX, p.x);
|
|
1190
|
+
minY = Math.min(minY, p.y);
|
|
1191
|
+
maxX = Math.max(maxX, p.x);
|
|
1192
|
+
maxY = Math.max(maxY, p.y);
|
|
1193
|
+
}
|
|
1194
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// src/react/NavMenu.tsx
|
|
1198
|
+
init_rect();
|
|
1199
|
+
var shellLook = {
|
|
1438
1200
|
display: "flex",
|
|
1439
1201
|
flexDirection: "column",
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1202
|
+
gap: 8,
|
|
1203
|
+
minWidth: 160,
|
|
1204
|
+
padding: "10px 12px",
|
|
1205
|
+
borderRadius: 8,
|
|
1206
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
1207
|
+
background: "rgba(255,255,255,0.96)",
|
|
1208
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1209
|
+
pointerEvents: "auto"
|
|
1445
1210
|
};
|
|
1446
|
-
var
|
|
1447
|
-
pointerEvents: "auto",
|
|
1211
|
+
var labelStyle = {
|
|
1448
1212
|
display: "flex",
|
|
1449
|
-
flexDirection: "
|
|
1450
|
-
alignItems: "center",
|
|
1213
|
+
flexDirection: "column",
|
|
1451
1214
|
gap: 4,
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1456
|
-
padding: 4
|
|
1215
|
+
fontSize: 11,
|
|
1216
|
+
fontWeight: 600,
|
|
1217
|
+
color: "#52525b"
|
|
1457
1218
|
};
|
|
1458
|
-
function
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1219
|
+
function normalizeHex(stroke) {
|
|
1220
|
+
if (/^#[0-9A-Fa-f]{6}$/.test(stroke)) return stroke;
|
|
1221
|
+
return "#2563eb";
|
|
1222
|
+
}
|
|
1223
|
+
function isStylableKind(tk) {
|
|
1224
|
+
return tk === "rect" || tk === "ellipse" || tk === "line" || tk === "arrow" || tk === "draw" || tk === "pencil" || tk === "brush" || tk === "marker" || tk === "text";
|
|
1225
|
+
}
|
|
1226
|
+
function VectorSelectionInspector({
|
|
1462
1227
|
items: itemsProp,
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
onUndo: onUndoProp,
|
|
1467
|
-
onRedo: onRedoProp,
|
|
1468
|
-
canUndo: canUndoProp,
|
|
1469
|
-
canRedo: canRedoProp,
|
|
1470
|
-
onRequestRender: onRequestRenderProp,
|
|
1471
|
-
position = "bottom-left",
|
|
1228
|
+
activeToolStyle: activeToolStyleProp,
|
|
1229
|
+
onChange: onChangeProp,
|
|
1230
|
+
position = "top-left",
|
|
1472
1231
|
inset = 12,
|
|
1473
|
-
zIndex =
|
|
1232
|
+
zIndex = 24,
|
|
1474
1233
|
className,
|
|
1475
1234
|
style
|
|
1476
1235
|
}) {
|
|
1477
1236
|
const ctx = useCanvuChromeContext();
|
|
1478
|
-
const
|
|
1479
|
-
const
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
const
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
const onRedo = onRedoProp ?? ctx?.onRedo ?? noop;
|
|
1487
|
-
const canUndo = canUndoProp ?? ctx?.canUndo ?? false;
|
|
1488
|
-
const canRedo = canRedoProp ?? ctx?.canRedo ?? false;
|
|
1489
|
-
const onRequestRender = onRequestRenderProp ?? ctx?.onRequestRender ?? noop;
|
|
1490
|
-
const [expanded, setExpanded] = useState(false);
|
|
1491
|
-
const svgRef = useRef(null);
|
|
1492
|
-
const [dragging, setDragging] = useState(false);
|
|
1493
|
-
const [mouseDown, setMouseDown] = useState(false);
|
|
1494
|
-
const worldBounds = computeWorldBounds(items);
|
|
1495
|
-
const isEmpty = worldBounds.width <= 0 || worldBounds.height <= 0;
|
|
1496
|
-
const scale = Math.min(
|
|
1497
|
-
(MINIMAP_W - PADDING * 2) / Math.max(worldBounds.width, 1),
|
|
1498
|
-
(MINIMAP_H - PADDING * 2) / Math.max(worldBounds.height, 1)
|
|
1499
|
-
);
|
|
1500
|
-
const originX = worldBounds.x;
|
|
1501
|
-
const originY = worldBounds.y;
|
|
1502
|
-
const toMinimapX = (wx) => (wx - originX) * scale + PADDING;
|
|
1503
|
-
const toMinimapY = (wy) => (wy - originY) * scale + PADDING;
|
|
1504
|
-
const toMinimapW = (ww) => ww * scale;
|
|
1505
|
-
const toMinimapH = (wh) => wh * scale;
|
|
1506
|
-
const viewportWorld = camera ? camera.getVisibleWorldRect(viewportWidth, viewportHeight) : { x: 0, y: 0, width: 0, height: 0 };
|
|
1507
|
-
const vpMinimap = {
|
|
1508
|
-
x: toMinimapX(viewportWorld.x),
|
|
1509
|
-
y: toMinimapY(viewportWorld.y),
|
|
1510
|
-
width: toMinimapW(viewportWorld.width),
|
|
1511
|
-
height: toMinimapH(viewportWorld.height)
|
|
1237
|
+
const items = itemsProp ?? ctx?.selectedItems ?? [];
|
|
1238
|
+
const activeToolStyle = activeToolStyleProp === void 0 ? ctx?.activeToolStyle ?? null : activeToolStyleProp;
|
|
1239
|
+
const onChange = onChangeProp ?? ctx?.onSelectionStyleChange ?? null;
|
|
1240
|
+
if (!onChange) return null;
|
|
1241
|
+
const shell = {
|
|
1242
|
+
...getBoardPositionStyle(position, inset, zIndex),
|
|
1243
|
+
...shellLook,
|
|
1244
|
+
...style
|
|
1512
1245
|
};
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1246
|
+
if (activeToolStyle) {
|
|
1247
|
+
const stroke2 = activeToolStyle.stroke;
|
|
1248
|
+
const strokeWidth2 = activeToolStyle.strokeWidth;
|
|
1249
|
+
const hex2 = normalizeHex(stroke2);
|
|
1250
|
+
const showMarkerOpacity2 = activeToolStyle.toolKind === "marker";
|
|
1251
|
+
const opacityPct2 = Math.round((activeToolStyle.strokeOpacity ?? 1) * 100);
|
|
1252
|
+
return /* @__PURE__ */ jsxs(
|
|
1253
|
+
"section",
|
|
1254
|
+
{
|
|
1255
|
+
"data-slot": "vector-selection-inspector",
|
|
1256
|
+
"data-position": position,
|
|
1257
|
+
className,
|
|
1258
|
+
"aria-label": activeToolStyle.label ?? "Estilo da ferramenta",
|
|
1259
|
+
style: shell,
|
|
1260
|
+
children: [
|
|
1261
|
+
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1262
|
+
"Cor",
|
|
1263
|
+
/* @__PURE__ */ jsx(
|
|
1264
|
+
"input",
|
|
1265
|
+
{
|
|
1266
|
+
type: "color",
|
|
1267
|
+
value: hex2,
|
|
1268
|
+
onChange: (e) => onChange({
|
|
1269
|
+
stroke: e.target.value,
|
|
1270
|
+
strokeWidth: strokeWidth2,
|
|
1271
|
+
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
1272
|
+
}),
|
|
1273
|
+
style: {
|
|
1274
|
+
width: "100%",
|
|
1275
|
+
height: 32,
|
|
1276
|
+
padding: 0,
|
|
1277
|
+
border: "none",
|
|
1278
|
+
cursor: "pointer",
|
|
1279
|
+
background: "transparent"
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
)
|
|
1283
|
+
] }),
|
|
1284
|
+
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1285
|
+
"Grossura",
|
|
1286
|
+
/* @__PURE__ */ jsx(
|
|
1287
|
+
"input",
|
|
1288
|
+
{
|
|
1289
|
+
type: "range",
|
|
1290
|
+
min: 1,
|
|
1291
|
+
max: 48,
|
|
1292
|
+
value: strokeWidth2,
|
|
1293
|
+
onChange: (e) => onChange({
|
|
1294
|
+
stroke: hex2,
|
|
1295
|
+
strokeWidth: Number(e.target.value),
|
|
1296
|
+
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
1297
|
+
})
|
|
1298
|
+
}
|
|
1299
|
+
),
|
|
1300
|
+
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1301
|
+
strokeWidth2,
|
|
1302
|
+
"px"
|
|
1303
|
+
] })
|
|
1304
|
+
] }),
|
|
1305
|
+
showMarkerOpacity2 && /* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1306
|
+
"Opacidade (marcador)",
|
|
1307
|
+
/* @__PURE__ */ jsx(
|
|
1308
|
+
"input",
|
|
1309
|
+
{
|
|
1310
|
+
type: "range",
|
|
1311
|
+
min: 10,
|
|
1312
|
+
max: 100,
|
|
1313
|
+
value: opacityPct2,
|
|
1314
|
+
onChange: (e) => {
|
|
1315
|
+
const v = Number(e.target.value) / 100;
|
|
1316
|
+
onChange({
|
|
1317
|
+
stroke: hex2,
|
|
1318
|
+
strokeWidth: strokeWidth2,
|
|
1319
|
+
strokeOpacity: v
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
),
|
|
1324
|
+
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1325
|
+
opacityPct2,
|
|
1326
|
+
"%"
|
|
1327
|
+
] })
|
|
1328
|
+
] })
|
|
1329
|
+
]
|
|
1330
|
+
}
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
const stylable = items.filter(
|
|
1334
|
+
(it) => !it.locked && it.toolKind && isStylableKind(it.toolKind)
|
|
1519
1335
|
);
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
camera.y = viewportHeight / 2 - worldY * camera.zoom;
|
|
1526
|
-
onRequestRender();
|
|
1527
|
-
},
|
|
1528
|
-
[worldFromMinimap, camera, viewportWidth, viewportHeight, onRequestRender]
|
|
1336
|
+
if (stylable.length === 0) return null;
|
|
1337
|
+
const first = stylable[0];
|
|
1338
|
+
if (!first) return null;
|
|
1339
|
+
const allSameStroke = stylable.every(
|
|
1340
|
+
(it) => (it.stroke ?? "#2563eb") === (first.stroke ?? "#2563eb")
|
|
1529
1341
|
);
|
|
1530
|
-
const
|
|
1531
|
-
(
|
|
1532
|
-
if (isEmpty) return;
|
|
1533
|
-
setDragging(true);
|
|
1534
|
-
setMouseDown(true);
|
|
1535
|
-
const rect = svgRef.current?.getBoundingClientRect();
|
|
1536
|
-
if (!rect) return;
|
|
1537
|
-
panTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
1538
|
-
svgRef.current?.setPointerCapture(e.pointerId);
|
|
1539
|
-
},
|
|
1540
|
-
[isEmpty, panTo]
|
|
1342
|
+
const allSameWidth = stylable.every(
|
|
1343
|
+
(it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
|
|
1541
1344
|
);
|
|
1542
|
-
const
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1345
|
+
const stroke = first.stroke ?? "#2563eb";
|
|
1346
|
+
const strokeWidth = first.strokeWidth ?? 2;
|
|
1347
|
+
const hex = normalizeHex(stroke);
|
|
1348
|
+
const markers = stylable.filter((it) => it.toolKind === "marker");
|
|
1349
|
+
const showMarkerOpacity = markers.length > 0;
|
|
1350
|
+
const firstMarker = markers[0];
|
|
1351
|
+
const allSameMarkerOpacity = markers.length > 0 && markers.every(
|
|
1352
|
+
(it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
|
|
1550
1353
|
);
|
|
1551
|
-
const
|
|
1552
|
-
setDragging(false);
|
|
1553
|
-
setMouseDown(false);
|
|
1554
|
-
}, []);
|
|
1555
|
-
const toggleExpanded = useCallback(() => {
|
|
1556
|
-
setExpanded((v) => !v);
|
|
1557
|
-
}, []);
|
|
1558
|
-
const anchorStyle = getBoardPositionStyle(position, inset, zIndex);
|
|
1354
|
+
const opacityPct = firstMarker ? Math.round((firstMarker.strokeOpacity ?? 1) * 100) : 100;
|
|
1559
1355
|
return /* @__PURE__ */ jsxs(
|
|
1560
|
-
"
|
|
1356
|
+
"section",
|
|
1561
1357
|
{
|
|
1562
|
-
"data-slot": "
|
|
1358
|
+
"data-slot": "vector-selection-inspector",
|
|
1563
1359
|
"data-position": position,
|
|
1564
1360
|
className,
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
...panelLayoutStyle,
|
|
1568
|
-
border: "none",
|
|
1569
|
-
margin: 0,
|
|
1570
|
-
padding: 0,
|
|
1571
|
-
minWidth: 0,
|
|
1572
|
-
...style
|
|
1573
|
-
},
|
|
1574
|
-
"aria-label": "Zoom and minimap controls",
|
|
1361
|
+
"aria-label": "Estilo da sele\xE7\xE3o",
|
|
1362
|
+
style: shell,
|
|
1575
1363
|
children: [
|
|
1576
|
-
/* @__PURE__ */ jsxs("
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
if (e.pointerType === "mouse") e.preventDefault();
|
|
1586
|
-
},
|
|
1587
|
-
onClick: onZoomOut,
|
|
1588
|
-
children: "\u2212"
|
|
1589
|
-
}
|
|
1590
|
-
),
|
|
1591
|
-
/* @__PURE__ */ jsxs("span", { style: labelStyle2, "aria-hidden": true, children: [
|
|
1592
|
-
zoomPercent,
|
|
1593
|
-
"%"
|
|
1364
|
+
stylable.length > 1 && /* @__PURE__ */ jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
|
|
1365
|
+
stylable.length,
|
|
1366
|
+
" objetos selecionados"
|
|
1367
|
+
] }),
|
|
1368
|
+
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1369
|
+
"Cor",
|
|
1370
|
+
!allSameStroke && /* @__PURE__ */ jsxs("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: [
|
|
1371
|
+
" ",
|
|
1372
|
+
"(valores misturados \u2014 novo valor aplica a todos)"
|
|
1594
1373
|
] }),
|
|
1595
1374
|
/* @__PURE__ */ jsx(
|
|
1596
|
-
"
|
|
1597
|
-
{
|
|
1598
|
-
type: "button",
|
|
1599
|
-
style: btnStyle,
|
|
1600
|
-
"aria-label": "Zoom in",
|
|
1601
|
-
title: "Zoom in",
|
|
1602
|
-
onPointerDown: (e) => {
|
|
1603
|
-
if (e.pointerType === "mouse") e.preventDefault();
|
|
1604
|
-
},
|
|
1605
|
-
onClick: onZoomIn,
|
|
1606
|
-
children: "+"
|
|
1607
|
-
}
|
|
1608
|
-
),
|
|
1609
|
-
/* @__PURE__ */ jsx(
|
|
1610
|
-
"button",
|
|
1375
|
+
"input",
|
|
1611
1376
|
{
|
|
1612
|
-
type: "
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1377
|
+
type: "color",
|
|
1378
|
+
value: hex,
|
|
1379
|
+
onChange: (e) => onChange({
|
|
1380
|
+
stroke: e.target.value,
|
|
1381
|
+
strokeWidth
|
|
1382
|
+
}),
|
|
1383
|
+
style: {
|
|
1384
|
+
width: "100%",
|
|
1385
|
+
height: 32,
|
|
1386
|
+
padding: 0,
|
|
1387
|
+
border: "none",
|
|
1388
|
+
cursor: "pointer",
|
|
1389
|
+
background: "transparent"
|
|
1390
|
+
}
|
|
1622
1391
|
}
|
|
1623
|
-
)
|
|
1392
|
+
)
|
|
1393
|
+
] }),
|
|
1394
|
+
/* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1395
|
+
"Grossura",
|
|
1396
|
+
!allSameWidth && /* @__PURE__ */ jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1624
1397
|
/* @__PURE__ */ jsx(
|
|
1625
|
-
"
|
|
1398
|
+
"input",
|
|
1626
1399
|
{
|
|
1627
|
-
type: "
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
}
|
|
1635
|
-
onClick: onRedo,
|
|
1636
|
-
children: /* @__PURE__ */ jsx(Redo2, { size: 16 })
|
|
1400
|
+
type: "range",
|
|
1401
|
+
min: 1,
|
|
1402
|
+
max: 48,
|
|
1403
|
+
value: strokeWidth,
|
|
1404
|
+
onChange: (e) => onChange({
|
|
1405
|
+
stroke: hex,
|
|
1406
|
+
strokeWidth: Number(e.target.value)
|
|
1407
|
+
})
|
|
1637
1408
|
}
|
|
1638
1409
|
),
|
|
1410
|
+
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1411
|
+
strokeWidth,
|
|
1412
|
+
"px"
|
|
1413
|
+
] })
|
|
1414
|
+
] }),
|
|
1415
|
+
showMarkerOpacity && firstMarker && /* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
|
|
1416
|
+
"Opacidade (marcador)",
|
|
1417
|
+
!allSameMarkerOpacity && markers.length > 1 && /* @__PURE__ */ jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1639
1418
|
/* @__PURE__ */ jsx(
|
|
1640
|
-
"
|
|
1419
|
+
"input",
|
|
1641
1420
|
{
|
|
1642
|
-
type: "
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
height: "12",
|
|
1655
|
-
viewBox: "0 0 12 12",
|
|
1656
|
-
style: {
|
|
1657
|
-
transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
1658
|
-
transition: "transform 0.15s ease"
|
|
1659
|
-
},
|
|
1660
|
-
"aria-hidden": true,
|
|
1661
|
-
children: /* @__PURE__ */ jsx(
|
|
1662
|
-
"path",
|
|
1663
|
-
{
|
|
1664
|
-
d: "M2 4 L6 8 L10 4",
|
|
1665
|
-
fill: "none",
|
|
1666
|
-
stroke: "currentColor",
|
|
1667
|
-
strokeWidth: "1.5",
|
|
1668
|
-
strokeLinecap: "round",
|
|
1669
|
-
strokeLinejoin: "round"
|
|
1670
|
-
}
|
|
1671
|
-
)
|
|
1672
|
-
}
|
|
1673
|
-
)
|
|
1421
|
+
type: "range",
|
|
1422
|
+
min: 10,
|
|
1423
|
+
max: 100,
|
|
1424
|
+
value: opacityPct,
|
|
1425
|
+
onChange: (e) => {
|
|
1426
|
+
const v = Number(e.target.value) / 100;
|
|
1427
|
+
onChange({
|
|
1428
|
+
stroke: hex,
|
|
1429
|
+
strokeWidth,
|
|
1430
|
+
strokeOpacity: v
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1674
1433
|
}
|
|
1675
|
-
)
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
style: {
|
|
1682
|
-
width: MINIMAP_W,
|
|
1683
|
-
height: MINIMAP_H,
|
|
1684
|
-
borderRadius: BORDER_RADIUS,
|
|
1685
|
-
border: `1px solid ${BORDER_COLOR}`,
|
|
1686
|
-
background: BG,
|
|
1687
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1688
|
-
cursor: isEmpty ? "default" : dragging ? "grabbing" : "grab",
|
|
1689
|
-
display: "block",
|
|
1690
|
-
pointerEvents: "auto"
|
|
1691
|
-
},
|
|
1692
|
-
onPointerDown: handlePointerDown,
|
|
1693
|
-
onPointerMove: handlePointerMove,
|
|
1694
|
-
onPointerUp: handlePointerUp,
|
|
1695
|
-
onPointerCancel: handlePointerUp,
|
|
1696
|
-
children: isEmpty ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1697
|
-
items.map((it) => {
|
|
1698
|
-
const b = normalizeRect(boundsAabbForRotatedItem(it));
|
|
1699
|
-
return /* @__PURE__ */ jsx(
|
|
1700
|
-
"rect",
|
|
1701
|
-
{
|
|
1702
|
-
x: toMinimapX(b.x),
|
|
1703
|
-
y: toMinimapY(b.y),
|
|
1704
|
-
width: toMinimapW(b.width),
|
|
1705
|
-
height: toMinimapH(b.height),
|
|
1706
|
-
fill: ITEM_FILL,
|
|
1707
|
-
stroke: ITEM_STROKE,
|
|
1708
|
-
strokeWidth: 0.5,
|
|
1709
|
-
rx: 1
|
|
1710
|
-
},
|
|
1711
|
-
it.id
|
|
1712
|
-
);
|
|
1713
|
-
}),
|
|
1714
|
-
/* @__PURE__ */ jsx(
|
|
1715
|
-
"rect",
|
|
1716
|
-
{
|
|
1717
|
-
x: vpMinimap.x,
|
|
1718
|
-
y: vpMinimap.y,
|
|
1719
|
-
width: vpMinimap.width,
|
|
1720
|
-
height: vpMinimap.height,
|
|
1721
|
-
fill: VIEWPORT_FILL,
|
|
1722
|
-
stroke: VIEWPORT_STROKE,
|
|
1723
|
-
strokeWidth: 1.5,
|
|
1724
|
-
rx: 2
|
|
1725
|
-
}
|
|
1726
|
-
)
|
|
1727
|
-
] })
|
|
1728
|
-
}
|
|
1729
|
-
) })
|
|
1434
|
+
),
|
|
1435
|
+
/* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1436
|
+
opacityPct,
|
|
1437
|
+
"%"
|
|
1438
|
+
] })
|
|
1439
|
+
] })
|
|
1730
1440
|
]
|
|
1731
1441
|
}
|
|
1732
1442
|
);
|
|
1733
1443
|
}
|
|
1734
|
-
function
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1444
|
+
function getBoardPositionStyle(position, inset = 12, zIndex = 40) {
|
|
1445
|
+
const base2 = { position: "absolute", zIndex };
|
|
1446
|
+
switch (position) {
|
|
1447
|
+
case "fill":
|
|
1448
|
+
return { ...base2, inset };
|
|
1449
|
+
case "top-left":
|
|
1450
|
+
case "left-top":
|
|
1451
|
+
return { ...base2, top: inset, left: inset };
|
|
1452
|
+
case "top-center":
|
|
1453
|
+
return {
|
|
1454
|
+
...base2,
|
|
1455
|
+
top: inset,
|
|
1456
|
+
left: "50%",
|
|
1457
|
+
transform: "translateX(-50%)"
|
|
1458
|
+
};
|
|
1459
|
+
case "top-right":
|
|
1460
|
+
case "right-top":
|
|
1461
|
+
return { ...base2, top: inset, right: inset };
|
|
1462
|
+
case "bottom-left":
|
|
1463
|
+
case "left-bottom":
|
|
1464
|
+
return { ...base2, bottom: inset, left: inset };
|
|
1465
|
+
case "bottom-center":
|
|
1466
|
+
return {
|
|
1467
|
+
...base2,
|
|
1468
|
+
bottom: inset,
|
|
1469
|
+
left: "50%",
|
|
1470
|
+
transform: "translateX(-50%)"
|
|
1471
|
+
};
|
|
1472
|
+
case "bottom-right":
|
|
1473
|
+
case "right-bottom":
|
|
1474
|
+
return { ...base2, bottom: inset, right: inset };
|
|
1475
|
+
case "left-center":
|
|
1476
|
+
return {
|
|
1477
|
+
...base2,
|
|
1478
|
+
left: inset,
|
|
1479
|
+
top: "50%",
|
|
1480
|
+
transform: "translateY(-50%)"
|
|
1481
|
+
};
|
|
1482
|
+
case "right-center":
|
|
1483
|
+
return {
|
|
1484
|
+
...base2,
|
|
1485
|
+
right: inset,
|
|
1486
|
+
top: "50%",
|
|
1487
|
+
transform: "translateY(-50%)"
|
|
1488
|
+
};
|
|
1489
|
+
case "center":
|
|
1490
|
+
return {
|
|
1491
|
+
...base2,
|
|
1492
|
+
top: "50%",
|
|
1493
|
+
left: "50%",
|
|
1494
|
+
transform: "translate(-50%, -50%)"
|
|
1495
|
+
};
|
|
1496
|
+
default:
|
|
1497
|
+
return base2;
|
|
1750
1498
|
}
|
|
1751
|
-
const pad = Math.max((maxX - minX) * 0.1, (maxY - minY) * 0.1, 40);
|
|
1752
|
-
return {
|
|
1753
|
-
x: minX - pad,
|
|
1754
|
-
y: minY - pad,
|
|
1755
|
-
width: maxX - minX + pad * 2,
|
|
1756
|
-
height: maxY - minY + pad * 2
|
|
1757
|
-
};
|
|
1758
1499
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1500
|
+
var rootStyle = {
|
|
1501
|
+
display: "flex",
|
|
1502
|
+
flexDirection: "column",
|
|
1503
|
+
height: "100%",
|
|
1504
|
+
minHeight: 0,
|
|
1505
|
+
width: "100%"
|
|
1506
|
+
};
|
|
1507
|
+
var headerStyle = {
|
|
1508
|
+
flexShrink: 0,
|
|
1509
|
+
display: "flex",
|
|
1510
|
+
flexWrap: "wrap",
|
|
1511
|
+
alignItems: "center",
|
|
1512
|
+
gap: "0.75rem",
|
|
1513
|
+
padding: "0.75rem 1rem",
|
|
1514
|
+
borderBottom: "1px solid #e4e4e7",
|
|
1515
|
+
background: "#fff",
|
|
1516
|
+
fontSize: "0.875rem"
|
|
1517
|
+
};
|
|
1518
|
+
var bodyStyle = {
|
|
1519
|
+
flex: 1,
|
|
1520
|
+
minHeight: 0,
|
|
1521
|
+
display: "flex",
|
|
1522
|
+
flexDirection: "column"
|
|
1523
|
+
};
|
|
1524
|
+
var mainStyle = {
|
|
1525
|
+
flex: 1,
|
|
1526
|
+
minHeight: 0,
|
|
1527
|
+
position: "relative",
|
|
1528
|
+
display: "flex",
|
|
1529
|
+
flexDirection: "column"
|
|
1530
|
+
};
|
|
1531
|
+
var viewportSurfaceStyle = {
|
|
1532
|
+
flex: 1,
|
|
1533
|
+
minHeight: 0,
|
|
1534
|
+
position: "relative",
|
|
1535
|
+
width: "100%",
|
|
1536
|
+
alignSelf: "stretch",
|
|
1537
|
+
background: "#fff",
|
|
1538
|
+
touchAction: "none"
|
|
1539
|
+
};
|
|
1540
|
+
function mergeStyle(base2, style) {
|
|
1541
|
+
return style ? { ...base2, ...style } : base2;
|
|
1778
1542
|
}
|
|
1779
|
-
function
|
|
1780
|
-
return
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
req.onerror = () => reject(req.error);
|
|
1785
|
-
});
|
|
1543
|
+
function vectorCanvasSpaceStyle(position, inset, zIndex) {
|
|
1544
|
+
return {
|
|
1545
|
+
...getBoardPositionStyle(position, inset, zIndex),
|
|
1546
|
+
pointerEvents: "none"
|
|
1547
|
+
};
|
|
1786
1548
|
}
|
|
1787
|
-
function
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1549
|
+
function VectorCanvasRoot({
|
|
1550
|
+
children,
|
|
1551
|
+
className,
|
|
1552
|
+
style
|
|
1553
|
+
}) {
|
|
1554
|
+
return /* @__PURE__ */ jsx(
|
|
1555
|
+
"div",
|
|
1556
|
+
{
|
|
1557
|
+
"data-slot": "vector-canvas-root",
|
|
1558
|
+
className,
|
|
1559
|
+
style: mergeStyle(rootStyle, style),
|
|
1560
|
+
children
|
|
1561
|
+
}
|
|
1562
|
+
);
|
|
1794
1563
|
}
|
|
1795
|
-
function
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1564
|
+
function VectorCanvasHeader({
|
|
1565
|
+
children,
|
|
1566
|
+
className,
|
|
1567
|
+
style
|
|
1568
|
+
}) {
|
|
1569
|
+
return /* @__PURE__ */ jsx(
|
|
1570
|
+
"header",
|
|
1571
|
+
{
|
|
1572
|
+
"data-slot": "vector-canvas-header",
|
|
1573
|
+
className,
|
|
1574
|
+
style: mergeStyle(headerStyle, style),
|
|
1575
|
+
children
|
|
1576
|
+
}
|
|
1577
|
+
);
|
|
1802
1578
|
}
|
|
1803
|
-
function
|
|
1804
|
-
|
|
1579
|
+
function VectorCanvasBody({
|
|
1580
|
+
children,
|
|
1581
|
+
className,
|
|
1582
|
+
style
|
|
1583
|
+
}) {
|
|
1584
|
+
return /* @__PURE__ */ jsx(
|
|
1585
|
+
"div",
|
|
1586
|
+
{
|
|
1587
|
+
"data-slot": "vector-canvas-body",
|
|
1588
|
+
className,
|
|
1589
|
+
style: mergeStyle(bodyStyle, style),
|
|
1590
|
+
children
|
|
1591
|
+
}
|
|
1592
|
+
);
|
|
1805
1593
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1594
|
+
function VectorCanvasMain({
|
|
1595
|
+
children,
|
|
1596
|
+
className,
|
|
1597
|
+
style
|
|
1598
|
+
}) {
|
|
1599
|
+
return /* @__PURE__ */ jsx(
|
|
1600
|
+
"div",
|
|
1601
|
+
{
|
|
1602
|
+
"data-slot": "vector-canvas-main",
|
|
1603
|
+
className,
|
|
1604
|
+
style: mergeStyle(mainStyle, style),
|
|
1605
|
+
children
|
|
1811
1606
|
}
|
|
1812
|
-
|
|
1813
|
-
}
|
|
1814
|
-
async storeOriginal(blob) {
|
|
1815
|
-
const id = generateBlobId();
|
|
1816
|
-
const db = await this.getDb();
|
|
1817
|
-
await putInStore(db, "images", id, blob);
|
|
1818
|
-
return id;
|
|
1819
|
-
}
|
|
1820
|
-
async getOriginal(id) {
|
|
1821
|
-
const db = await this.getDb();
|
|
1822
|
-
return getFromStore(db, "images", id);
|
|
1823
|
-
}
|
|
1824
|
-
async deleteOriginal(id) {
|
|
1825
|
-
const db = await this.getDb();
|
|
1826
|
-
await deleteFromStore(db, "images", id);
|
|
1827
|
-
}
|
|
1828
|
-
async storeThumbnail(blob) {
|
|
1829
|
-
const id = generateBlobId();
|
|
1830
|
-
const db = await this.getDb();
|
|
1831
|
-
await putInStore(db, "thumbnails", id, blob);
|
|
1832
|
-
return id;
|
|
1833
|
-
}
|
|
1834
|
-
async getThumbnail(id) {
|
|
1835
|
-
const db = await this.getDb();
|
|
1836
|
-
return getFromStore(db, "thumbnails", id);
|
|
1837
|
-
}
|
|
1838
|
-
async deleteThumbnail(id) {
|
|
1839
|
-
const db = await this.getDb();
|
|
1840
|
-
await deleteFromStore(db, "thumbnails", id);
|
|
1841
|
-
}
|
|
1842
|
-
};
|
|
1843
|
-
function decodeImageToCanvas(blob, maxDimension) {
|
|
1844
|
-
return new Promise((resolve, reject) => {
|
|
1845
|
-
const img = new Image();
|
|
1846
|
-
img.onload = () => {
|
|
1847
|
-
const w0 = img.naturalWidth;
|
|
1848
|
-
const h0 = img.naturalHeight;
|
|
1849
|
-
if (w0 < 1 || h0 < 1) {
|
|
1850
|
-
reject(new Error("Image has no dimensions"));
|
|
1851
|
-
return;
|
|
1852
|
-
}
|
|
1853
|
-
const scale = 1;
|
|
1854
|
-
const cw = Math.max(1, Math.round(w0 * scale));
|
|
1855
|
-
const ch = Math.max(1, Math.round(h0 * scale));
|
|
1856
|
-
const canvas = document.createElement("canvas");
|
|
1857
|
-
canvas.width = cw;
|
|
1858
|
-
canvas.height = ch;
|
|
1859
|
-
const ctx = canvas.getContext("2d");
|
|
1860
|
-
if (!ctx) {
|
|
1861
|
-
reject(new Error("Canvas 2D context unavailable"));
|
|
1862
|
-
return;
|
|
1863
|
-
}
|
|
1864
|
-
ctx.imageSmoothingEnabled = true;
|
|
1865
|
-
ctx.imageSmoothingQuality = "high";
|
|
1866
|
-
ctx.drawImage(img, 0, 0, cw, ch);
|
|
1867
|
-
URL.revokeObjectURL(img.src);
|
|
1868
|
-
resolve({ canvas, width: cw, height: ch });
|
|
1869
|
-
};
|
|
1870
|
-
img.onerror = () => {
|
|
1871
|
-
URL.revokeObjectURL(img.src);
|
|
1872
|
-
reject(new Error("Could not decode image"));
|
|
1873
|
-
};
|
|
1874
|
-
img.src = URL.createObjectURL(blob);
|
|
1875
|
-
});
|
|
1607
|
+
);
|
|
1876
1608
|
}
|
|
1877
|
-
function
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1609
|
+
function VectorCanvasViewportSurface({
|
|
1610
|
+
children,
|
|
1611
|
+
className,
|
|
1612
|
+
style
|
|
1613
|
+
}) {
|
|
1614
|
+
return /* @__PURE__ */ jsx(
|
|
1615
|
+
"div",
|
|
1616
|
+
{
|
|
1617
|
+
"data-slot": "vector-canvas-viewport-surface",
|
|
1618
|
+
className,
|
|
1619
|
+
style: mergeStyle(viewportSurfaceStyle, style),
|
|
1620
|
+
children
|
|
1621
|
+
}
|
|
1622
|
+
);
|
|
1891
1623
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1624
|
+
function VectorCanvasToolbar({
|
|
1625
|
+
children,
|
|
1626
|
+
className,
|
|
1627
|
+
style,
|
|
1628
|
+
position = "bottom-center",
|
|
1629
|
+
inset = 12,
|
|
1630
|
+
zIndex = 30
|
|
1631
|
+
}) {
|
|
1632
|
+
const base2 = {
|
|
1633
|
+
...getBoardPositionStyle(position, inset, zIndex),
|
|
1634
|
+
display: "flex",
|
|
1635
|
+
justifyContent: "center",
|
|
1636
|
+
alignItems: "center",
|
|
1637
|
+
maxWidth: "calc(100% - 24px)",
|
|
1638
|
+
pointerEvents: "none"
|
|
1639
|
+
};
|
|
1640
|
+
return /* @__PURE__ */ jsx(
|
|
1641
|
+
"div",
|
|
1642
|
+
{
|
|
1643
|
+
"data-slot": "vector-canvas-toolbar",
|
|
1644
|
+
"data-position": position,
|
|
1645
|
+
className,
|
|
1646
|
+
style: mergeStyle(base2, style),
|
|
1647
|
+
children: /* @__PURE__ */ jsx("div", { style: { pointerEvents: "auto" }, children })
|
|
1648
|
+
}
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
function VectorCanvasSpace({
|
|
1652
|
+
children,
|
|
1653
|
+
className,
|
|
1654
|
+
style,
|
|
1655
|
+
position = "top-right",
|
|
1656
|
+
inset = 12,
|
|
1657
|
+
zIndex = 40,
|
|
1658
|
+
contentPointerEvents = "auto"
|
|
1659
|
+
}) {
|
|
1660
|
+
return /* @__PURE__ */ jsx(
|
|
1661
|
+
"div",
|
|
1662
|
+
{
|
|
1663
|
+
"data-slot": `vector-canvas-space-${position}`,
|
|
1664
|
+
className,
|
|
1665
|
+
style: mergeStyle(vectorCanvasSpaceStyle(position, inset, zIndex), style),
|
|
1666
|
+
children: /* @__PURE__ */ jsx("div", { style: { pointerEvents: contentPointerEvents }, children })
|
|
1667
|
+
}
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
var VectorCanvas = {
|
|
1671
|
+
Root: VectorCanvasRoot,
|
|
1672
|
+
Header: VectorCanvasHeader,
|
|
1673
|
+
Body: VectorCanvasBody,
|
|
1674
|
+
Main: VectorCanvasMain,
|
|
1675
|
+
ViewportSurface: VectorCanvasViewportSurface,
|
|
1676
|
+
Toolbar: VectorCanvasToolbar,
|
|
1677
|
+
Space: VectorCanvasSpace,
|
|
1678
|
+
NavMenu,
|
|
1679
|
+
SelectionInspector: VectorSelectionInspector
|
|
1680
|
+
};
|
|
1681
|
+
var MINIMAP_W = 180;
|
|
1682
|
+
var MINIMAP_H = 120;
|
|
1683
|
+
var PADDING = 12;
|
|
1684
|
+
var BORDER_RADIUS = 8;
|
|
1685
|
+
var BG = "#ffffff";
|
|
1686
|
+
var BORDER_COLOR = "rgba(0,0,0,0.12)";
|
|
1687
|
+
var ITEM_FILL = "rgba(0,0,0,0.08)";
|
|
1688
|
+
var ITEM_STROKE = "rgba(0,0,0,0.15)";
|
|
1689
|
+
var VIEWPORT_FILL = "rgba(59,130,246,0.08)";
|
|
1690
|
+
var VIEWPORT_STROKE = "#3b82f6";
|
|
1691
|
+
var btnStyle = {
|
|
1692
|
+
pointerEvents: "auto",
|
|
1693
|
+
width: 36,
|
|
1694
|
+
height: 36,
|
|
1695
|
+
display: "inline-flex",
|
|
1696
|
+
alignItems: "center",
|
|
1697
|
+
justifyContent: "center",
|
|
1698
|
+
cursor: "pointer",
|
|
1699
|
+
fontSize: 18,
|
|
1700
|
+
lineHeight: 1,
|
|
1701
|
+
color: "#18181b",
|
|
1702
|
+
padding: 0,
|
|
1703
|
+
border: "none",
|
|
1704
|
+
outline: "none",
|
|
1705
|
+
background: "none",
|
|
1706
|
+
WebkitTapHighlightColor: "transparent"
|
|
1707
|
+
};
|
|
1708
|
+
var chevronBtnStyle = {
|
|
1709
|
+
pointerEvents: "auto",
|
|
1710
|
+
width: 24,
|
|
1711
|
+
height: 36,
|
|
1712
|
+
display: "inline-flex",
|
|
1713
|
+
alignItems: "center",
|
|
1714
|
+
justifyContent: "center",
|
|
1715
|
+
cursor: "pointer",
|
|
1716
|
+
fontSize: 12,
|
|
1717
|
+
lineHeight: 1,
|
|
1718
|
+
color: "#52525b",
|
|
1719
|
+
padding: 0,
|
|
1720
|
+
border: "none",
|
|
1721
|
+
outline: "none",
|
|
1722
|
+
background: "none",
|
|
1723
|
+
WebkitTapHighlightColor: "transparent"
|
|
1724
|
+
};
|
|
1725
|
+
var undoBtnStyle = {
|
|
1726
|
+
...chevronBtnStyle,
|
|
1727
|
+
width: 30,
|
|
1728
|
+
opacity: 1,
|
|
1729
|
+
color: "#18181b"
|
|
1730
|
+
};
|
|
1731
|
+
var labelStyle2 = {
|
|
1732
|
+
fontSize: 10,
|
|
1733
|
+
fontWeight: 600,
|
|
1734
|
+
color: "#52525b",
|
|
1735
|
+
textAlign: "center",
|
|
1736
|
+
userSelect: "none",
|
|
1737
|
+
padding: "2px 0"
|
|
1738
|
+
};
|
|
1739
|
+
var panelLayoutStyle = {
|
|
1740
|
+
display: "flex",
|
|
1741
|
+
flexDirection: "column",
|
|
1742
|
+
alignItems: "flex-start",
|
|
1743
|
+
gap: 4,
|
|
1744
|
+
touchAction: "none",
|
|
1745
|
+
pointerEvents: "none",
|
|
1746
|
+
userSelect: "none"
|
|
1747
|
+
};
|
|
1748
|
+
var innerStyle = {
|
|
1749
|
+
pointerEvents: "auto",
|
|
1750
|
+
display: "flex",
|
|
1751
|
+
flexDirection: "row",
|
|
1752
|
+
alignItems: "center",
|
|
1753
|
+
gap: 4,
|
|
1754
|
+
background: BG,
|
|
1755
|
+
borderRadius: BORDER_RADIUS,
|
|
1756
|
+
border: `1px solid ${BORDER_COLOR}`,
|
|
1757
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1758
|
+
padding: 4
|
|
1759
|
+
};
|
|
1760
|
+
function NavMenu({
|
|
1761
|
+
camera: cameraProp,
|
|
1762
|
+
viewportWidth: viewportWidthProp,
|
|
1763
|
+
viewportHeight: viewportHeightProp,
|
|
1764
|
+
items: itemsProp,
|
|
1765
|
+
zoomPercent: zoomPercentProp,
|
|
1766
|
+
onZoomIn: onZoomInProp,
|
|
1767
|
+
onZoomOut: onZoomOutProp,
|
|
1768
|
+
onUndo: onUndoProp,
|
|
1769
|
+
onRedo: onRedoProp,
|
|
1770
|
+
canUndo: canUndoProp,
|
|
1771
|
+
canRedo: canRedoProp,
|
|
1772
|
+
onRequestRender: onRequestRenderProp,
|
|
1773
|
+
position = "bottom-left",
|
|
1774
|
+
inset = 12,
|
|
1775
|
+
zIndex = 23,
|
|
1776
|
+
className,
|
|
1777
|
+
style
|
|
1778
|
+
}) {
|
|
1779
|
+
const ctx = useCanvuChromeContext();
|
|
1780
|
+
const camera = cameraProp ?? ctx?.camera ?? null;
|
|
1781
|
+
const viewportWidth = viewportWidthProp ?? ctx?.viewportWidth ?? 0;
|
|
1782
|
+
const viewportHeight = viewportHeightProp ?? ctx?.viewportHeight ?? 0;
|
|
1783
|
+
const items = itemsProp ?? ctx?.items ?? [];
|
|
1784
|
+
const zoomPercent = zoomPercentProp ?? ctx?.zoomPercent ?? 100;
|
|
1785
|
+
const onZoomIn = onZoomInProp ?? ctx?.onZoomIn ?? noop;
|
|
1786
|
+
const onZoomOut = onZoomOutProp ?? ctx?.onZoomOut ?? noop;
|
|
1787
|
+
const onUndo = onUndoProp ?? ctx?.onUndo ?? noop;
|
|
1788
|
+
const onRedo = onRedoProp ?? ctx?.onRedo ?? noop;
|
|
1789
|
+
const canUndo = canUndoProp ?? ctx?.canUndo ?? false;
|
|
1790
|
+
const canRedo = canRedoProp ?? ctx?.canRedo ?? false;
|
|
1791
|
+
const onRequestRender = onRequestRenderProp ?? ctx?.onRequestRender ?? noop;
|
|
1792
|
+
const [expanded, setExpanded] = useState(false);
|
|
1793
|
+
const svgRef = useRef(null);
|
|
1794
|
+
const [dragging, setDragging] = useState(false);
|
|
1795
|
+
const [mouseDown, setMouseDown] = useState(false);
|
|
1796
|
+
const worldBounds = computeWorldBounds(items);
|
|
1797
|
+
const isEmpty = worldBounds.width <= 0 || worldBounds.height <= 0;
|
|
1798
|
+
const scale = Math.min(
|
|
1799
|
+
(MINIMAP_W - PADDING * 2) / Math.max(worldBounds.width, 1),
|
|
1800
|
+
(MINIMAP_H - PADDING * 2) / Math.max(worldBounds.height, 1)
|
|
1801
|
+
);
|
|
1802
|
+
const originX = worldBounds.x;
|
|
1803
|
+
const originY = worldBounds.y;
|
|
1804
|
+
const toMinimapX = (wx) => (wx - originX) * scale + PADDING;
|
|
1805
|
+
const toMinimapY = (wy) => (wy - originY) * scale + PADDING;
|
|
1806
|
+
const toMinimapW = (ww) => ww * scale;
|
|
1807
|
+
const toMinimapH = (wh) => wh * scale;
|
|
1808
|
+
const viewportWorld = camera ? camera.getVisibleWorldRect(viewportWidth, viewportHeight) : { x: 0, y: 0, width: 0, height: 0 };
|
|
1809
|
+
const vpMinimap = {
|
|
1810
|
+
x: toMinimapX(viewportWorld.x),
|
|
1811
|
+
y: toMinimapY(viewportWorld.y),
|
|
1812
|
+
width: toMinimapW(viewportWorld.width),
|
|
1813
|
+
height: toMinimapH(viewportWorld.height)
|
|
1814
|
+
};
|
|
1815
|
+
const worldFromMinimap = useCallback(
|
|
1816
|
+
(mx, my) => ({
|
|
1817
|
+
worldX: (mx - PADDING) / scale + originX,
|
|
1818
|
+
worldY: (my - PADDING) / scale + originY
|
|
1819
|
+
}),
|
|
1820
|
+
[scale, originX, originY]
|
|
1821
|
+
);
|
|
1822
|
+
const panTo = useCallback(
|
|
1823
|
+
(mx, my) => {
|
|
1824
|
+
if (!camera) return;
|
|
1825
|
+
const { worldX, worldY } = worldFromMinimap(mx, my);
|
|
1826
|
+
camera.x = viewportWidth / 2 - worldX * camera.zoom;
|
|
1827
|
+
camera.y = viewportHeight / 2 - worldY * camera.zoom;
|
|
1828
|
+
onRequestRender();
|
|
1829
|
+
},
|
|
1830
|
+
[worldFromMinimap, camera, viewportWidth, viewportHeight, onRequestRender]
|
|
1831
|
+
);
|
|
1832
|
+
const handlePointerDown = useCallback(
|
|
1833
|
+
(e) => {
|
|
1834
|
+
if (isEmpty) return;
|
|
1835
|
+
setDragging(true);
|
|
1836
|
+
setMouseDown(true);
|
|
1837
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
1838
|
+
if (!rect) return;
|
|
1839
|
+
panTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
1840
|
+
svgRef.current?.setPointerCapture(e.pointerId);
|
|
1841
|
+
},
|
|
1842
|
+
[isEmpty, panTo]
|
|
1843
|
+
);
|
|
1844
|
+
const handlePointerMove = useCallback(
|
|
1845
|
+
(e) => {
|
|
1846
|
+
if (!mouseDown || isEmpty) return;
|
|
1847
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
1848
|
+
if (!rect) return;
|
|
1849
|
+
panTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
1850
|
+
},
|
|
1851
|
+
[mouseDown, isEmpty, panTo]
|
|
1852
|
+
);
|
|
1853
|
+
const handlePointerUp = useCallback(() => {
|
|
1854
|
+
setDragging(false);
|
|
1855
|
+
setMouseDown(false);
|
|
1856
|
+
}, []);
|
|
1857
|
+
const toggleExpanded = useCallback(() => {
|
|
1858
|
+
setExpanded((v) => !v);
|
|
1859
|
+
}, []);
|
|
1860
|
+
const anchorStyle = getBoardPositionStyle(position, inset, zIndex);
|
|
1861
|
+
return /* @__PURE__ */ jsxs(
|
|
1862
|
+
"fieldset",
|
|
1863
|
+
{
|
|
1864
|
+
"data-slot": "canvu-nav-menu",
|
|
1865
|
+
"data-position": position,
|
|
1866
|
+
className,
|
|
1867
|
+
style: {
|
|
1868
|
+
...anchorStyle,
|
|
1869
|
+
...panelLayoutStyle,
|
|
1870
|
+
border: "none",
|
|
1871
|
+
margin: 0,
|
|
1872
|
+
padding: 0,
|
|
1873
|
+
minWidth: 0,
|
|
1874
|
+
...style
|
|
1875
|
+
},
|
|
1876
|
+
"aria-label": "Zoom and minimap controls",
|
|
1877
|
+
children: [
|
|
1878
|
+
/* @__PURE__ */ jsxs("div", { style: innerStyle, children: [
|
|
1879
|
+
/* @__PURE__ */ jsx(
|
|
1880
|
+
"button",
|
|
1881
|
+
{
|
|
1882
|
+
type: "button",
|
|
1883
|
+
style: btnStyle,
|
|
1884
|
+
"aria-label": "Zoom out",
|
|
1885
|
+
title: "Zoom out",
|
|
1886
|
+
onPointerDown: (e) => {
|
|
1887
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1888
|
+
},
|
|
1889
|
+
onClick: onZoomOut,
|
|
1890
|
+
children: "\u2212"
|
|
1891
|
+
}
|
|
1892
|
+
),
|
|
1893
|
+
/* @__PURE__ */ jsxs("span", { style: labelStyle2, "aria-hidden": true, children: [
|
|
1894
|
+
zoomPercent,
|
|
1895
|
+
"%"
|
|
1896
|
+
] }),
|
|
1897
|
+
/* @__PURE__ */ jsx(
|
|
1898
|
+
"button",
|
|
1899
|
+
{
|
|
1900
|
+
type: "button",
|
|
1901
|
+
style: btnStyle,
|
|
1902
|
+
"aria-label": "Zoom in",
|
|
1903
|
+
title: "Zoom in",
|
|
1904
|
+
onPointerDown: (e) => {
|
|
1905
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1906
|
+
},
|
|
1907
|
+
onClick: onZoomIn,
|
|
1908
|
+
children: "+"
|
|
1909
|
+
}
|
|
1910
|
+
),
|
|
1911
|
+
/* @__PURE__ */ jsx(
|
|
1912
|
+
"button",
|
|
1913
|
+
{
|
|
1914
|
+
type: "button",
|
|
1915
|
+
style: undoBtnStyle,
|
|
1916
|
+
"aria-label": "Undo",
|
|
1917
|
+
title: "Undo",
|
|
1918
|
+
disabled: !canUndo,
|
|
1919
|
+
onPointerDown: (e) => {
|
|
1920
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1921
|
+
},
|
|
1922
|
+
onClick: onUndo,
|
|
1923
|
+
children: /* @__PURE__ */ jsx(Undo2, { size: 16 })
|
|
1924
|
+
}
|
|
1925
|
+
),
|
|
1926
|
+
/* @__PURE__ */ jsx(
|
|
1927
|
+
"button",
|
|
1928
|
+
{
|
|
1929
|
+
type: "button",
|
|
1930
|
+
style: undoBtnStyle,
|
|
1931
|
+
"aria-label": "Redo",
|
|
1932
|
+
title: "Redo",
|
|
1933
|
+
disabled: !canRedo,
|
|
1934
|
+
onPointerDown: (e) => {
|
|
1935
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1936
|
+
},
|
|
1937
|
+
onClick: onRedo,
|
|
1938
|
+
children: /* @__PURE__ */ jsx(Redo2, { size: 16 })
|
|
1939
|
+
}
|
|
1940
|
+
),
|
|
1941
|
+
/* @__PURE__ */ jsx(
|
|
1942
|
+
"button",
|
|
1943
|
+
{
|
|
1944
|
+
type: "button",
|
|
1945
|
+
style: chevronBtnStyle,
|
|
1946
|
+
"aria-label": expanded ? "Hide minimap" : "Show minimap",
|
|
1947
|
+
title: expanded ? "Hide minimap" : "Show minimap",
|
|
1948
|
+
onPointerDown: (e) => {
|
|
1949
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1950
|
+
},
|
|
1951
|
+
onClick: toggleExpanded,
|
|
1952
|
+
children: /* @__PURE__ */ jsx(
|
|
1953
|
+
"svg",
|
|
1954
|
+
{
|
|
1955
|
+
width: "12",
|
|
1956
|
+
height: "12",
|
|
1957
|
+
viewBox: "0 0 12 12",
|
|
1958
|
+
style: {
|
|
1959
|
+
transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
1960
|
+
transition: "transform 0.15s ease"
|
|
1961
|
+
},
|
|
1962
|
+
"aria-hidden": true,
|
|
1963
|
+
children: /* @__PURE__ */ jsx(
|
|
1964
|
+
"path",
|
|
1965
|
+
{
|
|
1966
|
+
d: "M2 4 L6 8 L10 4",
|
|
1967
|
+
fill: "none",
|
|
1968
|
+
stroke: "currentColor",
|
|
1969
|
+
strokeWidth: "1.5",
|
|
1970
|
+
strokeLinecap: "round",
|
|
1971
|
+
strokeLinejoin: "round"
|
|
1972
|
+
}
|
|
1973
|
+
)
|
|
1974
|
+
}
|
|
1975
|
+
)
|
|
1976
|
+
}
|
|
1977
|
+
)
|
|
1978
|
+
] }),
|
|
1979
|
+
expanded && /* @__PURE__ */ jsx("nav", { "aria-label": "Minimap", children: /* @__PURE__ */ jsx(
|
|
1980
|
+
"svg",
|
|
1981
|
+
{
|
|
1982
|
+
ref: svgRef,
|
|
1983
|
+
style: {
|
|
1984
|
+
width: MINIMAP_W,
|
|
1985
|
+
height: MINIMAP_H,
|
|
1986
|
+
borderRadius: BORDER_RADIUS,
|
|
1987
|
+
border: `1px solid ${BORDER_COLOR}`,
|
|
1988
|
+
background: BG,
|
|
1989
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1990
|
+
cursor: isEmpty ? "default" : dragging ? "grabbing" : "grab",
|
|
1991
|
+
display: "block",
|
|
1992
|
+
pointerEvents: "auto"
|
|
1993
|
+
},
|
|
1994
|
+
onPointerDown: handlePointerDown,
|
|
1995
|
+
onPointerMove: handlePointerMove,
|
|
1996
|
+
onPointerUp: handlePointerUp,
|
|
1997
|
+
onPointerCancel: handlePointerUp,
|
|
1998
|
+
children: isEmpty ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1999
|
+
items.map((it) => {
|
|
2000
|
+
const b = normalizeRect(boundsAabbForRotatedItem(it));
|
|
2001
|
+
return /* @__PURE__ */ jsx(
|
|
2002
|
+
"rect",
|
|
2003
|
+
{
|
|
2004
|
+
x: toMinimapX(b.x),
|
|
2005
|
+
y: toMinimapY(b.y),
|
|
2006
|
+
width: toMinimapW(b.width),
|
|
2007
|
+
height: toMinimapH(b.height),
|
|
2008
|
+
fill: ITEM_FILL,
|
|
2009
|
+
stroke: ITEM_STROKE,
|
|
2010
|
+
strokeWidth: 0.5,
|
|
2011
|
+
rx: 1
|
|
2012
|
+
},
|
|
2013
|
+
it.id
|
|
2014
|
+
);
|
|
2015
|
+
}),
|
|
2016
|
+
/* @__PURE__ */ jsx(
|
|
2017
|
+
"rect",
|
|
2018
|
+
{
|
|
2019
|
+
x: vpMinimap.x,
|
|
2020
|
+
y: vpMinimap.y,
|
|
2021
|
+
width: vpMinimap.width,
|
|
2022
|
+
height: vpMinimap.height,
|
|
2023
|
+
fill: VIEWPORT_FILL,
|
|
2024
|
+
stroke: VIEWPORT_STROKE,
|
|
2025
|
+
strokeWidth: 1.5,
|
|
2026
|
+
rx: 2
|
|
2027
|
+
}
|
|
2028
|
+
)
|
|
2029
|
+
] })
|
|
2030
|
+
}
|
|
2031
|
+
) })
|
|
2032
|
+
]
|
|
2033
|
+
}
|
|
1912
2034
|
);
|
|
1913
|
-
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
1914
|
-
return { blobId, thumbnailBlobId, width, height };
|
|
1915
2035
|
}
|
|
1916
|
-
|
|
1917
|
-
const blob = await store.getOriginal(blobId);
|
|
1918
|
-
if (!blob) return null;
|
|
1919
|
-
return URL.createObjectURL(blob);
|
|
2036
|
+
function noop() {
|
|
1920
2037
|
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2038
|
+
function computeWorldBounds(items) {
|
|
2039
|
+
if (items.length === 0) {
|
|
2040
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
2041
|
+
}
|
|
2042
|
+
let minX = Infinity;
|
|
2043
|
+
let minY = Infinity;
|
|
2044
|
+
let maxX = -Infinity;
|
|
2045
|
+
let maxY = -Infinity;
|
|
2046
|
+
for (const it of items) {
|
|
2047
|
+
const b = boundsAabbForRotatedItem(it);
|
|
2048
|
+
minX = Math.min(minX, b.x);
|
|
2049
|
+
minY = Math.min(minY, b.y);
|
|
2050
|
+
maxX = Math.max(maxX, b.x + b.width);
|
|
2051
|
+
maxY = Math.max(maxY, b.y + b.height);
|
|
2052
|
+
}
|
|
2053
|
+
const pad = Math.max((maxX - minX) * 0.1, (maxY - minY) * 0.1, 40);
|
|
2054
|
+
return {
|
|
2055
|
+
x: minX - pad,
|
|
2056
|
+
y: minY - pad,
|
|
2057
|
+
width: maxX - minX + pad * 2,
|
|
2058
|
+
height: maxY - minY + pad * 2
|
|
2059
|
+
};
|
|
1925
2060
|
}
|
|
1926
2061
|
|
|
1927
2062
|
// src/react/persistence/indexed-db-adapter.ts
|
|
@@ -3253,83 +3388,6 @@ var Camera2D = class {
|
|
|
3253
3388
|
}
|
|
3254
3389
|
};
|
|
3255
3390
|
|
|
3256
|
-
// src/image/pdf-loader.ts
|
|
3257
|
-
var pdfjsPromise = null;
|
|
3258
|
-
function getPdfJs() {
|
|
3259
|
-
if (!pdfjsPromise) {
|
|
3260
|
-
pdfjsPromise = import('pdfjs-dist').then((mod) => {
|
|
3261
|
-
const workerSrc = new URL(
|
|
3262
|
-
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
3263
|
-
import.meta.url
|
|
3264
|
-
).toString();
|
|
3265
|
-
mod.GlobalWorkerOptions.workerSrc = workerSrc;
|
|
3266
|
-
return mod;
|
|
3267
|
-
});
|
|
3268
|
-
}
|
|
3269
|
-
return pdfjsPromise;
|
|
3270
|
-
}
|
|
3271
|
-
async function renderPageToCanvas(page, scale) {
|
|
3272
|
-
const raw = page.getViewport({ scale: 1 });
|
|
3273
|
-
const adjustedScale = Math.round(raw.width * scale) / raw.width;
|
|
3274
|
-
const viewport = page.getViewport({ scale: adjustedScale });
|
|
3275
|
-
const w = Math.round(viewport.width);
|
|
3276
|
-
const h = Math.round(viewport.height);
|
|
3277
|
-
const canvas = document.createElement("canvas");
|
|
3278
|
-
canvas.width = w;
|
|
3279
|
-
canvas.height = h;
|
|
3280
|
-
const ctx = canvas.getContext("2d");
|
|
3281
|
-
if (!ctx) throw new Error("Canvas 2D context unavailable");
|
|
3282
|
-
ctx.imageSmoothingEnabled = true;
|
|
3283
|
-
ctx.imageSmoothingQuality = "high";
|
|
3284
|
-
await page.render({ canvas, viewport }).promise;
|
|
3285
|
-
return { canvas, width: w, height: h };
|
|
3286
|
-
}
|
|
3287
|
-
function canvasToBlob2(canvas, mime, quality) {
|
|
3288
|
-
return new Promise((resolve, reject) => {
|
|
3289
|
-
canvas.toBlob(
|
|
3290
|
-
(blob) => {
|
|
3291
|
-
if (!blob) {
|
|
3292
|
-
reject(new Error("Could not encode blob"));
|
|
3293
|
-
return;
|
|
3294
|
-
}
|
|
3295
|
-
resolve(blob);
|
|
3296
|
-
},
|
|
3297
|
-
mime,
|
|
3298
|
-
quality
|
|
3299
|
-
);
|
|
3300
|
-
});
|
|
3301
|
-
}
|
|
3302
|
-
async function loadPdfToStore(file, store, options) {
|
|
3303
|
-
const scale = 1.5;
|
|
3304
|
-
const pdfjs = await getPdfJs();
|
|
3305
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
3306
|
-
const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
|
|
3307
|
-
const results = [];
|
|
3308
|
-
for (let i = 1; i <= pdf.numPages; i++) {
|
|
3309
|
-
const page = await pdf.getPage(i);
|
|
3310
|
-
const { canvas, width, height } = await renderPageToCanvas(page, scale);
|
|
3311
|
-
const mime = "image/png";
|
|
3312
|
-
const pageBlob = await canvasToBlob2(canvas, mime);
|
|
3313
|
-
const blobId = await store.storeOriginal(pageBlob);
|
|
3314
|
-
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
3315
|
-
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
3316
|
-
const th = Math.max(1, Math.round(height * thumbScale));
|
|
3317
|
-
const thumbCanvas = document.createElement("canvas");
|
|
3318
|
-
thumbCanvas.width = tw;
|
|
3319
|
-
thumbCanvas.height = th;
|
|
3320
|
-
const tCtx = thumbCanvas.getContext("2d");
|
|
3321
|
-
if (tCtx) {
|
|
3322
|
-
tCtx.imageSmoothingEnabled = true;
|
|
3323
|
-
tCtx.imageSmoothingQuality = "high";
|
|
3324
|
-
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
3325
|
-
}
|
|
3326
|
-
const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
|
|
3327
|
-
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
3328
|
-
results.push({ blobId, thumbnailBlobId, width, height, pageNumber: i });
|
|
3329
|
-
}
|
|
3330
|
-
return results;
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
3391
|
// src/input/apple-pencil-navigation.ts
|
|
3334
3392
|
var DRAWING_LIKE_TOOLS = /* @__PURE__ */ new Set([
|
|
3335
3393
|
"draw",
|
|
@@ -3528,7 +3586,7 @@ function attachViewportInput(options) {
|
|
|
3528
3586
|
if (e.ctrlKey || e.metaKey) {
|
|
3529
3587
|
e.preventDefault();
|
|
3530
3588
|
const dy = wheelDeltaYPixels(e);
|
|
3531
|
-
const normDy = dy < 20 ? dy * 12 : dy;
|
|
3589
|
+
const normDy = Math.abs(dy) < 20 ? dy * 12 : dy;
|
|
3532
3590
|
const factor = Math.exp(-normDy * wheelZoomSensitivity);
|
|
3533
3591
|
const rect = element.getBoundingClientRect();
|
|
3534
3592
|
camera.setZoom(camera.zoom * factor, {
|
|
@@ -4682,7 +4740,42 @@ init_shape_builders();
|
|
|
4682
4740
|
|
|
4683
4741
|
// src/react/InteractionOverlay.tsx
|
|
4684
4742
|
init_rect();
|
|
4685
|
-
|
|
4743
|
+
|
|
4744
|
+
// src/scene/freehand-path.ts
|
|
4745
|
+
function smoothFreehandPointsToPathD(points) {
|
|
4746
|
+
const n = points.length;
|
|
4747
|
+
if (n === 0) return "";
|
|
4748
|
+
if (n === 1) {
|
|
4749
|
+
const p = points[0];
|
|
4750
|
+
if (!p) return "";
|
|
4751
|
+
return `M ${p.x} ${p.y}`;
|
|
4752
|
+
}
|
|
4753
|
+
if (n === 2) {
|
|
4754
|
+
const a = points[0];
|
|
4755
|
+
const b = points[1];
|
|
4756
|
+
if (!a || !b) return "";
|
|
4757
|
+
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
4758
|
+
}
|
|
4759
|
+
const p0 = points[0];
|
|
4760
|
+
if (!p0) return "";
|
|
4761
|
+
let d = `M ${p0.x} ${p0.y}`;
|
|
4762
|
+
let i = 1;
|
|
4763
|
+
for (; i < n - 2; i++) {
|
|
4764
|
+
const pi = points[i];
|
|
4765
|
+
const pi1 = points[i + 1];
|
|
4766
|
+
if (!pi || !pi1) continue;
|
|
4767
|
+
const xc = (pi.x + pi1.x) / 2;
|
|
4768
|
+
const yc = (pi.y + pi1.y) / 2;
|
|
4769
|
+
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
4770
|
+
}
|
|
4771
|
+
const pLast = points[i];
|
|
4772
|
+
const pEnd = points[i + 1];
|
|
4773
|
+
if (!pLast || !pEnd) return d;
|
|
4774
|
+
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
4775
|
+
return d;
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
// src/react/InteractionOverlay.tsx
|
|
4686
4779
|
init_shape_builders();
|
|
4687
4780
|
var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
4688
4781
|
var ERASER_TINT = "#cbd5e1";
|
|
@@ -4791,6 +4884,7 @@ function InteractionOverlay({
|
|
|
4791
4884
|
eraserTrail = [],
|
|
4792
4885
|
laserTrail = [],
|
|
4793
4886
|
eraserPreviewItems = [],
|
|
4887
|
+
marqueeCandidateItems = [],
|
|
4794
4888
|
previewStrokeStyle
|
|
4795
4889
|
}) {
|
|
4796
4890
|
const z = camera.zoom;
|
|
@@ -5039,6 +5133,33 @@ function InteractionOverlay({
|
|
|
5039
5133
|
);
|
|
5040
5134
|
}) });
|
|
5041
5135
|
}
|
|
5136
|
+
let marqueeCandidates = null;
|
|
5137
|
+
if (marqueeCandidateItems.length > 0) {
|
|
5138
|
+
marqueeCandidates = /* @__PURE__ */ jsx("g", { children: marqueeCandidateItems.map((it) => {
|
|
5139
|
+
const b = normalizeRect(it.bounds);
|
|
5140
|
+
return /* @__PURE__ */ jsx(
|
|
5141
|
+
"g",
|
|
5142
|
+
{
|
|
5143
|
+
transform: formatItemPlacementTransform(it),
|
|
5144
|
+
children: /* @__PURE__ */ jsx(
|
|
5145
|
+
"rect",
|
|
5146
|
+
{
|
|
5147
|
+
x: 0,
|
|
5148
|
+
y: 0,
|
|
5149
|
+
width: b.width,
|
|
5150
|
+
height: b.height,
|
|
5151
|
+
fill: "rgba(59, 130, 246, 0.10)",
|
|
5152
|
+
stroke: "#3b82f6",
|
|
5153
|
+
strokeWidth: overlayStrokePx,
|
|
5154
|
+
strokeDasharray: dashPattern,
|
|
5155
|
+
vectorEffect: "non-scaling-stroke"
|
|
5156
|
+
}
|
|
5157
|
+
)
|
|
5158
|
+
},
|
|
5159
|
+
`marquee-cand-${it.id}`
|
|
5160
|
+
);
|
|
5161
|
+
}) });
|
|
5162
|
+
}
|
|
5042
5163
|
let eraserTrailOverlay = null;
|
|
5043
5164
|
if (eraserTrail.length >= 1) {
|
|
5044
5165
|
const now = Date.now();
|
|
@@ -5139,6 +5260,7 @@ function InteractionOverlay({
|
|
|
5139
5260
|
width: "100%",
|
|
5140
5261
|
height: "100%",
|
|
5141
5262
|
children: /* @__PURE__ */ jsxs("g", { transform: rootTransform, children: [
|
|
5263
|
+
marqueeCandidates,
|
|
5142
5264
|
preview,
|
|
5143
5265
|
laserTrailOverlay,
|
|
5144
5266
|
eraserTrailOverlay,
|
|
@@ -5148,7 +5270,6 @@ function InteractionOverlay({
|
|
|
5148
5270
|
}
|
|
5149
5271
|
);
|
|
5150
5272
|
}
|
|
5151
|
-
init_freehand_path();
|
|
5152
5273
|
|
|
5153
5274
|
// src/react/presence/peer-color.ts
|
|
5154
5275
|
function defaultPresenceColorForId(id) {
|
|
@@ -5298,6 +5419,17 @@ function PresenceRemoteLayer({
|
|
|
5298
5419
|
);
|
|
5299
5420
|
}
|
|
5300
5421
|
|
|
5422
|
+
// src/react/stable-selection.ts
|
|
5423
|
+
function shallowEqualStringArray(a, b) {
|
|
5424
|
+
if (a === b) return true;
|
|
5425
|
+
if (!a || !b) return a === b;
|
|
5426
|
+
if (a.length !== b.length) return false;
|
|
5427
|
+
for (let i = 0; i < a.length; i++) {
|
|
5428
|
+
if (a[i] !== b[i]) return false;
|
|
5429
|
+
}
|
|
5430
|
+
return true;
|
|
5431
|
+
}
|
|
5432
|
+
|
|
5301
5433
|
// src/react/TextEditOverlay.tsx
|
|
5302
5434
|
init_rect();
|
|
5303
5435
|
init_text_svg();
|
|
@@ -5467,7 +5599,8 @@ var visuallyHidden = {
|
|
|
5467
5599
|
whiteSpace: "nowrap",
|
|
5468
5600
|
border: 0
|
|
5469
5601
|
};
|
|
5470
|
-
var
|
|
5602
|
+
var VIEWPORT_ZOOM_IN_STEP = 1.15;
|
|
5603
|
+
var VIEWPORT_ZOOM_OUT_STEP = 1 / 1.15;
|
|
5471
5604
|
function replaceItem(items, id, next) {
|
|
5472
5605
|
return items.map((it) => it.id === id ? next : it);
|
|
5473
5606
|
}
|
|
@@ -5671,22 +5804,8 @@ function appendInterpolatedPoints(points, next, maxStepWorld) {
|
|
|
5671
5804
|
if (!last) return [...points, next];
|
|
5672
5805
|
const dx = next.x - last.x;
|
|
5673
5806
|
const dy = next.y - last.y;
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
const safeStep = Math.max(maxStepWorld, 1e-4);
|
|
5677
|
-
const steps = Math.max(1, Math.ceil(dist / safeStep));
|
|
5678
|
-
if (steps === 1) return [...points, next];
|
|
5679
|
-
const out = [...points];
|
|
5680
|
-
for (let i = 1; i <= steps; i++) {
|
|
5681
|
-
const t = i / steps;
|
|
5682
|
-
const pressure = last.pressure != null || next.pressure != null ? (last.pressure ?? next.pressure ?? 0.5) + ((next.pressure ?? last.pressure ?? 0.5) - (last.pressure ?? next.pressure ?? 0.5)) * t : void 0;
|
|
5683
|
-
out.push({
|
|
5684
|
-
x: last.x + dx * t,
|
|
5685
|
-
y: last.y + dy * t,
|
|
5686
|
-
...pressure != null ? { pressure } : {}
|
|
5687
|
-
});
|
|
5688
|
-
}
|
|
5689
|
-
return out;
|
|
5807
|
+
if (dx * dx + dy * dy < 1e-12) return points;
|
|
5808
|
+
return [...points, next];
|
|
5690
5809
|
}
|
|
5691
5810
|
function pointerSampleToWorldPoint(screenToWorldFn, clientX, clientY, pressure) {
|
|
5692
5811
|
const { worldX, worldY } = screenToWorldFn(clientX, clientY);
|
|
@@ -5718,7 +5837,7 @@ function isLikelyStylusTouch(touch) {
|
|
|
5718
5837
|
}
|
|
5719
5838
|
function appendPointerEventSamplesToStrokePoints(points, ev, zoom, screenToWorldFn) {
|
|
5720
5839
|
let interpolated = points;
|
|
5721
|
-
|
|
5840
|
+
ev.pointerType === "pen" ? 0.35 / zoom : 1 / zoom;
|
|
5722
5841
|
for (const sample of pointerEventSamples(ev)) {
|
|
5723
5842
|
const nextPoint = pointerSampleToWorldPoint(
|
|
5724
5843
|
screenToWorldFn,
|
|
@@ -5726,7 +5845,7 @@ function appendPointerEventSamplesToStrokePoints(points, ev, zoom, screenToWorld
|
|
|
5726
5845
|
sample.clientY,
|
|
5727
5846
|
sample.pointerType === "pen" ? sample.pressure : void 0
|
|
5728
5847
|
);
|
|
5729
|
-
interpolated = appendInterpolatedPoints(interpolated, nextPoint
|
|
5848
|
+
interpolated = appendInterpolatedPoints(interpolated, nextPoint);
|
|
5730
5849
|
}
|
|
5731
5850
|
return interpolated;
|
|
5732
5851
|
}
|
|
@@ -5737,7 +5856,7 @@ function appendTouchToStrokePoints(points, touch, zoom, screenToWorldFn) {
|
|
|
5737
5856
|
touch.clientY,
|
|
5738
5857
|
touchPressure(touch)
|
|
5739
5858
|
);
|
|
5740
|
-
return appendInterpolatedPoints(points, nextPoint
|
|
5859
|
+
return appendInterpolatedPoints(points, nextPoint);
|
|
5741
5860
|
}
|
|
5742
5861
|
var VectorViewport = forwardRef(
|
|
5743
5862
|
function VectorViewport2({
|
|
@@ -5759,6 +5878,7 @@ var VectorViewport = forwardRef(
|
|
|
5759
5878
|
onCameraChange: consumerOnCameraChange,
|
|
5760
5879
|
customPlacement: consumerCustomPlacement,
|
|
5761
5880
|
customPlacements: consumerCustomPlacements,
|
|
5881
|
+
assetStore,
|
|
5762
5882
|
toolLocked = false,
|
|
5763
5883
|
autoResetToolTo = "select",
|
|
5764
5884
|
onToolChangeRequest,
|
|
@@ -5929,6 +6049,8 @@ var VectorViewport = forwardRef(
|
|
|
5929
6049
|
const itemsRef = useRef(items);
|
|
5930
6050
|
const onWorldPointerDownRef = useRef(onWorldPointerDown);
|
|
5931
6051
|
const onItemsChangeRef = useRef(onItemsChange);
|
|
6052
|
+
const assetStoreRef = useRef(assetStore);
|
|
6053
|
+
assetStoreRef.current = assetStore;
|
|
5932
6054
|
const customPlacementRef = useRef(customPlacement);
|
|
5933
6055
|
customPlacementRef.current = customPlacement;
|
|
5934
6056
|
const dragStateRef = useRef({ kind: "idle" });
|
|
@@ -6031,6 +6153,9 @@ var VectorViewport = forwardRef(
|
|
|
6031
6153
|
const [laserTrail, setLaserTrail] = useState([]);
|
|
6032
6154
|
const [eraserPreviewIds, setEraserPreviewIds] = useState([]);
|
|
6033
6155
|
const eraserPreviewIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
6156
|
+
const [marqueeCandidateIds, setMarqueeCandidateIds] = useState([]);
|
|
6157
|
+
const marqueeCandidateIdsRef = useRef([]);
|
|
6158
|
+
marqueeCandidateIdsRef.current = marqueeCandidateIds;
|
|
6034
6159
|
const resolvedSceneItems = useMemo(() => {
|
|
6035
6160
|
if (eraserPreviewIds.length === 0) return resolvedItems;
|
|
6036
6161
|
const hiddenIds = new Set(eraserPreviewIds);
|
|
@@ -6545,14 +6670,14 @@ var VectorViewport = forwardRef(
|
|
|
6545
6670
|
handled = true;
|
|
6546
6671
|
} else if (e.key === "+" || e.key === "=") {
|
|
6547
6672
|
const rect = el.getBoundingClientRect();
|
|
6548
|
-
camera.setZoom(camera.zoom *
|
|
6673
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_IN_STEP, {
|
|
6549
6674
|
x: rect.width / 2,
|
|
6550
6675
|
y: rect.height / 2
|
|
6551
6676
|
});
|
|
6552
6677
|
handled = true;
|
|
6553
6678
|
} else if (e.key === "-" || e.key === "_") {
|
|
6554
6679
|
const rect = el.getBoundingClientRect();
|
|
6555
|
-
camera.setZoom(camera.zoom
|
|
6680
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_OUT_STEP, {
|
|
6556
6681
|
x: rect.width / 2,
|
|
6557
6682
|
y: rect.height / 2
|
|
6558
6683
|
});
|
|
@@ -6821,7 +6946,7 @@ var VectorViewport = forwardRef(
|
|
|
6821
6946
|
const el = sceneContainerRef.current;
|
|
6822
6947
|
if (!camera || !el) return;
|
|
6823
6948
|
const rect = el.getBoundingClientRect();
|
|
6824
|
-
camera.setZoom(camera.zoom *
|
|
6949
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_IN_STEP, {
|
|
6825
6950
|
x: rect.width / 2,
|
|
6826
6951
|
y: rect.height / 2
|
|
6827
6952
|
});
|
|
@@ -6832,7 +6957,7 @@ var VectorViewport = forwardRef(
|
|
|
6832
6957
|
const el = sceneContainerRef.current;
|
|
6833
6958
|
if (!camera || !el) return;
|
|
6834
6959
|
const rect = el.getBoundingClientRect();
|
|
6835
|
-
camera.setZoom(camera.zoom
|
|
6960
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_OUT_STEP, {
|
|
6836
6961
|
x: rect.width / 2,
|
|
6837
6962
|
y: rect.height / 2
|
|
6838
6963
|
});
|
|
@@ -6850,13 +6975,13 @@ var VectorViewport = forwardRef(
|
|
|
6850
6975
|
if (isDefaultMarkerToolStyle(current)) {
|
|
6851
6976
|
setCurrentStrokeStyle({
|
|
6852
6977
|
stroke: DEFAULT_STROKE_STYLE.stroke,
|
|
6853
|
-
strokeWidth: toolId === "draw" ?
|
|
6978
|
+
strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth
|
|
6854
6979
|
});
|
|
6855
6980
|
return;
|
|
6856
6981
|
}
|
|
6857
6982
|
setCurrentStrokeStyle({
|
|
6858
6983
|
stroke: current.stroke,
|
|
6859
|
-
strokeWidth: toolId === "draw" ?
|
|
6984
|
+
strokeWidth: toolId === "draw" ? 10 : current.strokeWidth
|
|
6860
6985
|
});
|
|
6861
6986
|
}, [setCurrentStrokeStyle, toolId]);
|
|
6862
6987
|
useEffect(() => {
|
|
@@ -6958,97 +7083,49 @@ var VectorViewport = forwardRef(
|
|
|
6958
7083
|
if (!change || files.length === 0) return;
|
|
6959
7084
|
const store = imageStoreRef.current;
|
|
6960
7085
|
if (!store) return;
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
const estPages = 10;
|
|
6972
|
-
const skels = [];
|
|
6973
|
-
const skelIds = [];
|
|
6974
|
-
for (let i = 0; i < estPages; i++) {
|
|
7086
|
+
try {
|
|
7087
|
+
const pdfFiles = files.filter((file) => file.type === "application/pdf");
|
|
7088
|
+
if (pdfFiles.length > 0) {
|
|
7089
|
+
const gapWorld = 16;
|
|
7090
|
+
const estW = 1241;
|
|
7091
|
+
const estH = 1754;
|
|
7092
|
+
const estPages = 10;
|
|
7093
|
+
const skels = [];
|
|
7094
|
+
for (const [fileIndex] of pdfFiles.entries()) {
|
|
7095
|
+
for (let pageIndex = 0; pageIndex < estPages; pageIndex++) {
|
|
6975
7096
|
const id = `skeleton-${skeletonSeqRef.current++}`;
|
|
6976
|
-
|
|
7097
|
+
const offsetIndex = fileIndex * estPages + pageIndex;
|
|
6977
7098
|
skels.push({
|
|
6978
7099
|
id,
|
|
6979
7100
|
x: worldX - estW / 2,
|
|
6980
|
-
y: worldY - estH / 2 +
|
|
7101
|
+
y: worldY - estH / 2 + offsetIndex * (estH + gapWorld),
|
|
6981
7102
|
width: estW,
|
|
6982
7103
|
height: estH
|
|
6983
7104
|
});
|
|
6984
7105
|
}
|
|
6985
|
-
setLoadingSkeletons(skels);
|
|
6986
|
-
const pages = await loadPdfToStore(file, store);
|
|
6987
|
-
setLoadingSkeletons([]);
|
|
6988
|
-
for (const page of pages) {
|
|
6989
|
-
const { blobId, width, height } = page;
|
|
6990
|
-
const fullUrl = await createBlobUrlFromStore(store, blobId);
|
|
6991
|
-
const id = createShapeId();
|
|
6992
|
-
const bounds = {
|
|
6993
|
-
x: worldX - width / 2,
|
|
6994
|
-
y: worldY - height / 2 + placed * (height + gapWorld),
|
|
6995
|
-
width,
|
|
6996
|
-
height
|
|
6997
|
-
};
|
|
6998
|
-
newItems.push({
|
|
6999
|
-
id,
|
|
7000
|
-
x: bounds.x,
|
|
7001
|
-
y: bounds.y,
|
|
7002
|
-
bounds: { ...bounds },
|
|
7003
|
-
toolKind: "image",
|
|
7004
|
-
imageBlobId: blobId,
|
|
7005
|
-
imageRasterHref: fullUrl ?? void 0,
|
|
7006
|
-
imageIntrinsicSize: { width, height },
|
|
7007
|
-
childrenSvg: fullUrl ? buildRasterImageChildrenSvg2(fullUrl, { width, height }, bounds) : ""
|
|
7008
|
-
});
|
|
7009
|
-
placed++;
|
|
7010
|
-
}
|
|
7011
|
-
} else if (file.type.startsWith("image/")) {
|
|
7012
|
-
const { blobId, thumbnailBlobId, width, height } = await loadImageToStore(file, store);
|
|
7013
|
-
const fullUrl = await createBlobUrlFromStore(store, blobId);
|
|
7014
|
-
const thumbBlob = await store.getThumbnail(thumbnailBlobId);
|
|
7015
|
-
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
7016
|
-
const id = createShapeId();
|
|
7017
|
-
const ox = placed % 8 * stepWorld;
|
|
7018
|
-
const oy = Math.floor(placed / 8) * stepWorld;
|
|
7019
|
-
const bounds = {
|
|
7020
|
-
x: worldX - width / 2 + ox,
|
|
7021
|
-
y: worldY - height / 2 + oy,
|
|
7022
|
-
width,
|
|
7023
|
-
height
|
|
7024
|
-
};
|
|
7025
|
-
const href = thumbnailHref ?? fullUrl;
|
|
7026
|
-
newItems.push({
|
|
7027
|
-
id,
|
|
7028
|
-
x: bounds.x,
|
|
7029
|
-
y: bounds.y,
|
|
7030
|
-
bounds: { ...bounds },
|
|
7031
|
-
toolKind: "image",
|
|
7032
|
-
imageBlobId: blobId,
|
|
7033
|
-
imageThumbnailBlobId: thumbnailBlobId,
|
|
7034
|
-
imageRasterHref: fullUrl ?? void 0,
|
|
7035
|
-
imageThumbnailHref: thumbnailHref ?? void 0,
|
|
7036
|
-
imageIntrinsicSize: { width, height },
|
|
7037
|
-
childrenSvg: buildRasterImageChildrenSvg2(
|
|
7038
|
-
href ?? "",
|
|
7039
|
-
{ width, height },
|
|
7040
|
-
bounds
|
|
7041
|
-
)
|
|
7042
|
-
});
|
|
7043
|
-
placed++;
|
|
7044
7106
|
}
|
|
7045
|
-
|
|
7046
|
-
|
|
7107
|
+
setLoadingSkeletons(skels);
|
|
7108
|
+
}
|
|
7109
|
+
const result = await ingestAssetFilesToSceneItems({
|
|
7110
|
+
files,
|
|
7111
|
+
worldCenter: {
|
|
7112
|
+
x: worldX,
|
|
7113
|
+
y: worldY
|
|
7114
|
+
},
|
|
7115
|
+
imageStore: store,
|
|
7116
|
+
assetStore: assetStoreRef.current ?? void 0
|
|
7117
|
+
});
|
|
7118
|
+
if (result.errors.length > 0) {
|
|
7119
|
+
for (const error of result.errors) {
|
|
7120
|
+
console.error("Failed to add file:", error.error);
|
|
7121
|
+
}
|
|
7047
7122
|
}
|
|
7123
|
+
if (result.items.length === 0) return;
|
|
7124
|
+
change([...itemsRef.current, ...result.items]);
|
|
7125
|
+
setEffectiveSelectedIdsRef.current(result.items.map((item) => item.id));
|
|
7126
|
+
} finally {
|
|
7127
|
+
setLoadingSkeletons([]);
|
|
7048
7128
|
}
|
|
7049
|
-
if (newItems.length === 0) return;
|
|
7050
|
-
change([...itemsRef.current, ...newItems]);
|
|
7051
|
-
setEffectiveSelectedIdsRef.current(newItems.map((it) => it.id));
|
|
7052
7129
|
},
|
|
7053
7130
|
[]
|
|
7054
7131
|
);
|
|
@@ -7719,6 +7796,14 @@ var VectorViewport = forwardRef(
|
|
|
7719
7796
|
const { worldX: worldX2, worldY: worldY2 } = screenToWorld(ev.clientX, ev.clientY);
|
|
7720
7797
|
const raw = rectFromCorners(st.startWorld, { x: worldX2, y: worldY2 });
|
|
7721
7798
|
setPlacementPreview({ kind: "marquee", rect: raw });
|
|
7799
|
+
const nextCand = collectItemIdsInRect(
|
|
7800
|
+
resolvedItemsRef.current,
|
|
7801
|
+
normalizeRect(raw)
|
|
7802
|
+
);
|
|
7803
|
+
if (!shallowEqualStringArray(nextCand, marqueeCandidateIdsRef.current)) {
|
|
7804
|
+
marqueeCandidateIdsRef.current = nextCand;
|
|
7805
|
+
setMarqueeCandidateIds(nextCand);
|
|
7806
|
+
}
|
|
7722
7807
|
return;
|
|
7723
7808
|
}
|
|
7724
7809
|
if (st.kind === "stroke") {
|
|
@@ -7898,6 +7983,8 @@ var VectorViewport = forwardRef(
|
|
|
7898
7983
|
dragStateRef.current = { kind: "idle" };
|
|
7899
7984
|
releaseInteractionPointer();
|
|
7900
7985
|
setPlacementPreview(null);
|
|
7986
|
+
marqueeCandidateIdsRef.current = [];
|
|
7987
|
+
setMarqueeCandidateIds([]);
|
|
7901
7988
|
return;
|
|
7902
7989
|
}
|
|
7903
7990
|
if (st.kind === "move" || st.kind === "resize" || st.kind === "rotate") {
|
|
@@ -7909,6 +7996,8 @@ var VectorViewport = forwardRef(
|
|
|
7909
7996
|
dragStateRef.current = { kind: "idle" };
|
|
7910
7997
|
releaseInteractionPointer();
|
|
7911
7998
|
setPlacementPreview(null);
|
|
7999
|
+
marqueeCandidateIdsRef.current = [];
|
|
8000
|
+
setMarqueeCandidateIds([]);
|
|
7912
8001
|
const { worldX, worldY } = screenToWorld(ev.clientX, ev.clientY);
|
|
7913
8002
|
const raw = rectFromCorners(st.startWorld, { x: worldX, y: worldY });
|
|
7914
8003
|
const br = normalizeRect(raw);
|
|
@@ -8139,6 +8228,11 @@ var VectorViewport = forwardRef(
|
|
|
8139
8228
|
const eraserPreviewItemsForOverlay = useMemo(() => {
|
|
8140
8229
|
return eraserPreviewIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
|
|
8141
8230
|
}, [eraserPreviewIds, resolvedItems]);
|
|
8231
|
+
const marqueeCandidateItemsForOverlay = useMemo(() => {
|
|
8232
|
+
if (marqueeCandidateIds.length === 0) return [];
|
|
8233
|
+
const selected = new Set(effectiveSelectedIds);
|
|
8234
|
+
return marqueeCandidateIds.filter((id) => !selected.has(id)).map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
|
|
8235
|
+
}, [marqueeCandidateIds, effectiveSelectedIds, resolvedItems]);
|
|
8142
8236
|
const presenceLayer = useMemo(() => {
|
|
8143
8237
|
if (!cameraForOverlay) return null;
|
|
8144
8238
|
if (presenceOverlay) {
|
|
@@ -8284,6 +8378,7 @@ var VectorViewport = forwardRef(
|
|
|
8284
8378
|
eraserTrail,
|
|
8285
8379
|
laserTrail,
|
|
8286
8380
|
eraserPreviewItems: eraserPreviewItemsForOverlay,
|
|
8381
|
+
marqueeCandidateItems: marqueeCandidateItemsForOverlay,
|
|
8287
8382
|
previewStrokeStyle: strokeStyleRef.current
|
|
8288
8383
|
}
|
|
8289
8384
|
),
|
|
@@ -8500,6 +8595,6 @@ function ViewportZoomControls({
|
|
|
8500
8595
|
);
|
|
8501
8596
|
}
|
|
8502
8597
|
|
|
8503
|
-
export { CanvuChromeContext, CanvuPluginContext, DEFAULT_OVERFLOW_TOOL_IDS, DEFAULT_VECTOR_CANVAS_STORAGE_KEY, DEFAULT_VECTOR_TOOLS, IconArrow, IconDraw, IconEllipse, IconHand, IconImage, IconLaser, IconLine, IconRect, IconSelect, IconText, NavMenu, ShapeContextMenu, VectorCanvas, VectorCanvasBody, VectorCanvasHeader, VectorCanvasMain, VectorCanvasRoot, VectorCanvasToolbar, VectorCanvasViewportSurface, VectorSelectionInspector, VectorToolbar, VectorViewport, ViewportZoomControls, createCanvuPlugin, createIndexedDbPersistenceAdapter, createLocalStoragePersistenceAdapter, createNoopPersistenceAdapter, createToolPlugin, cursorForVectorToolId, getBoardPositionStyle, useCanvuChromeContext, useCanvuDocumentContext, useCanvuPluginContext, useCanvuPluginContribution, useCanvuResolvedTools, useCanvuViewportContext, useVectorCanvasDocument };
|
|
8598
|
+
export { CanvuChromeContext, CanvuPluginContext, DEFAULT_OVERFLOW_TOOL_IDS, DEFAULT_VECTOR_CANVAS_STORAGE_KEY, DEFAULT_VECTOR_TOOLS, IconArrow, IconDraw, IconEllipse, IconHand, IconImage, IconLaser, IconLine, IconRect, IconSelect, IconText, NavMenu, ShapeContextMenu, VectorCanvas, VectorCanvasBody, VectorCanvasHeader, VectorCanvasMain, VectorCanvasRoot, VectorCanvasToolbar, VectorCanvasViewportSurface, VectorSelectionInspector, VectorToolbar, VectorViewport, ViewportZoomControls, createCanvuPlugin, createIndexedDbPersistenceAdapter, createLocalStoragePersistenceAdapter, createNoopPersistenceAdapter, createToolPlugin, cursorForVectorToolId, getBoardPositionStyle, ingestAssetFilesToSceneItems, useCanvuChromeContext, useCanvuDocumentContext, useCanvuPluginContext, useCanvuPluginContribution, useCanvuResolvedTools, useCanvuViewportContext, useVectorCanvasDocument };
|
|
8504
8599
|
//# sourceMappingURL=react.js.map
|
|
8505
8600
|
//# sourceMappingURL=react.js.map
|