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.cjs
CHANGED
|
@@ -53,93 +53,6 @@ var init_custom_shape = __esm({
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
// src/scene/freehand-path.ts
|
|
57
|
-
function dedupeFreehandPoints(points, minDist) {
|
|
58
|
-
if (points.length <= 2) {
|
|
59
|
-
return points.map((p) => ({ ...p }));
|
|
60
|
-
}
|
|
61
|
-
const minSq = minDist * minDist;
|
|
62
|
-
const first = points[0];
|
|
63
|
-
if (!first) return [];
|
|
64
|
-
const out = [{ ...first }];
|
|
65
|
-
for (let i = 1; i < points.length - 1; i++) {
|
|
66
|
-
const p = points[i];
|
|
67
|
-
const last = out[out.length - 1];
|
|
68
|
-
if (!p || !last) continue;
|
|
69
|
-
const dx = p.x - last.x;
|
|
70
|
-
const dy = p.y - last.y;
|
|
71
|
-
if (dx * dx + dy * dy >= minSq) {
|
|
72
|
-
out.push({ ...p });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const end = points[points.length - 1];
|
|
76
|
-
const lastKept = out[out.length - 1];
|
|
77
|
-
if (!end || !lastKept) return out;
|
|
78
|
-
if ((end.x - lastKept.x) ** 2 + (end.y - lastKept.y) ** 2 > 1e-12) {
|
|
79
|
-
out.push({ ...end });
|
|
80
|
-
}
|
|
81
|
-
return out;
|
|
82
|
-
}
|
|
83
|
-
function smoothFreehandPointsToPathD(points) {
|
|
84
|
-
const n = points.length;
|
|
85
|
-
if (n === 0) return "";
|
|
86
|
-
if (n === 1) {
|
|
87
|
-
const p = points[0];
|
|
88
|
-
if (!p) return "";
|
|
89
|
-
return `M ${p.x} ${p.y}`;
|
|
90
|
-
}
|
|
91
|
-
if (n === 2) {
|
|
92
|
-
const a = points[0];
|
|
93
|
-
const b = points[1];
|
|
94
|
-
if (!a || !b) return "";
|
|
95
|
-
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
96
|
-
}
|
|
97
|
-
const p0 = points[0];
|
|
98
|
-
if (!p0) return "";
|
|
99
|
-
let d = `M ${p0.x} ${p0.y}`;
|
|
100
|
-
let i = 1;
|
|
101
|
-
for (; i < n - 2; i++) {
|
|
102
|
-
const pi = points[i];
|
|
103
|
-
const pi1 = points[i + 1];
|
|
104
|
-
if (!pi || !pi1) continue;
|
|
105
|
-
const xc = (pi.x + pi1.x) / 2;
|
|
106
|
-
const yc = (pi.y + pi1.y) / 2;
|
|
107
|
-
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
108
|
-
}
|
|
109
|
-
const pLast = points[i];
|
|
110
|
-
const pEnd = points[i + 1];
|
|
111
|
-
if (!pLast || !pEnd) return d;
|
|
112
|
-
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
113
|
-
return d;
|
|
114
|
-
}
|
|
115
|
-
function outlineStrokeToClosedPathD(outline) {
|
|
116
|
-
const len = outline.length;
|
|
117
|
-
if (len === 0) return "";
|
|
118
|
-
const first = outline[0];
|
|
119
|
-
if (!first) return "";
|
|
120
|
-
if (len < 3) {
|
|
121
|
-
let d2 = `M ${first[0]} ${first[1]}`;
|
|
122
|
-
for (let i = 1; i < len; i++) {
|
|
123
|
-
const pt = outline[i];
|
|
124
|
-
if (!pt) continue;
|
|
125
|
-
d2 += ` L ${pt[0]} ${pt[1]}`;
|
|
126
|
-
}
|
|
127
|
-
return `${d2} Z`;
|
|
128
|
-
}
|
|
129
|
-
let d = `M ${first[0]} ${first[1]} Q`;
|
|
130
|
-
for (let i = 0; i < len; i++) {
|
|
131
|
-
const p0 = outline[i];
|
|
132
|
-
const p1 = outline[(i + 1) % len];
|
|
133
|
-
if (!p0 || !p1) continue;
|
|
134
|
-
d += ` ${p0[0]} ${p0[1]} ${(p0[0] + p1[0]) / 2} ${(p0[1] + p1[1]) / 2}`;
|
|
135
|
-
}
|
|
136
|
-
return `${d} Z`;
|
|
137
|
-
}
|
|
138
|
-
var init_freehand_path = __esm({
|
|
139
|
-
"src/scene/freehand-path.ts"() {
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
56
|
// src/scene/text-svg.ts
|
|
144
57
|
function escapeSvgTextContent(s) {
|
|
145
58
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -325,18 +238,18 @@ function perfectFreehandOptions(toolKind, style, strokeComplete, pressureAware =
|
|
|
325
238
|
...base2,
|
|
326
239
|
size: Math.max(2, sw * 1.05),
|
|
327
240
|
thinning: 0.42,
|
|
328
|
-
smoothing: 0.
|
|
329
|
-
streamline: 0.
|
|
330
|
-
simulatePressure:
|
|
241
|
+
smoothing: 0.78,
|
|
242
|
+
streamline: 0.62,
|
|
243
|
+
simulatePressure: true
|
|
331
244
|
};
|
|
332
245
|
}
|
|
333
246
|
return {
|
|
334
247
|
...base2,
|
|
335
248
|
size: Math.max(2, sw * 1.18),
|
|
336
249
|
thinning: 0.12,
|
|
337
|
-
smoothing: 0.
|
|
338
|
-
streamline: 0.
|
|
339
|
-
simulatePressure:
|
|
250
|
+
smoothing: 0.85,
|
|
251
|
+
streamline: 0.78,
|
|
252
|
+
simulatePressure: true
|
|
340
253
|
};
|
|
341
254
|
}
|
|
342
255
|
if (toolKind === "brush") {
|
|
@@ -354,7 +267,7 @@ function perfectFreehandOptions(toolKind, style, strokeComplete, pressureAware =
|
|
|
354
267
|
thinning: 0.08,
|
|
355
268
|
smoothing: 0.88,
|
|
356
269
|
streamline: 0.84,
|
|
357
|
-
simulatePressure:
|
|
270
|
+
simulatePressure: true
|
|
358
271
|
};
|
|
359
272
|
}
|
|
360
273
|
function resolveStrokeStyle(item) {
|
|
@@ -424,70 +337,41 @@ function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeCompl
|
|
|
424
337
|
if (pathPointsLocal.length === 1) {
|
|
425
338
|
const p = pathPointsLocal[0];
|
|
426
339
|
if (!p) return null;
|
|
427
|
-
const r = Math.max(0.5, style.strokeWidth / 2);
|
|
428
|
-
return {
|
|
429
|
-
kind: "circle",
|
|
430
|
-
cx: p.x,
|
|
431
|
-
cy: p.y,
|
|
432
|
-
r,
|
|
433
|
-
fill: style.stroke,
|
|
434
|
-
fillOpacity: style.strokeOpacity
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
const minDist = Math.min(0.25, Math.max(0.02, style.strokeWidth * 0.02));
|
|
438
|
-
const pts = dedupeFreehandPoints(pathPointsLocal, minDist);
|
|
439
|
-
if (pts.length === 0) return null;
|
|
440
|
-
if (pts.length === 1) {
|
|
441
|
-
const p = pts[0];
|
|
442
|
-
if (!p) return null;
|
|
443
|
-
const r = Math.max(0.5, style.strokeWidth / 2);
|
|
444
340
|
return {
|
|
445
341
|
kind: "circle",
|
|
446
342
|
cx: p.x,
|
|
447
343
|
cy: p.y,
|
|
448
|
-
r,
|
|
344
|
+
r: Math.max(0.5, style.strokeWidth / 2),
|
|
449
345
|
fill: style.stroke,
|
|
450
346
|
fillOpacity: style.strokeOpacity
|
|
451
347
|
};
|
|
452
348
|
}
|
|
453
|
-
const hasPressure =
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
kind: "strokePath",
|
|
458
|
-
d: d2,
|
|
459
|
-
stroke: style.stroke,
|
|
460
|
-
strokeWidth: style.strokeWidth,
|
|
461
|
-
strokeOpacity: style.strokeOpacity
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
const input = hasPressure ? pts.map(
|
|
349
|
+
const hasPressure = pathPointsLocal.some(
|
|
350
|
+
(p) => p.pressure != null && Number.isFinite(p.pressure)
|
|
351
|
+
);
|
|
352
|
+
const input = hasPressure ? pathPointsLocal.map(
|
|
465
353
|
(p) => [p.x, p.y, Math.min(1, Math.max(0, p.pressure ?? 0.5))]
|
|
466
|
-
) :
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
fill: style.stroke,
|
|
481
|
-
fillOpacity: style.strokeOpacity
|
|
482
|
-
};
|
|
354
|
+
) : pathPointsLocal.map((p) => [p.x, p.y]);
|
|
355
|
+
const stroke = getStroke__default.default(
|
|
356
|
+
input,
|
|
357
|
+
perfectFreehandOptions(toolKind, style, strokeComplete, hasPressure)
|
|
358
|
+
);
|
|
359
|
+
if (stroke.length < 3) return null;
|
|
360
|
+
const first = stroke[0];
|
|
361
|
+
if (!first) return null;
|
|
362
|
+
let d = `M ${first[0]} ${first[1]} Q`;
|
|
363
|
+
for (let i = 0; i < stroke.length; i++) {
|
|
364
|
+
const a = stroke[i];
|
|
365
|
+
const b = stroke[(i + 1) % stroke.length];
|
|
366
|
+
if (!a || !b) continue;
|
|
367
|
+
d += ` ${a[0]} ${a[1]} ${(a[0] + b[0]) / 2} ${(a[1] + b[1]) / 2}`;
|
|
483
368
|
}
|
|
484
|
-
|
|
369
|
+
d += " Z";
|
|
485
370
|
return {
|
|
486
|
-
kind: "
|
|
371
|
+
kind: "fillPath",
|
|
487
372
|
d,
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
strokeOpacity: style.strokeOpacity
|
|
373
|
+
fill: style.stroke,
|
|
374
|
+
fillOpacity: style.strokeOpacity
|
|
491
375
|
};
|
|
492
376
|
}
|
|
493
377
|
function freehandPayloadToSvgString(payload) {
|
|
@@ -817,1118 +701,1369 @@ var init_shape_builders = __esm({
|
|
|
817
701
|
"src/scene/shape-builders.ts"() {
|
|
818
702
|
init_rect();
|
|
819
703
|
init_custom_shape();
|
|
820
|
-
init_freehand_path();
|
|
821
704
|
init_text_svg();
|
|
822
705
|
DEFAULT_STROKE_STYLE = {
|
|
823
706
|
stroke: "#2563eb",
|
|
824
707
|
strokeWidth: 2
|
|
825
708
|
};
|
|
826
709
|
TOOL_FREEHAND_DEFAULTS = {
|
|
827
|
-
draw: { strokeWidth:
|
|
710
|
+
draw: { strokeWidth: 10 },
|
|
828
711
|
pencil: { strokeWidth: 3 },
|
|
829
712
|
brush: { strokeWidth: 10 },
|
|
830
713
|
marker: { stroke: "#fde047", strokeWidth: 16, strokeOpacity: 0.5 }
|
|
831
714
|
};
|
|
832
715
|
}
|
|
833
716
|
});
|
|
834
|
-
var CanvuChromeContext = react.createContext(
|
|
835
|
-
null
|
|
836
|
-
);
|
|
837
|
-
function useCanvuChromeContext() {
|
|
838
|
-
return react.useContext(CanvuChromeContext);
|
|
839
|
-
}
|
|
840
717
|
|
|
841
|
-
// src/
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
718
|
+
// src/image/indexed-db-image-store.ts
|
|
719
|
+
var DB_NAME = "canvu-image-store";
|
|
720
|
+
var DB_VERSION = 1;
|
|
721
|
+
function openDb() {
|
|
722
|
+
return new Promise((resolve, reject) => {
|
|
723
|
+
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
|
724
|
+
req.onupgradeneeded = () => {
|
|
725
|
+
const db = req.result;
|
|
726
|
+
if (!db.objectStoreNames.contains("images")) {
|
|
727
|
+
db.createObjectStore("images");
|
|
728
|
+
}
|
|
729
|
+
if (!db.objectStoreNames.contains("thumbnails")) {
|
|
730
|
+
db.createObjectStore("thumbnails");
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
req.onsuccess = () => resolve(req.result);
|
|
734
|
+
req.onerror = () => reject(req.error);
|
|
735
|
+
});
|
|
845
736
|
}
|
|
846
|
-
function
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
x: itemX + c.x + cos * dlx - sin * dly,
|
|
854
|
-
y: itemY + c.y + sin * dlx + cos * dly
|
|
855
|
-
};
|
|
737
|
+
function getFromStore(db, storeName, id) {
|
|
738
|
+
return new Promise((resolve, reject) => {
|
|
739
|
+
const tx = db.transaction(storeName, "readonly");
|
|
740
|
+
const req = tx.objectStore(storeName).get(id);
|
|
741
|
+
req.onsuccess = () => resolve(req.result ?? void 0);
|
|
742
|
+
req.onerror = () => reject(req.error);
|
|
743
|
+
});
|
|
856
744
|
}
|
|
857
|
-
function
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const sin = Math.sin(-rotationRad);
|
|
865
|
-
const lx = cos * dx - sin * dy;
|
|
866
|
-
const ly = sin * dx + cos * dy;
|
|
867
|
-
return { x: c.x + lx, y: c.y + ly };
|
|
745
|
+
function putInStore(db, storeName, id, blob) {
|
|
746
|
+
return new Promise((resolve, reject) => {
|
|
747
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
748
|
+
const req = tx.objectStore(storeName).put(blob, id);
|
|
749
|
+
req.onsuccess = () => resolve();
|
|
750
|
+
req.onerror = () => reject(req.error);
|
|
751
|
+
});
|
|
868
752
|
}
|
|
869
|
-
function
|
|
870
|
-
|
|
871
|
-
|
|
753
|
+
function deleteFromStore(db, storeName, id) {
|
|
754
|
+
return new Promise((resolve, reject) => {
|
|
755
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
756
|
+
const req = tx.objectStore(storeName).delete(id);
|
|
757
|
+
req.onsuccess = () => resolve();
|
|
758
|
+
req.onerror = () => reject(req.error);
|
|
759
|
+
});
|
|
872
760
|
}
|
|
873
|
-
function
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
761
|
+
function generateBlobId() {
|
|
762
|
+
return crypto.randomUUID();
|
|
763
|
+
}
|
|
764
|
+
var IndexedDbImageStore = class {
|
|
765
|
+
dbPromise = null;
|
|
766
|
+
getDb() {
|
|
767
|
+
if (!this.dbPromise) {
|
|
768
|
+
this.dbPromise = openDb();
|
|
769
|
+
}
|
|
770
|
+
return this.dbPromise;
|
|
877
771
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
772
|
+
async storeOriginal(blob) {
|
|
773
|
+
const id = generateBlobId();
|
|
774
|
+
const db = await this.getDb();
|
|
775
|
+
await putInStore(db, "images", id, blob);
|
|
776
|
+
return id;
|
|
881
777
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
778
|
+
async getOriginal(id) {
|
|
779
|
+
const db = await this.getDb();
|
|
780
|
+
return getFromStore(db, "images", id);
|
|
781
|
+
}
|
|
782
|
+
async deleteOriginal(id) {
|
|
783
|
+
const db = await this.getDb();
|
|
784
|
+
await deleteFromStore(db, "images", id);
|
|
785
|
+
}
|
|
786
|
+
async storeThumbnail(blob) {
|
|
787
|
+
const id = generateBlobId();
|
|
788
|
+
const db = await this.getDb();
|
|
789
|
+
await putInStore(db, "thumbnails", id, blob);
|
|
790
|
+
return id;
|
|
791
|
+
}
|
|
792
|
+
async getThumbnail(id) {
|
|
793
|
+
const db = await this.getDb();
|
|
794
|
+
return getFromStore(db, "thumbnails", id);
|
|
795
|
+
}
|
|
796
|
+
async deleteThumbnail(id) {
|
|
797
|
+
const db = await this.getDb();
|
|
798
|
+
await deleteFromStore(db, "thumbnails", id);
|
|
898
799
|
}
|
|
899
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// src/react/NavMenu.tsx
|
|
903
|
-
init_rect();
|
|
904
|
-
var shellLook = {
|
|
905
|
-
display: "flex",
|
|
906
|
-
flexDirection: "column",
|
|
907
|
-
gap: 8,
|
|
908
|
-
minWidth: 160,
|
|
909
|
-
padding: "10px 12px",
|
|
910
|
-
borderRadius: 8,
|
|
911
|
-
border: "1px solid rgba(0,0,0,0.12)",
|
|
912
|
-
background: "rgba(255,255,255,0.96)",
|
|
913
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
914
|
-
pointerEvents: "auto"
|
|
915
|
-
};
|
|
916
|
-
var labelStyle = {
|
|
917
|
-
display: "flex",
|
|
918
|
-
flexDirection: "column",
|
|
919
|
-
gap: 4,
|
|
920
|
-
fontSize: 11,
|
|
921
|
-
fontWeight: 600,
|
|
922
|
-
color: "#52525b"
|
|
923
800
|
};
|
|
924
|
-
function
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
const onChange = onChangeProp ?? ctx?.onSelectionStyleChange ?? null;
|
|
945
|
-
if (!onChange) return null;
|
|
946
|
-
const shell = {
|
|
947
|
-
...getBoardPositionStyle(position, inset, zIndex),
|
|
948
|
-
...shellLook,
|
|
949
|
-
...style
|
|
950
|
-
};
|
|
951
|
-
if (activeToolStyle) {
|
|
952
|
-
const stroke2 = activeToolStyle.stroke;
|
|
953
|
-
const strokeWidth2 = activeToolStyle.strokeWidth;
|
|
954
|
-
const hex2 = normalizeHex(stroke2);
|
|
955
|
-
const showMarkerOpacity2 = activeToolStyle.toolKind === "marker";
|
|
956
|
-
const opacityPct2 = Math.round((activeToolStyle.strokeOpacity ?? 1) * 100);
|
|
957
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
958
|
-
"section",
|
|
959
|
-
{
|
|
960
|
-
"data-slot": "vector-selection-inspector",
|
|
961
|
-
"data-position": position,
|
|
962
|
-
className,
|
|
963
|
-
"aria-label": activeToolStyle.label ?? "Estilo da ferramenta",
|
|
964
|
-
style: shell,
|
|
965
|
-
children: [
|
|
966
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
967
|
-
"Cor",
|
|
968
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
969
|
-
"input",
|
|
970
|
-
{
|
|
971
|
-
type: "color",
|
|
972
|
-
value: hex2,
|
|
973
|
-
onChange: (e) => onChange({
|
|
974
|
-
stroke: e.target.value,
|
|
975
|
-
strokeWidth: strokeWidth2,
|
|
976
|
-
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
977
|
-
}),
|
|
978
|
-
style: {
|
|
979
|
-
width: "100%",
|
|
980
|
-
height: 32,
|
|
981
|
-
padding: 0,
|
|
982
|
-
border: "none",
|
|
983
|
-
cursor: "pointer",
|
|
984
|
-
background: "transparent"
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
)
|
|
988
|
-
] }),
|
|
989
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
990
|
-
"Grossura",
|
|
991
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
992
|
-
"input",
|
|
993
|
-
{
|
|
994
|
-
type: "range",
|
|
995
|
-
min: 1,
|
|
996
|
-
max: 48,
|
|
997
|
-
value: strokeWidth2,
|
|
998
|
-
onChange: (e) => onChange({
|
|
999
|
-
stroke: hex2,
|
|
1000
|
-
strokeWidth: Number(e.target.value),
|
|
1001
|
-
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
1002
|
-
})
|
|
1003
|
-
}
|
|
1004
|
-
),
|
|
1005
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1006
|
-
strokeWidth2,
|
|
1007
|
-
"px"
|
|
1008
|
-
] })
|
|
1009
|
-
] }),
|
|
1010
|
-
showMarkerOpacity2 && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1011
|
-
"Opacidade (marcador)",
|
|
1012
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1013
|
-
"input",
|
|
1014
|
-
{
|
|
1015
|
-
type: "range",
|
|
1016
|
-
min: 10,
|
|
1017
|
-
max: 100,
|
|
1018
|
-
value: opacityPct2,
|
|
1019
|
-
onChange: (e) => {
|
|
1020
|
-
const v = Number(e.target.value) / 100;
|
|
1021
|
-
onChange({
|
|
1022
|
-
stroke: hex2,
|
|
1023
|
-
strokeWidth: strokeWidth2,
|
|
1024
|
-
strokeOpacity: v
|
|
1025
|
-
});
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
),
|
|
1029
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1030
|
-
opacityPct2,
|
|
1031
|
-
"%"
|
|
1032
|
-
] })
|
|
1033
|
-
] })
|
|
1034
|
-
]
|
|
801
|
+
function decodeImageToCanvas(blob, maxDimension) {
|
|
802
|
+
return new Promise((resolve, reject) => {
|
|
803
|
+
const img = new Image();
|
|
804
|
+
img.onload = () => {
|
|
805
|
+
const w0 = img.naturalWidth;
|
|
806
|
+
const h0 = img.naturalHeight;
|
|
807
|
+
if (w0 < 1 || h0 < 1) {
|
|
808
|
+
reject(new Error("Image has no dimensions"));
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const scale = 1;
|
|
812
|
+
const cw = Math.max(1, Math.round(w0 * scale));
|
|
813
|
+
const ch = Math.max(1, Math.round(h0 * scale));
|
|
814
|
+
const canvas = document.createElement("canvas");
|
|
815
|
+
canvas.width = cw;
|
|
816
|
+
canvas.height = ch;
|
|
817
|
+
const ctx = canvas.getContext("2d");
|
|
818
|
+
if (!ctx) {
|
|
819
|
+
reject(new Error("Canvas 2D context unavailable"));
|
|
820
|
+
return;
|
|
1035
821
|
}
|
|
822
|
+
ctx.imageSmoothingEnabled = true;
|
|
823
|
+
ctx.imageSmoothingQuality = "high";
|
|
824
|
+
ctx.drawImage(img, 0, 0, cw, ch);
|
|
825
|
+
URL.revokeObjectURL(img.src);
|
|
826
|
+
resolve({ canvas, width: cw, height: ch });
|
|
827
|
+
};
|
|
828
|
+
img.onerror = () => {
|
|
829
|
+
URL.revokeObjectURL(img.src);
|
|
830
|
+
reject(new Error("Could not decode image"));
|
|
831
|
+
};
|
|
832
|
+
img.src = URL.createObjectURL(blob);
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
function canvasToBlob(canvas, mime, quality) {
|
|
836
|
+
return new Promise((resolve, reject) => {
|
|
837
|
+
canvas.toBlob(
|
|
838
|
+
(blob) => {
|
|
839
|
+
if (!blob) {
|
|
840
|
+
reject(new Error("Could not encode blob"));
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
resolve(blob);
|
|
844
|
+
},
|
|
845
|
+
mime,
|
|
846
|
+
quality
|
|
1036
847
|
);
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
async function loadImageToStore(file, store) {
|
|
851
|
+
const originalBlob = file;
|
|
852
|
+
const blobId = await store.storeOriginal(originalBlob);
|
|
853
|
+
const { canvas, width, height } = await decodeImageToCanvas(originalBlob);
|
|
854
|
+
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
855
|
+
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
856
|
+
const th = Math.max(1, Math.round(height * thumbScale));
|
|
857
|
+
const thumbCanvas = document.createElement("canvas");
|
|
858
|
+
thumbCanvas.width = tw;
|
|
859
|
+
thumbCanvas.height = th;
|
|
860
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
861
|
+
if (tCtx) {
|
|
862
|
+
tCtx.imageSmoothingEnabled = true;
|
|
863
|
+
tCtx.imageSmoothingQuality = "high";
|
|
864
|
+
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
1037
865
|
}
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
const first = stylable[0];
|
|
1043
|
-
if (!first) return null;
|
|
1044
|
-
const allSameStroke = stylable.every(
|
|
1045
|
-
(it) => (it.stroke ?? "#2563eb") === (first.stroke ?? "#2563eb")
|
|
1046
|
-
);
|
|
1047
|
-
const allSameWidth = stylable.every(
|
|
1048
|
-
(it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
|
|
1049
|
-
);
|
|
1050
|
-
const stroke = first.stroke ?? "#2563eb";
|
|
1051
|
-
const strokeWidth = first.strokeWidth ?? 2;
|
|
1052
|
-
const hex = normalizeHex(stroke);
|
|
1053
|
-
const markers = stylable.filter((it) => it.toolKind === "marker");
|
|
1054
|
-
const showMarkerOpacity = markers.length > 0;
|
|
1055
|
-
const firstMarker = markers[0];
|
|
1056
|
-
const allSameMarkerOpacity = markers.length > 0 && markers.every(
|
|
1057
|
-
(it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
|
|
1058
|
-
);
|
|
1059
|
-
const opacityPct = firstMarker ? Math.round((firstMarker.strokeOpacity ?? 1) * 100) : 100;
|
|
1060
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1061
|
-
"section",
|
|
1062
|
-
{
|
|
1063
|
-
"data-slot": "vector-selection-inspector",
|
|
1064
|
-
"data-position": position,
|
|
1065
|
-
className,
|
|
1066
|
-
"aria-label": "Estilo da sele\xE7\xE3o",
|
|
1067
|
-
style: shell,
|
|
1068
|
-
children: [
|
|
1069
|
-
stylable.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
|
|
1070
|
-
stylable.length,
|
|
1071
|
-
" objetos selecionados"
|
|
1072
|
-
] }),
|
|
1073
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1074
|
-
"Cor",
|
|
1075
|
-
!allSameStroke && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: [
|
|
1076
|
-
" ",
|
|
1077
|
-
"(valores misturados \u2014 novo valor aplica a todos)"
|
|
1078
|
-
] }),
|
|
1079
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1080
|
-
"input",
|
|
1081
|
-
{
|
|
1082
|
-
type: "color",
|
|
1083
|
-
value: hex,
|
|
1084
|
-
onChange: (e) => onChange({
|
|
1085
|
-
stroke: e.target.value,
|
|
1086
|
-
strokeWidth
|
|
1087
|
-
}),
|
|
1088
|
-
style: {
|
|
1089
|
-
width: "100%",
|
|
1090
|
-
height: 32,
|
|
1091
|
-
padding: 0,
|
|
1092
|
-
border: "none",
|
|
1093
|
-
cursor: "pointer",
|
|
1094
|
-
background: "transparent"
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
)
|
|
1098
|
-
] }),
|
|
1099
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1100
|
-
"Grossura",
|
|
1101
|
-
!allSameWidth && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1102
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1103
|
-
"input",
|
|
1104
|
-
{
|
|
1105
|
-
type: "range",
|
|
1106
|
-
min: 1,
|
|
1107
|
-
max: 48,
|
|
1108
|
-
value: strokeWidth,
|
|
1109
|
-
onChange: (e) => onChange({
|
|
1110
|
-
stroke: hex,
|
|
1111
|
-
strokeWidth: Number(e.target.value)
|
|
1112
|
-
})
|
|
1113
|
-
}
|
|
1114
|
-
),
|
|
1115
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1116
|
-
strokeWidth,
|
|
1117
|
-
"px"
|
|
1118
|
-
] })
|
|
1119
|
-
] }),
|
|
1120
|
-
showMarkerOpacity && firstMarker && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1121
|
-
"Opacidade (marcador)",
|
|
1122
|
-
!allSameMarkerOpacity && markers.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1123
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1124
|
-
"input",
|
|
1125
|
-
{
|
|
1126
|
-
type: "range",
|
|
1127
|
-
min: 10,
|
|
1128
|
-
max: 100,
|
|
1129
|
-
value: opacityPct,
|
|
1130
|
-
onChange: (e) => {
|
|
1131
|
-
const v = Number(e.target.value) / 100;
|
|
1132
|
-
onChange({
|
|
1133
|
-
stroke: hex,
|
|
1134
|
-
strokeWidth,
|
|
1135
|
-
strokeOpacity: v
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
),
|
|
1140
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1141
|
-
opacityPct,
|
|
1142
|
-
"%"
|
|
1143
|
-
] })
|
|
1144
|
-
] })
|
|
1145
|
-
]
|
|
1146
|
-
}
|
|
866
|
+
const thumbBlob = await canvasToBlob(
|
|
867
|
+
thumbCanvas,
|
|
868
|
+
file.type === "image/jpeg" || file.type === "image/jpg" ? "image/jpeg" : "image/png",
|
|
869
|
+
file.type.startsWith("image/jpeg") ? 0.85 : void 0
|
|
1147
870
|
);
|
|
871
|
+
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
872
|
+
return { blobId, thumbnailBlobId, width, height };
|
|
1148
873
|
}
|
|
1149
|
-
function
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
...base2,
|
|
1173
|
-
bottom: inset,
|
|
1174
|
-
left: "50%",
|
|
1175
|
-
transform: "translateX(-50%)"
|
|
1176
|
-
};
|
|
1177
|
-
case "bottom-right":
|
|
1178
|
-
case "right-bottom":
|
|
1179
|
-
return { ...base2, bottom: inset, right: inset };
|
|
1180
|
-
case "left-center":
|
|
1181
|
-
return {
|
|
1182
|
-
...base2,
|
|
1183
|
-
left: inset,
|
|
1184
|
-
top: "50%",
|
|
1185
|
-
transform: "translateY(-50%)"
|
|
1186
|
-
};
|
|
1187
|
-
case "right-center":
|
|
1188
|
-
return {
|
|
1189
|
-
...base2,
|
|
1190
|
-
right: inset,
|
|
1191
|
-
top: "50%",
|
|
1192
|
-
transform: "translateY(-50%)"
|
|
1193
|
-
};
|
|
1194
|
-
case "center":
|
|
1195
|
-
return {
|
|
1196
|
-
...base2,
|
|
1197
|
-
top: "50%",
|
|
1198
|
-
left: "50%",
|
|
1199
|
-
transform: "translate(-50%, -50%)"
|
|
1200
|
-
};
|
|
1201
|
-
default:
|
|
1202
|
-
return base2;
|
|
874
|
+
async function createBlobUrlFromStore(store, blobId) {
|
|
875
|
+
const blob = await store.getOriginal(blobId);
|
|
876
|
+
if (!blob) return null;
|
|
877
|
+
return URL.createObjectURL(blob);
|
|
878
|
+
}
|
|
879
|
+
async function createThumbnailBlobUrlFromStore(store, thumbnailBlobId) {
|
|
880
|
+
const blob = await store.getThumbnail(thumbnailBlobId);
|
|
881
|
+
if (!blob) return null;
|
|
882
|
+
return URL.createObjectURL(blob);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/image/pdf-loader.ts
|
|
886
|
+
var pdfjsPromise = null;
|
|
887
|
+
function getPdfJs() {
|
|
888
|
+
if (!pdfjsPromise) {
|
|
889
|
+
pdfjsPromise = import('pdfjs-dist').then((mod) => {
|
|
890
|
+
const workerSrc = new URL(
|
|
891
|
+
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
892
|
+
(typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('react.cjs', document.baseURI).href))
|
|
893
|
+
).toString();
|
|
894
|
+
mod.GlobalWorkerOptions.workerSrc = workerSrc;
|
|
895
|
+
return mod;
|
|
896
|
+
});
|
|
1203
897
|
}
|
|
898
|
+
return pdfjsPromise;
|
|
1204
899
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
background: "#fff",
|
|
1221
|
-
fontSize: "0.875rem"
|
|
1222
|
-
};
|
|
1223
|
-
var bodyStyle = {
|
|
1224
|
-
flex: 1,
|
|
1225
|
-
minHeight: 0,
|
|
1226
|
-
display: "flex",
|
|
1227
|
-
flexDirection: "column"
|
|
1228
|
-
};
|
|
1229
|
-
var mainStyle = {
|
|
1230
|
-
flex: 1,
|
|
1231
|
-
minHeight: 0,
|
|
1232
|
-
position: "relative",
|
|
1233
|
-
display: "flex",
|
|
1234
|
-
flexDirection: "column"
|
|
1235
|
-
};
|
|
1236
|
-
var viewportSurfaceStyle = {
|
|
1237
|
-
flex: 1,
|
|
1238
|
-
minHeight: 0,
|
|
1239
|
-
position: "relative",
|
|
1240
|
-
width: "100%",
|
|
1241
|
-
alignSelf: "stretch",
|
|
1242
|
-
background: "#fff",
|
|
1243
|
-
touchAction: "none"
|
|
1244
|
-
};
|
|
1245
|
-
function mergeStyle(base2, style) {
|
|
1246
|
-
return style ? { ...base2, ...style } : base2;
|
|
900
|
+
async function renderPageToCanvas(page, scale) {
|
|
901
|
+
const raw = page.getViewport({ scale: 1 });
|
|
902
|
+
const adjustedScale = Math.round(raw.width * scale) / raw.width;
|
|
903
|
+
const viewport = page.getViewport({ scale: adjustedScale });
|
|
904
|
+
const w = Math.round(viewport.width);
|
|
905
|
+
const h = Math.round(viewport.height);
|
|
906
|
+
const canvas = document.createElement("canvas");
|
|
907
|
+
canvas.width = w;
|
|
908
|
+
canvas.height = h;
|
|
909
|
+
const ctx = canvas.getContext("2d");
|
|
910
|
+
if (!ctx) throw new Error("Canvas 2D context unavailable");
|
|
911
|
+
ctx.imageSmoothingEnabled = true;
|
|
912
|
+
ctx.imageSmoothingQuality = "high";
|
|
913
|
+
await page.render({ canvas, viewport }).promise;
|
|
914
|
+
return { canvas, width: w, height: h };
|
|
1247
915
|
}
|
|
1248
|
-
function
|
|
1249
|
-
return {
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
916
|
+
function canvasToBlob2(canvas, mime, quality) {
|
|
917
|
+
return new Promise((resolve, reject) => {
|
|
918
|
+
canvas.toBlob(
|
|
919
|
+
(blob) => {
|
|
920
|
+
if (!blob) {
|
|
921
|
+
reject(new Error("Could not encode blob"));
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
resolve(blob);
|
|
925
|
+
},
|
|
926
|
+
mime,
|
|
927
|
+
quality
|
|
928
|
+
);
|
|
929
|
+
});
|
|
1253
930
|
}
|
|
1254
|
-
function
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
})
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
931
|
+
async function loadPdfToStore(file, store, options) {
|
|
932
|
+
const scale = options?.scale ?? 1.5;
|
|
933
|
+
const pdfjs = await getPdfJs();
|
|
934
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
935
|
+
const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
|
|
936
|
+
const results = [];
|
|
937
|
+
for (let i = 1; i <= pdf.numPages; i++) {
|
|
938
|
+
const page = await pdf.getPage(i);
|
|
939
|
+
const { canvas, width, height } = await renderPageToCanvas(page, scale);
|
|
940
|
+
const mime = "image/png";
|
|
941
|
+
const pageBlob = await canvasToBlob2(canvas, mime);
|
|
942
|
+
const blobId = await store.storeOriginal(pageBlob);
|
|
943
|
+
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
944
|
+
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
945
|
+
const th = Math.max(1, Math.round(height * thumbScale));
|
|
946
|
+
const thumbCanvas = document.createElement("canvas");
|
|
947
|
+
thumbCanvas.width = tw;
|
|
948
|
+
thumbCanvas.height = th;
|
|
949
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
950
|
+
if (tCtx) {
|
|
951
|
+
tCtx.imageSmoothingEnabled = true;
|
|
952
|
+
tCtx.imageSmoothingQuality = "high";
|
|
953
|
+
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
1266
954
|
}
|
|
1267
|
-
|
|
955
|
+
const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
|
|
956
|
+
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
957
|
+
results.push({ blobId, thumbnailBlobId, width, height, pageNumber: i });
|
|
958
|
+
}
|
|
959
|
+
return results;
|
|
1268
960
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
961
|
+
|
|
962
|
+
// src/react/asset-ingestion.ts
|
|
963
|
+
init_shape_builders();
|
|
964
|
+
|
|
965
|
+
// src/react/asset-store.ts
|
|
966
|
+
function applyAssetUploadResultToItem(item, result) {
|
|
967
|
+
if (!result?.pluginData) return item;
|
|
968
|
+
return {
|
|
969
|
+
...item,
|
|
970
|
+
pluginData: {
|
|
971
|
+
...item.pluginData ?? {},
|
|
972
|
+
...result.pluginData
|
|
1281
973
|
}
|
|
1282
|
-
|
|
974
|
+
};
|
|
1283
975
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
return
|
|
1290
|
-
"div",
|
|
1291
|
-
{
|
|
1292
|
-
"data-slot": "vector-canvas-body",
|
|
1293
|
-
className,
|
|
1294
|
-
style: mergeStyle(bodyStyle, style),
|
|
1295
|
-
children
|
|
1296
|
-
}
|
|
1297
|
-
);
|
|
976
|
+
|
|
977
|
+
// src/react/asset-ingestion.ts
|
|
978
|
+
function getAssetKindForFile(file) {
|
|
979
|
+
if (file.type === "application/pdf") return "pdf";
|
|
980
|
+
if (file.type.startsWith("image/")) return "image";
|
|
981
|
+
return null;
|
|
1298
982
|
}
|
|
1299
|
-
function
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
style
|
|
1303
|
-
}) {
|
|
1304
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1305
|
-
"div",
|
|
1306
|
-
{
|
|
1307
|
-
"data-slot": "vector-canvas-main",
|
|
1308
|
-
className,
|
|
1309
|
-
style: mergeStyle(mainStyle, style),
|
|
1310
|
-
children
|
|
1311
|
-
}
|
|
1312
|
-
);
|
|
983
|
+
function finalizeIngestedItem(item, context, uploadResult, decorateItem) {
|
|
984
|
+
const itemWithAssetData = applyAssetUploadResultToItem(item, uploadResult);
|
|
985
|
+
return decorateItem ? decorateItem(itemWithAssetData, context) : itemWithAssetData;
|
|
1313
986
|
}
|
|
1314
|
-
function
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
987
|
+
async function uploadAssetIfNeeded(assetStore, file, kind) {
|
|
988
|
+
if (!assetStore) return null;
|
|
989
|
+
const result = await assetStore.upload({ file, kind });
|
|
990
|
+
return result ?? null;
|
|
991
|
+
}
|
|
992
|
+
async function ingestAssetFilesToSceneItems(options) {
|
|
993
|
+
const {
|
|
994
|
+
files,
|
|
995
|
+
worldCenter,
|
|
996
|
+
assetStore,
|
|
997
|
+
imageStore = new IndexedDbImageStore(),
|
|
998
|
+
createId = createShapeId,
|
|
999
|
+
gapWorld = 16,
|
|
1000
|
+
stepWorld = 48,
|
|
1001
|
+
pdfScale = 1.5,
|
|
1002
|
+
decorateItem,
|
|
1003
|
+
onError
|
|
1004
|
+
} = options;
|
|
1005
|
+
const items = [];
|
|
1006
|
+
const errors = [];
|
|
1007
|
+
let imagePlacementIndex = 0;
|
|
1008
|
+
let occupiedBottomY = null;
|
|
1009
|
+
let imageYOffsetAdjustment = 0;
|
|
1010
|
+
let hasImagePlacementBase = false;
|
|
1011
|
+
for (const file of files) {
|
|
1012
|
+
const kind = getAssetKindForFile(file);
|
|
1013
|
+
if (!kind) {
|
|
1014
|
+
const error = {
|
|
1015
|
+
file,
|
|
1016
|
+
error: new Error(`Unsupported asset type: ${file.type || "unknown"}`)
|
|
1017
|
+
};
|
|
1018
|
+
errors.push(error);
|
|
1019
|
+
onError?.(error);
|
|
1020
|
+
continue;
|
|
1326
1021
|
}
|
|
1327
|
-
|
|
1022
|
+
try {
|
|
1023
|
+
const uploadResult = await uploadAssetIfNeeded(assetStore, file, kind);
|
|
1024
|
+
if (kind === "pdf") {
|
|
1025
|
+
const pages = await loadPdfToStore(file, imageStore, { scale: pdfScale });
|
|
1026
|
+
for (const page of pages) {
|
|
1027
|
+
const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
|
|
1028
|
+
const naturalTopY2 = worldCenter.y - page.height / 2;
|
|
1029
|
+
const stackedTopY = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
|
|
1030
|
+
const bounds2 = {
|
|
1031
|
+
x: worldCenter.x - page.width / 2,
|
|
1032
|
+
y: Math.max(naturalTopY2, stackedTopY),
|
|
1033
|
+
width: page.width,
|
|
1034
|
+
height: page.height
|
|
1035
|
+
};
|
|
1036
|
+
const itemContext2 = {
|
|
1037
|
+
file,
|
|
1038
|
+
kind,
|
|
1039
|
+
itemIndex: items.length,
|
|
1040
|
+
pageNumber: page.pageNumber
|
|
1041
|
+
};
|
|
1042
|
+
const item2 = finalizeIngestedItem(
|
|
1043
|
+
{
|
|
1044
|
+
id: createId(),
|
|
1045
|
+
x: bounds2.x,
|
|
1046
|
+
y: bounds2.y,
|
|
1047
|
+
bounds: { ...bounds2 },
|
|
1048
|
+
toolKind: "image",
|
|
1049
|
+
imageBlobId: page.blobId,
|
|
1050
|
+
imageRasterHref: fullUrl2 ?? void 0,
|
|
1051
|
+
imageIntrinsicSize: {
|
|
1052
|
+
width: page.width,
|
|
1053
|
+
height: page.height
|
|
1054
|
+
},
|
|
1055
|
+
childrenSvg: fullUrl2 ? buildRasterImageChildrenSvg(
|
|
1056
|
+
fullUrl2,
|
|
1057
|
+
{ width: page.width, height: page.height },
|
|
1058
|
+
bounds2
|
|
1059
|
+
) : ""
|
|
1060
|
+
},
|
|
1061
|
+
itemContext2,
|
|
1062
|
+
uploadResult,
|
|
1063
|
+
decorateItem
|
|
1064
|
+
);
|
|
1065
|
+
items.push(item2);
|
|
1066
|
+
occupiedBottomY = bounds2.y + page.height;
|
|
1067
|
+
}
|
|
1068
|
+
hasImagePlacementBase = false;
|
|
1069
|
+
imagePlacementIndex = 0;
|
|
1070
|
+
imageYOffsetAdjustment = 0;
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
const { blobId, thumbnailBlobId, width, height } = await loadImageToStore(
|
|
1074
|
+
file,
|
|
1075
|
+
imageStore
|
|
1076
|
+
);
|
|
1077
|
+
const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
|
|
1078
|
+
const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
|
|
1079
|
+
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
1080
|
+
const ox = imagePlacementIndex % 8 * stepWorld;
|
|
1081
|
+
const oy = Math.floor(imagePlacementIndex / 8) * stepWorld;
|
|
1082
|
+
const naturalTopY = worldCenter.y - height / 2 + oy;
|
|
1083
|
+
if (!hasImagePlacementBase) {
|
|
1084
|
+
const minimumTopY = occupiedBottomY == null ? naturalTopY : occupiedBottomY + gapWorld;
|
|
1085
|
+
imageYOffsetAdjustment = Math.max(0, minimumTopY - naturalTopY);
|
|
1086
|
+
hasImagePlacementBase = true;
|
|
1087
|
+
}
|
|
1088
|
+
const bounds = {
|
|
1089
|
+
x: worldCenter.x - width / 2 + ox,
|
|
1090
|
+
y: naturalTopY + imageYOffsetAdjustment,
|
|
1091
|
+
width,
|
|
1092
|
+
height
|
|
1093
|
+
};
|
|
1094
|
+
const href = thumbnailHref ?? fullUrl ?? "";
|
|
1095
|
+
const itemContext = {
|
|
1096
|
+
file,
|
|
1097
|
+
kind,
|
|
1098
|
+
itemIndex: items.length
|
|
1099
|
+
};
|
|
1100
|
+
const item = finalizeIngestedItem(
|
|
1101
|
+
{
|
|
1102
|
+
id: createId(),
|
|
1103
|
+
x: bounds.x,
|
|
1104
|
+
y: bounds.y,
|
|
1105
|
+
bounds: { ...bounds },
|
|
1106
|
+
toolKind: "image",
|
|
1107
|
+
imageBlobId: blobId,
|
|
1108
|
+
imageThumbnailBlobId: thumbnailBlobId,
|
|
1109
|
+
imageRasterHref: fullUrl ?? void 0,
|
|
1110
|
+
imageThumbnailHref: thumbnailHref ?? void 0,
|
|
1111
|
+
imageIntrinsicSize: { width, height },
|
|
1112
|
+
childrenSvg: buildRasterImageChildrenSvg(href, { width, height }, bounds)
|
|
1113
|
+
},
|
|
1114
|
+
itemContext,
|
|
1115
|
+
uploadResult,
|
|
1116
|
+
decorateItem
|
|
1117
|
+
);
|
|
1118
|
+
items.push(item);
|
|
1119
|
+
imagePlacementIndex++;
|
|
1120
|
+
occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
const fileError = {
|
|
1123
|
+
file,
|
|
1124
|
+
kind,
|
|
1125
|
+
error
|
|
1126
|
+
};
|
|
1127
|
+
errors.push(fileError);
|
|
1128
|
+
onError?.(fileError);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return {
|
|
1132
|
+
items,
|
|
1133
|
+
errors
|
|
1134
|
+
};
|
|
1328
1135
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1136
|
+
var CanvuChromeContext = react.createContext(
|
|
1137
|
+
null
|
|
1138
|
+
);
|
|
1139
|
+
function useCanvuChromeContext() {
|
|
1140
|
+
return react.useContext(CanvuChromeContext);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// src/math/item-transform.ts
|
|
1144
|
+
init_rect();
|
|
1145
|
+
function getItemRotationRad(item) {
|
|
1146
|
+
return item.rotation ?? 0;
|
|
1147
|
+
}
|
|
1148
|
+
function itemLocalToWorld(lx, ly, itemX, itemY, w, h, rotationRad) {
|
|
1149
|
+
const c = { x: w / 2, y: h / 2 };
|
|
1150
|
+
const dlx = lx - c.x;
|
|
1151
|
+
const dly = ly - c.y;
|
|
1152
|
+
const cos = Math.cos(rotationRad);
|
|
1153
|
+
const sin = Math.sin(rotationRad);
|
|
1154
|
+
return {
|
|
1155
|
+
x: itemX + c.x + cos * dlx - sin * dly,
|
|
1156
|
+
y: itemY + c.y + sin * dlx + cos * dly
|
|
1344
1157
|
};
|
|
1345
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1346
|
-
"div",
|
|
1347
|
-
{
|
|
1348
|
-
"data-slot": "vector-canvas-toolbar",
|
|
1349
|
-
"data-position": position,
|
|
1350
|
-
className,
|
|
1351
|
-
style: mergeStyle(base2, style),
|
|
1352
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { pointerEvents: "auto" }, children })
|
|
1353
|
-
}
|
|
1354
|
-
);
|
|
1355
1158
|
}
|
|
1356
|
-
function
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
{
|
|
1368
|
-
"data-slot": `vector-canvas-space-${position}`,
|
|
1369
|
-
className,
|
|
1370
|
-
style: mergeStyle(vectorCanvasSpaceStyle(position, inset, zIndex), style),
|
|
1371
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { pointerEvents: contentPointerEvents }, children })
|
|
1372
|
-
}
|
|
1373
|
-
);
|
|
1159
|
+
function worldToItemLocal(wx, wy, itemX, itemY, w, h, rotationRad) {
|
|
1160
|
+
const c = { x: w / 2, y: h / 2 };
|
|
1161
|
+
const vx = wx - itemX;
|
|
1162
|
+
const vy = wy - itemY;
|
|
1163
|
+
const dx = vx - c.x;
|
|
1164
|
+
const dy = vy - c.y;
|
|
1165
|
+
const cos = Math.cos(-rotationRad);
|
|
1166
|
+
const sin = Math.sin(-rotationRad);
|
|
1167
|
+
const lx = cos * dx - sin * dy;
|
|
1168
|
+
const ly = sin * dx + cos * dy;
|
|
1169
|
+
return { x: c.x + lx, y: c.y + ly };
|
|
1374
1170
|
}
|
|
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
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
WebkitTapHighlightColor: "transparent"
|
|
1412
|
-
};
|
|
1413
|
-
var chevronBtnStyle = {
|
|
1414
|
-
pointerEvents: "auto",
|
|
1415
|
-
width: 24,
|
|
1416
|
-
height: 36,
|
|
1417
|
-
display: "inline-flex",
|
|
1418
|
-
alignItems: "center",
|
|
1419
|
-
justifyContent: "center",
|
|
1420
|
-
cursor: "pointer",
|
|
1421
|
-
fontSize: 12,
|
|
1422
|
-
lineHeight: 1,
|
|
1423
|
-
color: "#52525b",
|
|
1424
|
-
padding: 0,
|
|
1425
|
-
border: "none",
|
|
1426
|
-
outline: "none",
|
|
1427
|
-
background: "none",
|
|
1428
|
-
WebkitTapHighlightColor: "transparent"
|
|
1429
|
-
};
|
|
1430
|
-
var undoBtnStyle = {
|
|
1431
|
-
...chevronBtnStyle,
|
|
1432
|
-
width: 30,
|
|
1433
|
-
opacity: 1,
|
|
1434
|
-
color: "#18181b"
|
|
1435
|
-
};
|
|
1436
|
-
var labelStyle2 = {
|
|
1437
|
-
fontSize: 10,
|
|
1438
|
-
fontWeight: 600,
|
|
1439
|
-
color: "#52525b",
|
|
1440
|
-
textAlign: "center",
|
|
1441
|
-
userSelect: "none",
|
|
1442
|
-
padding: "2px 0"
|
|
1443
|
-
};
|
|
1444
|
-
var panelLayoutStyle = {
|
|
1171
|
+
function itemPivotWorld(item) {
|
|
1172
|
+
const r = normalizeRect(item.bounds);
|
|
1173
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
1174
|
+
}
|
|
1175
|
+
function boundsAabbForRotatedItem(item) {
|
|
1176
|
+
const rot = getItemRotationRad(item);
|
|
1177
|
+
if (Math.abs(rot) < 1e-12 && item.bounds.width >= 0 && item.bounds.height >= 0) {
|
|
1178
|
+
return item.bounds;
|
|
1179
|
+
}
|
|
1180
|
+
const r = normalizeRect(item.bounds);
|
|
1181
|
+
if (Math.abs(rot) < 1e-12) {
|
|
1182
|
+
return r;
|
|
1183
|
+
}
|
|
1184
|
+
const corners = [
|
|
1185
|
+
[0, 0],
|
|
1186
|
+
[r.width, 0],
|
|
1187
|
+
[r.width, r.height],
|
|
1188
|
+
[0, r.height]
|
|
1189
|
+
];
|
|
1190
|
+
let minX = Infinity;
|
|
1191
|
+
let minY = Infinity;
|
|
1192
|
+
let maxX = -Infinity;
|
|
1193
|
+
let maxY = -Infinity;
|
|
1194
|
+
for (const [lx, ly] of corners) {
|
|
1195
|
+
const p = itemLocalToWorld(lx, ly, item.x, item.y, r.width, r.height, rot);
|
|
1196
|
+
minX = Math.min(minX, p.x);
|
|
1197
|
+
minY = Math.min(minY, p.y);
|
|
1198
|
+
maxX = Math.max(maxX, p.x);
|
|
1199
|
+
maxY = Math.max(maxY, p.y);
|
|
1200
|
+
}
|
|
1201
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// src/react/NavMenu.tsx
|
|
1205
|
+
init_rect();
|
|
1206
|
+
var shellLook = {
|
|
1445
1207
|
display: "flex",
|
|
1446
1208
|
flexDirection: "column",
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1209
|
+
gap: 8,
|
|
1210
|
+
minWidth: 160,
|
|
1211
|
+
padding: "10px 12px",
|
|
1212
|
+
borderRadius: 8,
|
|
1213
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
1214
|
+
background: "rgba(255,255,255,0.96)",
|
|
1215
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1216
|
+
pointerEvents: "auto"
|
|
1452
1217
|
};
|
|
1453
|
-
var
|
|
1454
|
-
pointerEvents: "auto",
|
|
1218
|
+
var labelStyle = {
|
|
1455
1219
|
display: "flex",
|
|
1456
|
-
flexDirection: "
|
|
1457
|
-
alignItems: "center",
|
|
1220
|
+
flexDirection: "column",
|
|
1458
1221
|
gap: 4,
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1463
|
-
padding: 4
|
|
1222
|
+
fontSize: 11,
|
|
1223
|
+
fontWeight: 600,
|
|
1224
|
+
color: "#52525b"
|
|
1464
1225
|
};
|
|
1465
|
-
function
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1226
|
+
function normalizeHex(stroke) {
|
|
1227
|
+
if (/^#[0-9A-Fa-f]{6}$/.test(stroke)) return stroke;
|
|
1228
|
+
return "#2563eb";
|
|
1229
|
+
}
|
|
1230
|
+
function isStylableKind(tk) {
|
|
1231
|
+
return tk === "rect" || tk === "ellipse" || tk === "line" || tk === "arrow" || tk === "draw" || tk === "pencil" || tk === "brush" || tk === "marker" || tk === "text";
|
|
1232
|
+
}
|
|
1233
|
+
function VectorSelectionInspector({
|
|
1469
1234
|
items: itemsProp,
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
onUndo: onUndoProp,
|
|
1474
|
-
onRedo: onRedoProp,
|
|
1475
|
-
canUndo: canUndoProp,
|
|
1476
|
-
canRedo: canRedoProp,
|
|
1477
|
-
onRequestRender: onRequestRenderProp,
|
|
1478
|
-
position = "bottom-left",
|
|
1235
|
+
activeToolStyle: activeToolStyleProp,
|
|
1236
|
+
onChange: onChangeProp,
|
|
1237
|
+
position = "top-left",
|
|
1479
1238
|
inset = 12,
|
|
1480
|
-
zIndex =
|
|
1239
|
+
zIndex = 24,
|
|
1481
1240
|
className,
|
|
1482
1241
|
style
|
|
1483
1242
|
}) {
|
|
1484
1243
|
const ctx = useCanvuChromeContext();
|
|
1485
|
-
const
|
|
1486
|
-
const
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
const onRedo = onRedoProp ?? ctx?.onRedo ?? noop;
|
|
1494
|
-
const canUndo = canUndoProp ?? ctx?.canUndo ?? false;
|
|
1495
|
-
const canRedo = canRedoProp ?? ctx?.canRedo ?? false;
|
|
1496
|
-
const onRequestRender = onRequestRenderProp ?? ctx?.onRequestRender ?? noop;
|
|
1497
|
-
const [expanded, setExpanded] = react.useState(false);
|
|
1498
|
-
const svgRef = react.useRef(null);
|
|
1499
|
-
const [dragging, setDragging] = react.useState(false);
|
|
1500
|
-
const [mouseDown, setMouseDown] = react.useState(false);
|
|
1501
|
-
const worldBounds = computeWorldBounds(items);
|
|
1502
|
-
const isEmpty = worldBounds.width <= 0 || worldBounds.height <= 0;
|
|
1503
|
-
const scale = Math.min(
|
|
1504
|
-
(MINIMAP_W - PADDING * 2) / Math.max(worldBounds.width, 1),
|
|
1505
|
-
(MINIMAP_H - PADDING * 2) / Math.max(worldBounds.height, 1)
|
|
1506
|
-
);
|
|
1507
|
-
const originX = worldBounds.x;
|
|
1508
|
-
const originY = worldBounds.y;
|
|
1509
|
-
const toMinimapX = (wx) => (wx - originX) * scale + PADDING;
|
|
1510
|
-
const toMinimapY = (wy) => (wy - originY) * scale + PADDING;
|
|
1511
|
-
const toMinimapW = (ww) => ww * scale;
|
|
1512
|
-
const toMinimapH = (wh) => wh * scale;
|
|
1513
|
-
const viewportWorld = camera ? camera.getVisibleWorldRect(viewportWidth, viewportHeight) : { x: 0, y: 0, width: 0, height: 0 };
|
|
1514
|
-
const vpMinimap = {
|
|
1515
|
-
x: toMinimapX(viewportWorld.x),
|
|
1516
|
-
y: toMinimapY(viewportWorld.y),
|
|
1517
|
-
width: toMinimapW(viewportWorld.width),
|
|
1518
|
-
height: toMinimapH(viewportWorld.height)
|
|
1244
|
+
const items = itemsProp ?? ctx?.selectedItems ?? [];
|
|
1245
|
+
const activeToolStyle = activeToolStyleProp === void 0 ? ctx?.activeToolStyle ?? null : activeToolStyleProp;
|
|
1246
|
+
const onChange = onChangeProp ?? ctx?.onSelectionStyleChange ?? null;
|
|
1247
|
+
if (!onChange) return null;
|
|
1248
|
+
const shell = {
|
|
1249
|
+
...getBoardPositionStyle(position, inset, zIndex),
|
|
1250
|
+
...shellLook,
|
|
1251
|
+
...style
|
|
1519
1252
|
};
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1253
|
+
if (activeToolStyle) {
|
|
1254
|
+
const stroke2 = activeToolStyle.stroke;
|
|
1255
|
+
const strokeWidth2 = activeToolStyle.strokeWidth;
|
|
1256
|
+
const hex2 = normalizeHex(stroke2);
|
|
1257
|
+
const showMarkerOpacity2 = activeToolStyle.toolKind === "marker";
|
|
1258
|
+
const opacityPct2 = Math.round((activeToolStyle.strokeOpacity ?? 1) * 100);
|
|
1259
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1260
|
+
"section",
|
|
1261
|
+
{
|
|
1262
|
+
"data-slot": "vector-selection-inspector",
|
|
1263
|
+
"data-position": position,
|
|
1264
|
+
className,
|
|
1265
|
+
"aria-label": activeToolStyle.label ?? "Estilo da ferramenta",
|
|
1266
|
+
style: shell,
|
|
1267
|
+
children: [
|
|
1268
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1269
|
+
"Cor",
|
|
1270
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1271
|
+
"input",
|
|
1272
|
+
{
|
|
1273
|
+
type: "color",
|
|
1274
|
+
value: hex2,
|
|
1275
|
+
onChange: (e) => onChange({
|
|
1276
|
+
stroke: e.target.value,
|
|
1277
|
+
strokeWidth: strokeWidth2,
|
|
1278
|
+
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
1279
|
+
}),
|
|
1280
|
+
style: {
|
|
1281
|
+
width: "100%",
|
|
1282
|
+
height: 32,
|
|
1283
|
+
padding: 0,
|
|
1284
|
+
border: "none",
|
|
1285
|
+
cursor: "pointer",
|
|
1286
|
+
background: "transparent"
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
)
|
|
1290
|
+
] }),
|
|
1291
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1292
|
+
"Grossura",
|
|
1293
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1294
|
+
"input",
|
|
1295
|
+
{
|
|
1296
|
+
type: "range",
|
|
1297
|
+
min: 1,
|
|
1298
|
+
max: 48,
|
|
1299
|
+
value: strokeWidth2,
|
|
1300
|
+
onChange: (e) => onChange({
|
|
1301
|
+
stroke: hex2,
|
|
1302
|
+
strokeWidth: Number(e.target.value),
|
|
1303
|
+
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
1304
|
+
})
|
|
1305
|
+
}
|
|
1306
|
+
),
|
|
1307
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1308
|
+
strokeWidth2,
|
|
1309
|
+
"px"
|
|
1310
|
+
] })
|
|
1311
|
+
] }),
|
|
1312
|
+
showMarkerOpacity2 && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1313
|
+
"Opacidade (marcador)",
|
|
1314
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1315
|
+
"input",
|
|
1316
|
+
{
|
|
1317
|
+
type: "range",
|
|
1318
|
+
min: 10,
|
|
1319
|
+
max: 100,
|
|
1320
|
+
value: opacityPct2,
|
|
1321
|
+
onChange: (e) => {
|
|
1322
|
+
const v = Number(e.target.value) / 100;
|
|
1323
|
+
onChange({
|
|
1324
|
+
stroke: hex2,
|
|
1325
|
+
strokeWidth: strokeWidth2,
|
|
1326
|
+
strokeOpacity: v
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
),
|
|
1331
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1332
|
+
opacityPct2,
|
|
1333
|
+
"%"
|
|
1334
|
+
] })
|
|
1335
|
+
] })
|
|
1336
|
+
]
|
|
1337
|
+
}
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
const stylable = items.filter(
|
|
1341
|
+
(it) => !it.locked && it.toolKind && isStylableKind(it.toolKind)
|
|
1526
1342
|
);
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
camera.y = viewportHeight / 2 - worldY * camera.zoom;
|
|
1533
|
-
onRequestRender();
|
|
1534
|
-
},
|
|
1535
|
-
[worldFromMinimap, camera, viewportWidth, viewportHeight, onRequestRender]
|
|
1343
|
+
if (stylable.length === 0) return null;
|
|
1344
|
+
const first = stylable[0];
|
|
1345
|
+
if (!first) return null;
|
|
1346
|
+
const allSameStroke = stylable.every(
|
|
1347
|
+
(it) => (it.stroke ?? "#2563eb") === (first.stroke ?? "#2563eb")
|
|
1536
1348
|
);
|
|
1537
|
-
const
|
|
1538
|
-
(
|
|
1539
|
-
if (isEmpty) return;
|
|
1540
|
-
setDragging(true);
|
|
1541
|
-
setMouseDown(true);
|
|
1542
|
-
const rect = svgRef.current?.getBoundingClientRect();
|
|
1543
|
-
if (!rect) return;
|
|
1544
|
-
panTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
1545
|
-
svgRef.current?.setPointerCapture(e.pointerId);
|
|
1546
|
-
},
|
|
1547
|
-
[isEmpty, panTo]
|
|
1349
|
+
const allSameWidth = stylable.every(
|
|
1350
|
+
(it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
|
|
1548
1351
|
);
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1352
|
+
const stroke = first.stroke ?? "#2563eb";
|
|
1353
|
+
const strokeWidth = first.strokeWidth ?? 2;
|
|
1354
|
+
const hex = normalizeHex(stroke);
|
|
1355
|
+
const markers = stylable.filter((it) => it.toolKind === "marker");
|
|
1356
|
+
const showMarkerOpacity = markers.length > 0;
|
|
1357
|
+
const firstMarker = markers[0];
|
|
1358
|
+
const allSameMarkerOpacity = markers.length > 0 && markers.every(
|
|
1359
|
+
(it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
|
|
1557
1360
|
);
|
|
1558
|
-
const
|
|
1559
|
-
setDragging(false);
|
|
1560
|
-
setMouseDown(false);
|
|
1561
|
-
}, []);
|
|
1562
|
-
const toggleExpanded = react.useCallback(() => {
|
|
1563
|
-
setExpanded((v) => !v);
|
|
1564
|
-
}, []);
|
|
1565
|
-
const anchorStyle = getBoardPositionStyle(position, inset, zIndex);
|
|
1361
|
+
const opacityPct = firstMarker ? Math.round((firstMarker.strokeOpacity ?? 1) * 100) : 100;
|
|
1566
1362
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1567
|
-
"
|
|
1363
|
+
"section",
|
|
1568
1364
|
{
|
|
1569
|
-
"data-slot": "
|
|
1365
|
+
"data-slot": "vector-selection-inspector",
|
|
1570
1366
|
"data-position": position,
|
|
1571
1367
|
className,
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
...panelLayoutStyle,
|
|
1575
|
-
border: "none",
|
|
1576
|
-
margin: 0,
|
|
1577
|
-
padding: 0,
|
|
1578
|
-
minWidth: 0,
|
|
1579
|
-
...style
|
|
1580
|
-
},
|
|
1581
|
-
"aria-label": "Zoom and minimap controls",
|
|
1368
|
+
"aria-label": "Estilo da sele\xE7\xE3o",
|
|
1369
|
+
style: shell,
|
|
1582
1370
|
children: [
|
|
1583
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
if (e.pointerType === "mouse") e.preventDefault();
|
|
1593
|
-
},
|
|
1594
|
-
onClick: onZoomOut,
|
|
1595
|
-
children: "\u2212"
|
|
1596
|
-
}
|
|
1597
|
-
),
|
|
1598
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: labelStyle2, "aria-hidden": true, children: [
|
|
1599
|
-
zoomPercent,
|
|
1600
|
-
"%"
|
|
1371
|
+
stylable.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
|
|
1372
|
+
stylable.length,
|
|
1373
|
+
" objetos selecionados"
|
|
1374
|
+
] }),
|
|
1375
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1376
|
+
"Cor",
|
|
1377
|
+
!allSameStroke && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: [
|
|
1378
|
+
" ",
|
|
1379
|
+
"(valores misturados \u2014 novo valor aplica a todos)"
|
|
1601
1380
|
] }),
|
|
1602
1381
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1603
|
-
"
|
|
1604
|
-
{
|
|
1605
|
-
type: "button",
|
|
1606
|
-
style: btnStyle,
|
|
1607
|
-
"aria-label": "Zoom in",
|
|
1608
|
-
title: "Zoom in",
|
|
1609
|
-
onPointerDown: (e) => {
|
|
1610
|
-
if (e.pointerType === "mouse") e.preventDefault();
|
|
1611
|
-
},
|
|
1612
|
-
onClick: onZoomIn,
|
|
1613
|
-
children: "+"
|
|
1614
|
-
}
|
|
1615
|
-
),
|
|
1616
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1617
|
-
"button",
|
|
1382
|
+
"input",
|
|
1618
1383
|
{
|
|
1619
|
-
type: "
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1384
|
+
type: "color",
|
|
1385
|
+
value: hex,
|
|
1386
|
+
onChange: (e) => onChange({
|
|
1387
|
+
stroke: e.target.value,
|
|
1388
|
+
strokeWidth
|
|
1389
|
+
}),
|
|
1390
|
+
style: {
|
|
1391
|
+
width: "100%",
|
|
1392
|
+
height: 32,
|
|
1393
|
+
padding: 0,
|
|
1394
|
+
border: "none",
|
|
1395
|
+
cursor: "pointer",
|
|
1396
|
+
background: "transparent"
|
|
1397
|
+
}
|
|
1629
1398
|
}
|
|
1630
|
-
)
|
|
1399
|
+
)
|
|
1400
|
+
] }),
|
|
1401
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1402
|
+
"Grossura",
|
|
1403
|
+
!allSameWidth && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1631
1404
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1632
|
-
"
|
|
1405
|
+
"input",
|
|
1633
1406
|
{
|
|
1634
|
-
type: "
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
}
|
|
1642
|
-
onClick: onRedo,
|
|
1643
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { size: 16 })
|
|
1407
|
+
type: "range",
|
|
1408
|
+
min: 1,
|
|
1409
|
+
max: 48,
|
|
1410
|
+
value: strokeWidth,
|
|
1411
|
+
onChange: (e) => onChange({
|
|
1412
|
+
stroke: hex,
|
|
1413
|
+
strokeWidth: Number(e.target.value)
|
|
1414
|
+
})
|
|
1644
1415
|
}
|
|
1645
1416
|
),
|
|
1417
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1418
|
+
strokeWidth,
|
|
1419
|
+
"px"
|
|
1420
|
+
] })
|
|
1421
|
+
] }),
|
|
1422
|
+
showMarkerOpacity && firstMarker && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle, children: [
|
|
1423
|
+
"Opacidade (marcador)",
|
|
1424
|
+
!allSameMarkerOpacity && markers.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
|
|
1646
1425
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1647
|
-
"
|
|
1426
|
+
"input",
|
|
1648
1427
|
{
|
|
1649
|
-
type: "
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
height: "12",
|
|
1662
|
-
viewBox: "0 0 12 12",
|
|
1663
|
-
style: {
|
|
1664
|
-
transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
1665
|
-
transition: "transform 0.15s ease"
|
|
1666
|
-
},
|
|
1667
|
-
"aria-hidden": true,
|
|
1668
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1669
|
-
"path",
|
|
1670
|
-
{
|
|
1671
|
-
d: "M2 4 L6 8 L10 4",
|
|
1672
|
-
fill: "none",
|
|
1673
|
-
stroke: "currentColor",
|
|
1674
|
-
strokeWidth: "1.5",
|
|
1675
|
-
strokeLinecap: "round",
|
|
1676
|
-
strokeLinejoin: "round"
|
|
1677
|
-
}
|
|
1678
|
-
)
|
|
1679
|
-
}
|
|
1680
|
-
)
|
|
1428
|
+
type: "range",
|
|
1429
|
+
min: 10,
|
|
1430
|
+
max: 100,
|
|
1431
|
+
value: opacityPct,
|
|
1432
|
+
onChange: (e) => {
|
|
1433
|
+
const v = Number(e.target.value) / 100;
|
|
1434
|
+
onChange({
|
|
1435
|
+
stroke: hex,
|
|
1436
|
+
strokeWidth,
|
|
1437
|
+
strokeOpacity: v
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1681
1440
|
}
|
|
1682
|
-
)
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
style: {
|
|
1689
|
-
width: MINIMAP_W,
|
|
1690
|
-
height: MINIMAP_H,
|
|
1691
|
-
borderRadius: BORDER_RADIUS,
|
|
1692
|
-
border: `1px solid ${BORDER_COLOR}`,
|
|
1693
|
-
background: BG,
|
|
1694
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1695
|
-
cursor: isEmpty ? "default" : dragging ? "grabbing" : "grab",
|
|
1696
|
-
display: "block",
|
|
1697
|
-
pointerEvents: "auto"
|
|
1698
|
-
},
|
|
1699
|
-
onPointerDown: handlePointerDown,
|
|
1700
|
-
onPointerMove: handlePointerMove,
|
|
1701
|
-
onPointerUp: handlePointerUp,
|
|
1702
|
-
onPointerCancel: handlePointerUp,
|
|
1703
|
-
children: isEmpty ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1704
|
-
items.map((it) => {
|
|
1705
|
-
const b = normalizeRect(boundsAabbForRotatedItem(it));
|
|
1706
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1707
|
-
"rect",
|
|
1708
|
-
{
|
|
1709
|
-
x: toMinimapX(b.x),
|
|
1710
|
-
y: toMinimapY(b.y),
|
|
1711
|
-
width: toMinimapW(b.width),
|
|
1712
|
-
height: toMinimapH(b.height),
|
|
1713
|
-
fill: ITEM_FILL,
|
|
1714
|
-
stroke: ITEM_STROKE,
|
|
1715
|
-
strokeWidth: 0.5,
|
|
1716
|
-
rx: 1
|
|
1717
|
-
},
|
|
1718
|
-
it.id
|
|
1719
|
-
);
|
|
1720
|
-
}),
|
|
1721
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1722
|
-
"rect",
|
|
1723
|
-
{
|
|
1724
|
-
x: vpMinimap.x,
|
|
1725
|
-
y: vpMinimap.y,
|
|
1726
|
-
width: vpMinimap.width,
|
|
1727
|
-
height: vpMinimap.height,
|
|
1728
|
-
fill: VIEWPORT_FILL,
|
|
1729
|
-
stroke: VIEWPORT_STROKE,
|
|
1730
|
-
strokeWidth: 1.5,
|
|
1731
|
-
rx: 2
|
|
1732
|
-
}
|
|
1733
|
-
)
|
|
1734
|
-
] })
|
|
1735
|
-
}
|
|
1736
|
-
) })
|
|
1441
|
+
),
|
|
1442
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
1443
|
+
opacityPct,
|
|
1444
|
+
"%"
|
|
1445
|
+
] })
|
|
1446
|
+
] })
|
|
1737
1447
|
]
|
|
1738
1448
|
}
|
|
1739
1449
|
);
|
|
1740
1450
|
}
|
|
1741
|
-
function
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1451
|
+
function getBoardPositionStyle(position, inset = 12, zIndex = 40) {
|
|
1452
|
+
const base2 = { position: "absolute", zIndex };
|
|
1453
|
+
switch (position) {
|
|
1454
|
+
case "fill":
|
|
1455
|
+
return { ...base2, inset };
|
|
1456
|
+
case "top-left":
|
|
1457
|
+
case "left-top":
|
|
1458
|
+
return { ...base2, top: inset, left: inset };
|
|
1459
|
+
case "top-center":
|
|
1460
|
+
return {
|
|
1461
|
+
...base2,
|
|
1462
|
+
top: inset,
|
|
1463
|
+
left: "50%",
|
|
1464
|
+
transform: "translateX(-50%)"
|
|
1465
|
+
};
|
|
1466
|
+
case "top-right":
|
|
1467
|
+
case "right-top":
|
|
1468
|
+
return { ...base2, top: inset, right: inset };
|
|
1469
|
+
case "bottom-left":
|
|
1470
|
+
case "left-bottom":
|
|
1471
|
+
return { ...base2, bottom: inset, left: inset };
|
|
1472
|
+
case "bottom-center":
|
|
1473
|
+
return {
|
|
1474
|
+
...base2,
|
|
1475
|
+
bottom: inset,
|
|
1476
|
+
left: "50%",
|
|
1477
|
+
transform: "translateX(-50%)"
|
|
1478
|
+
};
|
|
1479
|
+
case "bottom-right":
|
|
1480
|
+
case "right-bottom":
|
|
1481
|
+
return { ...base2, bottom: inset, right: inset };
|
|
1482
|
+
case "left-center":
|
|
1483
|
+
return {
|
|
1484
|
+
...base2,
|
|
1485
|
+
left: inset,
|
|
1486
|
+
top: "50%",
|
|
1487
|
+
transform: "translateY(-50%)"
|
|
1488
|
+
};
|
|
1489
|
+
case "right-center":
|
|
1490
|
+
return {
|
|
1491
|
+
...base2,
|
|
1492
|
+
right: inset,
|
|
1493
|
+
top: "50%",
|
|
1494
|
+
transform: "translateY(-50%)"
|
|
1495
|
+
};
|
|
1496
|
+
case "center":
|
|
1497
|
+
return {
|
|
1498
|
+
...base2,
|
|
1499
|
+
top: "50%",
|
|
1500
|
+
left: "50%",
|
|
1501
|
+
transform: "translate(-50%, -50%)"
|
|
1502
|
+
};
|
|
1503
|
+
default:
|
|
1504
|
+
return base2;
|
|
1757
1505
|
}
|
|
1758
|
-
const pad = Math.max((maxX - minX) * 0.1, (maxY - minY) * 0.1, 40);
|
|
1759
|
-
return {
|
|
1760
|
-
x: minX - pad,
|
|
1761
|
-
y: minY - pad,
|
|
1762
|
-
width: maxX - minX + pad * 2,
|
|
1763
|
-
height: maxY - minY + pad * 2
|
|
1764
|
-
};
|
|
1765
1506
|
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1507
|
+
var rootStyle = {
|
|
1508
|
+
display: "flex",
|
|
1509
|
+
flexDirection: "column",
|
|
1510
|
+
height: "100%",
|
|
1511
|
+
minHeight: 0,
|
|
1512
|
+
width: "100%"
|
|
1513
|
+
};
|
|
1514
|
+
var headerStyle = {
|
|
1515
|
+
flexShrink: 0,
|
|
1516
|
+
display: "flex",
|
|
1517
|
+
flexWrap: "wrap",
|
|
1518
|
+
alignItems: "center",
|
|
1519
|
+
gap: "0.75rem",
|
|
1520
|
+
padding: "0.75rem 1rem",
|
|
1521
|
+
borderBottom: "1px solid #e4e4e7",
|
|
1522
|
+
background: "#fff",
|
|
1523
|
+
fontSize: "0.875rem"
|
|
1524
|
+
};
|
|
1525
|
+
var bodyStyle = {
|
|
1526
|
+
flex: 1,
|
|
1527
|
+
minHeight: 0,
|
|
1528
|
+
display: "flex",
|
|
1529
|
+
flexDirection: "column"
|
|
1530
|
+
};
|
|
1531
|
+
var mainStyle = {
|
|
1532
|
+
flex: 1,
|
|
1533
|
+
minHeight: 0,
|
|
1534
|
+
position: "relative",
|
|
1535
|
+
display: "flex",
|
|
1536
|
+
flexDirection: "column"
|
|
1537
|
+
};
|
|
1538
|
+
var viewportSurfaceStyle = {
|
|
1539
|
+
flex: 1,
|
|
1540
|
+
minHeight: 0,
|
|
1541
|
+
position: "relative",
|
|
1542
|
+
width: "100%",
|
|
1543
|
+
alignSelf: "stretch",
|
|
1544
|
+
background: "#fff",
|
|
1545
|
+
touchAction: "none"
|
|
1546
|
+
};
|
|
1547
|
+
function mergeStyle(base2, style) {
|
|
1548
|
+
return style ? { ...base2, ...style } : base2;
|
|
1785
1549
|
}
|
|
1786
|
-
function
|
|
1787
|
-
return
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
req.onerror = () => reject(req.error);
|
|
1792
|
-
});
|
|
1550
|
+
function vectorCanvasSpaceStyle(position, inset, zIndex) {
|
|
1551
|
+
return {
|
|
1552
|
+
...getBoardPositionStyle(position, inset, zIndex),
|
|
1553
|
+
pointerEvents: "none"
|
|
1554
|
+
};
|
|
1793
1555
|
}
|
|
1794
|
-
function
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1556
|
+
function VectorCanvasRoot({
|
|
1557
|
+
children,
|
|
1558
|
+
className,
|
|
1559
|
+
style
|
|
1560
|
+
}) {
|
|
1561
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1562
|
+
"div",
|
|
1563
|
+
{
|
|
1564
|
+
"data-slot": "vector-canvas-root",
|
|
1565
|
+
className,
|
|
1566
|
+
style: mergeStyle(rootStyle, style),
|
|
1567
|
+
children
|
|
1568
|
+
}
|
|
1569
|
+
);
|
|
1801
1570
|
}
|
|
1802
|
-
function
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1571
|
+
function VectorCanvasHeader({
|
|
1572
|
+
children,
|
|
1573
|
+
className,
|
|
1574
|
+
style
|
|
1575
|
+
}) {
|
|
1576
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1577
|
+
"header",
|
|
1578
|
+
{
|
|
1579
|
+
"data-slot": "vector-canvas-header",
|
|
1580
|
+
className,
|
|
1581
|
+
style: mergeStyle(headerStyle, style),
|
|
1582
|
+
children
|
|
1583
|
+
}
|
|
1584
|
+
);
|
|
1809
1585
|
}
|
|
1810
|
-
function
|
|
1811
|
-
|
|
1586
|
+
function VectorCanvasBody({
|
|
1587
|
+
children,
|
|
1588
|
+
className,
|
|
1589
|
+
style
|
|
1590
|
+
}) {
|
|
1591
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1592
|
+
"div",
|
|
1593
|
+
{
|
|
1594
|
+
"data-slot": "vector-canvas-body",
|
|
1595
|
+
className,
|
|
1596
|
+
style: mergeStyle(bodyStyle, style),
|
|
1597
|
+
children
|
|
1598
|
+
}
|
|
1599
|
+
);
|
|
1812
1600
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1601
|
+
function VectorCanvasMain({
|
|
1602
|
+
children,
|
|
1603
|
+
className,
|
|
1604
|
+
style
|
|
1605
|
+
}) {
|
|
1606
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1607
|
+
"div",
|
|
1608
|
+
{
|
|
1609
|
+
"data-slot": "vector-canvas-main",
|
|
1610
|
+
className,
|
|
1611
|
+
style: mergeStyle(mainStyle, style),
|
|
1612
|
+
children
|
|
1818
1613
|
}
|
|
1819
|
-
|
|
1820
|
-
}
|
|
1821
|
-
async storeOriginal(blob) {
|
|
1822
|
-
const id = generateBlobId();
|
|
1823
|
-
const db = await this.getDb();
|
|
1824
|
-
await putInStore(db, "images", id, blob);
|
|
1825
|
-
return id;
|
|
1826
|
-
}
|
|
1827
|
-
async getOriginal(id) {
|
|
1828
|
-
const db = await this.getDb();
|
|
1829
|
-
return getFromStore(db, "images", id);
|
|
1830
|
-
}
|
|
1831
|
-
async deleteOriginal(id) {
|
|
1832
|
-
const db = await this.getDb();
|
|
1833
|
-
await deleteFromStore(db, "images", id);
|
|
1834
|
-
}
|
|
1835
|
-
async storeThumbnail(blob) {
|
|
1836
|
-
const id = generateBlobId();
|
|
1837
|
-
const db = await this.getDb();
|
|
1838
|
-
await putInStore(db, "thumbnails", id, blob);
|
|
1839
|
-
return id;
|
|
1840
|
-
}
|
|
1841
|
-
async getThumbnail(id) {
|
|
1842
|
-
const db = await this.getDb();
|
|
1843
|
-
return getFromStore(db, "thumbnails", id);
|
|
1844
|
-
}
|
|
1845
|
-
async deleteThumbnail(id) {
|
|
1846
|
-
const db = await this.getDb();
|
|
1847
|
-
await deleteFromStore(db, "thumbnails", id);
|
|
1848
|
-
}
|
|
1849
|
-
};
|
|
1850
|
-
function decodeImageToCanvas(blob, maxDimension) {
|
|
1851
|
-
return new Promise((resolve, reject) => {
|
|
1852
|
-
const img = new Image();
|
|
1853
|
-
img.onload = () => {
|
|
1854
|
-
const w0 = img.naturalWidth;
|
|
1855
|
-
const h0 = img.naturalHeight;
|
|
1856
|
-
if (w0 < 1 || h0 < 1) {
|
|
1857
|
-
reject(new Error("Image has no dimensions"));
|
|
1858
|
-
return;
|
|
1859
|
-
}
|
|
1860
|
-
const scale = 1;
|
|
1861
|
-
const cw = Math.max(1, Math.round(w0 * scale));
|
|
1862
|
-
const ch = Math.max(1, Math.round(h0 * scale));
|
|
1863
|
-
const canvas = document.createElement("canvas");
|
|
1864
|
-
canvas.width = cw;
|
|
1865
|
-
canvas.height = ch;
|
|
1866
|
-
const ctx = canvas.getContext("2d");
|
|
1867
|
-
if (!ctx) {
|
|
1868
|
-
reject(new Error("Canvas 2D context unavailable"));
|
|
1869
|
-
return;
|
|
1870
|
-
}
|
|
1871
|
-
ctx.imageSmoothingEnabled = true;
|
|
1872
|
-
ctx.imageSmoothingQuality = "high";
|
|
1873
|
-
ctx.drawImage(img, 0, 0, cw, ch);
|
|
1874
|
-
URL.revokeObjectURL(img.src);
|
|
1875
|
-
resolve({ canvas, width: cw, height: ch });
|
|
1876
|
-
};
|
|
1877
|
-
img.onerror = () => {
|
|
1878
|
-
URL.revokeObjectURL(img.src);
|
|
1879
|
-
reject(new Error("Could not decode image"));
|
|
1880
|
-
};
|
|
1881
|
-
img.src = URL.createObjectURL(blob);
|
|
1882
|
-
});
|
|
1614
|
+
);
|
|
1883
1615
|
}
|
|
1884
|
-
function
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1616
|
+
function VectorCanvasViewportSurface({
|
|
1617
|
+
children,
|
|
1618
|
+
className,
|
|
1619
|
+
style
|
|
1620
|
+
}) {
|
|
1621
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1622
|
+
"div",
|
|
1623
|
+
{
|
|
1624
|
+
"data-slot": "vector-canvas-viewport-surface",
|
|
1625
|
+
className,
|
|
1626
|
+
style: mergeStyle(viewportSurfaceStyle, style),
|
|
1627
|
+
children
|
|
1628
|
+
}
|
|
1629
|
+
);
|
|
1898
1630
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1631
|
+
function VectorCanvasToolbar({
|
|
1632
|
+
children,
|
|
1633
|
+
className,
|
|
1634
|
+
style,
|
|
1635
|
+
position = "bottom-center",
|
|
1636
|
+
inset = 12,
|
|
1637
|
+
zIndex = 30
|
|
1638
|
+
}) {
|
|
1639
|
+
const base2 = {
|
|
1640
|
+
...getBoardPositionStyle(position, inset, zIndex),
|
|
1641
|
+
display: "flex",
|
|
1642
|
+
justifyContent: "center",
|
|
1643
|
+
alignItems: "center",
|
|
1644
|
+
maxWidth: "calc(100% - 24px)",
|
|
1645
|
+
pointerEvents: "none"
|
|
1646
|
+
};
|
|
1647
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1648
|
+
"div",
|
|
1649
|
+
{
|
|
1650
|
+
"data-slot": "vector-canvas-toolbar",
|
|
1651
|
+
"data-position": position,
|
|
1652
|
+
className,
|
|
1653
|
+
style: mergeStyle(base2, style),
|
|
1654
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { pointerEvents: "auto" }, children })
|
|
1655
|
+
}
|
|
1656
|
+
);
|
|
1657
|
+
}
|
|
1658
|
+
function VectorCanvasSpace({
|
|
1659
|
+
children,
|
|
1660
|
+
className,
|
|
1661
|
+
style,
|
|
1662
|
+
position = "top-right",
|
|
1663
|
+
inset = 12,
|
|
1664
|
+
zIndex = 40,
|
|
1665
|
+
contentPointerEvents = "auto"
|
|
1666
|
+
}) {
|
|
1667
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1668
|
+
"div",
|
|
1669
|
+
{
|
|
1670
|
+
"data-slot": `vector-canvas-space-${position}`,
|
|
1671
|
+
className,
|
|
1672
|
+
style: mergeStyle(vectorCanvasSpaceStyle(position, inset, zIndex), style),
|
|
1673
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { pointerEvents: contentPointerEvents }, children })
|
|
1674
|
+
}
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
var VectorCanvas = {
|
|
1678
|
+
Root: VectorCanvasRoot,
|
|
1679
|
+
Header: VectorCanvasHeader,
|
|
1680
|
+
Body: VectorCanvasBody,
|
|
1681
|
+
Main: VectorCanvasMain,
|
|
1682
|
+
ViewportSurface: VectorCanvasViewportSurface,
|
|
1683
|
+
Toolbar: VectorCanvasToolbar,
|
|
1684
|
+
Space: VectorCanvasSpace,
|
|
1685
|
+
NavMenu,
|
|
1686
|
+
SelectionInspector: VectorSelectionInspector
|
|
1687
|
+
};
|
|
1688
|
+
var MINIMAP_W = 180;
|
|
1689
|
+
var MINIMAP_H = 120;
|
|
1690
|
+
var PADDING = 12;
|
|
1691
|
+
var BORDER_RADIUS = 8;
|
|
1692
|
+
var BG = "#ffffff";
|
|
1693
|
+
var BORDER_COLOR = "rgba(0,0,0,0.12)";
|
|
1694
|
+
var ITEM_FILL = "rgba(0,0,0,0.08)";
|
|
1695
|
+
var ITEM_STROKE = "rgba(0,0,0,0.15)";
|
|
1696
|
+
var VIEWPORT_FILL = "rgba(59,130,246,0.08)";
|
|
1697
|
+
var VIEWPORT_STROKE = "#3b82f6";
|
|
1698
|
+
var btnStyle = {
|
|
1699
|
+
pointerEvents: "auto",
|
|
1700
|
+
width: 36,
|
|
1701
|
+
height: 36,
|
|
1702
|
+
display: "inline-flex",
|
|
1703
|
+
alignItems: "center",
|
|
1704
|
+
justifyContent: "center",
|
|
1705
|
+
cursor: "pointer",
|
|
1706
|
+
fontSize: 18,
|
|
1707
|
+
lineHeight: 1,
|
|
1708
|
+
color: "#18181b",
|
|
1709
|
+
padding: 0,
|
|
1710
|
+
border: "none",
|
|
1711
|
+
outline: "none",
|
|
1712
|
+
background: "none",
|
|
1713
|
+
WebkitTapHighlightColor: "transparent"
|
|
1714
|
+
};
|
|
1715
|
+
var chevronBtnStyle = {
|
|
1716
|
+
pointerEvents: "auto",
|
|
1717
|
+
width: 24,
|
|
1718
|
+
height: 36,
|
|
1719
|
+
display: "inline-flex",
|
|
1720
|
+
alignItems: "center",
|
|
1721
|
+
justifyContent: "center",
|
|
1722
|
+
cursor: "pointer",
|
|
1723
|
+
fontSize: 12,
|
|
1724
|
+
lineHeight: 1,
|
|
1725
|
+
color: "#52525b",
|
|
1726
|
+
padding: 0,
|
|
1727
|
+
border: "none",
|
|
1728
|
+
outline: "none",
|
|
1729
|
+
background: "none",
|
|
1730
|
+
WebkitTapHighlightColor: "transparent"
|
|
1731
|
+
};
|
|
1732
|
+
var undoBtnStyle = {
|
|
1733
|
+
...chevronBtnStyle,
|
|
1734
|
+
width: 30,
|
|
1735
|
+
opacity: 1,
|
|
1736
|
+
color: "#18181b"
|
|
1737
|
+
};
|
|
1738
|
+
var labelStyle2 = {
|
|
1739
|
+
fontSize: 10,
|
|
1740
|
+
fontWeight: 600,
|
|
1741
|
+
color: "#52525b",
|
|
1742
|
+
textAlign: "center",
|
|
1743
|
+
userSelect: "none",
|
|
1744
|
+
padding: "2px 0"
|
|
1745
|
+
};
|
|
1746
|
+
var panelLayoutStyle = {
|
|
1747
|
+
display: "flex",
|
|
1748
|
+
flexDirection: "column",
|
|
1749
|
+
alignItems: "flex-start",
|
|
1750
|
+
gap: 4,
|
|
1751
|
+
touchAction: "none",
|
|
1752
|
+
pointerEvents: "none",
|
|
1753
|
+
userSelect: "none"
|
|
1754
|
+
};
|
|
1755
|
+
var innerStyle = {
|
|
1756
|
+
pointerEvents: "auto",
|
|
1757
|
+
display: "flex",
|
|
1758
|
+
flexDirection: "row",
|
|
1759
|
+
alignItems: "center",
|
|
1760
|
+
gap: 4,
|
|
1761
|
+
background: BG,
|
|
1762
|
+
borderRadius: BORDER_RADIUS,
|
|
1763
|
+
border: `1px solid ${BORDER_COLOR}`,
|
|
1764
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1765
|
+
padding: 4
|
|
1766
|
+
};
|
|
1767
|
+
function NavMenu({
|
|
1768
|
+
camera: cameraProp,
|
|
1769
|
+
viewportWidth: viewportWidthProp,
|
|
1770
|
+
viewportHeight: viewportHeightProp,
|
|
1771
|
+
items: itemsProp,
|
|
1772
|
+
zoomPercent: zoomPercentProp,
|
|
1773
|
+
onZoomIn: onZoomInProp,
|
|
1774
|
+
onZoomOut: onZoomOutProp,
|
|
1775
|
+
onUndo: onUndoProp,
|
|
1776
|
+
onRedo: onRedoProp,
|
|
1777
|
+
canUndo: canUndoProp,
|
|
1778
|
+
canRedo: canRedoProp,
|
|
1779
|
+
onRequestRender: onRequestRenderProp,
|
|
1780
|
+
position = "bottom-left",
|
|
1781
|
+
inset = 12,
|
|
1782
|
+
zIndex = 23,
|
|
1783
|
+
className,
|
|
1784
|
+
style
|
|
1785
|
+
}) {
|
|
1786
|
+
const ctx = useCanvuChromeContext();
|
|
1787
|
+
const camera = cameraProp ?? ctx?.camera ?? null;
|
|
1788
|
+
const viewportWidth = viewportWidthProp ?? ctx?.viewportWidth ?? 0;
|
|
1789
|
+
const viewportHeight = viewportHeightProp ?? ctx?.viewportHeight ?? 0;
|
|
1790
|
+
const items = itemsProp ?? ctx?.items ?? [];
|
|
1791
|
+
const zoomPercent = zoomPercentProp ?? ctx?.zoomPercent ?? 100;
|
|
1792
|
+
const onZoomIn = onZoomInProp ?? ctx?.onZoomIn ?? noop;
|
|
1793
|
+
const onZoomOut = onZoomOutProp ?? ctx?.onZoomOut ?? noop;
|
|
1794
|
+
const onUndo = onUndoProp ?? ctx?.onUndo ?? noop;
|
|
1795
|
+
const onRedo = onRedoProp ?? ctx?.onRedo ?? noop;
|
|
1796
|
+
const canUndo = canUndoProp ?? ctx?.canUndo ?? false;
|
|
1797
|
+
const canRedo = canRedoProp ?? ctx?.canRedo ?? false;
|
|
1798
|
+
const onRequestRender = onRequestRenderProp ?? ctx?.onRequestRender ?? noop;
|
|
1799
|
+
const [expanded, setExpanded] = react.useState(false);
|
|
1800
|
+
const svgRef = react.useRef(null);
|
|
1801
|
+
const [dragging, setDragging] = react.useState(false);
|
|
1802
|
+
const [mouseDown, setMouseDown] = react.useState(false);
|
|
1803
|
+
const worldBounds = computeWorldBounds(items);
|
|
1804
|
+
const isEmpty = worldBounds.width <= 0 || worldBounds.height <= 0;
|
|
1805
|
+
const scale = Math.min(
|
|
1806
|
+
(MINIMAP_W - PADDING * 2) / Math.max(worldBounds.width, 1),
|
|
1807
|
+
(MINIMAP_H - PADDING * 2) / Math.max(worldBounds.height, 1)
|
|
1808
|
+
);
|
|
1809
|
+
const originX = worldBounds.x;
|
|
1810
|
+
const originY = worldBounds.y;
|
|
1811
|
+
const toMinimapX = (wx) => (wx - originX) * scale + PADDING;
|
|
1812
|
+
const toMinimapY = (wy) => (wy - originY) * scale + PADDING;
|
|
1813
|
+
const toMinimapW = (ww) => ww * scale;
|
|
1814
|
+
const toMinimapH = (wh) => wh * scale;
|
|
1815
|
+
const viewportWorld = camera ? camera.getVisibleWorldRect(viewportWidth, viewportHeight) : { x: 0, y: 0, width: 0, height: 0 };
|
|
1816
|
+
const vpMinimap = {
|
|
1817
|
+
x: toMinimapX(viewportWorld.x),
|
|
1818
|
+
y: toMinimapY(viewportWorld.y),
|
|
1819
|
+
width: toMinimapW(viewportWorld.width),
|
|
1820
|
+
height: toMinimapH(viewportWorld.height)
|
|
1821
|
+
};
|
|
1822
|
+
const worldFromMinimap = react.useCallback(
|
|
1823
|
+
(mx, my) => ({
|
|
1824
|
+
worldX: (mx - PADDING) / scale + originX,
|
|
1825
|
+
worldY: (my - PADDING) / scale + originY
|
|
1826
|
+
}),
|
|
1827
|
+
[scale, originX, originY]
|
|
1828
|
+
);
|
|
1829
|
+
const panTo = react.useCallback(
|
|
1830
|
+
(mx, my) => {
|
|
1831
|
+
if (!camera) return;
|
|
1832
|
+
const { worldX, worldY } = worldFromMinimap(mx, my);
|
|
1833
|
+
camera.x = viewportWidth / 2 - worldX * camera.zoom;
|
|
1834
|
+
camera.y = viewportHeight / 2 - worldY * camera.zoom;
|
|
1835
|
+
onRequestRender();
|
|
1836
|
+
},
|
|
1837
|
+
[worldFromMinimap, camera, viewportWidth, viewportHeight, onRequestRender]
|
|
1838
|
+
);
|
|
1839
|
+
const handlePointerDown = react.useCallback(
|
|
1840
|
+
(e) => {
|
|
1841
|
+
if (isEmpty) return;
|
|
1842
|
+
setDragging(true);
|
|
1843
|
+
setMouseDown(true);
|
|
1844
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
1845
|
+
if (!rect) return;
|
|
1846
|
+
panTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
1847
|
+
svgRef.current?.setPointerCapture(e.pointerId);
|
|
1848
|
+
},
|
|
1849
|
+
[isEmpty, panTo]
|
|
1850
|
+
);
|
|
1851
|
+
const handlePointerMove = react.useCallback(
|
|
1852
|
+
(e) => {
|
|
1853
|
+
if (!mouseDown || isEmpty) return;
|
|
1854
|
+
const rect = svgRef.current?.getBoundingClientRect();
|
|
1855
|
+
if (!rect) return;
|
|
1856
|
+
panTo(e.clientX - rect.left, e.clientY - rect.top);
|
|
1857
|
+
},
|
|
1858
|
+
[mouseDown, isEmpty, panTo]
|
|
1859
|
+
);
|
|
1860
|
+
const handlePointerUp = react.useCallback(() => {
|
|
1861
|
+
setDragging(false);
|
|
1862
|
+
setMouseDown(false);
|
|
1863
|
+
}, []);
|
|
1864
|
+
const toggleExpanded = react.useCallback(() => {
|
|
1865
|
+
setExpanded((v) => !v);
|
|
1866
|
+
}, []);
|
|
1867
|
+
const anchorStyle = getBoardPositionStyle(position, inset, zIndex);
|
|
1868
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1869
|
+
"fieldset",
|
|
1870
|
+
{
|
|
1871
|
+
"data-slot": "canvu-nav-menu",
|
|
1872
|
+
"data-position": position,
|
|
1873
|
+
className,
|
|
1874
|
+
style: {
|
|
1875
|
+
...anchorStyle,
|
|
1876
|
+
...panelLayoutStyle,
|
|
1877
|
+
border: "none",
|
|
1878
|
+
margin: 0,
|
|
1879
|
+
padding: 0,
|
|
1880
|
+
minWidth: 0,
|
|
1881
|
+
...style
|
|
1882
|
+
},
|
|
1883
|
+
"aria-label": "Zoom and minimap controls",
|
|
1884
|
+
children: [
|
|
1885
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: innerStyle, children: [
|
|
1886
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1887
|
+
"button",
|
|
1888
|
+
{
|
|
1889
|
+
type: "button",
|
|
1890
|
+
style: btnStyle,
|
|
1891
|
+
"aria-label": "Zoom out",
|
|
1892
|
+
title: "Zoom out",
|
|
1893
|
+
onPointerDown: (e) => {
|
|
1894
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1895
|
+
},
|
|
1896
|
+
onClick: onZoomOut,
|
|
1897
|
+
children: "\u2212"
|
|
1898
|
+
}
|
|
1899
|
+
),
|
|
1900
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: labelStyle2, "aria-hidden": true, children: [
|
|
1901
|
+
zoomPercent,
|
|
1902
|
+
"%"
|
|
1903
|
+
] }),
|
|
1904
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1905
|
+
"button",
|
|
1906
|
+
{
|
|
1907
|
+
type: "button",
|
|
1908
|
+
style: btnStyle,
|
|
1909
|
+
"aria-label": "Zoom in",
|
|
1910
|
+
title: "Zoom in",
|
|
1911
|
+
onPointerDown: (e) => {
|
|
1912
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1913
|
+
},
|
|
1914
|
+
onClick: onZoomIn,
|
|
1915
|
+
children: "+"
|
|
1916
|
+
}
|
|
1917
|
+
),
|
|
1918
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1919
|
+
"button",
|
|
1920
|
+
{
|
|
1921
|
+
type: "button",
|
|
1922
|
+
style: undoBtnStyle,
|
|
1923
|
+
"aria-label": "Undo",
|
|
1924
|
+
title: "Undo",
|
|
1925
|
+
disabled: !canUndo,
|
|
1926
|
+
onPointerDown: (e) => {
|
|
1927
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1928
|
+
},
|
|
1929
|
+
onClick: onUndo,
|
|
1930
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { size: 16 })
|
|
1931
|
+
}
|
|
1932
|
+
),
|
|
1933
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1934
|
+
"button",
|
|
1935
|
+
{
|
|
1936
|
+
type: "button",
|
|
1937
|
+
style: undoBtnStyle,
|
|
1938
|
+
"aria-label": "Redo",
|
|
1939
|
+
title: "Redo",
|
|
1940
|
+
disabled: !canRedo,
|
|
1941
|
+
onPointerDown: (e) => {
|
|
1942
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1943
|
+
},
|
|
1944
|
+
onClick: onRedo,
|
|
1945
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { size: 16 })
|
|
1946
|
+
}
|
|
1947
|
+
),
|
|
1948
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1949
|
+
"button",
|
|
1950
|
+
{
|
|
1951
|
+
type: "button",
|
|
1952
|
+
style: chevronBtnStyle,
|
|
1953
|
+
"aria-label": expanded ? "Hide minimap" : "Show minimap",
|
|
1954
|
+
title: expanded ? "Hide minimap" : "Show minimap",
|
|
1955
|
+
onPointerDown: (e) => {
|
|
1956
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
1957
|
+
},
|
|
1958
|
+
onClick: toggleExpanded,
|
|
1959
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1960
|
+
"svg",
|
|
1961
|
+
{
|
|
1962
|
+
width: "12",
|
|
1963
|
+
height: "12",
|
|
1964
|
+
viewBox: "0 0 12 12",
|
|
1965
|
+
style: {
|
|
1966
|
+
transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
1967
|
+
transition: "transform 0.15s ease"
|
|
1968
|
+
},
|
|
1969
|
+
"aria-hidden": true,
|
|
1970
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1971
|
+
"path",
|
|
1972
|
+
{
|
|
1973
|
+
d: "M2 4 L6 8 L10 4",
|
|
1974
|
+
fill: "none",
|
|
1975
|
+
stroke: "currentColor",
|
|
1976
|
+
strokeWidth: "1.5",
|
|
1977
|
+
strokeLinecap: "round",
|
|
1978
|
+
strokeLinejoin: "round"
|
|
1979
|
+
}
|
|
1980
|
+
)
|
|
1981
|
+
}
|
|
1982
|
+
)
|
|
1983
|
+
}
|
|
1984
|
+
)
|
|
1985
|
+
] }),
|
|
1986
|
+
expanded && /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Minimap", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1987
|
+
"svg",
|
|
1988
|
+
{
|
|
1989
|
+
ref: svgRef,
|
|
1990
|
+
style: {
|
|
1991
|
+
width: MINIMAP_W,
|
|
1992
|
+
height: MINIMAP_H,
|
|
1993
|
+
borderRadius: BORDER_RADIUS,
|
|
1994
|
+
border: `1px solid ${BORDER_COLOR}`,
|
|
1995
|
+
background: BG,
|
|
1996
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
1997
|
+
cursor: isEmpty ? "default" : dragging ? "grabbing" : "grab",
|
|
1998
|
+
display: "block",
|
|
1999
|
+
pointerEvents: "auto"
|
|
2000
|
+
},
|
|
2001
|
+
onPointerDown: handlePointerDown,
|
|
2002
|
+
onPointerMove: handlePointerMove,
|
|
2003
|
+
onPointerUp: handlePointerUp,
|
|
2004
|
+
onPointerCancel: handlePointerUp,
|
|
2005
|
+
children: isEmpty ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2006
|
+
items.map((it) => {
|
|
2007
|
+
const b = normalizeRect(boundsAabbForRotatedItem(it));
|
|
2008
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2009
|
+
"rect",
|
|
2010
|
+
{
|
|
2011
|
+
x: toMinimapX(b.x),
|
|
2012
|
+
y: toMinimapY(b.y),
|
|
2013
|
+
width: toMinimapW(b.width),
|
|
2014
|
+
height: toMinimapH(b.height),
|
|
2015
|
+
fill: ITEM_FILL,
|
|
2016
|
+
stroke: ITEM_STROKE,
|
|
2017
|
+
strokeWidth: 0.5,
|
|
2018
|
+
rx: 1
|
|
2019
|
+
},
|
|
2020
|
+
it.id
|
|
2021
|
+
);
|
|
2022
|
+
}),
|
|
2023
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2024
|
+
"rect",
|
|
2025
|
+
{
|
|
2026
|
+
x: vpMinimap.x,
|
|
2027
|
+
y: vpMinimap.y,
|
|
2028
|
+
width: vpMinimap.width,
|
|
2029
|
+
height: vpMinimap.height,
|
|
2030
|
+
fill: VIEWPORT_FILL,
|
|
2031
|
+
stroke: VIEWPORT_STROKE,
|
|
2032
|
+
strokeWidth: 1.5,
|
|
2033
|
+
rx: 2
|
|
2034
|
+
}
|
|
2035
|
+
)
|
|
2036
|
+
] })
|
|
2037
|
+
}
|
|
2038
|
+
) })
|
|
2039
|
+
]
|
|
2040
|
+
}
|
|
1919
2041
|
);
|
|
1920
|
-
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
1921
|
-
return { blobId, thumbnailBlobId, width, height };
|
|
1922
2042
|
}
|
|
1923
|
-
|
|
1924
|
-
const blob = await store.getOriginal(blobId);
|
|
1925
|
-
if (!blob) return null;
|
|
1926
|
-
return URL.createObjectURL(blob);
|
|
2043
|
+
function noop() {
|
|
1927
2044
|
}
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2045
|
+
function computeWorldBounds(items) {
|
|
2046
|
+
if (items.length === 0) {
|
|
2047
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
2048
|
+
}
|
|
2049
|
+
let minX = Infinity;
|
|
2050
|
+
let minY = Infinity;
|
|
2051
|
+
let maxX = -Infinity;
|
|
2052
|
+
let maxY = -Infinity;
|
|
2053
|
+
for (const it of items) {
|
|
2054
|
+
const b = boundsAabbForRotatedItem(it);
|
|
2055
|
+
minX = Math.min(minX, b.x);
|
|
2056
|
+
minY = Math.min(minY, b.y);
|
|
2057
|
+
maxX = Math.max(maxX, b.x + b.width);
|
|
2058
|
+
maxY = Math.max(maxY, b.y + b.height);
|
|
2059
|
+
}
|
|
2060
|
+
const pad = Math.max((maxX - minX) * 0.1, (maxY - minY) * 0.1, 40);
|
|
2061
|
+
return {
|
|
2062
|
+
x: minX - pad,
|
|
2063
|
+
y: minY - pad,
|
|
2064
|
+
width: maxX - minX + pad * 2,
|
|
2065
|
+
height: maxY - minY + pad * 2
|
|
2066
|
+
};
|
|
1932
2067
|
}
|
|
1933
2068
|
|
|
1934
2069
|
// src/react/persistence/indexed-db-adapter.ts
|
|
@@ -3260,83 +3395,6 @@ var Camera2D = class {
|
|
|
3260
3395
|
}
|
|
3261
3396
|
};
|
|
3262
3397
|
|
|
3263
|
-
// src/image/pdf-loader.ts
|
|
3264
|
-
var pdfjsPromise = null;
|
|
3265
|
-
function getPdfJs() {
|
|
3266
|
-
if (!pdfjsPromise) {
|
|
3267
|
-
pdfjsPromise = import('pdfjs-dist').then((mod) => {
|
|
3268
|
-
const workerSrc = new URL(
|
|
3269
|
-
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
3270
|
-
(typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('react.cjs', document.baseURI).href))
|
|
3271
|
-
).toString();
|
|
3272
|
-
mod.GlobalWorkerOptions.workerSrc = workerSrc;
|
|
3273
|
-
return mod;
|
|
3274
|
-
});
|
|
3275
|
-
}
|
|
3276
|
-
return pdfjsPromise;
|
|
3277
|
-
}
|
|
3278
|
-
async function renderPageToCanvas(page, scale) {
|
|
3279
|
-
const raw = page.getViewport({ scale: 1 });
|
|
3280
|
-
const adjustedScale = Math.round(raw.width * scale) / raw.width;
|
|
3281
|
-
const viewport = page.getViewport({ scale: adjustedScale });
|
|
3282
|
-
const w = Math.round(viewport.width);
|
|
3283
|
-
const h = Math.round(viewport.height);
|
|
3284
|
-
const canvas = document.createElement("canvas");
|
|
3285
|
-
canvas.width = w;
|
|
3286
|
-
canvas.height = h;
|
|
3287
|
-
const ctx = canvas.getContext("2d");
|
|
3288
|
-
if (!ctx) throw new Error("Canvas 2D context unavailable");
|
|
3289
|
-
ctx.imageSmoothingEnabled = true;
|
|
3290
|
-
ctx.imageSmoothingQuality = "high";
|
|
3291
|
-
await page.render({ canvas, viewport }).promise;
|
|
3292
|
-
return { canvas, width: w, height: h };
|
|
3293
|
-
}
|
|
3294
|
-
function canvasToBlob2(canvas, mime, quality) {
|
|
3295
|
-
return new Promise((resolve, reject) => {
|
|
3296
|
-
canvas.toBlob(
|
|
3297
|
-
(blob) => {
|
|
3298
|
-
if (!blob) {
|
|
3299
|
-
reject(new Error("Could not encode blob"));
|
|
3300
|
-
return;
|
|
3301
|
-
}
|
|
3302
|
-
resolve(blob);
|
|
3303
|
-
},
|
|
3304
|
-
mime,
|
|
3305
|
-
quality
|
|
3306
|
-
);
|
|
3307
|
-
});
|
|
3308
|
-
}
|
|
3309
|
-
async function loadPdfToStore(file, store, options) {
|
|
3310
|
-
const scale = 1.5;
|
|
3311
|
-
const pdfjs = await getPdfJs();
|
|
3312
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
3313
|
-
const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
|
|
3314
|
-
const results = [];
|
|
3315
|
-
for (let i = 1; i <= pdf.numPages; i++) {
|
|
3316
|
-
const page = await pdf.getPage(i);
|
|
3317
|
-
const { canvas, width, height } = await renderPageToCanvas(page, scale);
|
|
3318
|
-
const mime = "image/png";
|
|
3319
|
-
const pageBlob = await canvasToBlob2(canvas, mime);
|
|
3320
|
-
const blobId = await store.storeOriginal(pageBlob);
|
|
3321
|
-
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
3322
|
-
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
3323
|
-
const th = Math.max(1, Math.round(height * thumbScale));
|
|
3324
|
-
const thumbCanvas = document.createElement("canvas");
|
|
3325
|
-
thumbCanvas.width = tw;
|
|
3326
|
-
thumbCanvas.height = th;
|
|
3327
|
-
const tCtx = thumbCanvas.getContext("2d");
|
|
3328
|
-
if (tCtx) {
|
|
3329
|
-
tCtx.imageSmoothingEnabled = true;
|
|
3330
|
-
tCtx.imageSmoothingQuality = "high";
|
|
3331
|
-
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
3332
|
-
}
|
|
3333
|
-
const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
|
|
3334
|
-
const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
|
|
3335
|
-
results.push({ blobId, thumbnailBlobId, width, height, pageNumber: i });
|
|
3336
|
-
}
|
|
3337
|
-
return results;
|
|
3338
|
-
}
|
|
3339
|
-
|
|
3340
3398
|
// src/input/apple-pencil-navigation.ts
|
|
3341
3399
|
var DRAWING_LIKE_TOOLS = /* @__PURE__ */ new Set([
|
|
3342
3400
|
"draw",
|
|
@@ -3535,7 +3593,7 @@ function attachViewportInput(options) {
|
|
|
3535
3593
|
if (e.ctrlKey || e.metaKey) {
|
|
3536
3594
|
e.preventDefault();
|
|
3537
3595
|
const dy = wheelDeltaYPixels(e);
|
|
3538
|
-
const normDy = dy < 20 ? dy * 12 : dy;
|
|
3596
|
+
const normDy = Math.abs(dy) < 20 ? dy * 12 : dy;
|
|
3539
3597
|
const factor = Math.exp(-normDy * wheelZoomSensitivity);
|
|
3540
3598
|
const rect = element.getBoundingClientRect();
|
|
3541
3599
|
camera.setZoom(camera.zoom * factor, {
|
|
@@ -4689,7 +4747,42 @@ init_shape_builders();
|
|
|
4689
4747
|
|
|
4690
4748
|
// src/react/InteractionOverlay.tsx
|
|
4691
4749
|
init_rect();
|
|
4692
|
-
|
|
4750
|
+
|
|
4751
|
+
// src/scene/freehand-path.ts
|
|
4752
|
+
function smoothFreehandPointsToPathD(points) {
|
|
4753
|
+
const n = points.length;
|
|
4754
|
+
if (n === 0) return "";
|
|
4755
|
+
if (n === 1) {
|
|
4756
|
+
const p = points[0];
|
|
4757
|
+
if (!p) return "";
|
|
4758
|
+
return `M ${p.x} ${p.y}`;
|
|
4759
|
+
}
|
|
4760
|
+
if (n === 2) {
|
|
4761
|
+
const a = points[0];
|
|
4762
|
+
const b = points[1];
|
|
4763
|
+
if (!a || !b) return "";
|
|
4764
|
+
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
4765
|
+
}
|
|
4766
|
+
const p0 = points[0];
|
|
4767
|
+
if (!p0) return "";
|
|
4768
|
+
let d = `M ${p0.x} ${p0.y}`;
|
|
4769
|
+
let i = 1;
|
|
4770
|
+
for (; i < n - 2; i++) {
|
|
4771
|
+
const pi = points[i];
|
|
4772
|
+
const pi1 = points[i + 1];
|
|
4773
|
+
if (!pi || !pi1) continue;
|
|
4774
|
+
const xc = (pi.x + pi1.x) / 2;
|
|
4775
|
+
const yc = (pi.y + pi1.y) / 2;
|
|
4776
|
+
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
4777
|
+
}
|
|
4778
|
+
const pLast = points[i];
|
|
4779
|
+
const pEnd = points[i + 1];
|
|
4780
|
+
if (!pLast || !pEnd) return d;
|
|
4781
|
+
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
4782
|
+
return d;
|
|
4783
|
+
}
|
|
4784
|
+
|
|
4785
|
+
// src/react/InteractionOverlay.tsx
|
|
4693
4786
|
init_shape_builders();
|
|
4694
4787
|
var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
4695
4788
|
var ERASER_TINT = "#cbd5e1";
|
|
@@ -4798,6 +4891,7 @@ function InteractionOverlay({
|
|
|
4798
4891
|
eraserTrail = [],
|
|
4799
4892
|
laserTrail = [],
|
|
4800
4893
|
eraserPreviewItems = [],
|
|
4894
|
+
marqueeCandidateItems = [],
|
|
4801
4895
|
previewStrokeStyle
|
|
4802
4896
|
}) {
|
|
4803
4897
|
const z = camera.zoom;
|
|
@@ -5046,6 +5140,33 @@ function InteractionOverlay({
|
|
|
5046
5140
|
);
|
|
5047
5141
|
}) });
|
|
5048
5142
|
}
|
|
5143
|
+
let marqueeCandidates = null;
|
|
5144
|
+
if (marqueeCandidateItems.length > 0) {
|
|
5145
|
+
marqueeCandidates = /* @__PURE__ */ jsxRuntime.jsx("g", { children: marqueeCandidateItems.map((it) => {
|
|
5146
|
+
const b = normalizeRect(it.bounds);
|
|
5147
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5148
|
+
"g",
|
|
5149
|
+
{
|
|
5150
|
+
transform: formatItemPlacementTransform(it),
|
|
5151
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5152
|
+
"rect",
|
|
5153
|
+
{
|
|
5154
|
+
x: 0,
|
|
5155
|
+
y: 0,
|
|
5156
|
+
width: b.width,
|
|
5157
|
+
height: b.height,
|
|
5158
|
+
fill: "rgba(59, 130, 246, 0.10)",
|
|
5159
|
+
stroke: "#3b82f6",
|
|
5160
|
+
strokeWidth: overlayStrokePx,
|
|
5161
|
+
strokeDasharray: dashPattern,
|
|
5162
|
+
vectorEffect: "non-scaling-stroke"
|
|
5163
|
+
}
|
|
5164
|
+
)
|
|
5165
|
+
},
|
|
5166
|
+
`marquee-cand-${it.id}`
|
|
5167
|
+
);
|
|
5168
|
+
}) });
|
|
5169
|
+
}
|
|
5049
5170
|
let eraserTrailOverlay = null;
|
|
5050
5171
|
if (eraserTrail.length >= 1) {
|
|
5051
5172
|
const now = Date.now();
|
|
@@ -5146,6 +5267,7 @@ function InteractionOverlay({
|
|
|
5146
5267
|
width: "100%",
|
|
5147
5268
|
height: "100%",
|
|
5148
5269
|
children: /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: rootTransform, children: [
|
|
5270
|
+
marqueeCandidates,
|
|
5149
5271
|
preview,
|
|
5150
5272
|
laserTrailOverlay,
|
|
5151
5273
|
eraserTrailOverlay,
|
|
@@ -5155,7 +5277,6 @@ function InteractionOverlay({
|
|
|
5155
5277
|
}
|
|
5156
5278
|
);
|
|
5157
5279
|
}
|
|
5158
|
-
init_freehand_path();
|
|
5159
5280
|
|
|
5160
5281
|
// src/react/presence/peer-color.ts
|
|
5161
5282
|
function defaultPresenceColorForId(id) {
|
|
@@ -5305,6 +5426,17 @@ function PresenceRemoteLayer({
|
|
|
5305
5426
|
);
|
|
5306
5427
|
}
|
|
5307
5428
|
|
|
5429
|
+
// src/react/stable-selection.ts
|
|
5430
|
+
function shallowEqualStringArray(a, b) {
|
|
5431
|
+
if (a === b) return true;
|
|
5432
|
+
if (!a || !b) return a === b;
|
|
5433
|
+
if (a.length !== b.length) return false;
|
|
5434
|
+
for (let i = 0; i < a.length; i++) {
|
|
5435
|
+
if (a[i] !== b[i]) return false;
|
|
5436
|
+
}
|
|
5437
|
+
return true;
|
|
5438
|
+
}
|
|
5439
|
+
|
|
5308
5440
|
// src/react/TextEditOverlay.tsx
|
|
5309
5441
|
init_rect();
|
|
5310
5442
|
init_text_svg();
|
|
@@ -5474,7 +5606,8 @@ var visuallyHidden = {
|
|
|
5474
5606
|
whiteSpace: "nowrap",
|
|
5475
5607
|
border: 0
|
|
5476
5608
|
};
|
|
5477
|
-
var
|
|
5609
|
+
var VIEWPORT_ZOOM_IN_STEP = 1.15;
|
|
5610
|
+
var VIEWPORT_ZOOM_OUT_STEP = 1 / 1.15;
|
|
5478
5611
|
function replaceItem(items, id, next) {
|
|
5479
5612
|
return items.map((it) => it.id === id ? next : it);
|
|
5480
5613
|
}
|
|
@@ -5678,22 +5811,8 @@ function appendInterpolatedPoints(points, next, maxStepWorld) {
|
|
|
5678
5811
|
if (!last) return [...points, next];
|
|
5679
5812
|
const dx = next.x - last.x;
|
|
5680
5813
|
const dy = next.y - last.y;
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
const safeStep = Math.max(maxStepWorld, 1e-4);
|
|
5684
|
-
const steps = Math.max(1, Math.ceil(dist / safeStep));
|
|
5685
|
-
if (steps === 1) return [...points, next];
|
|
5686
|
-
const out = [...points];
|
|
5687
|
-
for (let i = 1; i <= steps; i++) {
|
|
5688
|
-
const t = i / steps;
|
|
5689
|
-
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;
|
|
5690
|
-
out.push({
|
|
5691
|
-
x: last.x + dx * t,
|
|
5692
|
-
y: last.y + dy * t,
|
|
5693
|
-
...pressure != null ? { pressure } : {}
|
|
5694
|
-
});
|
|
5695
|
-
}
|
|
5696
|
-
return out;
|
|
5814
|
+
if (dx * dx + dy * dy < 1e-12) return points;
|
|
5815
|
+
return [...points, next];
|
|
5697
5816
|
}
|
|
5698
5817
|
function pointerSampleToWorldPoint(screenToWorldFn, clientX, clientY, pressure) {
|
|
5699
5818
|
const { worldX, worldY } = screenToWorldFn(clientX, clientY);
|
|
@@ -5725,7 +5844,7 @@ function isLikelyStylusTouch(touch) {
|
|
|
5725
5844
|
}
|
|
5726
5845
|
function appendPointerEventSamplesToStrokePoints(points, ev, zoom, screenToWorldFn) {
|
|
5727
5846
|
let interpolated = points;
|
|
5728
|
-
|
|
5847
|
+
ev.pointerType === "pen" ? 0.35 / zoom : 1 / zoom;
|
|
5729
5848
|
for (const sample of pointerEventSamples(ev)) {
|
|
5730
5849
|
const nextPoint = pointerSampleToWorldPoint(
|
|
5731
5850
|
screenToWorldFn,
|
|
@@ -5733,7 +5852,7 @@ function appendPointerEventSamplesToStrokePoints(points, ev, zoom, screenToWorld
|
|
|
5733
5852
|
sample.clientY,
|
|
5734
5853
|
sample.pointerType === "pen" ? sample.pressure : void 0
|
|
5735
5854
|
);
|
|
5736
|
-
interpolated = appendInterpolatedPoints(interpolated, nextPoint
|
|
5855
|
+
interpolated = appendInterpolatedPoints(interpolated, nextPoint);
|
|
5737
5856
|
}
|
|
5738
5857
|
return interpolated;
|
|
5739
5858
|
}
|
|
@@ -5744,7 +5863,7 @@ function appendTouchToStrokePoints(points, touch, zoom, screenToWorldFn) {
|
|
|
5744
5863
|
touch.clientY,
|
|
5745
5864
|
touchPressure(touch)
|
|
5746
5865
|
);
|
|
5747
|
-
return appendInterpolatedPoints(points, nextPoint
|
|
5866
|
+
return appendInterpolatedPoints(points, nextPoint);
|
|
5748
5867
|
}
|
|
5749
5868
|
var VectorViewport = react.forwardRef(
|
|
5750
5869
|
function VectorViewport2({
|
|
@@ -5766,6 +5885,7 @@ var VectorViewport = react.forwardRef(
|
|
|
5766
5885
|
onCameraChange: consumerOnCameraChange,
|
|
5767
5886
|
customPlacement: consumerCustomPlacement,
|
|
5768
5887
|
customPlacements: consumerCustomPlacements,
|
|
5888
|
+
assetStore,
|
|
5769
5889
|
toolLocked = false,
|
|
5770
5890
|
autoResetToolTo = "select",
|
|
5771
5891
|
onToolChangeRequest,
|
|
@@ -5936,6 +6056,8 @@ var VectorViewport = react.forwardRef(
|
|
|
5936
6056
|
const itemsRef = react.useRef(items);
|
|
5937
6057
|
const onWorldPointerDownRef = react.useRef(onWorldPointerDown);
|
|
5938
6058
|
const onItemsChangeRef = react.useRef(onItemsChange);
|
|
6059
|
+
const assetStoreRef = react.useRef(assetStore);
|
|
6060
|
+
assetStoreRef.current = assetStore;
|
|
5939
6061
|
const customPlacementRef = react.useRef(customPlacement);
|
|
5940
6062
|
customPlacementRef.current = customPlacement;
|
|
5941
6063
|
const dragStateRef = react.useRef({ kind: "idle" });
|
|
@@ -6038,6 +6160,9 @@ var VectorViewport = react.forwardRef(
|
|
|
6038
6160
|
const [laserTrail, setLaserTrail] = react.useState([]);
|
|
6039
6161
|
const [eraserPreviewIds, setEraserPreviewIds] = react.useState([]);
|
|
6040
6162
|
const eraserPreviewIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
6163
|
+
const [marqueeCandidateIds, setMarqueeCandidateIds] = react.useState([]);
|
|
6164
|
+
const marqueeCandidateIdsRef = react.useRef([]);
|
|
6165
|
+
marqueeCandidateIdsRef.current = marqueeCandidateIds;
|
|
6041
6166
|
const resolvedSceneItems = react.useMemo(() => {
|
|
6042
6167
|
if (eraserPreviewIds.length === 0) return resolvedItems;
|
|
6043
6168
|
const hiddenIds = new Set(eraserPreviewIds);
|
|
@@ -6552,14 +6677,14 @@ var VectorViewport = react.forwardRef(
|
|
|
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_IN_STEP, {
|
|
6556
6681
|
x: rect.width / 2,
|
|
6557
6682
|
y: rect.height / 2
|
|
6558
6683
|
});
|
|
6559
6684
|
handled = true;
|
|
6560
6685
|
} else if (e.key === "-" || e.key === "_") {
|
|
6561
6686
|
const rect = el.getBoundingClientRect();
|
|
6562
|
-
camera.setZoom(camera.zoom
|
|
6687
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_OUT_STEP, {
|
|
6563
6688
|
x: rect.width / 2,
|
|
6564
6689
|
y: rect.height / 2
|
|
6565
6690
|
});
|
|
@@ -6828,7 +6953,7 @@ var VectorViewport = react.forwardRef(
|
|
|
6828
6953
|
const el = sceneContainerRef.current;
|
|
6829
6954
|
if (!camera || !el) return;
|
|
6830
6955
|
const rect = el.getBoundingClientRect();
|
|
6831
|
-
camera.setZoom(camera.zoom *
|
|
6956
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_IN_STEP, {
|
|
6832
6957
|
x: rect.width / 2,
|
|
6833
6958
|
y: rect.height / 2
|
|
6834
6959
|
});
|
|
@@ -6839,7 +6964,7 @@ var VectorViewport = react.forwardRef(
|
|
|
6839
6964
|
const el = sceneContainerRef.current;
|
|
6840
6965
|
if (!camera || !el) return;
|
|
6841
6966
|
const rect = el.getBoundingClientRect();
|
|
6842
|
-
camera.setZoom(camera.zoom
|
|
6967
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_OUT_STEP, {
|
|
6843
6968
|
x: rect.width / 2,
|
|
6844
6969
|
y: rect.height / 2
|
|
6845
6970
|
});
|
|
@@ -6857,13 +6982,13 @@ var VectorViewport = react.forwardRef(
|
|
|
6857
6982
|
if (isDefaultMarkerToolStyle(current)) {
|
|
6858
6983
|
setCurrentStrokeStyle({
|
|
6859
6984
|
stroke: DEFAULT_STROKE_STYLE.stroke,
|
|
6860
|
-
strokeWidth: toolId === "draw" ?
|
|
6985
|
+
strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth
|
|
6861
6986
|
});
|
|
6862
6987
|
return;
|
|
6863
6988
|
}
|
|
6864
6989
|
setCurrentStrokeStyle({
|
|
6865
6990
|
stroke: current.stroke,
|
|
6866
|
-
strokeWidth: toolId === "draw" ?
|
|
6991
|
+
strokeWidth: toolId === "draw" ? 10 : current.strokeWidth
|
|
6867
6992
|
});
|
|
6868
6993
|
}, [setCurrentStrokeStyle, toolId]);
|
|
6869
6994
|
react.useEffect(() => {
|
|
@@ -6965,97 +7090,49 @@ var VectorViewport = react.forwardRef(
|
|
|
6965
7090
|
if (!change || files.length === 0) return;
|
|
6966
7091
|
const store = imageStoreRef.current;
|
|
6967
7092
|
if (!store) return;
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
const estPages = 10;
|
|
6979
|
-
const skels = [];
|
|
6980
|
-
const skelIds = [];
|
|
6981
|
-
for (let i = 0; i < estPages; i++) {
|
|
7093
|
+
try {
|
|
7094
|
+
const pdfFiles = files.filter((file) => file.type === "application/pdf");
|
|
7095
|
+
if (pdfFiles.length > 0) {
|
|
7096
|
+
const gapWorld = 16;
|
|
7097
|
+
const estW = 1241;
|
|
7098
|
+
const estH = 1754;
|
|
7099
|
+
const estPages = 10;
|
|
7100
|
+
const skels = [];
|
|
7101
|
+
for (const [fileIndex] of pdfFiles.entries()) {
|
|
7102
|
+
for (let pageIndex = 0; pageIndex < estPages; pageIndex++) {
|
|
6982
7103
|
const id = `skeleton-${skeletonSeqRef.current++}`;
|
|
6983
|
-
|
|
7104
|
+
const offsetIndex = fileIndex * estPages + pageIndex;
|
|
6984
7105
|
skels.push({
|
|
6985
7106
|
id,
|
|
6986
7107
|
x: worldX - estW / 2,
|
|
6987
|
-
y: worldY - estH / 2 +
|
|
7108
|
+
y: worldY - estH / 2 + offsetIndex * (estH + gapWorld),
|
|
6988
7109
|
width: estW,
|
|
6989
7110
|
height: estH
|
|
6990
7111
|
});
|
|
6991
7112
|
}
|
|
6992
|
-
setLoadingSkeletons(skels);
|
|
6993
|
-
const pages = await loadPdfToStore(file, store);
|
|
6994
|
-
setLoadingSkeletons([]);
|
|
6995
|
-
for (const page of pages) {
|
|
6996
|
-
const { blobId, width, height } = page;
|
|
6997
|
-
const fullUrl = await createBlobUrlFromStore(store, blobId);
|
|
6998
|
-
const id = createShapeId();
|
|
6999
|
-
const bounds = {
|
|
7000
|
-
x: worldX - width / 2,
|
|
7001
|
-
y: worldY - height / 2 + placed * (height + gapWorld),
|
|
7002
|
-
width,
|
|
7003
|
-
height
|
|
7004
|
-
};
|
|
7005
|
-
newItems.push({
|
|
7006
|
-
id,
|
|
7007
|
-
x: bounds.x,
|
|
7008
|
-
y: bounds.y,
|
|
7009
|
-
bounds: { ...bounds },
|
|
7010
|
-
toolKind: "image",
|
|
7011
|
-
imageBlobId: blobId,
|
|
7012
|
-
imageRasterHref: fullUrl ?? void 0,
|
|
7013
|
-
imageIntrinsicSize: { width, height },
|
|
7014
|
-
childrenSvg: fullUrl ? buildRasterImageChildrenSvg2(fullUrl, { width, height }, bounds) : ""
|
|
7015
|
-
});
|
|
7016
|
-
placed++;
|
|
7017
|
-
}
|
|
7018
|
-
} else if (file.type.startsWith("image/")) {
|
|
7019
|
-
const { blobId, thumbnailBlobId, width, height } = await loadImageToStore(file, store);
|
|
7020
|
-
const fullUrl = await createBlobUrlFromStore(store, blobId);
|
|
7021
|
-
const thumbBlob = await store.getThumbnail(thumbnailBlobId);
|
|
7022
|
-
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
7023
|
-
const id = createShapeId();
|
|
7024
|
-
const ox = placed % 8 * stepWorld;
|
|
7025
|
-
const oy = Math.floor(placed / 8) * stepWorld;
|
|
7026
|
-
const bounds = {
|
|
7027
|
-
x: worldX - width / 2 + ox,
|
|
7028
|
-
y: worldY - height / 2 + oy,
|
|
7029
|
-
width,
|
|
7030
|
-
height
|
|
7031
|
-
};
|
|
7032
|
-
const href = thumbnailHref ?? fullUrl;
|
|
7033
|
-
newItems.push({
|
|
7034
|
-
id,
|
|
7035
|
-
x: bounds.x,
|
|
7036
|
-
y: bounds.y,
|
|
7037
|
-
bounds: { ...bounds },
|
|
7038
|
-
toolKind: "image",
|
|
7039
|
-
imageBlobId: blobId,
|
|
7040
|
-
imageThumbnailBlobId: thumbnailBlobId,
|
|
7041
|
-
imageRasterHref: fullUrl ?? void 0,
|
|
7042
|
-
imageThumbnailHref: thumbnailHref ?? void 0,
|
|
7043
|
-
imageIntrinsicSize: { width, height },
|
|
7044
|
-
childrenSvg: buildRasterImageChildrenSvg2(
|
|
7045
|
-
href ?? "",
|
|
7046
|
-
{ width, height },
|
|
7047
|
-
bounds
|
|
7048
|
-
)
|
|
7049
|
-
});
|
|
7050
|
-
placed++;
|
|
7051
7113
|
}
|
|
7052
|
-
|
|
7053
|
-
|
|
7114
|
+
setLoadingSkeletons(skels);
|
|
7115
|
+
}
|
|
7116
|
+
const result = await ingestAssetFilesToSceneItems({
|
|
7117
|
+
files,
|
|
7118
|
+
worldCenter: {
|
|
7119
|
+
x: worldX,
|
|
7120
|
+
y: worldY
|
|
7121
|
+
},
|
|
7122
|
+
imageStore: store,
|
|
7123
|
+
assetStore: assetStoreRef.current ?? void 0
|
|
7124
|
+
});
|
|
7125
|
+
if (result.errors.length > 0) {
|
|
7126
|
+
for (const error of result.errors) {
|
|
7127
|
+
console.error("Failed to add file:", error.error);
|
|
7128
|
+
}
|
|
7054
7129
|
}
|
|
7130
|
+
if (result.items.length === 0) return;
|
|
7131
|
+
change([...itemsRef.current, ...result.items]);
|
|
7132
|
+
setEffectiveSelectedIdsRef.current(result.items.map((item) => item.id));
|
|
7133
|
+
} finally {
|
|
7134
|
+
setLoadingSkeletons([]);
|
|
7055
7135
|
}
|
|
7056
|
-
if (newItems.length === 0) return;
|
|
7057
|
-
change([...itemsRef.current, ...newItems]);
|
|
7058
|
-
setEffectiveSelectedIdsRef.current(newItems.map((it) => it.id));
|
|
7059
7136
|
},
|
|
7060
7137
|
[]
|
|
7061
7138
|
);
|
|
@@ -7726,6 +7803,14 @@ var VectorViewport = react.forwardRef(
|
|
|
7726
7803
|
const { worldX: worldX2, worldY: worldY2 } = screenToWorld(ev.clientX, ev.clientY);
|
|
7727
7804
|
const raw = rectFromCorners(st.startWorld, { x: worldX2, y: worldY2 });
|
|
7728
7805
|
setPlacementPreview({ kind: "marquee", rect: raw });
|
|
7806
|
+
const nextCand = collectItemIdsInRect(
|
|
7807
|
+
resolvedItemsRef.current,
|
|
7808
|
+
normalizeRect(raw)
|
|
7809
|
+
);
|
|
7810
|
+
if (!shallowEqualStringArray(nextCand, marqueeCandidateIdsRef.current)) {
|
|
7811
|
+
marqueeCandidateIdsRef.current = nextCand;
|
|
7812
|
+
setMarqueeCandidateIds(nextCand);
|
|
7813
|
+
}
|
|
7729
7814
|
return;
|
|
7730
7815
|
}
|
|
7731
7816
|
if (st.kind === "stroke") {
|
|
@@ -7905,6 +7990,8 @@ var VectorViewport = react.forwardRef(
|
|
|
7905
7990
|
dragStateRef.current = { kind: "idle" };
|
|
7906
7991
|
releaseInteractionPointer();
|
|
7907
7992
|
setPlacementPreview(null);
|
|
7993
|
+
marqueeCandidateIdsRef.current = [];
|
|
7994
|
+
setMarqueeCandidateIds([]);
|
|
7908
7995
|
return;
|
|
7909
7996
|
}
|
|
7910
7997
|
if (st.kind === "move" || st.kind === "resize" || st.kind === "rotate") {
|
|
@@ -7916,6 +8003,8 @@ var VectorViewport = react.forwardRef(
|
|
|
7916
8003
|
dragStateRef.current = { kind: "idle" };
|
|
7917
8004
|
releaseInteractionPointer();
|
|
7918
8005
|
setPlacementPreview(null);
|
|
8006
|
+
marqueeCandidateIdsRef.current = [];
|
|
8007
|
+
setMarqueeCandidateIds([]);
|
|
7919
8008
|
const { worldX, worldY } = screenToWorld(ev.clientX, ev.clientY);
|
|
7920
8009
|
const raw = rectFromCorners(st.startWorld, { x: worldX, y: worldY });
|
|
7921
8010
|
const br = normalizeRect(raw);
|
|
@@ -8146,6 +8235,11 @@ var VectorViewport = react.forwardRef(
|
|
|
8146
8235
|
const eraserPreviewItemsForOverlay = react.useMemo(() => {
|
|
8147
8236
|
return eraserPreviewIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
|
|
8148
8237
|
}, [eraserPreviewIds, resolvedItems]);
|
|
8238
|
+
const marqueeCandidateItemsForOverlay = react.useMemo(() => {
|
|
8239
|
+
if (marqueeCandidateIds.length === 0) return [];
|
|
8240
|
+
const selected = new Set(effectiveSelectedIds);
|
|
8241
|
+
return marqueeCandidateIds.filter((id) => !selected.has(id)).map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
|
|
8242
|
+
}, [marqueeCandidateIds, effectiveSelectedIds, resolvedItems]);
|
|
8149
8243
|
const presenceLayer = react.useMemo(() => {
|
|
8150
8244
|
if (!cameraForOverlay) return null;
|
|
8151
8245
|
if (presenceOverlay) {
|
|
@@ -8291,6 +8385,7 @@ var VectorViewport = react.forwardRef(
|
|
|
8291
8385
|
eraserTrail,
|
|
8292
8386
|
laserTrail,
|
|
8293
8387
|
eraserPreviewItems: eraserPreviewItemsForOverlay,
|
|
8388
|
+
marqueeCandidateItems: marqueeCandidateItemsForOverlay,
|
|
8294
8389
|
previewStrokeStyle: strokeStyleRef.current
|
|
8295
8390
|
}
|
|
8296
8391
|
),
|
|
@@ -8542,6 +8637,7 @@ exports.createNoopPersistenceAdapter = createNoopPersistenceAdapter;
|
|
|
8542
8637
|
exports.createToolPlugin = createToolPlugin;
|
|
8543
8638
|
exports.cursorForVectorToolId = cursorForVectorToolId;
|
|
8544
8639
|
exports.getBoardPositionStyle = getBoardPositionStyle;
|
|
8640
|
+
exports.ingestAssetFilesToSceneItems = ingestAssetFilesToSceneItems;
|
|
8545
8641
|
exports.useCanvuChromeContext = useCanvuChromeContext;
|
|
8546
8642
|
exports.useCanvuDocumentContext = useCanvuDocumentContext;
|
|
8547
8643
|
exports.useCanvuPluginContext = useCanvuPluginContext;
|