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