canvu-react 0.4.4 → 0.4.6
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/dist/{asset-hydration-B7yMDQE-.d.cts → asset-hydration-CWhld14A.d.cts} +1 -1
- package/dist/{asset-hydration-CbwQVAwh.d.ts → asset-hydration-D35mHbUP.d.ts} +1 -1
- package/dist/chatbot.d.cts +3 -3
- package/dist/chatbot.d.ts +3 -3
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/link-item-Bg5vj0RI.d.ts +151 -0
- package/dist/link-item-D870_X6P.d.cts +151 -0
- package/dist/native.cjs +539 -142
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +82 -4
- package/dist/native.d.ts +82 -4
- package/dist/native.js +537 -144
- package/dist/native.js.map +1 -1
- package/dist/react.d.cts +7 -7
- package/dist/react.d.ts +7 -7
- package/dist/realtime.d.cts +4 -4
- package/dist/realtime.d.ts +4 -4
- package/dist/{shape-builders-BAWu-PxX.d.cts → shape-builders-CKEMjivV.d.cts} +42 -143
- package/dist/{shape-builders-ClKv9tz9.d.ts → shape-builders-Cyh8zvDG.d.ts} +42 -143
- package/dist/{types-BC9Xgfu6.d.cts → types-Bw1SdC9v.d.cts} +2 -2
- package/dist/{types-DlSVGX0w.d.ts → types-DeXFfs7Y.d.ts} +2 -2
- package/package.json +1 -1
- package/dist/camera-CVVG7z56.d.cts +0 -50
- package/dist/camera-CoRYN_IV.d.ts +0 -50
package/dist/native.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var getStroke = require('perfect-freehand');
|
|
3
4
|
var reactNativeSkia = require('@shopify/react-native-skia');
|
|
4
5
|
var react = require('react');
|
|
5
|
-
var getStroke = require('perfect-freehand');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
7
|
var reactNative = require('react-native');
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
10
10
|
|
|
11
11
|
var getStroke__default = /*#__PURE__*/_interopDefault(getStroke);
|
|
12
12
|
|
|
13
|
-
// src/
|
|
13
|
+
// src/scene/shape-builders.ts
|
|
14
14
|
|
|
15
15
|
// src/math/rect.ts
|
|
16
16
|
function rectsIntersect(a, b) {
|
|
@@ -27,141 +27,6 @@ function normalizeRect(r) {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// src/math/item-transform.ts
|
|
31
|
-
function getItemRotationRad(item) {
|
|
32
|
-
return item.rotation ?? 0;
|
|
33
|
-
}
|
|
34
|
-
function itemLocalToWorld(lx, ly, itemX, itemY, w, h, rotationRad) {
|
|
35
|
-
const c = { x: w / 2, y: h / 2 };
|
|
36
|
-
const dlx = lx - c.x;
|
|
37
|
-
const dly = ly - c.y;
|
|
38
|
-
const cos = Math.cos(rotationRad);
|
|
39
|
-
const sin = Math.sin(rotationRad);
|
|
40
|
-
return {
|
|
41
|
-
x: itemX + c.x + cos * dlx - sin * dly,
|
|
42
|
-
y: itemY + c.y + sin * dlx + cos * dly
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
function worldToItemLocal(wx, wy, itemX, itemY, w, h, rotationRad) {
|
|
46
|
-
const c = { x: w / 2, y: h / 2 };
|
|
47
|
-
const vx = wx - itemX;
|
|
48
|
-
const vy = wy - itemY;
|
|
49
|
-
const dx = vx - c.x;
|
|
50
|
-
const dy = vy - c.y;
|
|
51
|
-
const cos = Math.cos(-rotationRad);
|
|
52
|
-
const sin = Math.sin(-rotationRad);
|
|
53
|
-
const lx = cos * dx - sin * dy;
|
|
54
|
-
const ly = sin * dx + cos * dy;
|
|
55
|
-
return { x: c.x + lx, y: c.y + ly };
|
|
56
|
-
}
|
|
57
|
-
function boundsAabbForRotatedItem(item) {
|
|
58
|
-
const rot = getItemRotationRad(item);
|
|
59
|
-
if (Math.abs(rot) < 1e-12 && item.bounds.width >= 0 && item.bounds.height >= 0) {
|
|
60
|
-
return item.bounds;
|
|
61
|
-
}
|
|
62
|
-
const r = normalizeRect(item.bounds);
|
|
63
|
-
if (Math.abs(rot) < 1e-12) {
|
|
64
|
-
return r;
|
|
65
|
-
}
|
|
66
|
-
const corners = [
|
|
67
|
-
[0, 0],
|
|
68
|
-
[r.width, 0],
|
|
69
|
-
[r.width, r.height],
|
|
70
|
-
[0, r.height]
|
|
71
|
-
];
|
|
72
|
-
let minX = Infinity;
|
|
73
|
-
let minY = Infinity;
|
|
74
|
-
let maxX = -Infinity;
|
|
75
|
-
let maxY = -Infinity;
|
|
76
|
-
for (const [lx, ly] of corners) {
|
|
77
|
-
const p = itemLocalToWorld(lx, ly, item.x, item.y, r.width, r.height, rot);
|
|
78
|
-
minX = Math.min(minX, p.x);
|
|
79
|
-
minY = Math.min(minY, p.y);
|
|
80
|
-
maxX = Math.max(maxX, p.x);
|
|
81
|
-
maxY = Math.max(maxY, p.y);
|
|
82
|
-
}
|
|
83
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// src/interaction/resize-handles.ts
|
|
87
|
-
function getHandleWorldPosition(bounds, id) {
|
|
88
|
-
const r = normalizeRect(bounds);
|
|
89
|
-
const cx = r.x + r.width / 2;
|
|
90
|
-
const cy = r.y + r.height / 2;
|
|
91
|
-
switch (id) {
|
|
92
|
-
case "nw":
|
|
93
|
-
return { x: r.x, y: r.y };
|
|
94
|
-
case "n":
|
|
95
|
-
return { x: cx, y: r.y };
|
|
96
|
-
case "ne":
|
|
97
|
-
return { x: r.x + r.width, y: r.y };
|
|
98
|
-
case "e":
|
|
99
|
-
return { x: r.x + r.width, y: cy };
|
|
100
|
-
case "se":
|
|
101
|
-
return { x: r.x + r.width, y: r.y + r.height };
|
|
102
|
-
case "s":
|
|
103
|
-
return { x: cx, y: r.y + r.height };
|
|
104
|
-
case "sw":
|
|
105
|
-
return { x: r.x, y: r.y + r.height };
|
|
106
|
-
case "w":
|
|
107
|
-
return { x: r.x, y: cy };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function getHandleWorldPositionRotated(bounds, handle, rotationRad) {
|
|
111
|
-
const r = normalizeRect(bounds);
|
|
112
|
-
const p = getHandleWorldPosition(
|
|
113
|
-
{ x: 0, y: 0, width: r.width, height: r.height },
|
|
114
|
-
handle
|
|
115
|
-
);
|
|
116
|
-
return itemLocalToWorld(p.x, p.y, r.x, r.y, r.width, r.height, rotationRad);
|
|
117
|
-
}
|
|
118
|
-
function getRotationHandleWorldPosition(bounds, rotationRad, handleOffsetWorld) {
|
|
119
|
-
const r = normalizeRect(bounds);
|
|
120
|
-
return itemLocalToWorld(
|
|
121
|
-
r.width / 2,
|
|
122
|
-
-handleOffsetWorld,
|
|
123
|
-
r.x,
|
|
124
|
-
r.y,
|
|
125
|
-
r.width,
|
|
126
|
-
r.height,
|
|
127
|
-
rotationRad
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// src/scene/freehand-path.ts
|
|
132
|
-
function smoothFreehandPointsToPathD(points) {
|
|
133
|
-
const n = points.length;
|
|
134
|
-
if (n === 0) return "";
|
|
135
|
-
if (n === 1) {
|
|
136
|
-
const p = points[0];
|
|
137
|
-
if (!p) return "";
|
|
138
|
-
return `M ${p.x} ${p.y}`;
|
|
139
|
-
}
|
|
140
|
-
if (n === 2) {
|
|
141
|
-
const a = points[0];
|
|
142
|
-
const b = points[1];
|
|
143
|
-
if (!a || !b) return "";
|
|
144
|
-
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
145
|
-
}
|
|
146
|
-
const p0 = points[0];
|
|
147
|
-
if (!p0) return "";
|
|
148
|
-
let d = `M ${p0.x} ${p0.y}`;
|
|
149
|
-
let i = 1;
|
|
150
|
-
for (; i < n - 2; i++) {
|
|
151
|
-
const pi = points[i];
|
|
152
|
-
const pi1 = points[i + 1];
|
|
153
|
-
if (!pi || !pi1) continue;
|
|
154
|
-
const xc = (pi.x + pi1.x) / 2;
|
|
155
|
-
const yc = (pi.y + pi1.y) / 2;
|
|
156
|
-
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
157
|
-
}
|
|
158
|
-
const pLast = points[i];
|
|
159
|
-
const pEnd = points[i + 1];
|
|
160
|
-
if (!pLast || !pEnd) return d;
|
|
161
|
-
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
162
|
-
return d;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
30
|
// src/scene/custom-shape.ts
|
|
166
31
|
function buildCustomShapeChildrenSvg(inner, intrinsic, bounds) {
|
|
167
32
|
const b = normalizeRect(bounds);
|
|
@@ -786,6 +651,15 @@ function createShapeId() {
|
|
|
786
651
|
const uid = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : String(Date.now());
|
|
787
652
|
return `user-shape-${uid}`;
|
|
788
653
|
}
|
|
654
|
+
function lineEndpointsToLocal(bounds, worldEndA, worldEndB) {
|
|
655
|
+
const b = normalizeRect(bounds);
|
|
656
|
+
return {
|
|
657
|
+
x1: worldEndA.x - b.x,
|
|
658
|
+
y1: worldEndA.y - b.y,
|
|
659
|
+
x2: worldEndB.x - b.x,
|
|
660
|
+
y2: worldEndB.y - b.y
|
|
661
|
+
};
|
|
662
|
+
}
|
|
789
663
|
function rebuildItemSvg(item) {
|
|
790
664
|
const style = resolveStrokeStyle(item);
|
|
791
665
|
const k = item.toolKind;
|
|
@@ -889,6 +763,85 @@ function rebuildItemSvg(item) {
|
|
|
889
763
|
}
|
|
890
764
|
return item;
|
|
891
765
|
}
|
|
766
|
+
function createRectangleItem(id, bounds, style) {
|
|
767
|
+
const r = normalizeRect(bounds);
|
|
768
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
769
|
+
return rebuildItemSvg({
|
|
770
|
+
id,
|
|
771
|
+
x: r.x,
|
|
772
|
+
y: r.y,
|
|
773
|
+
bounds: { ...r },
|
|
774
|
+
toolKind: "rect",
|
|
775
|
+
stroke: s.stroke,
|
|
776
|
+
strokeWidth: s.strokeWidth,
|
|
777
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
778
|
+
childrenSvg: ""
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
function createEllipseItem(id, bounds, style) {
|
|
782
|
+
const r = normalizeRect(bounds);
|
|
783
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
784
|
+
return rebuildItemSvg({
|
|
785
|
+
id,
|
|
786
|
+
x: r.x,
|
|
787
|
+
y: r.y,
|
|
788
|
+
bounds: { ...r },
|
|
789
|
+
toolKind: "ellipse",
|
|
790
|
+
stroke: s.stroke,
|
|
791
|
+
strokeWidth: s.strokeWidth,
|
|
792
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
793
|
+
childrenSvg: ""
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
function createArchitecturalCloudItem(id, bounds, style) {
|
|
797
|
+
const r = normalizeRect(bounds);
|
|
798
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
799
|
+
return rebuildItemSvg({
|
|
800
|
+
id,
|
|
801
|
+
x: r.x,
|
|
802
|
+
y: r.y,
|
|
803
|
+
bounds: { ...r },
|
|
804
|
+
toolKind: "architectural-cloud",
|
|
805
|
+
stroke: s.stroke,
|
|
806
|
+
strokeWidth: s.strokeWidth,
|
|
807
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
808
|
+
childrenSvg: ""
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
function createLineItem(id, bounds, line, toolKind, style, arrowBind) {
|
|
812
|
+
const r = normalizeRect(bounds);
|
|
813
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
814
|
+
return rebuildItemSvg({
|
|
815
|
+
id,
|
|
816
|
+
x: r.x,
|
|
817
|
+
y: r.y,
|
|
818
|
+
bounds: { ...r },
|
|
819
|
+
toolKind,
|
|
820
|
+
line: { ...line },
|
|
821
|
+
stroke: s.stroke,
|
|
822
|
+
strokeWidth: s.strokeWidth,
|
|
823
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
824
|
+
...{},
|
|
825
|
+
childrenSvg: ""
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
function createTextItem(id, bounds, text = "", style, textFontSize) {
|
|
829
|
+
const r = normalizeRect(bounds);
|
|
830
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
831
|
+
return rebuildItemSvg({
|
|
832
|
+
id,
|
|
833
|
+
x: r.x,
|
|
834
|
+
y: r.y,
|
|
835
|
+
bounds: { ...r },
|
|
836
|
+
toolKind: "text",
|
|
837
|
+
text,
|
|
838
|
+
stroke: s.stroke,
|
|
839
|
+
strokeWidth: s.strokeWidth,
|
|
840
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
841
|
+
...{ textFontSize } ,
|
|
842
|
+
childrenSvg: ""
|
|
843
|
+
});
|
|
844
|
+
}
|
|
892
845
|
function createFreehandStrokeItem(id, pointsWorld, toolKind, style) {
|
|
893
846
|
if (pointsWorld.length === 0) return null;
|
|
894
847
|
const merged = {
|
|
@@ -947,6 +900,148 @@ function buildRasterImageChildrenSvg(dataUrl, intrinsic, bounds) {
|
|
|
947
900
|
return `<g transform="translate(${tx}, ${ty}) scale(${s})"><image href="${href}" x="0" y="0" width="${iw}" height="${ih}" /></g>`;
|
|
948
901
|
}
|
|
949
902
|
|
|
903
|
+
// src/math/item-transform.ts
|
|
904
|
+
function getItemRotationRad(item) {
|
|
905
|
+
return item.rotation ?? 0;
|
|
906
|
+
}
|
|
907
|
+
function itemLocalToWorld(lx, ly, itemX, itemY, w, h, rotationRad) {
|
|
908
|
+
const c = { x: w / 2, y: h / 2 };
|
|
909
|
+
const dlx = lx - c.x;
|
|
910
|
+
const dly = ly - c.y;
|
|
911
|
+
const cos = Math.cos(rotationRad);
|
|
912
|
+
const sin = Math.sin(rotationRad);
|
|
913
|
+
return {
|
|
914
|
+
x: itemX + c.x + cos * dlx - sin * dly,
|
|
915
|
+
y: itemY + c.y + sin * dlx + cos * dly
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function worldToItemLocal(wx, wy, itemX, itemY, w, h, rotationRad) {
|
|
919
|
+
const c = { x: w / 2, y: h / 2 };
|
|
920
|
+
const vx = wx - itemX;
|
|
921
|
+
const vy = wy - itemY;
|
|
922
|
+
const dx = vx - c.x;
|
|
923
|
+
const dy = vy - c.y;
|
|
924
|
+
const cos = Math.cos(-rotationRad);
|
|
925
|
+
const sin = Math.sin(-rotationRad);
|
|
926
|
+
const lx = cos * dx - sin * dy;
|
|
927
|
+
const ly = sin * dx + cos * dy;
|
|
928
|
+
return { x: c.x + lx, y: c.y + ly };
|
|
929
|
+
}
|
|
930
|
+
function boundsAabbForRotatedItem(item) {
|
|
931
|
+
const rot = getItemRotationRad(item);
|
|
932
|
+
if (Math.abs(rot) < 1e-12 && item.bounds.width >= 0 && item.bounds.height >= 0) {
|
|
933
|
+
return item.bounds;
|
|
934
|
+
}
|
|
935
|
+
const r = normalizeRect(item.bounds);
|
|
936
|
+
if (Math.abs(rot) < 1e-12) {
|
|
937
|
+
return r;
|
|
938
|
+
}
|
|
939
|
+
const corners = [
|
|
940
|
+
[0, 0],
|
|
941
|
+
[r.width, 0],
|
|
942
|
+
[r.width, r.height],
|
|
943
|
+
[0, r.height]
|
|
944
|
+
];
|
|
945
|
+
let minX = Infinity;
|
|
946
|
+
let minY = Infinity;
|
|
947
|
+
let maxX = -Infinity;
|
|
948
|
+
let maxY = -Infinity;
|
|
949
|
+
for (const [lx, ly] of corners) {
|
|
950
|
+
const p = itemLocalToWorld(lx, ly, item.x, item.y, r.width, r.height, rot);
|
|
951
|
+
minX = Math.min(minX, p.x);
|
|
952
|
+
minY = Math.min(minY, p.y);
|
|
953
|
+
maxX = Math.max(maxX, p.x);
|
|
954
|
+
maxY = Math.max(maxY, p.y);
|
|
955
|
+
}
|
|
956
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// src/interaction/resize-handles.ts
|
|
960
|
+
function getHandleWorldPosition(bounds, id) {
|
|
961
|
+
const r = normalizeRect(bounds);
|
|
962
|
+
const cx = r.x + r.width / 2;
|
|
963
|
+
const cy = r.y + r.height / 2;
|
|
964
|
+
switch (id) {
|
|
965
|
+
case "nw":
|
|
966
|
+
return { x: r.x, y: r.y };
|
|
967
|
+
case "n":
|
|
968
|
+
return { x: cx, y: r.y };
|
|
969
|
+
case "ne":
|
|
970
|
+
return { x: r.x + r.width, y: r.y };
|
|
971
|
+
case "e":
|
|
972
|
+
return { x: r.x + r.width, y: cy };
|
|
973
|
+
case "se":
|
|
974
|
+
return { x: r.x + r.width, y: r.y + r.height };
|
|
975
|
+
case "s":
|
|
976
|
+
return { x: cx, y: r.y + r.height };
|
|
977
|
+
case "sw":
|
|
978
|
+
return { x: r.x, y: r.y + r.height };
|
|
979
|
+
case "w":
|
|
980
|
+
return { x: r.x, y: cy };
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
function getHandleWorldPositionRotated(bounds, handle, rotationRad) {
|
|
984
|
+
const r = normalizeRect(bounds);
|
|
985
|
+
const p = getHandleWorldPosition(
|
|
986
|
+
{ x: 0, y: 0, width: r.width, height: r.height },
|
|
987
|
+
handle
|
|
988
|
+
);
|
|
989
|
+
return itemLocalToWorld(p.x, p.y, r.x, r.y, r.width, r.height, rotationRad);
|
|
990
|
+
}
|
|
991
|
+
function getRotationHandleWorldPosition(bounds, rotationRad, handleOffsetWorld) {
|
|
992
|
+
const r = normalizeRect(bounds);
|
|
993
|
+
return itemLocalToWorld(
|
|
994
|
+
r.width / 2,
|
|
995
|
+
-handleOffsetWorld,
|
|
996
|
+
r.x,
|
|
997
|
+
r.y,
|
|
998
|
+
r.width,
|
|
999
|
+
r.height,
|
|
1000
|
+
rotationRad
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
function rectFromCorners(a, b) {
|
|
1004
|
+
const minX = Math.min(a.x, b.x);
|
|
1005
|
+
const maxX = Math.max(a.x, b.x);
|
|
1006
|
+
const minY = Math.min(a.y, b.y);
|
|
1007
|
+
const maxY = Math.max(a.y, b.y);
|
|
1008
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/scene/freehand-path.ts
|
|
1012
|
+
function smoothFreehandPointsToPathD(points) {
|
|
1013
|
+
const n = points.length;
|
|
1014
|
+
if (n === 0) return "";
|
|
1015
|
+
if (n === 1) {
|
|
1016
|
+
const p = points[0];
|
|
1017
|
+
if (!p) return "";
|
|
1018
|
+
return `M ${p.x} ${p.y}`;
|
|
1019
|
+
}
|
|
1020
|
+
if (n === 2) {
|
|
1021
|
+
const a = points[0];
|
|
1022
|
+
const b = points[1];
|
|
1023
|
+
if (!a || !b) return "";
|
|
1024
|
+
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
1025
|
+
}
|
|
1026
|
+
const p0 = points[0];
|
|
1027
|
+
if (!p0) return "";
|
|
1028
|
+
let d = `M ${p0.x} ${p0.y}`;
|
|
1029
|
+
let i = 1;
|
|
1030
|
+
for (; i < n - 2; i++) {
|
|
1031
|
+
const pi = points[i];
|
|
1032
|
+
const pi1 = points[i + 1];
|
|
1033
|
+
if (!pi || !pi1) continue;
|
|
1034
|
+
const xc = (pi.x + pi1.x) / 2;
|
|
1035
|
+
const yc = (pi.y + pi1.y) / 2;
|
|
1036
|
+
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
1037
|
+
}
|
|
1038
|
+
const pLast = points[i];
|
|
1039
|
+
const pEnd = points[i + 1];
|
|
1040
|
+
if (!pLast || !pEnd) return d;
|
|
1041
|
+
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
1042
|
+
return d;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
950
1045
|
// src/native/skia-transform.ts
|
|
951
1046
|
function parseNum(s) {
|
|
952
1047
|
return Number(s);
|
|
@@ -1771,9 +1866,9 @@ function NativeInteractionOverlay({
|
|
|
1771
1866
|
const previewElements = react.useMemo(() => {
|
|
1772
1867
|
if (!placementPreview) return null;
|
|
1773
1868
|
const p = placementPreview;
|
|
1774
|
-
if (p.kind === "rect" || p.kind === "ellipse") {
|
|
1869
|
+
if (p.kind === "rect" || p.kind === "ellipse" || p.kind === "architectural-cloud") {
|
|
1775
1870
|
const r = normalizeRect(p.rect);
|
|
1776
|
-
return p.kind === "rect" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1871
|
+
return p.kind === "rect" || p.kind === "architectural-cloud" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1777
1872
|
reactNativeSkia.Rect,
|
|
1778
1873
|
{
|
|
1779
1874
|
x: r.x,
|
|
@@ -2105,6 +2200,171 @@ function NativeSceneRenderer({
|
|
|
2105
2200
|
if (width <= 0 || height <= 0) return null;
|
|
2106
2201
|
return /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Canvas, { style: { width, height }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { transform: cameraTransform, children: visible.map((item) => /* @__PURE__ */ jsxRuntime.jsx(MemoShape, { item }, item.id)) }) });
|
|
2107
2202
|
}
|
|
2203
|
+
var DEFAULT_NATIVE_VECTOR_TOOLS = [
|
|
2204
|
+
{ id: "hand", label: "Hand", shortLabel: "H" },
|
|
2205
|
+
{ id: "select", label: "Select", shortLabel: "V" },
|
|
2206
|
+
{ id: "draw", label: "Draw", shortLabel: "D" },
|
|
2207
|
+
{ id: "marker", label: "Marker", shortLabel: "M" },
|
|
2208
|
+
{ id: "eraser", label: "Eraser", shortLabel: "E" },
|
|
2209
|
+
{ id: "text", label: "Text", shortLabel: "T" },
|
|
2210
|
+
{ id: "note", label: "Note", shortLabel: "N" },
|
|
2211
|
+
{ id: "rect", label: "Rectangle", shortLabel: "R" },
|
|
2212
|
+
{ id: "ellipse", label: "Ellipse", shortLabel: "O" },
|
|
2213
|
+
{ id: "architectural-cloud", label: "Cloud", shortLabel: "C" },
|
|
2214
|
+
{ id: "line", label: "Line", shortLabel: "L" },
|
|
2215
|
+
{ id: "arrow", label: "Arrow", shortLabel: "A" }
|
|
2216
|
+
];
|
|
2217
|
+
function NativeVectorToolbar({
|
|
2218
|
+
value,
|
|
2219
|
+
onChange,
|
|
2220
|
+
tools = DEFAULT_NATIVE_VECTOR_TOOLS,
|
|
2221
|
+
disabled = false,
|
|
2222
|
+
disabledToolIds = [],
|
|
2223
|
+
accessibilityLabel = "Canvas tools",
|
|
2224
|
+
style,
|
|
2225
|
+
contentContainerStyle,
|
|
2226
|
+
toolButtonStyle,
|
|
2227
|
+
activeToolButtonStyle,
|
|
2228
|
+
toolLabelStyle,
|
|
2229
|
+
activeToolLabelStyle,
|
|
2230
|
+
renderToolIcon,
|
|
2231
|
+
renderToolButton
|
|
2232
|
+
}) {
|
|
2233
|
+
const disabledIds = react.useMemo(() => new Set(disabledToolIds), [disabledToolIds]);
|
|
2234
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { accessibilityLabel, style: [styles.shell, style], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2235
|
+
reactNative.ScrollView,
|
|
2236
|
+
{
|
|
2237
|
+
horizontal: true,
|
|
2238
|
+
showsHorizontalScrollIndicator: false,
|
|
2239
|
+
contentContainerStyle: [styles.content, contentContainerStyle],
|
|
2240
|
+
children: tools.map((tool) => {
|
|
2241
|
+
const selected = tool.id === value;
|
|
2242
|
+
const toolDisabled = disabled || tool.disabled || disabledIds.has(tool.id);
|
|
2243
|
+
const foregroundColor = selected ? "#fafaf9" : "#18181b";
|
|
2244
|
+
const onSelect = () => {
|
|
2245
|
+
if (!toolDisabled) {
|
|
2246
|
+
onChange(tool.id);
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
const input = {
|
|
2250
|
+
tool,
|
|
2251
|
+
selected,
|
|
2252
|
+
disabled: toolDisabled,
|
|
2253
|
+
foregroundColor,
|
|
2254
|
+
onSelect
|
|
2255
|
+
};
|
|
2256
|
+
if (renderToolButton) {
|
|
2257
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { children: renderToolButton(input) }, tool.id);
|
|
2258
|
+
}
|
|
2259
|
+
const icon = renderToolIcon?.(input) ?? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2260
|
+
reactNative.Text,
|
|
2261
|
+
{
|
|
2262
|
+
style: [
|
|
2263
|
+
styles.shortLabel,
|
|
2264
|
+
{ color: foregroundColor },
|
|
2265
|
+
toolLabelStyle,
|
|
2266
|
+
selected ? activeToolLabelStyle : void 0
|
|
2267
|
+
],
|
|
2268
|
+
children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase()
|
|
2269
|
+
}
|
|
2270
|
+
);
|
|
2271
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2272
|
+
reactNative.Pressable,
|
|
2273
|
+
{
|
|
2274
|
+
accessibilityLabel: tool.accessibilityLabel ?? tool.label,
|
|
2275
|
+
accessibilityRole: "button",
|
|
2276
|
+
accessibilityState: { selected, disabled: toolDisabled },
|
|
2277
|
+
disabled: toolDisabled,
|
|
2278
|
+
onPress: onSelect,
|
|
2279
|
+
style: ({ pressed }) => [
|
|
2280
|
+
styles.toolButton,
|
|
2281
|
+
toolButtonStyle,
|
|
2282
|
+
selected ? styles.activeToolButton : void 0,
|
|
2283
|
+
selected ? activeToolButtonStyle : void 0,
|
|
2284
|
+
pressed && !toolDisabled ? styles.pressedToolButton : void 0,
|
|
2285
|
+
toolDisabled ? styles.disabledToolButton : void 0
|
|
2286
|
+
],
|
|
2287
|
+
children: [
|
|
2288
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.iconSlot, children: icon }),
|
|
2289
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2290
|
+
reactNative.Text,
|
|
2291
|
+
{
|
|
2292
|
+
numberOfLines: 1,
|
|
2293
|
+
style: [
|
|
2294
|
+
styles.toolLabel,
|
|
2295
|
+
{ color: foregroundColor },
|
|
2296
|
+
toolLabelStyle,
|
|
2297
|
+
selected ? activeToolLabelStyle : void 0
|
|
2298
|
+
],
|
|
2299
|
+
children: tool.label
|
|
2300
|
+
}
|
|
2301
|
+
)
|
|
2302
|
+
]
|
|
2303
|
+
},
|
|
2304
|
+
tool.id
|
|
2305
|
+
);
|
|
2306
|
+
})
|
|
2307
|
+
}
|
|
2308
|
+
) });
|
|
2309
|
+
}
|
|
2310
|
+
var styles = reactNative.StyleSheet.create({
|
|
2311
|
+
shell: {
|
|
2312
|
+
borderRadius: 10,
|
|
2313
|
+
borderWidth: reactNative.StyleSheet.hairlineWidth,
|
|
2314
|
+
borderColor: "rgba(24, 24, 27, 0.14)",
|
|
2315
|
+
backgroundColor: "rgba(255, 255, 255, 0.96)",
|
|
2316
|
+
shadowColor: "#18181b",
|
|
2317
|
+
shadowOpacity: 0.12,
|
|
2318
|
+
shadowRadius: 16,
|
|
2319
|
+
shadowOffset: { width: 0, height: 8 },
|
|
2320
|
+
elevation: 6
|
|
2321
|
+
},
|
|
2322
|
+
content: {
|
|
2323
|
+
alignItems: "center",
|
|
2324
|
+
gap: 6,
|
|
2325
|
+
paddingHorizontal: 6,
|
|
2326
|
+
paddingVertical: 6
|
|
2327
|
+
},
|
|
2328
|
+
toolButton: {
|
|
2329
|
+
minWidth: 58,
|
|
2330
|
+
height: 52,
|
|
2331
|
+
alignItems: "center",
|
|
2332
|
+
justifyContent: "center",
|
|
2333
|
+
gap: 3,
|
|
2334
|
+
borderRadius: 8,
|
|
2335
|
+
paddingHorizontal: 8,
|
|
2336
|
+
borderWidth: reactNative.StyleSheet.hairlineWidth,
|
|
2337
|
+
borderColor: "transparent",
|
|
2338
|
+
backgroundColor: "transparent"
|
|
2339
|
+
},
|
|
2340
|
+
activeToolButton: {
|
|
2341
|
+
borderColor: "rgba(24, 24, 27, 0.24)",
|
|
2342
|
+
backgroundColor: "#18181b"
|
|
2343
|
+
},
|
|
2344
|
+
pressedToolButton: {
|
|
2345
|
+
backgroundColor: "rgba(24, 24, 27, 0.08)"
|
|
2346
|
+
},
|
|
2347
|
+
disabledToolButton: {
|
|
2348
|
+
opacity: 0.42
|
|
2349
|
+
},
|
|
2350
|
+
iconSlot: {
|
|
2351
|
+
height: 22,
|
|
2352
|
+
minWidth: 22,
|
|
2353
|
+
alignItems: "center",
|
|
2354
|
+
justifyContent: "center"
|
|
2355
|
+
},
|
|
2356
|
+
shortLabel: {
|
|
2357
|
+
fontSize: 16,
|
|
2358
|
+
fontWeight: "700",
|
|
2359
|
+
lineHeight: 20
|
|
2360
|
+
},
|
|
2361
|
+
toolLabel: {
|
|
2362
|
+
maxWidth: 68,
|
|
2363
|
+
fontSize: 10,
|
|
2364
|
+
fontWeight: "600",
|
|
2365
|
+
lineHeight: 12
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2108
2368
|
|
|
2109
2369
|
// src/camera/camera.ts
|
|
2110
2370
|
var Camera2D = class {
|
|
@@ -2292,6 +2552,37 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
|
|
|
2292
2552
|
}
|
|
2293
2553
|
return ids;
|
|
2294
2554
|
}
|
|
2555
|
+
var MIN_PLACE_SIZE = 8;
|
|
2556
|
+
var MIN_ARROW_DRAG_PX = 8;
|
|
2557
|
+
var TAP_PX = 20;
|
|
2558
|
+
function isPlacementTool(toolId) {
|
|
2559
|
+
return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
|
|
2560
|
+
}
|
|
2561
|
+
function placementPreviewForTool(toolId, start, end) {
|
|
2562
|
+
if (toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud") {
|
|
2563
|
+
return { kind: toolId, rect: rectFromCorners(start, end) };
|
|
2564
|
+
}
|
|
2565
|
+
return { kind: toolId, start, end };
|
|
2566
|
+
}
|
|
2567
|
+
function defaultPlacementWorld(toolId, center) {
|
|
2568
|
+
const cx = center.x;
|
|
2569
|
+
const cy = center.y;
|
|
2570
|
+
if (toolId === "rect") {
|
|
2571
|
+
return { raw: { x: cx - 60, y: cy - 40, width: 120, height: 80 } };
|
|
2572
|
+
}
|
|
2573
|
+
if (toolId === "ellipse") {
|
|
2574
|
+
return { raw: { x: cx - 70, y: cy - 45, width: 140, height: 90 } };
|
|
2575
|
+
}
|
|
2576
|
+
if (toolId === "architectural-cloud") {
|
|
2577
|
+
return { raw: { x: cx - 90, y: cy - 50, width: 180, height: 100 } };
|
|
2578
|
+
}
|
|
2579
|
+
const start = { x: cx - 50, y: cy };
|
|
2580
|
+
const end = { x: cx + 50, y: cy };
|
|
2581
|
+
return {
|
|
2582
|
+
raw: rectFromCorners(start, end),
|
|
2583
|
+
lineWorld: [start, end]
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2295
2586
|
function collectIdsInRect(items, marquee) {
|
|
2296
2587
|
const m = normalizeRect(marquee);
|
|
2297
2588
|
const out = [];
|
|
@@ -2469,10 +2760,31 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2469
2760
|
setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
|
|
2470
2761
|
return;
|
|
2471
2762
|
}
|
|
2763
|
+
if (isPlacementTool(tool)) {
|
|
2764
|
+
dragStateRef.current = {
|
|
2765
|
+
kind: "place",
|
|
2766
|
+
tool,
|
|
2767
|
+
startWorld: { x: worldX, y: worldY },
|
|
2768
|
+
startScreen: { x: sx, y: sy }
|
|
2769
|
+
};
|
|
2770
|
+
setPlacementPreview(
|
|
2771
|
+
placementPreviewForTool(
|
|
2772
|
+
tool,
|
|
2773
|
+
{ x: worldX, y: worldY },
|
|
2774
|
+
{
|
|
2775
|
+
x: worldX,
|
|
2776
|
+
y: worldY
|
|
2777
|
+
}
|
|
2778
|
+
)
|
|
2779
|
+
);
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2472
2782
|
if (tool === "note" || tool === "text") {
|
|
2473
2783
|
dragStateRef.current = {
|
|
2474
2784
|
kind: "tap",
|
|
2475
|
-
|
|
2785
|
+
tool,
|
|
2786
|
+
startWorld: { x: worldX, y: worldY },
|
|
2787
|
+
startScreen: { x: sx, y: sy }
|
|
2476
2788
|
};
|
|
2477
2789
|
return;
|
|
2478
2790
|
}
|
|
@@ -2584,6 +2896,15 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2584
2896
|
setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
|
|
2585
2897
|
return;
|
|
2586
2898
|
}
|
|
2899
|
+
if (st.kind === "place") {
|
|
2900
|
+
setPlacementPreview(
|
|
2901
|
+
placementPreviewForTool(st.tool, st.startWorld, {
|
|
2902
|
+
x: worldX,
|
|
2903
|
+
y: worldY
|
|
2904
|
+
})
|
|
2905
|
+
);
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2587
2908
|
},
|
|
2588
2909
|
onPanResponderRelease: (evt) => {
|
|
2589
2910
|
lastPinchDist.current = null;
|
|
@@ -2639,12 +2960,83 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2639
2960
|
dragStateRef.current = { kind: "idle" };
|
|
2640
2961
|
return;
|
|
2641
2962
|
}
|
|
2963
|
+
if (st.kind === "place") {
|
|
2964
|
+
dragStateRef.current = { kind: "idle" };
|
|
2965
|
+
setPlacementPreview(null);
|
|
2966
|
+
const change = onItemsChangeRef.current;
|
|
2967
|
+
if (!change) return;
|
|
2968
|
+
const { worldX, worldY } = screenToWorld(
|
|
2969
|
+
evt.nativeEvent.locationX,
|
|
2970
|
+
evt.nativeEvent.locationY
|
|
2971
|
+
);
|
|
2972
|
+
const a = st.startWorld;
|
|
2973
|
+
const b = { x: worldX, y: worldY };
|
|
2974
|
+
const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
|
|
2975
|
+
const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
|
|
2976
|
+
if (st.tool === "arrow" && Math.hypot(screenDx, screenDy) < MIN_ARROW_DRAG_PX) {
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
let raw = rectFromCorners(a, b);
|
|
2980
|
+
let br = normalizeRect(raw);
|
|
2981
|
+
let lineStart = a;
|
|
2982
|
+
let lineEnd = b;
|
|
2983
|
+
if (br.width < MIN_PLACE_SIZE || br.height < MIN_PLACE_SIZE) {
|
|
2984
|
+
const center = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
2985
|
+
const defaults = defaultPlacementWorld(st.tool, center);
|
|
2986
|
+
raw = defaults.raw;
|
|
2987
|
+
br = normalizeRect(raw);
|
|
2988
|
+
if (defaults.lineWorld) {
|
|
2989
|
+
const [defaultStart, defaultEnd] = defaults.lineWorld;
|
|
2990
|
+
lineStart = defaultStart;
|
|
2991
|
+
lineEnd = defaultEnd;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
const id = createShapeId();
|
|
2995
|
+
if (st.tool === "rect") {
|
|
2996
|
+
change([...itemsRef.current, createRectangleItem(id, raw)]);
|
|
2997
|
+
onSelectionChangeRef.current?.([id]);
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
if (st.tool === "ellipse") {
|
|
3001
|
+
change([...itemsRef.current, createEllipseItem(id, raw)]);
|
|
3002
|
+
onSelectionChangeRef.current?.([id]);
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
if (st.tool === "architectural-cloud") {
|
|
3006
|
+
change([...itemsRef.current, createArchitecturalCloudItem(id, raw)]);
|
|
3007
|
+
onSelectionChangeRef.current?.([id]);
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
const line = lineEndpointsToLocal(br, lineStart, lineEnd);
|
|
3011
|
+
change([...itemsRef.current, createLineItem(id, br, line, st.tool)]);
|
|
3012
|
+
onSelectionChangeRef.current?.([id]);
|
|
3013
|
+
return;
|
|
3014
|
+
}
|
|
2642
3015
|
if (st.kind === "tap") {
|
|
2643
3016
|
dragStateRef.current = { kind: "idle" };
|
|
3017
|
+
const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
|
|
3018
|
+
const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
|
|
3019
|
+
if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
|
|
2644
3020
|
const change = onItemsChangeRef.current;
|
|
2645
3021
|
if (!change) return;
|
|
2646
|
-
|
|
2647
|
-
|
|
3022
|
+
if (st.tool === "text") {
|
|
3023
|
+
const id = createShapeId();
|
|
3024
|
+
const item = createTextItem(
|
|
3025
|
+
id,
|
|
3026
|
+
{
|
|
3027
|
+
x: st.startWorld.x - 4,
|
|
3028
|
+
y: st.startWorld.y - 18,
|
|
3029
|
+
width: 160,
|
|
3030
|
+
height: 26
|
|
3031
|
+
},
|
|
3032
|
+
"Text",
|
|
3033
|
+
void 0,
|
|
3034
|
+
18
|
|
3035
|
+
);
|
|
3036
|
+
change([...itemsRef.current, item]);
|
|
3037
|
+
onSelectionChangeRef.current?.([id]);
|
|
3038
|
+
}
|
|
3039
|
+
if (st.tool === "note") {
|
|
2648
3040
|
const id = createShapeId();
|
|
2649
3041
|
const note = {
|
|
2650
3042
|
id,
|
|
@@ -2660,6 +3052,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2660
3052
|
toolKind: "custom"
|
|
2661
3053
|
};
|
|
2662
3054
|
change([...itemsRef.current, note]);
|
|
3055
|
+
onSelectionChangeRef.current?.([id]);
|
|
2663
3056
|
}
|
|
2664
3057
|
return;
|
|
2665
3058
|
}
|
|
@@ -2747,10 +3140,14 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2747
3140
|
);
|
|
2748
3141
|
});
|
|
2749
3142
|
|
|
3143
|
+
exports.DEFAULT_NATIVE_VECTOR_TOOLS = DEFAULT_NATIVE_VECTOR_TOOLS;
|
|
2750
3144
|
exports.NativeInteractionOverlay = NativeInteractionOverlay;
|
|
2751
3145
|
exports.NativeSceneRenderer = NativeSceneRenderer;
|
|
2752
3146
|
exports.NativeShapeRenderer = NativeShapeRenderer;
|
|
3147
|
+
exports.NativeVectorToolbar = NativeVectorToolbar;
|
|
2753
3148
|
exports.NativeVectorViewport = NativeVectorViewport;
|
|
3149
|
+
exports.createFreehandStrokeItem = createFreehandStrokeItem;
|
|
3150
|
+
exports.createShapeId = createShapeId;
|
|
2754
3151
|
exports.parseSvgFragment = parseSvgFragment;
|
|
2755
3152
|
//# sourceMappingURL=native.cjs.map
|
|
2756
3153
|
//# sourceMappingURL=native.cjs.map
|