canvu-react 0.3.26 → 0.3.27
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/react.cjs +66 -24
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +66 -24
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs +190 -56
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.js +186 -56
- package/dist/realtime.js.map +1 -1
- package/package.json +1 -1
package/dist/realtime.cjs
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var lucideReact = require('lucide-react');
|
|
4
|
+
var getStroke = require('perfect-freehand');
|
|
4
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
6
|
var react = require('react');
|
|
6
7
|
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var getStroke__default = /*#__PURE__*/_interopDefault(getStroke);
|
|
11
|
+
|
|
7
12
|
// src/react/presence/map-placement-preview.ts
|
|
8
13
|
function remoteMarkupStrokeFromPlacementPreview(preview) {
|
|
9
14
|
if (!preview || preview.kind !== "stroke" || preview.points.length === 0) {
|
|
@@ -73,6 +78,126 @@ function smoothFreehandPointsToPathD(points) {
|
|
|
73
78
|
return d;
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
// src/scene/custom-shape.ts
|
|
82
|
+
function expandCustomShapeTemplate(template, width, height) {
|
|
83
|
+
return template.replace(/\{\{w\}\}/g, String(width)).replace(/\{\{h\}\}/g, String(height)).replace(/\{\{width\}\}/g, String(width)).replace(/\{\{height\}\}/g, String(height));
|
|
84
|
+
}
|
|
85
|
+
function resolveCustomInner(content, size) {
|
|
86
|
+
if ("render" in content) {
|
|
87
|
+
return content.render(size);
|
|
88
|
+
}
|
|
89
|
+
return expandCustomShapeTemplate(content.svg, size.width, size.height);
|
|
90
|
+
}
|
|
91
|
+
function buildCustomShapeChildrenSvg(inner, intrinsic, bounds) {
|
|
92
|
+
const b = normalizeRect(bounds);
|
|
93
|
+
const sx = b.width / intrinsic.width;
|
|
94
|
+
const sy = b.height / intrinsic.height;
|
|
95
|
+
return `<g transform="scale(${sx},${sy})">${inner}</g>`;
|
|
96
|
+
}
|
|
97
|
+
function createCustomShapeItem(id, bounds, content) {
|
|
98
|
+
const r = normalizeRect(bounds);
|
|
99
|
+
const intrinsic = { width: r.width, height: r.height };
|
|
100
|
+
const inner = resolveCustomInner(content, intrinsic);
|
|
101
|
+
return {
|
|
102
|
+
id,
|
|
103
|
+
x: r.x,
|
|
104
|
+
y: r.y,
|
|
105
|
+
bounds: { ...r },
|
|
106
|
+
toolKind: "custom",
|
|
107
|
+
customIntrinsicSize: intrinsic,
|
|
108
|
+
customInnerSvg: inner,
|
|
109
|
+
childrenSvg: buildCustomShapeChildrenSvg(inner, intrinsic, r)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/scene/shape-builders.ts
|
|
114
|
+
function perfectFreehandOptions(toolKind, style, strokeComplete, pressureAware = false) {
|
|
115
|
+
const sw = style.strokeWidth;
|
|
116
|
+
const base2 = {
|
|
117
|
+
last: strokeComplete,
|
|
118
|
+
simulatePressure: true
|
|
119
|
+
};
|
|
120
|
+
if (toolKind === "draw" || toolKind === "pencil") {
|
|
121
|
+
if (pressureAware && toolKind === "draw") {
|
|
122
|
+
return {
|
|
123
|
+
...base2,
|
|
124
|
+
size: Math.max(2, sw * 1.05),
|
|
125
|
+
thinning: 0.42,
|
|
126
|
+
smoothing: 0.78,
|
|
127
|
+
streamline: 0.62,
|
|
128
|
+
simulatePressure: true
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
...base2,
|
|
133
|
+
size: Math.max(2, sw * 1.18),
|
|
134
|
+
thinning: 0.12,
|
|
135
|
+
smoothing: 0.85,
|
|
136
|
+
streamline: 0.78,
|
|
137
|
+
simulatePressure: true
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (toolKind === "brush") {
|
|
141
|
+
return {
|
|
142
|
+
...base2,
|
|
143
|
+
size: Math.max(4, sw * 1.22),
|
|
144
|
+
thinning: 0.52,
|
|
145
|
+
smoothing: 0.64,
|
|
146
|
+
streamline: 0.68
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
...base2,
|
|
151
|
+
size: Math.max(6, sw * 1.08),
|
|
152
|
+
thinning: 0.08,
|
|
153
|
+
smoothing: 0.88,
|
|
154
|
+
streamline: 0.84,
|
|
155
|
+
simulatePressure: true
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeComplete = true) {
|
|
159
|
+
if (pathPointsLocal.length === 0) return null;
|
|
160
|
+
if (pathPointsLocal.length === 1) {
|
|
161
|
+
const p = pathPointsLocal[0];
|
|
162
|
+
if (!p) return null;
|
|
163
|
+
return {
|
|
164
|
+
kind: "circle",
|
|
165
|
+
cx: p.x,
|
|
166
|
+
cy: p.y,
|
|
167
|
+
r: Math.max(0.5, style.strokeWidth / 2),
|
|
168
|
+
fill: style.stroke,
|
|
169
|
+
fillOpacity: style.strokeOpacity
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const hasPressure = pathPointsLocal.some(
|
|
173
|
+
(p) => p.pressure != null && Number.isFinite(p.pressure)
|
|
174
|
+
);
|
|
175
|
+
const input = hasPressure ? pathPointsLocal.map(
|
|
176
|
+
(p) => [p.x, p.y, Math.min(1, Math.max(0, p.pressure ?? 0.5))]
|
|
177
|
+
) : pathPointsLocal.map((p) => [p.x, p.y]);
|
|
178
|
+
const stroke = getStroke__default.default(
|
|
179
|
+
input,
|
|
180
|
+
perfectFreehandOptions(toolKind, style, strokeComplete, hasPressure)
|
|
181
|
+
);
|
|
182
|
+
if (stroke.length < 3) return null;
|
|
183
|
+
const first = stroke[0];
|
|
184
|
+
if (!first) return null;
|
|
185
|
+
let d = `M ${first[0]} ${first[1]} Q`;
|
|
186
|
+
for (let i = 0; i < stroke.length; i++) {
|
|
187
|
+
const a = stroke[i];
|
|
188
|
+
const b = stroke[(i + 1) % stroke.length];
|
|
189
|
+
if (!a || !b) continue;
|
|
190
|
+
d += ` ${a[0]} ${a[1]} ${(a[0] + b[0]) / 2} ${(a[1] + b[1]) / 2}`;
|
|
191
|
+
}
|
|
192
|
+
d += " Z";
|
|
193
|
+
return {
|
|
194
|
+
kind: "fillPath",
|
|
195
|
+
d,
|
|
196
|
+
fill: style.stroke,
|
|
197
|
+
fillOpacity: style.strokeOpacity
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
76
201
|
// src/react/presence/peer-color.ts
|
|
77
202
|
function defaultPresenceColorForId(id) {
|
|
78
203
|
let h = 2166136261;
|
|
@@ -97,6 +222,9 @@ function strokePaint(tool, fallback) {
|
|
|
97
222
|
return { stroke: fallback, strokeOpacity: 0.95, widthWorld: 3.5 };
|
|
98
223
|
}
|
|
99
224
|
}
|
|
225
|
+
function isFreehandTool(tool) {
|
|
226
|
+
return tool === "draw" || tool === "marker" || tool === "pencil" || tool === "brush";
|
|
227
|
+
}
|
|
100
228
|
function PresenceRemoteLayer({
|
|
101
229
|
camera,
|
|
102
230
|
cameraVersion: _cameraVersion,
|
|
@@ -138,34 +266,72 @@ function PresenceRemoteLayer({
|
|
|
138
266
|
strokeOpacity: markup.strokeOpacity ?? fallbackPaint.strokeOpacity,
|
|
139
267
|
widthWorld: markup.strokeWidth ?? fallbackPaint.widthWorld
|
|
140
268
|
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
269
|
+
if (markup.tool === "laser") {
|
|
270
|
+
const d = markup.points.length >= 2 ? smoothFreehandPointsToPathD([...markup.points]) : null;
|
|
271
|
+
if (d) {
|
|
272
|
+
strokeNode = /* @__PURE__ */ jsxRuntime.jsx(
|
|
273
|
+
"path",
|
|
274
|
+
{
|
|
275
|
+
d,
|
|
276
|
+
fill: "none",
|
|
277
|
+
stroke: paint.stroke,
|
|
278
|
+
strokeOpacity: paint.strokeOpacity,
|
|
279
|
+
strokeWidth: Math.max(paint.widthWorld / z, overlayStrokePx),
|
|
280
|
+
strokeLinecap: "round",
|
|
281
|
+
strokeLinejoin: "round",
|
|
282
|
+
shapeRendering: "geometricPrecision",
|
|
283
|
+
vectorEffect: "non-scaling-stroke"
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
} else if (isFreehandTool(markup.tool)) {
|
|
288
|
+
const style = {
|
|
289
|
+
stroke: paint.stroke,
|
|
290
|
+
strokeWidth: paint.widthWorld,
|
|
291
|
+
...paint.strokeOpacity != null ? { strokeOpacity: paint.strokeOpacity } : {}
|
|
292
|
+
};
|
|
293
|
+
const payload = computeFreehandSvgPayload(
|
|
294
|
+
markup.points.map((p) => ({ x: p.x, y: p.y })),
|
|
295
|
+
style,
|
|
296
|
+
markup.tool,
|
|
297
|
+
false
|
|
156
298
|
);
|
|
157
|
-
|
|
158
|
-
const p0 = markup.points[0];
|
|
159
|
-
if (p0) {
|
|
299
|
+
if (payload?.kind === "circle") {
|
|
160
300
|
strokeNode = /* @__PURE__ */ jsxRuntime.jsx(
|
|
161
301
|
"circle",
|
|
162
302
|
{
|
|
163
|
-
cx:
|
|
164
|
-
cy:
|
|
165
|
-
r:
|
|
166
|
-
fill:
|
|
167
|
-
fillOpacity:
|
|
168
|
-
|
|
303
|
+
cx: payload.cx,
|
|
304
|
+
cy: payload.cy,
|
|
305
|
+
r: payload.r,
|
|
306
|
+
fill: payload.fill,
|
|
307
|
+
...payload.fillOpacity != null ? { fillOpacity: payload.fillOpacity } : {},
|
|
308
|
+
shapeRendering: "geometricPrecision"
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
} else if (payload?.kind === "fillPath") {
|
|
312
|
+
strokeNode = /* @__PURE__ */ jsxRuntime.jsx(
|
|
313
|
+
"path",
|
|
314
|
+
{
|
|
315
|
+
d: payload.d,
|
|
316
|
+
fill: payload.fill,
|
|
317
|
+
fillRule: "nonzero",
|
|
318
|
+
stroke: "none",
|
|
319
|
+
...payload.fillOpacity != null ? { fillOpacity: payload.fillOpacity } : {},
|
|
320
|
+
shapeRendering: "geometricPrecision"
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
} else if (payload?.kind === "strokePath") {
|
|
324
|
+
strokeNode = /* @__PURE__ */ jsxRuntime.jsx(
|
|
325
|
+
"path",
|
|
326
|
+
{
|
|
327
|
+
d: payload.d,
|
|
328
|
+
fill: "none",
|
|
329
|
+
stroke: payload.stroke,
|
|
330
|
+
strokeWidth: payload.strokeWidth,
|
|
331
|
+
...payload.strokeOpacity != null ? { strokeOpacity: payload.strokeOpacity } : {},
|
|
332
|
+
strokeLinecap: "round",
|
|
333
|
+
strokeLinejoin: "round",
|
|
334
|
+
shapeRendering: "geometricPrecision"
|
|
169
335
|
}
|
|
170
336
|
);
|
|
171
337
|
}
|
|
@@ -797,38 +963,6 @@ var DEFAULT_VECTOR_TOOLS = [
|
|
|
797
963
|
shortcutHint: "I"
|
|
798
964
|
}
|
|
799
965
|
];
|
|
800
|
-
|
|
801
|
-
// src/scene/custom-shape.ts
|
|
802
|
-
function expandCustomShapeTemplate(template, width, height) {
|
|
803
|
-
return template.replace(/\{\{w\}\}/g, String(width)).replace(/\{\{h\}\}/g, String(height)).replace(/\{\{width\}\}/g, String(width)).replace(/\{\{height\}\}/g, String(height));
|
|
804
|
-
}
|
|
805
|
-
function resolveCustomInner(content, size) {
|
|
806
|
-
if ("render" in content) {
|
|
807
|
-
return content.render(size);
|
|
808
|
-
}
|
|
809
|
-
return expandCustomShapeTemplate(content.svg, size.width, size.height);
|
|
810
|
-
}
|
|
811
|
-
function buildCustomShapeChildrenSvg(inner, intrinsic, bounds) {
|
|
812
|
-
const b = normalizeRect(bounds);
|
|
813
|
-
const sx = b.width / intrinsic.width;
|
|
814
|
-
const sy = b.height / intrinsic.height;
|
|
815
|
-
return `<g transform="scale(${sx},${sy})">${inner}</g>`;
|
|
816
|
-
}
|
|
817
|
-
function createCustomShapeItem(id, bounds, content) {
|
|
818
|
-
const r = normalizeRect(bounds);
|
|
819
|
-
const intrinsic = { width: r.width, height: r.height };
|
|
820
|
-
const inner = resolveCustomInner(content, intrinsic);
|
|
821
|
-
return {
|
|
822
|
-
id,
|
|
823
|
-
x: r.x,
|
|
824
|
-
y: r.y,
|
|
825
|
-
bounds: { ...r },
|
|
826
|
-
toolKind: "custom",
|
|
827
|
-
customIntrinsicSize: intrinsic,
|
|
828
|
-
customInnerSvg: inner,
|
|
829
|
-
childrenSvg: buildCustomShapeChildrenSvg(inner, intrinsic, r)
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
966
|
var iconProps = { size: 20, strokeWidth: 2 };
|
|
833
967
|
var COMMENT_PLUGIN_DATA_KEY = "realtimeComment";
|
|
834
968
|
var REALTIME_COMMENT_TOOL = {
|