canvu-react 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -0
- package/dist/chatbot.d.cts +1 -1
- package/dist/chatbot.d.ts +1 -1
- package/dist/index.cjs +32 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +32 -144
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +31 -109
- package/dist/native.cjs.map +1 -1
- package/dist/native.js +31 -109
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +1388 -1353
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +147 -4
- package/dist/react.d.ts +147 -4
- package/dist/react.js +1388 -1354
- package/dist/react.js.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/tldraw.cjs +30 -142
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.js +30 -142
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-B_rv7p8b.d.cts → types-CW146bKP.d.cts} +117 -1
- package/dist/{types-BCtWx3zP.d.ts → types-CpqlbbCP.d.ts} +117 -1
- package/package.json +1 -1
package/dist/react.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({
|
|
@@ -5766,6 +5845,7 @@ var VectorViewport = react.forwardRef(
|
|
|
5766
5845
|
onCameraChange: consumerOnCameraChange,
|
|
5767
5846
|
customPlacement: consumerCustomPlacement,
|
|
5768
5847
|
customPlacements: consumerCustomPlacements,
|
|
5848
|
+
assetStore,
|
|
5769
5849
|
toolLocked = false,
|
|
5770
5850
|
autoResetToolTo = "select",
|
|
5771
5851
|
onToolChangeRequest,
|
|
@@ -5936,6 +6016,8 @@ var VectorViewport = react.forwardRef(
|
|
|
5936
6016
|
const itemsRef = react.useRef(items);
|
|
5937
6017
|
const onWorldPointerDownRef = react.useRef(onWorldPointerDown);
|
|
5938
6018
|
const onItemsChangeRef = react.useRef(onItemsChange);
|
|
6019
|
+
const assetStoreRef = react.useRef(assetStore);
|
|
6020
|
+
assetStoreRef.current = assetStore;
|
|
5939
6021
|
const customPlacementRef = react.useRef(customPlacement);
|
|
5940
6022
|
customPlacementRef.current = customPlacement;
|
|
5941
6023
|
const dragStateRef = react.useRef({ kind: "idle" });
|
|
@@ -6552,14 +6634,14 @@ var VectorViewport = react.forwardRef(
|
|
|
6552
6634
|
handled = true;
|
|
6553
6635
|
} else if (e.key === "+" || e.key === "=") {
|
|
6554
6636
|
const rect = el.getBoundingClientRect();
|
|
6555
|
-
camera.setZoom(camera.zoom *
|
|
6637
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_IN_STEP, {
|
|
6556
6638
|
x: rect.width / 2,
|
|
6557
6639
|
y: rect.height / 2
|
|
6558
6640
|
});
|
|
6559
6641
|
handled = true;
|
|
6560
6642
|
} else if (e.key === "-" || e.key === "_") {
|
|
6561
6643
|
const rect = el.getBoundingClientRect();
|
|
6562
|
-
camera.setZoom(camera.zoom
|
|
6644
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_OUT_STEP, {
|
|
6563
6645
|
x: rect.width / 2,
|
|
6564
6646
|
y: rect.height / 2
|
|
6565
6647
|
});
|
|
@@ -6828,7 +6910,7 @@ var VectorViewport = react.forwardRef(
|
|
|
6828
6910
|
const el = sceneContainerRef.current;
|
|
6829
6911
|
if (!camera || !el) return;
|
|
6830
6912
|
const rect = el.getBoundingClientRect();
|
|
6831
|
-
camera.setZoom(camera.zoom *
|
|
6913
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_IN_STEP, {
|
|
6832
6914
|
x: rect.width / 2,
|
|
6833
6915
|
y: rect.height / 2
|
|
6834
6916
|
});
|
|
@@ -6839,7 +6921,7 @@ var VectorViewport = react.forwardRef(
|
|
|
6839
6921
|
const el = sceneContainerRef.current;
|
|
6840
6922
|
if (!camera || !el) return;
|
|
6841
6923
|
const rect = el.getBoundingClientRect();
|
|
6842
|
-
camera.setZoom(camera.zoom
|
|
6924
|
+
camera.setZoom(camera.zoom * VIEWPORT_ZOOM_OUT_STEP, {
|
|
6843
6925
|
x: rect.width / 2,
|
|
6844
6926
|
y: rect.height / 2
|
|
6845
6927
|
});
|
|
@@ -6857,13 +6939,13 @@ var VectorViewport = react.forwardRef(
|
|
|
6857
6939
|
if (isDefaultMarkerToolStyle(current)) {
|
|
6858
6940
|
setCurrentStrokeStyle({
|
|
6859
6941
|
stroke: DEFAULT_STROKE_STYLE.stroke,
|
|
6860
|
-
strokeWidth: toolId === "draw" ?
|
|
6942
|
+
strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth
|
|
6861
6943
|
});
|
|
6862
6944
|
return;
|
|
6863
6945
|
}
|
|
6864
6946
|
setCurrentStrokeStyle({
|
|
6865
6947
|
stroke: current.stroke,
|
|
6866
|
-
strokeWidth: toolId === "draw" ?
|
|
6948
|
+
strokeWidth: toolId === "draw" ? 10 : current.strokeWidth
|
|
6867
6949
|
});
|
|
6868
6950
|
}, [setCurrentStrokeStyle, toolId]);
|
|
6869
6951
|
react.useEffect(() => {
|
|
@@ -6965,97 +7047,49 @@ var VectorViewport = react.forwardRef(
|
|
|
6965
7047
|
if (!change || files.length === 0) return;
|
|
6966
7048
|
const store = imageStoreRef.current;
|
|
6967
7049
|
if (!store) return;
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
const estPages = 10;
|
|
6979
|
-
const skels = [];
|
|
6980
|
-
const skelIds = [];
|
|
6981
|
-
for (let i = 0; i < estPages; i++) {
|
|
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++) {
|
|
6982
7060
|
const id = `skeleton-${skeletonSeqRef.current++}`;
|
|
6983
|
-
|
|
7061
|
+
const offsetIndex = fileIndex * estPages + pageIndex;
|
|
6984
7062
|
skels.push({
|
|
6985
7063
|
id,
|
|
6986
7064
|
x: worldX - estW / 2,
|
|
6987
|
-
y: worldY - estH / 2 +
|
|
7065
|
+
y: worldY - estH / 2 + offsetIndex * (estH + gapWorld),
|
|
6988
7066
|
width: estW,
|
|
6989
7067
|
height: estH
|
|
6990
7068
|
});
|
|
6991
7069
|
}
|
|
6992
|
-
setLoadingSkeletons(skels);
|
|
6993
|
-
const pages = await loadPdfToStore(file, store);
|
|
6994
|
-
setLoadingSkeletons([]);
|
|
6995
|
-
for (const page of pages) {
|
|
6996
|
-
const { blobId, width, height } = page;
|
|
6997
|
-
const fullUrl = await createBlobUrlFromStore(store, blobId);
|
|
6998
|
-
const id = createShapeId();
|
|
6999
|
-
const bounds = {
|
|
7000
|
-
x: worldX - width / 2,
|
|
7001
|
-
y: worldY - height / 2 + placed * (height + gapWorld),
|
|
7002
|
-
width,
|
|
7003
|
-
height
|
|
7004
|
-
};
|
|
7005
|
-
newItems.push({
|
|
7006
|
-
id,
|
|
7007
|
-
x: bounds.x,
|
|
7008
|
-
y: bounds.y,
|
|
7009
|
-
bounds: { ...bounds },
|
|
7010
|
-
toolKind: "image",
|
|
7011
|
-
imageBlobId: blobId,
|
|
7012
|
-
imageRasterHref: fullUrl ?? void 0,
|
|
7013
|
-
imageIntrinsicSize: { width, height },
|
|
7014
|
-
childrenSvg: fullUrl ? buildRasterImageChildrenSvg2(fullUrl, { width, height }, bounds) : ""
|
|
7015
|
-
});
|
|
7016
|
-
placed++;
|
|
7017
|
-
}
|
|
7018
|
-
} else if (file.type.startsWith("image/")) {
|
|
7019
|
-
const { blobId, thumbnailBlobId, width, height } = await loadImageToStore(file, store);
|
|
7020
|
-
const fullUrl = await createBlobUrlFromStore(store, blobId);
|
|
7021
|
-
const thumbBlob = await store.getThumbnail(thumbnailBlobId);
|
|
7022
|
-
const thumbnailHref = thumbBlob ? URL.createObjectURL(thumbBlob) : null;
|
|
7023
|
-
const id = createShapeId();
|
|
7024
|
-
const ox = placed % 8 * stepWorld;
|
|
7025
|
-
const oy = Math.floor(placed / 8) * stepWorld;
|
|
7026
|
-
const bounds = {
|
|
7027
|
-
x: worldX - width / 2 + ox,
|
|
7028
|
-
y: worldY - height / 2 + oy,
|
|
7029
|
-
width,
|
|
7030
|
-
height
|
|
7031
|
-
};
|
|
7032
|
-
const href = thumbnailHref ?? fullUrl;
|
|
7033
|
-
newItems.push({
|
|
7034
|
-
id,
|
|
7035
|
-
x: bounds.x,
|
|
7036
|
-
y: bounds.y,
|
|
7037
|
-
bounds: { ...bounds },
|
|
7038
|
-
toolKind: "image",
|
|
7039
|
-
imageBlobId: blobId,
|
|
7040
|
-
imageThumbnailBlobId: thumbnailBlobId,
|
|
7041
|
-
imageRasterHref: fullUrl ?? void 0,
|
|
7042
|
-
imageThumbnailHref: thumbnailHref ?? void 0,
|
|
7043
|
-
imageIntrinsicSize: { width, height },
|
|
7044
|
-
childrenSvg: buildRasterImageChildrenSvg2(
|
|
7045
|
-
href ?? "",
|
|
7046
|
-
{ width, height },
|
|
7047
|
-
bounds
|
|
7048
|
-
)
|
|
7049
|
-
});
|
|
7050
|
-
placed++;
|
|
7051
7070
|
}
|
|
7052
|
-
|
|
7053
|
-
|
|
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
|
+
}
|
|
7054
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([]);
|
|
7055
7092
|
}
|
|
7056
|
-
if (newItems.length === 0) return;
|
|
7057
|
-
change([...itemsRef.current, ...newItems]);
|
|
7058
|
-
setEffectiveSelectedIdsRef.current(newItems.map((it) => it.id));
|
|
7059
7093
|
},
|
|
7060
7094
|
[]
|
|
7061
7095
|
);
|
|
@@ -8542,6 +8576,7 @@ exports.createNoopPersistenceAdapter = createNoopPersistenceAdapter;
|
|
|
8542
8576
|
exports.createToolPlugin = createToolPlugin;
|
|
8543
8577
|
exports.cursorForVectorToolId = cursorForVectorToolId;
|
|
8544
8578
|
exports.getBoardPositionStyle = getBoardPositionStyle;
|
|
8579
|
+
exports.ingestAssetFilesToSceneItems = ingestAssetFilesToSceneItems;
|
|
8545
8580
|
exports.useCanvuChromeContext = useCanvuChromeContext;
|
|
8546
8581
|
exports.useCanvuDocumentContext = useCanvuDocumentContext;
|
|
8547
8582
|
exports.useCanvuPluginContext = useCanvuPluginContext;
|