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