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.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import getStroke from 'perfect-freehand';
|
|
1
2
|
import { Group, RoundedRect, Circle, Line, vec, Path, matchFont, Text, Canvas, Rect, Oval } from '@shopify/react-native-skia';
|
|
2
3
|
import { memo, forwardRef, useState, useRef, useCallback, useMemo, useImperativeHandle } from 'react';
|
|
3
|
-
import getStroke from 'perfect-freehand';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
|
-
import { PanResponder, View } from 'react-native';
|
|
5
|
+
import { StyleSheet, PanResponder, View, ScrollView, Text as Text$1, Pressable } from 'react-native';
|
|
6
6
|
|
|
7
|
-
// src/
|
|
7
|
+
// src/scene/shape-builders.ts
|
|
8
8
|
|
|
9
9
|
// src/math/rect.ts
|
|
10
10
|
function rectsIntersect(a, b) {
|
|
@@ -21,141 +21,6 @@ function normalizeRect(r) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// src/math/item-transform.ts
|
|
25
|
-
function getItemRotationRad(item) {
|
|
26
|
-
return item.rotation ?? 0;
|
|
27
|
-
}
|
|
28
|
-
function itemLocalToWorld(lx, ly, itemX, itemY, w, h, rotationRad) {
|
|
29
|
-
const c = { x: w / 2, y: h / 2 };
|
|
30
|
-
const dlx = lx - c.x;
|
|
31
|
-
const dly = ly - c.y;
|
|
32
|
-
const cos = Math.cos(rotationRad);
|
|
33
|
-
const sin = Math.sin(rotationRad);
|
|
34
|
-
return {
|
|
35
|
-
x: itemX + c.x + cos * dlx - sin * dly,
|
|
36
|
-
y: itemY + c.y + sin * dlx + cos * dly
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function worldToItemLocal(wx, wy, itemX, itemY, w, h, rotationRad) {
|
|
40
|
-
const c = { x: w / 2, y: h / 2 };
|
|
41
|
-
const vx = wx - itemX;
|
|
42
|
-
const vy = wy - itemY;
|
|
43
|
-
const dx = vx - c.x;
|
|
44
|
-
const dy = vy - c.y;
|
|
45
|
-
const cos = Math.cos(-rotationRad);
|
|
46
|
-
const sin = Math.sin(-rotationRad);
|
|
47
|
-
const lx = cos * dx - sin * dy;
|
|
48
|
-
const ly = sin * dx + cos * dy;
|
|
49
|
-
return { x: c.x + lx, y: c.y + ly };
|
|
50
|
-
}
|
|
51
|
-
function boundsAabbForRotatedItem(item) {
|
|
52
|
-
const rot = getItemRotationRad(item);
|
|
53
|
-
if (Math.abs(rot) < 1e-12 && item.bounds.width >= 0 && item.bounds.height >= 0) {
|
|
54
|
-
return item.bounds;
|
|
55
|
-
}
|
|
56
|
-
const r = normalizeRect(item.bounds);
|
|
57
|
-
if (Math.abs(rot) < 1e-12) {
|
|
58
|
-
return r;
|
|
59
|
-
}
|
|
60
|
-
const corners = [
|
|
61
|
-
[0, 0],
|
|
62
|
-
[r.width, 0],
|
|
63
|
-
[r.width, r.height],
|
|
64
|
-
[0, r.height]
|
|
65
|
-
];
|
|
66
|
-
let minX = Infinity;
|
|
67
|
-
let minY = Infinity;
|
|
68
|
-
let maxX = -Infinity;
|
|
69
|
-
let maxY = -Infinity;
|
|
70
|
-
for (const [lx, ly] of corners) {
|
|
71
|
-
const p = itemLocalToWorld(lx, ly, item.x, item.y, r.width, r.height, rot);
|
|
72
|
-
minX = Math.min(minX, p.x);
|
|
73
|
-
minY = Math.min(minY, p.y);
|
|
74
|
-
maxX = Math.max(maxX, p.x);
|
|
75
|
-
maxY = Math.max(maxY, p.y);
|
|
76
|
-
}
|
|
77
|
-
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// src/interaction/resize-handles.ts
|
|
81
|
-
function getHandleWorldPosition(bounds, id) {
|
|
82
|
-
const r = normalizeRect(bounds);
|
|
83
|
-
const cx = r.x + r.width / 2;
|
|
84
|
-
const cy = r.y + r.height / 2;
|
|
85
|
-
switch (id) {
|
|
86
|
-
case "nw":
|
|
87
|
-
return { x: r.x, y: r.y };
|
|
88
|
-
case "n":
|
|
89
|
-
return { x: cx, y: r.y };
|
|
90
|
-
case "ne":
|
|
91
|
-
return { x: r.x + r.width, y: r.y };
|
|
92
|
-
case "e":
|
|
93
|
-
return { x: r.x + r.width, y: cy };
|
|
94
|
-
case "se":
|
|
95
|
-
return { x: r.x + r.width, y: r.y + r.height };
|
|
96
|
-
case "s":
|
|
97
|
-
return { x: cx, y: r.y + r.height };
|
|
98
|
-
case "sw":
|
|
99
|
-
return { x: r.x, y: r.y + r.height };
|
|
100
|
-
case "w":
|
|
101
|
-
return { x: r.x, y: cy };
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function getHandleWorldPositionRotated(bounds, handle, rotationRad) {
|
|
105
|
-
const r = normalizeRect(bounds);
|
|
106
|
-
const p = getHandleWorldPosition(
|
|
107
|
-
{ x: 0, y: 0, width: r.width, height: r.height },
|
|
108
|
-
handle
|
|
109
|
-
);
|
|
110
|
-
return itemLocalToWorld(p.x, p.y, r.x, r.y, r.width, r.height, rotationRad);
|
|
111
|
-
}
|
|
112
|
-
function getRotationHandleWorldPosition(bounds, rotationRad, handleOffsetWorld) {
|
|
113
|
-
const r = normalizeRect(bounds);
|
|
114
|
-
return itemLocalToWorld(
|
|
115
|
-
r.width / 2,
|
|
116
|
-
-handleOffsetWorld,
|
|
117
|
-
r.x,
|
|
118
|
-
r.y,
|
|
119
|
-
r.width,
|
|
120
|
-
r.height,
|
|
121
|
-
rotationRad
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// src/scene/freehand-path.ts
|
|
126
|
-
function smoothFreehandPointsToPathD(points) {
|
|
127
|
-
const n = points.length;
|
|
128
|
-
if (n === 0) return "";
|
|
129
|
-
if (n === 1) {
|
|
130
|
-
const p = points[0];
|
|
131
|
-
if (!p) return "";
|
|
132
|
-
return `M ${p.x} ${p.y}`;
|
|
133
|
-
}
|
|
134
|
-
if (n === 2) {
|
|
135
|
-
const a = points[0];
|
|
136
|
-
const b = points[1];
|
|
137
|
-
if (!a || !b) return "";
|
|
138
|
-
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
139
|
-
}
|
|
140
|
-
const p0 = points[0];
|
|
141
|
-
if (!p0) return "";
|
|
142
|
-
let d = `M ${p0.x} ${p0.y}`;
|
|
143
|
-
let i = 1;
|
|
144
|
-
for (; i < n - 2; i++) {
|
|
145
|
-
const pi = points[i];
|
|
146
|
-
const pi1 = points[i + 1];
|
|
147
|
-
if (!pi || !pi1) continue;
|
|
148
|
-
const xc = (pi.x + pi1.x) / 2;
|
|
149
|
-
const yc = (pi.y + pi1.y) / 2;
|
|
150
|
-
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
151
|
-
}
|
|
152
|
-
const pLast = points[i];
|
|
153
|
-
const pEnd = points[i + 1];
|
|
154
|
-
if (!pLast || !pEnd) return d;
|
|
155
|
-
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
156
|
-
return d;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
24
|
// src/scene/custom-shape.ts
|
|
160
25
|
function buildCustomShapeChildrenSvg(inner, intrinsic, bounds) {
|
|
161
26
|
const b = normalizeRect(bounds);
|
|
@@ -780,6 +645,15 @@ function createShapeId() {
|
|
|
780
645
|
const uid = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : String(Date.now());
|
|
781
646
|
return `user-shape-${uid}`;
|
|
782
647
|
}
|
|
648
|
+
function lineEndpointsToLocal(bounds, worldEndA, worldEndB) {
|
|
649
|
+
const b = normalizeRect(bounds);
|
|
650
|
+
return {
|
|
651
|
+
x1: worldEndA.x - b.x,
|
|
652
|
+
y1: worldEndA.y - b.y,
|
|
653
|
+
x2: worldEndB.x - b.x,
|
|
654
|
+
y2: worldEndB.y - b.y
|
|
655
|
+
};
|
|
656
|
+
}
|
|
783
657
|
function rebuildItemSvg(item) {
|
|
784
658
|
const style = resolveStrokeStyle(item);
|
|
785
659
|
const k = item.toolKind;
|
|
@@ -883,6 +757,85 @@ function rebuildItemSvg(item) {
|
|
|
883
757
|
}
|
|
884
758
|
return item;
|
|
885
759
|
}
|
|
760
|
+
function createRectangleItem(id, bounds, style) {
|
|
761
|
+
const r = normalizeRect(bounds);
|
|
762
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
763
|
+
return rebuildItemSvg({
|
|
764
|
+
id,
|
|
765
|
+
x: r.x,
|
|
766
|
+
y: r.y,
|
|
767
|
+
bounds: { ...r },
|
|
768
|
+
toolKind: "rect",
|
|
769
|
+
stroke: s.stroke,
|
|
770
|
+
strokeWidth: s.strokeWidth,
|
|
771
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
772
|
+
childrenSvg: ""
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
function createEllipseItem(id, bounds, style) {
|
|
776
|
+
const r = normalizeRect(bounds);
|
|
777
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
778
|
+
return rebuildItemSvg({
|
|
779
|
+
id,
|
|
780
|
+
x: r.x,
|
|
781
|
+
y: r.y,
|
|
782
|
+
bounds: { ...r },
|
|
783
|
+
toolKind: "ellipse",
|
|
784
|
+
stroke: s.stroke,
|
|
785
|
+
strokeWidth: s.strokeWidth,
|
|
786
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
787
|
+
childrenSvg: ""
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
function createArchitecturalCloudItem(id, bounds, style) {
|
|
791
|
+
const r = normalizeRect(bounds);
|
|
792
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
793
|
+
return rebuildItemSvg({
|
|
794
|
+
id,
|
|
795
|
+
x: r.x,
|
|
796
|
+
y: r.y,
|
|
797
|
+
bounds: { ...r },
|
|
798
|
+
toolKind: "architectural-cloud",
|
|
799
|
+
stroke: s.stroke,
|
|
800
|
+
strokeWidth: s.strokeWidth,
|
|
801
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
802
|
+
childrenSvg: ""
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
function createLineItem(id, bounds, line, toolKind, style, arrowBind) {
|
|
806
|
+
const r = normalizeRect(bounds);
|
|
807
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
808
|
+
return rebuildItemSvg({
|
|
809
|
+
id,
|
|
810
|
+
x: r.x,
|
|
811
|
+
y: r.y,
|
|
812
|
+
bounds: { ...r },
|
|
813
|
+
toolKind,
|
|
814
|
+
line: { ...line },
|
|
815
|
+
stroke: s.stroke,
|
|
816
|
+
strokeWidth: s.strokeWidth,
|
|
817
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
818
|
+
...{},
|
|
819
|
+
childrenSvg: ""
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
function createTextItem(id, bounds, text = "", style, textFontSize) {
|
|
823
|
+
const r = normalizeRect(bounds);
|
|
824
|
+
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
825
|
+
return rebuildItemSvg({
|
|
826
|
+
id,
|
|
827
|
+
x: r.x,
|
|
828
|
+
y: r.y,
|
|
829
|
+
bounds: { ...r },
|
|
830
|
+
toolKind: "text",
|
|
831
|
+
text,
|
|
832
|
+
stroke: s.stroke,
|
|
833
|
+
strokeWidth: s.strokeWidth,
|
|
834
|
+
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
835
|
+
...{ textFontSize } ,
|
|
836
|
+
childrenSvg: ""
|
|
837
|
+
});
|
|
838
|
+
}
|
|
886
839
|
function createFreehandStrokeItem(id, pointsWorld, toolKind, style) {
|
|
887
840
|
if (pointsWorld.length === 0) return null;
|
|
888
841
|
const merged = {
|
|
@@ -941,6 +894,148 @@ function buildRasterImageChildrenSvg(dataUrl, intrinsic, bounds) {
|
|
|
941
894
|
return `<g transform="translate(${tx}, ${ty}) scale(${s})"><image href="${href}" x="0" y="0" width="${iw}" height="${ih}" /></g>`;
|
|
942
895
|
}
|
|
943
896
|
|
|
897
|
+
// src/math/item-transform.ts
|
|
898
|
+
function getItemRotationRad(item) {
|
|
899
|
+
return item.rotation ?? 0;
|
|
900
|
+
}
|
|
901
|
+
function itemLocalToWorld(lx, ly, itemX, itemY, w, h, rotationRad) {
|
|
902
|
+
const c = { x: w / 2, y: h / 2 };
|
|
903
|
+
const dlx = lx - c.x;
|
|
904
|
+
const dly = ly - c.y;
|
|
905
|
+
const cos = Math.cos(rotationRad);
|
|
906
|
+
const sin = Math.sin(rotationRad);
|
|
907
|
+
return {
|
|
908
|
+
x: itemX + c.x + cos * dlx - sin * dly,
|
|
909
|
+
y: itemY + c.y + sin * dlx + cos * dly
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
function worldToItemLocal(wx, wy, itemX, itemY, w, h, rotationRad) {
|
|
913
|
+
const c = { x: w / 2, y: h / 2 };
|
|
914
|
+
const vx = wx - itemX;
|
|
915
|
+
const vy = wy - itemY;
|
|
916
|
+
const dx = vx - c.x;
|
|
917
|
+
const dy = vy - c.y;
|
|
918
|
+
const cos = Math.cos(-rotationRad);
|
|
919
|
+
const sin = Math.sin(-rotationRad);
|
|
920
|
+
const lx = cos * dx - sin * dy;
|
|
921
|
+
const ly = sin * dx + cos * dy;
|
|
922
|
+
return { x: c.x + lx, y: c.y + ly };
|
|
923
|
+
}
|
|
924
|
+
function boundsAabbForRotatedItem(item) {
|
|
925
|
+
const rot = getItemRotationRad(item);
|
|
926
|
+
if (Math.abs(rot) < 1e-12 && item.bounds.width >= 0 && item.bounds.height >= 0) {
|
|
927
|
+
return item.bounds;
|
|
928
|
+
}
|
|
929
|
+
const r = normalizeRect(item.bounds);
|
|
930
|
+
if (Math.abs(rot) < 1e-12) {
|
|
931
|
+
return r;
|
|
932
|
+
}
|
|
933
|
+
const corners = [
|
|
934
|
+
[0, 0],
|
|
935
|
+
[r.width, 0],
|
|
936
|
+
[r.width, r.height],
|
|
937
|
+
[0, r.height]
|
|
938
|
+
];
|
|
939
|
+
let minX = Infinity;
|
|
940
|
+
let minY = Infinity;
|
|
941
|
+
let maxX = -Infinity;
|
|
942
|
+
let maxY = -Infinity;
|
|
943
|
+
for (const [lx, ly] of corners) {
|
|
944
|
+
const p = itemLocalToWorld(lx, ly, item.x, item.y, r.width, r.height, rot);
|
|
945
|
+
minX = Math.min(minX, p.x);
|
|
946
|
+
minY = Math.min(minY, p.y);
|
|
947
|
+
maxX = Math.max(maxX, p.x);
|
|
948
|
+
maxY = Math.max(maxY, p.y);
|
|
949
|
+
}
|
|
950
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/interaction/resize-handles.ts
|
|
954
|
+
function getHandleWorldPosition(bounds, id) {
|
|
955
|
+
const r = normalizeRect(bounds);
|
|
956
|
+
const cx = r.x + r.width / 2;
|
|
957
|
+
const cy = r.y + r.height / 2;
|
|
958
|
+
switch (id) {
|
|
959
|
+
case "nw":
|
|
960
|
+
return { x: r.x, y: r.y };
|
|
961
|
+
case "n":
|
|
962
|
+
return { x: cx, y: r.y };
|
|
963
|
+
case "ne":
|
|
964
|
+
return { x: r.x + r.width, y: r.y };
|
|
965
|
+
case "e":
|
|
966
|
+
return { x: r.x + r.width, y: cy };
|
|
967
|
+
case "se":
|
|
968
|
+
return { x: r.x + r.width, y: r.y + r.height };
|
|
969
|
+
case "s":
|
|
970
|
+
return { x: cx, y: r.y + r.height };
|
|
971
|
+
case "sw":
|
|
972
|
+
return { x: r.x, y: r.y + r.height };
|
|
973
|
+
case "w":
|
|
974
|
+
return { x: r.x, y: cy };
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
function getHandleWorldPositionRotated(bounds, handle, rotationRad) {
|
|
978
|
+
const r = normalizeRect(bounds);
|
|
979
|
+
const p = getHandleWorldPosition(
|
|
980
|
+
{ x: 0, y: 0, width: r.width, height: r.height },
|
|
981
|
+
handle
|
|
982
|
+
);
|
|
983
|
+
return itemLocalToWorld(p.x, p.y, r.x, r.y, r.width, r.height, rotationRad);
|
|
984
|
+
}
|
|
985
|
+
function getRotationHandleWorldPosition(bounds, rotationRad, handleOffsetWorld) {
|
|
986
|
+
const r = normalizeRect(bounds);
|
|
987
|
+
return itemLocalToWorld(
|
|
988
|
+
r.width / 2,
|
|
989
|
+
-handleOffsetWorld,
|
|
990
|
+
r.x,
|
|
991
|
+
r.y,
|
|
992
|
+
r.width,
|
|
993
|
+
r.height,
|
|
994
|
+
rotationRad
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
function rectFromCorners(a, b) {
|
|
998
|
+
const minX = Math.min(a.x, b.x);
|
|
999
|
+
const maxX = Math.max(a.x, b.x);
|
|
1000
|
+
const minY = Math.min(a.y, b.y);
|
|
1001
|
+
const maxY = Math.max(a.y, b.y);
|
|
1002
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/scene/freehand-path.ts
|
|
1006
|
+
function smoothFreehandPointsToPathD(points) {
|
|
1007
|
+
const n = points.length;
|
|
1008
|
+
if (n === 0) return "";
|
|
1009
|
+
if (n === 1) {
|
|
1010
|
+
const p = points[0];
|
|
1011
|
+
if (!p) return "";
|
|
1012
|
+
return `M ${p.x} ${p.y}`;
|
|
1013
|
+
}
|
|
1014
|
+
if (n === 2) {
|
|
1015
|
+
const a = points[0];
|
|
1016
|
+
const b = points[1];
|
|
1017
|
+
if (!a || !b) return "";
|
|
1018
|
+
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
1019
|
+
}
|
|
1020
|
+
const p0 = points[0];
|
|
1021
|
+
if (!p0) return "";
|
|
1022
|
+
let d = `M ${p0.x} ${p0.y}`;
|
|
1023
|
+
let i = 1;
|
|
1024
|
+
for (; i < n - 2; i++) {
|
|
1025
|
+
const pi = points[i];
|
|
1026
|
+
const pi1 = points[i + 1];
|
|
1027
|
+
if (!pi || !pi1) continue;
|
|
1028
|
+
const xc = (pi.x + pi1.x) / 2;
|
|
1029
|
+
const yc = (pi.y + pi1.y) / 2;
|
|
1030
|
+
d += ` Q ${pi.x} ${pi.y} ${xc} ${yc}`;
|
|
1031
|
+
}
|
|
1032
|
+
const pLast = points[i];
|
|
1033
|
+
const pEnd = points[i + 1];
|
|
1034
|
+
if (!pLast || !pEnd) return d;
|
|
1035
|
+
d += ` Q ${pLast.x} ${pLast.y} ${pEnd.x} ${pEnd.y}`;
|
|
1036
|
+
return d;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
944
1039
|
// src/native/skia-transform.ts
|
|
945
1040
|
function parseNum(s) {
|
|
946
1041
|
return Number(s);
|
|
@@ -1765,9 +1860,9 @@ function NativeInteractionOverlay({
|
|
|
1765
1860
|
const previewElements = useMemo(() => {
|
|
1766
1861
|
if (!placementPreview) return null;
|
|
1767
1862
|
const p = placementPreview;
|
|
1768
|
-
if (p.kind === "rect" || p.kind === "ellipse") {
|
|
1863
|
+
if (p.kind === "rect" || p.kind === "ellipse" || p.kind === "architectural-cloud") {
|
|
1769
1864
|
const r = normalizeRect(p.rect);
|
|
1770
|
-
return p.kind === "rect" ? /* @__PURE__ */ jsx(
|
|
1865
|
+
return p.kind === "rect" || p.kind === "architectural-cloud" ? /* @__PURE__ */ jsx(
|
|
1771
1866
|
Rect,
|
|
1772
1867
|
{
|
|
1773
1868
|
x: r.x,
|
|
@@ -2099,6 +2194,171 @@ function NativeSceneRenderer({
|
|
|
2099
2194
|
if (width <= 0 || height <= 0) return null;
|
|
2100
2195
|
return /* @__PURE__ */ jsx(Canvas, { style: { width, height }, children: /* @__PURE__ */ jsx(Group, { transform: cameraTransform, children: visible.map((item) => /* @__PURE__ */ jsx(MemoShape, { item }, item.id)) }) });
|
|
2101
2196
|
}
|
|
2197
|
+
var DEFAULT_NATIVE_VECTOR_TOOLS = [
|
|
2198
|
+
{ id: "hand", label: "Hand", shortLabel: "H" },
|
|
2199
|
+
{ id: "select", label: "Select", shortLabel: "V" },
|
|
2200
|
+
{ id: "draw", label: "Draw", shortLabel: "D" },
|
|
2201
|
+
{ id: "marker", label: "Marker", shortLabel: "M" },
|
|
2202
|
+
{ id: "eraser", label: "Eraser", shortLabel: "E" },
|
|
2203
|
+
{ id: "text", label: "Text", shortLabel: "T" },
|
|
2204
|
+
{ id: "note", label: "Note", shortLabel: "N" },
|
|
2205
|
+
{ id: "rect", label: "Rectangle", shortLabel: "R" },
|
|
2206
|
+
{ id: "ellipse", label: "Ellipse", shortLabel: "O" },
|
|
2207
|
+
{ id: "architectural-cloud", label: "Cloud", shortLabel: "C" },
|
|
2208
|
+
{ id: "line", label: "Line", shortLabel: "L" },
|
|
2209
|
+
{ id: "arrow", label: "Arrow", shortLabel: "A" }
|
|
2210
|
+
];
|
|
2211
|
+
function NativeVectorToolbar({
|
|
2212
|
+
value,
|
|
2213
|
+
onChange,
|
|
2214
|
+
tools = DEFAULT_NATIVE_VECTOR_TOOLS,
|
|
2215
|
+
disabled = false,
|
|
2216
|
+
disabledToolIds = [],
|
|
2217
|
+
accessibilityLabel = "Canvas tools",
|
|
2218
|
+
style,
|
|
2219
|
+
contentContainerStyle,
|
|
2220
|
+
toolButtonStyle,
|
|
2221
|
+
activeToolButtonStyle,
|
|
2222
|
+
toolLabelStyle,
|
|
2223
|
+
activeToolLabelStyle,
|
|
2224
|
+
renderToolIcon,
|
|
2225
|
+
renderToolButton
|
|
2226
|
+
}) {
|
|
2227
|
+
const disabledIds = useMemo(() => new Set(disabledToolIds), [disabledToolIds]);
|
|
2228
|
+
return /* @__PURE__ */ jsx(View, { accessibilityLabel, style: [styles.shell, style], children: /* @__PURE__ */ jsx(
|
|
2229
|
+
ScrollView,
|
|
2230
|
+
{
|
|
2231
|
+
horizontal: true,
|
|
2232
|
+
showsHorizontalScrollIndicator: false,
|
|
2233
|
+
contentContainerStyle: [styles.content, contentContainerStyle],
|
|
2234
|
+
children: tools.map((tool) => {
|
|
2235
|
+
const selected = tool.id === value;
|
|
2236
|
+
const toolDisabled = disabled || tool.disabled || disabledIds.has(tool.id);
|
|
2237
|
+
const foregroundColor = selected ? "#fafaf9" : "#18181b";
|
|
2238
|
+
const onSelect = () => {
|
|
2239
|
+
if (!toolDisabled) {
|
|
2240
|
+
onChange(tool.id);
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
const input = {
|
|
2244
|
+
tool,
|
|
2245
|
+
selected,
|
|
2246
|
+
disabled: toolDisabled,
|
|
2247
|
+
foregroundColor,
|
|
2248
|
+
onSelect
|
|
2249
|
+
};
|
|
2250
|
+
if (renderToolButton) {
|
|
2251
|
+
return /* @__PURE__ */ jsx(View, { children: renderToolButton(input) }, tool.id);
|
|
2252
|
+
}
|
|
2253
|
+
const icon = renderToolIcon?.(input) ?? /* @__PURE__ */ jsx(
|
|
2254
|
+
Text$1,
|
|
2255
|
+
{
|
|
2256
|
+
style: [
|
|
2257
|
+
styles.shortLabel,
|
|
2258
|
+
{ color: foregroundColor },
|
|
2259
|
+
toolLabelStyle,
|
|
2260
|
+
selected ? activeToolLabelStyle : void 0
|
|
2261
|
+
],
|
|
2262
|
+
children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase()
|
|
2263
|
+
}
|
|
2264
|
+
);
|
|
2265
|
+
return /* @__PURE__ */ jsxs(
|
|
2266
|
+
Pressable,
|
|
2267
|
+
{
|
|
2268
|
+
accessibilityLabel: tool.accessibilityLabel ?? tool.label,
|
|
2269
|
+
accessibilityRole: "button",
|
|
2270
|
+
accessibilityState: { selected, disabled: toolDisabled },
|
|
2271
|
+
disabled: toolDisabled,
|
|
2272
|
+
onPress: onSelect,
|
|
2273
|
+
style: ({ pressed }) => [
|
|
2274
|
+
styles.toolButton,
|
|
2275
|
+
toolButtonStyle,
|
|
2276
|
+
selected ? styles.activeToolButton : void 0,
|
|
2277
|
+
selected ? activeToolButtonStyle : void 0,
|
|
2278
|
+
pressed && !toolDisabled ? styles.pressedToolButton : void 0,
|
|
2279
|
+
toolDisabled ? styles.disabledToolButton : void 0
|
|
2280
|
+
],
|
|
2281
|
+
children: [
|
|
2282
|
+
/* @__PURE__ */ jsx(View, { style: styles.iconSlot, children: icon }),
|
|
2283
|
+
/* @__PURE__ */ jsx(
|
|
2284
|
+
Text$1,
|
|
2285
|
+
{
|
|
2286
|
+
numberOfLines: 1,
|
|
2287
|
+
style: [
|
|
2288
|
+
styles.toolLabel,
|
|
2289
|
+
{ color: foregroundColor },
|
|
2290
|
+
toolLabelStyle,
|
|
2291
|
+
selected ? activeToolLabelStyle : void 0
|
|
2292
|
+
],
|
|
2293
|
+
children: tool.label
|
|
2294
|
+
}
|
|
2295
|
+
)
|
|
2296
|
+
]
|
|
2297
|
+
},
|
|
2298
|
+
tool.id
|
|
2299
|
+
);
|
|
2300
|
+
})
|
|
2301
|
+
}
|
|
2302
|
+
) });
|
|
2303
|
+
}
|
|
2304
|
+
var styles = StyleSheet.create({
|
|
2305
|
+
shell: {
|
|
2306
|
+
borderRadius: 10,
|
|
2307
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
2308
|
+
borderColor: "rgba(24, 24, 27, 0.14)",
|
|
2309
|
+
backgroundColor: "rgba(255, 255, 255, 0.96)",
|
|
2310
|
+
shadowColor: "#18181b",
|
|
2311
|
+
shadowOpacity: 0.12,
|
|
2312
|
+
shadowRadius: 16,
|
|
2313
|
+
shadowOffset: { width: 0, height: 8 },
|
|
2314
|
+
elevation: 6
|
|
2315
|
+
},
|
|
2316
|
+
content: {
|
|
2317
|
+
alignItems: "center",
|
|
2318
|
+
gap: 6,
|
|
2319
|
+
paddingHorizontal: 6,
|
|
2320
|
+
paddingVertical: 6
|
|
2321
|
+
},
|
|
2322
|
+
toolButton: {
|
|
2323
|
+
minWidth: 58,
|
|
2324
|
+
height: 52,
|
|
2325
|
+
alignItems: "center",
|
|
2326
|
+
justifyContent: "center",
|
|
2327
|
+
gap: 3,
|
|
2328
|
+
borderRadius: 8,
|
|
2329
|
+
paddingHorizontal: 8,
|
|
2330
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
2331
|
+
borderColor: "transparent",
|
|
2332
|
+
backgroundColor: "transparent"
|
|
2333
|
+
},
|
|
2334
|
+
activeToolButton: {
|
|
2335
|
+
borderColor: "rgba(24, 24, 27, 0.24)",
|
|
2336
|
+
backgroundColor: "#18181b"
|
|
2337
|
+
},
|
|
2338
|
+
pressedToolButton: {
|
|
2339
|
+
backgroundColor: "rgba(24, 24, 27, 0.08)"
|
|
2340
|
+
},
|
|
2341
|
+
disabledToolButton: {
|
|
2342
|
+
opacity: 0.42
|
|
2343
|
+
},
|
|
2344
|
+
iconSlot: {
|
|
2345
|
+
height: 22,
|
|
2346
|
+
minWidth: 22,
|
|
2347
|
+
alignItems: "center",
|
|
2348
|
+
justifyContent: "center"
|
|
2349
|
+
},
|
|
2350
|
+
shortLabel: {
|
|
2351
|
+
fontSize: 16,
|
|
2352
|
+
fontWeight: "700",
|
|
2353
|
+
lineHeight: 20
|
|
2354
|
+
},
|
|
2355
|
+
toolLabel: {
|
|
2356
|
+
maxWidth: 68,
|
|
2357
|
+
fontSize: 10,
|
|
2358
|
+
fontWeight: "600",
|
|
2359
|
+
lineHeight: 12
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2102
2362
|
|
|
2103
2363
|
// src/camera/camera.ts
|
|
2104
2364
|
var Camera2D = class {
|
|
@@ -2286,6 +2546,37 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
|
|
|
2286
2546
|
}
|
|
2287
2547
|
return ids;
|
|
2288
2548
|
}
|
|
2549
|
+
var MIN_PLACE_SIZE = 8;
|
|
2550
|
+
var MIN_ARROW_DRAG_PX = 8;
|
|
2551
|
+
var TAP_PX = 20;
|
|
2552
|
+
function isPlacementTool(toolId) {
|
|
2553
|
+
return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
|
|
2554
|
+
}
|
|
2555
|
+
function placementPreviewForTool(toolId, start, end) {
|
|
2556
|
+
if (toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud") {
|
|
2557
|
+
return { kind: toolId, rect: rectFromCorners(start, end) };
|
|
2558
|
+
}
|
|
2559
|
+
return { kind: toolId, start, end };
|
|
2560
|
+
}
|
|
2561
|
+
function defaultPlacementWorld(toolId, center) {
|
|
2562
|
+
const cx = center.x;
|
|
2563
|
+
const cy = center.y;
|
|
2564
|
+
if (toolId === "rect") {
|
|
2565
|
+
return { raw: { x: cx - 60, y: cy - 40, width: 120, height: 80 } };
|
|
2566
|
+
}
|
|
2567
|
+
if (toolId === "ellipse") {
|
|
2568
|
+
return { raw: { x: cx - 70, y: cy - 45, width: 140, height: 90 } };
|
|
2569
|
+
}
|
|
2570
|
+
if (toolId === "architectural-cloud") {
|
|
2571
|
+
return { raw: { x: cx - 90, y: cy - 50, width: 180, height: 100 } };
|
|
2572
|
+
}
|
|
2573
|
+
const start = { x: cx - 50, y: cy };
|
|
2574
|
+
const end = { x: cx + 50, y: cy };
|
|
2575
|
+
return {
|
|
2576
|
+
raw: rectFromCorners(start, end),
|
|
2577
|
+
lineWorld: [start, end]
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2289
2580
|
function collectIdsInRect(items, marquee) {
|
|
2290
2581
|
const m = normalizeRect(marquee);
|
|
2291
2582
|
const out = [];
|
|
@@ -2463,10 +2754,31 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
2463
2754
|
setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
|
|
2464
2755
|
return;
|
|
2465
2756
|
}
|
|
2757
|
+
if (isPlacementTool(tool)) {
|
|
2758
|
+
dragStateRef.current = {
|
|
2759
|
+
kind: "place",
|
|
2760
|
+
tool,
|
|
2761
|
+
startWorld: { x: worldX, y: worldY },
|
|
2762
|
+
startScreen: { x: sx, y: sy }
|
|
2763
|
+
};
|
|
2764
|
+
setPlacementPreview(
|
|
2765
|
+
placementPreviewForTool(
|
|
2766
|
+
tool,
|
|
2767
|
+
{ x: worldX, y: worldY },
|
|
2768
|
+
{
|
|
2769
|
+
x: worldX,
|
|
2770
|
+
y: worldY
|
|
2771
|
+
}
|
|
2772
|
+
)
|
|
2773
|
+
);
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2466
2776
|
if (tool === "note" || tool === "text") {
|
|
2467
2777
|
dragStateRef.current = {
|
|
2468
2778
|
kind: "tap",
|
|
2469
|
-
|
|
2779
|
+
tool,
|
|
2780
|
+
startWorld: { x: worldX, y: worldY },
|
|
2781
|
+
startScreen: { x: sx, y: sy }
|
|
2470
2782
|
};
|
|
2471
2783
|
return;
|
|
2472
2784
|
}
|
|
@@ -2578,6 +2890,15 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
2578
2890
|
setEraserPreviewIds(Array.from(eraserPreviewIdSetRef.current));
|
|
2579
2891
|
return;
|
|
2580
2892
|
}
|
|
2893
|
+
if (st.kind === "place") {
|
|
2894
|
+
setPlacementPreview(
|
|
2895
|
+
placementPreviewForTool(st.tool, st.startWorld, {
|
|
2896
|
+
x: worldX,
|
|
2897
|
+
y: worldY
|
|
2898
|
+
})
|
|
2899
|
+
);
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2581
2902
|
},
|
|
2582
2903
|
onPanResponderRelease: (evt) => {
|
|
2583
2904
|
lastPinchDist.current = null;
|
|
@@ -2633,12 +2954,83 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
2633
2954
|
dragStateRef.current = { kind: "idle" };
|
|
2634
2955
|
return;
|
|
2635
2956
|
}
|
|
2957
|
+
if (st.kind === "place") {
|
|
2958
|
+
dragStateRef.current = { kind: "idle" };
|
|
2959
|
+
setPlacementPreview(null);
|
|
2960
|
+
const change = onItemsChangeRef.current;
|
|
2961
|
+
if (!change) return;
|
|
2962
|
+
const { worldX, worldY } = screenToWorld(
|
|
2963
|
+
evt.nativeEvent.locationX,
|
|
2964
|
+
evt.nativeEvent.locationY
|
|
2965
|
+
);
|
|
2966
|
+
const a = st.startWorld;
|
|
2967
|
+
const b = { x: worldX, y: worldY };
|
|
2968
|
+
const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
|
|
2969
|
+
const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
|
|
2970
|
+
if (st.tool === "arrow" && Math.hypot(screenDx, screenDy) < MIN_ARROW_DRAG_PX) {
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
let raw = rectFromCorners(a, b);
|
|
2974
|
+
let br = normalizeRect(raw);
|
|
2975
|
+
let lineStart = a;
|
|
2976
|
+
let lineEnd = b;
|
|
2977
|
+
if (br.width < MIN_PLACE_SIZE || br.height < MIN_PLACE_SIZE) {
|
|
2978
|
+
const center = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
2979
|
+
const defaults = defaultPlacementWorld(st.tool, center);
|
|
2980
|
+
raw = defaults.raw;
|
|
2981
|
+
br = normalizeRect(raw);
|
|
2982
|
+
if (defaults.lineWorld) {
|
|
2983
|
+
const [defaultStart, defaultEnd] = defaults.lineWorld;
|
|
2984
|
+
lineStart = defaultStart;
|
|
2985
|
+
lineEnd = defaultEnd;
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
const id = createShapeId();
|
|
2989
|
+
if (st.tool === "rect") {
|
|
2990
|
+
change([...itemsRef.current, createRectangleItem(id, raw)]);
|
|
2991
|
+
onSelectionChangeRef.current?.([id]);
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
if (st.tool === "ellipse") {
|
|
2995
|
+
change([...itemsRef.current, createEllipseItem(id, raw)]);
|
|
2996
|
+
onSelectionChangeRef.current?.([id]);
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
if (st.tool === "architectural-cloud") {
|
|
3000
|
+
change([...itemsRef.current, createArchitecturalCloudItem(id, raw)]);
|
|
3001
|
+
onSelectionChangeRef.current?.([id]);
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
const line = lineEndpointsToLocal(br, lineStart, lineEnd);
|
|
3005
|
+
change([...itemsRef.current, createLineItem(id, br, line, st.tool)]);
|
|
3006
|
+
onSelectionChangeRef.current?.([id]);
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
2636
3009
|
if (st.kind === "tap") {
|
|
2637
3010
|
dragStateRef.current = { kind: "idle" };
|
|
3011
|
+
const screenDx = evt.nativeEvent.locationX - st.startScreen.x;
|
|
3012
|
+
const screenDy = evt.nativeEvent.locationY - st.startScreen.y;
|
|
3013
|
+
if (Math.hypot(screenDx, screenDy) > TAP_PX) return;
|
|
2638
3014
|
const change = onItemsChangeRef.current;
|
|
2639
3015
|
if (!change) return;
|
|
2640
|
-
|
|
2641
|
-
|
|
3016
|
+
if (st.tool === "text") {
|
|
3017
|
+
const id = createShapeId();
|
|
3018
|
+
const item = createTextItem(
|
|
3019
|
+
id,
|
|
3020
|
+
{
|
|
3021
|
+
x: st.startWorld.x - 4,
|
|
3022
|
+
y: st.startWorld.y - 18,
|
|
3023
|
+
width: 160,
|
|
3024
|
+
height: 26
|
|
3025
|
+
},
|
|
3026
|
+
"Text",
|
|
3027
|
+
void 0,
|
|
3028
|
+
18
|
|
3029
|
+
);
|
|
3030
|
+
change([...itemsRef.current, item]);
|
|
3031
|
+
onSelectionChangeRef.current?.([id]);
|
|
3032
|
+
}
|
|
3033
|
+
if (st.tool === "note") {
|
|
2642
3034
|
const id = createShapeId();
|
|
2643
3035
|
const note = {
|
|
2644
3036
|
id,
|
|
@@ -2654,6 +3046,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
2654
3046
|
toolKind: "custom"
|
|
2655
3047
|
};
|
|
2656
3048
|
change([...itemsRef.current, note]);
|
|
3049
|
+
onSelectionChangeRef.current?.([id]);
|
|
2657
3050
|
}
|
|
2658
3051
|
return;
|
|
2659
3052
|
}
|
|
@@ -2741,6 +3134,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
|
|
|
2741
3134
|
);
|
|
2742
3135
|
});
|
|
2743
3136
|
|
|
2744
|
-
export { NativeInteractionOverlay, NativeSceneRenderer, NativeShapeRenderer, NativeVectorViewport, parseSvgFragment };
|
|
3137
|
+
export { DEFAULT_NATIVE_VECTOR_TOOLS, NativeInteractionOverlay, NativeSceneRenderer, NativeShapeRenderer, NativeVectorToolbar, NativeVectorViewport, createFreehandStrokeItem, createShapeId, parseSvgFragment };
|
|
2745
3138
|
//# sourceMappingURL=native.js.map
|
|
2746
3139
|
//# sourceMappingURL=native.js.map
|