canvu-react 0.4.14 → 0.4.16

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/native.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import getStroke from 'perfect-freehand';
2
- import { Group, RoundedRect, Circle, Line, vec, Path, matchFont, Text, Canvas, Rect, Oval, useImage, Image } from '@shopify/react-native-skia';
2
+ import { Group, RoundedRect, Circle, Line, vec, Path, matchFont, Text, Canvas, Rect, Oval, DashPathEffect, useImage, Image } from '@shopify/react-native-skia';
3
3
  import { memo, forwardRef, useState, useRef, useEffect, useCallback, useMemo, useImperativeHandle } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { StyleSheet, PanResponder, View, ScrollView, Pressable, Text as Text$1 } from 'react-native';
5
+ import { StyleSheet, PanResponder, View, Pressable, Text as Text$1, ScrollView } from 'react-native';
6
6
 
7
7
  // src/scene/shape-builders.ts
8
8
 
@@ -1816,13 +1816,224 @@ function NativeShapeRenderer({ item }) {
1816
1816
  }
1817
1817
  return null;
1818
1818
  }
1819
+ var CURSOR_STROKE = "#18181b";
1820
+ var CURSOR_STROKE_WIDTH = 2;
1821
+ var CROSSHAIR_STROKE_WIDTH = 1.5;
1822
+ function NativeDrawCursor() {
1823
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1824
+ /* @__PURE__ */ jsx(
1825
+ Path,
1826
+ {
1827
+ path: "M13 21h8",
1828
+ color: CURSOR_STROKE,
1829
+ style: "stroke",
1830
+ strokeWidth: CURSOR_STROKE_WIDTH,
1831
+ strokeCap: "round",
1832
+ strokeJoin: "round",
1833
+ antiAlias: true
1834
+ }
1835
+ ),
1836
+ /* @__PURE__ */ jsx(
1837
+ Path,
1838
+ {
1839
+ path: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
1840
+ color: CURSOR_STROKE,
1841
+ style: "stroke",
1842
+ strokeWidth: CURSOR_STROKE_WIDTH,
1843
+ strokeCap: "round",
1844
+ strokeJoin: "round",
1845
+ antiAlias: true
1846
+ }
1847
+ )
1848
+ ] });
1849
+ }
1850
+ function NativeMarkerCursor() {
1851
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1852
+ /* @__PURE__ */ jsx(
1853
+ Path,
1854
+ {
1855
+ path: "m9 11-6 6v3h9l3-3",
1856
+ color: CURSOR_STROKE,
1857
+ style: "stroke",
1858
+ strokeWidth: CURSOR_STROKE_WIDTH,
1859
+ strokeCap: "round",
1860
+ strokeJoin: "round",
1861
+ antiAlias: true
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsx(
1865
+ Path,
1866
+ {
1867
+ path: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4",
1868
+ color: CURSOR_STROKE,
1869
+ style: "stroke",
1870
+ strokeWidth: CURSOR_STROKE_WIDTH,
1871
+ strokeCap: "round",
1872
+ strokeJoin: "round",
1873
+ antiAlias: true
1874
+ }
1875
+ )
1876
+ ] });
1877
+ }
1878
+ function NativeEraserCursor() {
1879
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1880
+ /* @__PURE__ */ jsx(
1881
+ Path,
1882
+ {
1883
+ path: "M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21",
1884
+ color: CURSOR_STROKE,
1885
+ style: "stroke",
1886
+ strokeWidth: CURSOR_STROKE_WIDTH,
1887
+ strokeCap: "round",
1888
+ strokeJoin: "round",
1889
+ antiAlias: true
1890
+ }
1891
+ ),
1892
+ /* @__PURE__ */ jsx(
1893
+ Path,
1894
+ {
1895
+ path: "m5.082 11.09 8.828 8.828",
1896
+ color: CURSOR_STROKE,
1897
+ style: "stroke",
1898
+ strokeWidth: CURSOR_STROKE_WIDTH,
1899
+ strokeCap: "round",
1900
+ strokeJoin: "round",
1901
+ antiAlias: true
1902
+ }
1903
+ )
1904
+ ] });
1905
+ }
1906
+ function NativeCrosshairCursor() {
1907
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1908
+ /* @__PURE__ */ jsx(
1909
+ Line,
1910
+ {
1911
+ p1: vec(12, 3),
1912
+ p2: vec(12, 21),
1913
+ color: CURSOR_STROKE,
1914
+ style: "stroke",
1915
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
1916
+ strokeCap: "round",
1917
+ antiAlias: true
1918
+ }
1919
+ ),
1920
+ /* @__PURE__ */ jsx(
1921
+ Line,
1922
+ {
1923
+ p1: vec(3, 12),
1924
+ p2: vec(21, 12),
1925
+ color: CURSOR_STROKE,
1926
+ style: "stroke",
1927
+ strokeWidth: CROSSHAIR_STROKE_WIDTH,
1928
+ strokeCap: "round",
1929
+ antiAlias: true
1930
+ }
1931
+ )
1932
+ ] });
1933
+ }
1934
+ function NativeTextCursor() {
1935
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1936
+ /* @__PURE__ */ jsx(
1937
+ Line,
1938
+ {
1939
+ p1: vec(12, 4),
1940
+ p2: vec(12, 20),
1941
+ color: CURSOR_STROKE,
1942
+ style: "stroke",
1943
+ strokeWidth: CURSOR_STROKE_WIDTH,
1944
+ strokeCap: "round",
1945
+ antiAlias: true
1946
+ }
1947
+ ),
1948
+ /* @__PURE__ */ jsx(
1949
+ Line,
1950
+ {
1951
+ p1: vec(8, 4),
1952
+ p2: vec(16, 4),
1953
+ color: CURSOR_STROKE,
1954
+ style: "stroke",
1955
+ strokeWidth: CURSOR_STROKE_WIDTH,
1956
+ strokeCap: "round",
1957
+ antiAlias: true
1958
+ }
1959
+ ),
1960
+ /* @__PURE__ */ jsx(
1961
+ Line,
1962
+ {
1963
+ p1: vec(8, 20),
1964
+ p2: vec(16, 20),
1965
+ color: CURSOR_STROKE,
1966
+ style: "stroke",
1967
+ strokeWidth: CURSOR_STROKE_WIDTH,
1968
+ strokeCap: "round",
1969
+ antiAlias: true
1970
+ }
1971
+ )
1972
+ ] });
1973
+ }
1974
+ function NativeToolCursorGlyph({
1975
+ cursor
1976
+ }) {
1977
+ if (cursor.kind === "draw") return /* @__PURE__ */ jsx(NativeDrawCursor, {});
1978
+ if (cursor.kind === "marker") return /* @__PURE__ */ jsx(NativeMarkerCursor, {});
1979
+ if (cursor.kind === "eraser") return /* @__PURE__ */ jsx(NativeEraserCursor, {});
1980
+ if (cursor.kind === "text") return /* @__PURE__ */ jsx(NativeTextCursor, {});
1981
+ return /* @__PURE__ */ jsx(NativeCrosshairCursor, {});
1982
+ }
1983
+ function NativeToolCursorRenderer({
1984
+ cursor,
1985
+ point
1986
+ }) {
1987
+ const x = point.x - cursor.hotspot.x;
1988
+ const y = point.y - cursor.hotspot.y;
1989
+ return /* @__PURE__ */ jsx(Group, { transform: [{ translateX: x }, { translateY: y }], children: /* @__PURE__ */ jsx(NativeToolCursorGlyph, { cursor }) });
1990
+ }
1991
+
1992
+ // src/native/native-overlay-style.ts
1993
+ var ERASER_TINT = "#cbd5e1";
1994
+ var ERASER_TINT_OPACITY = 0.95;
1995
+ var LASER_TINT = "#f43f5e";
1996
+ var LASER_TINT_OPACITY = 0.9;
1997
+ var ERASER_TRAIL_MAX_AGE_MS = 150;
1998
+ var LASER_TRAIL_MAX_AGE_MS = 650;
1999
+ function colorWithOpacity(hex, alpha) {
2000
+ if (alpha == null || alpha >= 1) return hex;
2001
+ const r = parseInt(hex.slice(1, 3), 16);
2002
+ const g = parseInt(hex.slice(3, 5), 16);
2003
+ const b = parseInt(hex.slice(5, 7), 16);
2004
+ if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return hex;
2005
+ return `rgba(${r},${g},${b},${alpha})`;
2006
+ }
2007
+ function freshTimedTrailPoints(points, now, maxAgeMs) {
2008
+ return points.filter((point) => now - point.t <= maxAgeMs);
2009
+ }
2010
+ function timedTrailHeadOpacity(point, now, maxAgeMs) {
2011
+ if (!point) return 0;
2012
+ return Math.max(0, 1 - (now - point.t) / maxAgeMs);
2013
+ }
2014
+ function resolveNativeStrokePreviewStyle(tool, previewStrokeStyle) {
2015
+ const isLaser = tool === "laser";
2016
+ return {
2017
+ stroke: isLaser ? LASER_TINT : previewStrokeStyle?.stroke ?? "#64748b",
2018
+ strokeWidth: isLaser ? 4 : previewStrokeStyle?.strokeWidth ?? (tool === "marker" ? 16 : 3),
2019
+ ...previewStrokeStyle?.strokeOpacity != null && !isLaser ? { strokeOpacity: previewStrokeStyle.strokeOpacity } : {},
2020
+ ...previewStrokeStyle?.strokeDash != null && !isLaser ? { strokeDash: previewStrokeStyle.strokeDash } : {}
2021
+ };
2022
+ }
1819
2023
  var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
1820
2024
  var ERASER_PREVIEW_OPACITY = 0.3;
2025
+ var OVERLAY_STROKE_PX = 1.25;
2026
+ var MARQUEE_DASH_PX = 4;
1821
2027
  function pointsToSmoothPathD(points) {
1822
2028
  if (points.length < 2) return null;
1823
2029
  const d = smoothFreehandPointsToPathD(points);
1824
2030
  return d || null;
1825
2031
  }
2032
+ function dashIntervalsFromStrokeDasharray(strokeDasharray) {
2033
+ if (!strokeDasharray) return null;
2034
+ const intervals = strokeDasharray.split(/\s+/).map((part) => Number(part)).filter((part) => Number.isFinite(part) && part > 0);
2035
+ return intervals.length > 0 ? intervals : null;
2036
+ }
1826
2037
  function NativeInteractionOverlay({
1827
2038
  camera,
1828
2039
  width,
@@ -1832,13 +2043,19 @@ function NativeInteractionOverlay({
1832
2043
  placementPreview,
1833
2044
  eraserTrail,
1834
2045
  laserTrail,
1835
- eraserPreviewItems = []
2046
+ eraserPreviewItems = [],
2047
+ previewStrokeStyle,
2048
+ toolCursor
1836
2049
  }) {
1837
2050
  const z = camera.zoom;
1838
2051
  const camTransform = skiaCameraTransform(z, camera.x, camera.y);
1839
2052
  const handleR = 5 / z;
1840
- const overlayStrokePx = 1.25;
2053
+ const overlayStrokeWorld = OVERLAY_STROKE_PX / z;
2054
+ const marqueeDashWorld = MARQUEE_DASH_PX / z;
1841
2055
  const rotateOffsetWorld = 24 / z;
2056
+ const rotateIconWorld = 16 / z;
2057
+ const rotateIconScale = rotateIconWorld / 24;
2058
+ const rotateIconStroke = OVERLAY_STROKE_PX / (z * rotateIconScale);
1842
2059
  const selectionElements = useMemo(() => {
1843
2060
  if (selectedItems.length === 0) return null;
1844
2061
  const single = selectedItems.length === 1 ? selectedItems[0] : void 0;
@@ -1860,7 +2077,7 @@ function NativeInteractionOverlay({
1860
2077
  height: b.height,
1861
2078
  color: "#3b82f6",
1862
2079
  style: "stroke",
1863
- strokeWidth: overlayStrokePx,
2080
+ strokeWidth: overlayStrokeWorld,
1864
2081
  antiAlias: true
1865
2082
  }
1866
2083
  ) }, it.id);
@@ -1868,83 +2085,167 @@ function NativeInteractionOverlay({
1868
2085
  showResizeHandles && bSingle && single && /* @__PURE__ */ jsxs(Fragment, { children: [
1869
2086
  HANDLE_ORDER.map((hid) => {
1870
2087
  const p = getHandleWorldPositionRotated(bSingle, hid, rotSingle);
1871
- return /* @__PURE__ */ jsx(
1872
- Circle,
1873
- {
1874
- cx: p.x,
1875
- cy: p.y,
1876
- r: handleR,
1877
- color: "#ffffff",
1878
- style: "fill",
1879
- antiAlias: true
1880
- },
1881
- hid
1882
- );
2088
+ return /* @__PURE__ */ jsxs(Group, { children: [
2089
+ /* @__PURE__ */ jsx(
2090
+ Circle,
2091
+ {
2092
+ cx: p.x,
2093
+ cy: p.y,
2094
+ r: handleR,
2095
+ color: "#ffffff",
2096
+ style: "fill",
2097
+ antiAlias: true
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ jsx(
2101
+ Circle,
2102
+ {
2103
+ cx: p.x,
2104
+ cy: p.y,
2105
+ r: handleR,
2106
+ color: "#3b82f6",
2107
+ style: "stroke",
2108
+ strokeWidth: overlayStrokeWorld,
2109
+ antiAlias: true
2110
+ }
2111
+ )
2112
+ ] }, hid);
1883
2113
  }),
1884
- rotHandlePos && /* @__PURE__ */ jsx(
1885
- Circle,
2114
+ rotHandlePos && /* @__PURE__ */ jsxs(
2115
+ Group,
1886
2116
  {
1887
- cx: rotHandlePos.x,
1888
- cy: rotHandlePos.y,
1889
- r: handleR * 1.5,
1890
- color: "#3b82f6",
1891
- style: "stroke",
1892
- strokeWidth: overlayStrokePx * 1.2,
1893
- antiAlias: true
2117
+ transform: [
2118
+ { translateX: rotHandlePos.x },
2119
+ { translateY: rotHandlePos.y },
2120
+ { rotate: rotSingle },
2121
+ { translateX: -rotateIconWorld / 2 },
2122
+ { translateY: -rotateIconWorld / 2 },
2123
+ { scale: rotateIconScale }
2124
+ ],
2125
+ children: [
2126
+ /* @__PURE__ */ jsx(
2127
+ Path,
2128
+ {
2129
+ path: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8",
2130
+ color: "#3b82f6",
2131
+ style: "stroke",
2132
+ strokeWidth: rotateIconStroke,
2133
+ strokeCap: "round",
2134
+ strokeJoin: "round",
2135
+ antiAlias: true
2136
+ }
2137
+ ),
2138
+ /* @__PURE__ */ jsx(
2139
+ Path,
2140
+ {
2141
+ path: "M21 3v5h-5",
2142
+ color: "#3b82f6",
2143
+ style: "stroke",
2144
+ strokeWidth: rotateIconStroke,
2145
+ strokeCap: "round",
2146
+ strokeJoin: "round",
2147
+ antiAlias: true
2148
+ }
2149
+ )
2150
+ ]
1894
2151
  }
1895
2152
  )
1896
2153
  ] })
1897
2154
  ] });
1898
- }, [selectedItems, showResizeHandles, rotateOffsetWorld, handleR]);
2155
+ }, [
2156
+ selectedItems,
2157
+ showResizeHandles,
2158
+ rotateOffsetWorld,
2159
+ handleR,
2160
+ overlayStrokeWorld,
2161
+ rotateIconWorld,
2162
+ rotateIconScale,
2163
+ rotateIconStroke
2164
+ ]);
1899
2165
  const previewElements = useMemo(() => {
1900
2166
  if (!placementPreview) return null;
1901
2167
  const p = placementPreview;
2168
+ const shapeStroke = previewStrokeStyle?.stroke ?? "#1d1d1d";
2169
+ const shapeWidth = previewStrokeStyle?.strokeWidth ?? 2;
2170
+ const shapeColor = colorWithOpacity(
2171
+ shapeStroke,
2172
+ previewStrokeStyle?.strokeOpacity
2173
+ );
1902
2174
  if (p.kind === "rect" || p.kind === "ellipse" || p.kind === "architectural-cloud") {
1903
2175
  const r = normalizeRect(p.rect);
1904
- return p.kind === "rect" || p.kind === "architectural-cloud" ? /* @__PURE__ */ jsx(
1905
- Rect,
2176
+ return p.kind === "rect" ? /* @__PURE__ */ jsx(
2177
+ RoundedRect,
1906
2178
  {
1907
2179
  x: r.x,
1908
2180
  y: r.y,
1909
2181
  width: r.width,
1910
2182
  height: r.height,
1911
- color: "#64748b",
1912
- style: "stroke",
1913
- strokeWidth: overlayStrokePx,
1914
- antiAlias: true
1915
- }
1916
- ) : /* @__PURE__ */ jsx(
1917
- Circle,
1918
- {
1919
- cx: r.x + r.width / 2,
1920
- cy: r.y + r.height / 2,
1921
- r: Math.max(0, r.width / 2),
1922
- color: "#64748b",
2183
+ r: 4,
2184
+ color: shapeColor,
1923
2185
  style: "stroke",
1924
- strokeWidth: overlayStrokePx,
2186
+ strokeWidth: shapeWidth,
1925
2187
  antiAlias: true
1926
2188
  }
1927
- );
1928
- }
1929
- if (p.kind === "marquee") {
1930
- const r = normalizeRect(p.rect);
1931
- return /* @__PURE__ */ jsx(
1932
- Rect,
2189
+ ) : p.kind === "ellipse" ? /* @__PURE__ */ jsx(
2190
+ Oval,
1933
2191
  {
1934
2192
  x: r.x,
1935
2193
  y: r.y,
1936
2194
  width: r.width,
1937
2195
  height: r.height,
1938
- color: "rgba(59, 130, 246, 0.12)",
1939
- style: "fill",
2196
+ color: shapeColor,
2197
+ style: "stroke",
2198
+ strokeWidth: shapeWidth,
1940
2199
  antiAlias: true
1941
2200
  }
1942
- );
2201
+ ) : /* @__PURE__ */ jsx(Group, { transform: [{ translateX: r.x }, { translateY: r.y }], children: /* @__PURE__ */ jsx(
2202
+ Path,
2203
+ {
2204
+ path: buildArchitecturalCloudPathD(r.width, r.height, shapeWidth),
2205
+ color: shapeColor,
2206
+ style: "stroke",
2207
+ strokeWidth: shapeWidth,
2208
+ strokeCap: "round",
2209
+ strokeJoin: "round",
2210
+ antiAlias: true
2211
+ }
2212
+ ) });
2213
+ }
2214
+ if (p.kind === "marquee") {
2215
+ const r = normalizeRect(p.rect);
2216
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2217
+ /* @__PURE__ */ jsx(
2218
+ Rect,
2219
+ {
2220
+ x: r.x,
2221
+ y: r.y,
2222
+ width: r.width,
2223
+ height: r.height,
2224
+ color: "rgba(59, 130, 246, 0.12)",
2225
+ style: "fill",
2226
+ antiAlias: true
2227
+ }
2228
+ ),
2229
+ /* @__PURE__ */ jsx(
2230
+ Rect,
2231
+ {
2232
+ x: r.x,
2233
+ y: r.y,
2234
+ width: r.width,
2235
+ height: r.height,
2236
+ color: "#3b82f6",
2237
+ style: "stroke",
2238
+ strokeWidth: overlayStrokeWorld,
2239
+ antiAlias: true,
2240
+ children: /* @__PURE__ */ jsx(DashPathEffect, { intervals: [marqueeDashWorld, marqueeDashWorld] })
2241
+ }
2242
+ )
2243
+ ] });
1943
2244
  }
1944
2245
  if (p.kind === "line" || p.kind === "arrow") {
1945
2246
  const geometry = p.kind === "arrow" ? computeStraightArrowGeometry(
1946
2247
  { x1: p.start.x, y1: p.start.y, x2: p.end.x, y2: p.end.y },
1947
- overlayStrokePx
2248
+ shapeWidth
1948
2249
  ) : null;
1949
2250
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1950
2251
  /* @__PURE__ */ jsx(
@@ -1952,9 +2253,9 @@ function NativeInteractionOverlay({
1952
2253
  {
1953
2254
  p1: vec(p.start.x, p.start.y),
1954
2255
  p2: vec(geometry?.shaftEndX ?? p.end.x, geometry?.shaftEndY ?? p.end.y),
1955
- color: "#64748b",
2256
+ color: shapeColor,
1956
2257
  style: "stroke",
1957
- strokeWidth: overlayStrokePx,
2258
+ strokeWidth: shapeWidth,
1958
2259
  strokeCap: "round",
1959
2260
  antiAlias: true
1960
2261
  }
@@ -1963,9 +2264,9 @@ function NativeInteractionOverlay({
1963
2264
  Path,
1964
2265
  {
1965
2266
  path: `M ${geometry.headLeftX} ${geometry.headLeftY} L ${geometry.headTipX} ${geometry.headTipY} L ${geometry.headRightX} ${geometry.headRightY}`,
1966
- color: "#64748b",
2267
+ color: shapeColor,
1967
2268
  style: "stroke",
1968
- strokeWidth: overlayStrokePx,
2269
+ strokeWidth: shapeWidth,
1969
2270
  strokeCap: "round",
1970
2271
  strokeJoin: "round",
1971
2272
  antiAlias: true
@@ -1974,10 +2275,15 @@ function NativeInteractionOverlay({
1974
2275
  ] });
1975
2276
  }
1976
2277
  if (p.kind === "stroke" && p.points.length >= 1) {
2278
+ const isLaser = p.tool === "laser";
2279
+ const style = resolveNativeStrokePreviewStyle(
2280
+ p.tool,
2281
+ p.style ?? previewStrokeStyle
2282
+ );
1977
2283
  const payload = computeFreehandSvgPayload(
1978
2284
  p.points,
1979
- { stroke: "#64748b", strokeWidth: 3 },
1980
- "draw",
2285
+ style,
2286
+ isLaser ? "draw" : p.tool,
1981
2287
  p.points.length === 2
1982
2288
  );
1983
2289
  if (!payload) return null;
@@ -1988,33 +2294,47 @@ function NativeInteractionOverlay({
1988
2294
  cx: payload.cx,
1989
2295
  cy: payload.cy,
1990
2296
  r: payload.r,
1991
- color: payload.fill,
2297
+ color: colorWithOpacity(payload.fill, payload.fillOpacity),
1992
2298
  style: "fill",
1993
2299
  antiAlias: true
1994
2300
  }
1995
2301
  );
1996
2302
  }
1997
2303
  if (payload.kind === "fillPath") {
1998
- return /* @__PURE__ */ jsx(Path, { path: payload.d, color: payload.fill, style: "fill", antiAlias: true });
2304
+ return /* @__PURE__ */ jsx(
2305
+ Path,
2306
+ {
2307
+ path: payload.d,
2308
+ color: colorWithOpacity(payload.fill, payload.fillOpacity),
2309
+ style: "fill",
2310
+ fillType: "winding",
2311
+ antiAlias: true
2312
+ }
2313
+ );
1999
2314
  }
2000
2315
  if (payload.kind === "strokePath") {
2316
+ const intervals = dashIntervalsFromStrokeDasharray(payload.strokeDasharray);
2001
2317
  return /* @__PURE__ */ jsx(
2002
2318
  Path,
2003
2319
  {
2004
2320
  path: payload.d,
2005
- color: "#64748b",
2321
+ color: colorWithOpacity(
2322
+ payload.stroke,
2323
+ isLaser ? 0.85 : payload.strokeOpacity
2324
+ ),
2006
2325
  style: "stroke",
2007
- strokeWidth: 3,
2326
+ strokeWidth: payload.strokeWidth,
2008
2327
  strokeCap: "round",
2009
2328
  strokeJoin: "round",
2010
- antiAlias: true
2329
+ antiAlias: true,
2330
+ children: intervals ? /* @__PURE__ */ jsx(DashPathEffect, { intervals }) : null
2011
2331
  }
2012
2332
  );
2013
2333
  }
2014
2334
  return null;
2015
2335
  }
2016
2336
  return null;
2017
- }, [placementPreview]);
2337
+ }, [placementPreview, previewStrokeStyle, overlayStrokeWorld, marqueeDashWorld]);
2018
2338
  const eraserPreviewElements = useMemo(() => {
2019
2339
  if (eraserPreviewItems.length === 0) return null;
2020
2340
  return /* @__PURE__ */ jsx(Fragment, { children: eraserPreviewItems.map((it) => {
@@ -2035,62 +2355,74 @@ function NativeInteractionOverlay({
2035
2355
  }, [eraserPreviewItems]);
2036
2356
  const eraserTrailElements = useMemo(() => {
2037
2357
  if (!eraserTrail || eraserTrail.length < 1) return null;
2038
- const d = pointsToSmoothPathD(eraserTrail);
2039
- if (!d)
2040
- return eraserTrail[0] ? /* @__PURE__ */ jsx(
2358
+ const now = Date.now();
2359
+ const alive = freshTimedTrailPoints(eraserTrail, now, ERASER_TRAIL_MAX_AGE_MS);
2360
+ if (alive.length === 0) return null;
2361
+ const d = pointsToSmoothPathD(alive);
2362
+ const newest = alive[alive.length - 1];
2363
+ const headOpacity = timedTrailHeadOpacity(newest, now, ERASER_TRAIL_MAX_AGE_MS);
2364
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2365
+ alive.length >= 2 && d ? /* @__PURE__ */ jsx(
2366
+ Path,
2367
+ {
2368
+ path: d,
2369
+ color: colorWithOpacity(ERASER_TINT, ERASER_TINT_OPACITY),
2370
+ style: "stroke",
2371
+ strokeWidth: Math.max(3.5, OVERLAY_STROKE_PX) / z,
2372
+ strokeCap: "round",
2373
+ strokeJoin: "round",
2374
+ antiAlias: true
2375
+ }
2376
+ ) : null,
2377
+ /* @__PURE__ */ jsx(
2041
2378
  Circle,
2042
2379
  {
2043
- cx: eraserTrail[0].x,
2044
- cy: eraserTrail[0].y,
2380
+ cx: newest?.x ?? alive[0]?.x ?? 0,
2381
+ cy: newest?.y ?? alive[0]?.y ?? 0,
2045
2382
  r: Math.max(5 / z, 3),
2046
- color: "#cbd5e1",
2383
+ color: colorWithOpacity(ERASER_TINT, headOpacity),
2047
2384
  style: "fill",
2048
2385
  antiAlias: true
2049
2386
  }
2050
- ) : null;
2051
- return /* @__PURE__ */ jsx(
2052
- Path,
2053
- {
2054
- path: d,
2055
- color: "#cbd5e1",
2056
- style: "stroke",
2057
- strokeWidth: Math.max(3.5 / z, overlayStrokePx),
2058
- strokeCap: "round",
2059
- strokeJoin: "round",
2060
- antiAlias: true
2061
- }
2062
- );
2387
+ )
2388
+ ] });
2063
2389
  }, [eraserTrail, z]);
2064
2390
  const laserTrailElements = useMemo(() => {
2065
2391
  if (!laserTrail || laserTrail.length < 1) return null;
2066
- const d = pointsToSmoothPathD(laserTrail);
2067
- if (!d)
2068
- return laserTrail[0] ? /* @__PURE__ */ jsx(
2392
+ const now = Date.now();
2393
+ const alive = freshTimedTrailPoints(laserTrail, now, LASER_TRAIL_MAX_AGE_MS);
2394
+ if (alive.length === 0) return null;
2395
+ const d = pointsToSmoothPathD(alive);
2396
+ const newest = alive[alive.length - 1];
2397
+ const headOpacity = timedTrailHeadOpacity(newest, now, LASER_TRAIL_MAX_AGE_MS);
2398
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2399
+ alive.length >= 2 && d ? /* @__PURE__ */ jsx(
2400
+ Path,
2401
+ {
2402
+ path: d,
2403
+ color: colorWithOpacity(LASER_TINT, LASER_TINT_OPACITY),
2404
+ style: "stroke",
2405
+ strokeWidth: Math.max(4, OVERLAY_STROKE_PX) / z,
2406
+ strokeCap: "round",
2407
+ strokeJoin: "round",
2408
+ antiAlias: true
2409
+ }
2410
+ ) : null,
2411
+ /* @__PURE__ */ jsx(
2069
2412
  Circle,
2070
2413
  {
2071
- cx: laserTrail[0].x,
2072
- cy: laserTrail[0].y,
2414
+ cx: newest?.x ?? alive[0]?.x ?? 0,
2415
+ cy: newest?.y ?? alive[0]?.y ?? 0,
2073
2416
  r: Math.max(5 / z, 3),
2074
- color: "#f43f5e",
2417
+ color: colorWithOpacity(LASER_TINT, headOpacity),
2075
2418
  style: "fill",
2076
2419
  antiAlias: true
2077
2420
  }
2078
- ) : null;
2079
- return /* @__PURE__ */ jsx(
2080
- Path,
2081
- {
2082
- path: d,
2083
- color: "#f43f5e",
2084
- style: "stroke",
2085
- strokeWidth: Math.max(4 / z, overlayStrokePx),
2086
- strokeCap: "round",
2087
- strokeJoin: "round",
2088
- antiAlias: true
2089
- }
2090
- );
2421
+ )
2422
+ ] });
2091
2423
  }, [laserTrail, z]);
2092
2424
  if (width <= 0 || height <= 0) return null;
2093
- return /* @__PURE__ */ jsx(
2425
+ return /* @__PURE__ */ jsxs(
2094
2426
  Canvas,
2095
2427
  {
2096
2428
  style: {
@@ -2101,13 +2433,22 @@ function NativeInteractionOverlay({
2101
2433
  height
2102
2434
  },
2103
2435
  pointerEvents: "none",
2104
- children: /* @__PURE__ */ jsxs(Group, { transform: camTransform, children: [
2105
- previewElements,
2106
- laserTrailElements,
2107
- eraserTrailElements,
2108
- eraserPreviewElements,
2109
- selectionElements
2110
- ] })
2436
+ children: [
2437
+ /* @__PURE__ */ jsxs(Group, { transform: camTransform, children: [
2438
+ previewElements,
2439
+ laserTrailElements,
2440
+ eraserTrailElements,
2441
+ eraserPreviewElements,
2442
+ selectionElements
2443
+ ] }),
2444
+ toolCursor ? /* @__PURE__ */ jsx(
2445
+ NativeToolCursorRenderer,
2446
+ {
2447
+ cursor: toolCursor.cursor,
2448
+ point: toolCursor.point
2449
+ }
2450
+ ) : null
2451
+ ]
2111
2452
  }
2112
2453
  );
2113
2454
  }
@@ -2233,6 +2574,383 @@ function NativeSceneRenderer({
2233
2574
  if (width <= 0 || height <= 0) return null;
2234
2575
  return /* @__PURE__ */ jsx(Canvas, { style: { width, height }, children: /* @__PURE__ */ jsx(Group, { transform: cameraTransform, children: visible.map((item) => /* @__PURE__ */ jsx(MemoShape, { item }, item.id)) }) });
2235
2576
  }
2577
+
2578
+ // src/native/native-style-inspector-values.ts
2579
+ var NATIVE_STYLE_PALETTE = [
2580
+ { name: "black", hex: "#1d1d1d" },
2581
+ { name: "grey", hex: "#9fa8b2" },
2582
+ { name: "light-violet", hex: "#e085f4" },
2583
+ { name: "violet", hex: "#ae3ec9" },
2584
+ { name: "blue", hex: "#4263eb" },
2585
+ { name: "light-blue", hex: "#4dabf7" },
2586
+ { name: "yellow", hex: "#ffc078" },
2587
+ { name: "orange", hex: "#f76707" },
2588
+ { name: "green", hex: "#099268" },
2589
+ { name: "light-green", hex: "#40c057" },
2590
+ { name: "light-red", hex: "#ff8787" },
2591
+ { name: "red", hex: "#e03131" }
2592
+ ];
2593
+ function normalizeNativeStyleHex(stroke) {
2594
+ if (stroke && /^#[0-9A-Fa-f]{6}$/.test(stroke)) return stroke;
2595
+ return "#1d1d1d";
2596
+ }
2597
+ function nativeStyleColorWithOpacity(hex, opacity) {
2598
+ const alpha = opacity ?? 1;
2599
+ if (alpha >= 1) return hex;
2600
+ const r = Number.parseInt(hex.slice(1, 3), 16);
2601
+ const g = Number.parseInt(hex.slice(3, 5), 16);
2602
+ const b = Number.parseInt(hex.slice(5, 7), 16);
2603
+ return `rgba(${r},${g},${b},${alpha})`;
2604
+ }
2605
+ function hexesEqual(a, b) {
2606
+ return a.toLowerCase() === b.toLowerCase();
2607
+ }
2608
+ function DashPreview({
2609
+ color,
2610
+ width,
2611
+ dashed = false
2612
+ }) {
2613
+ const strokeHeight = Math.max(3, Math.min(6, width));
2614
+ return /* @__PURE__ */ jsxs(View, { style: styles.dashPreviewTrack, children: [
2615
+ /* @__PURE__ */ jsx(
2616
+ View,
2617
+ {
2618
+ style: [
2619
+ styles.dashPreviewStroke,
2620
+ {
2621
+ width: dashed ? 11 : 26,
2622
+ height: strokeHeight,
2623
+ borderRadius: strokeHeight / 2,
2624
+ backgroundColor: color
2625
+ }
2626
+ ]
2627
+ }
2628
+ ),
2629
+ dashed ? /* @__PURE__ */ jsx(
2630
+ View,
2631
+ {
2632
+ style: [
2633
+ styles.dashPreviewStroke,
2634
+ {
2635
+ width: 11,
2636
+ height: strokeHeight,
2637
+ borderRadius: strokeHeight / 2,
2638
+ backgroundColor: color
2639
+ }
2640
+ ]
2641
+ }
2642
+ ) : null
2643
+ ] });
2644
+ }
2645
+ function clampValue(value, min, max) {
2646
+ return Math.min(max, Math.max(min, value));
2647
+ }
2648
+ function RangeControl({
2649
+ value,
2650
+ min,
2651
+ max,
2652
+ onChange,
2653
+ valueLabel
2654
+ }) {
2655
+ const [trackWidth, setTrackWidth] = useState(1);
2656
+ const normalized = (clampValue(value, min, max) - min) / (max - min);
2657
+ const updateFromX = useCallback(
2658
+ (x) => {
2659
+ const next = min + clampValue(x / trackWidth, 0, 1) * (max - min);
2660
+ onChange(Math.round(next));
2661
+ },
2662
+ [max, min, onChange, trackWidth]
2663
+ );
2664
+ const panResponder = useMemo(
2665
+ () => PanResponder.create({
2666
+ onMoveShouldSetPanResponder: () => true,
2667
+ onStartShouldSetPanResponder: () => true,
2668
+ onPanResponderGrant: (event) => {
2669
+ updateFromX(event.nativeEvent.locationX);
2670
+ },
2671
+ onPanResponderMove: (event) => {
2672
+ updateFromX(event.nativeEvent.locationX);
2673
+ }
2674
+ }),
2675
+ [updateFromX]
2676
+ );
2677
+ return /* @__PURE__ */ jsxs(View, { style: styles.rangeRow, children: [
2678
+ /* @__PURE__ */ jsxs(
2679
+ View,
2680
+ {
2681
+ style: styles.rangeTrack,
2682
+ onLayout: (event) => setTrackWidth(event.nativeEvent.layout.width),
2683
+ ...panResponder.panHandlers,
2684
+ children: [
2685
+ /* @__PURE__ */ jsx(View, { style: styles.rangeBase }),
2686
+ /* @__PURE__ */ jsx(View, { style: [styles.rangeFill, { width: `${normalized * 100}%` }] }),
2687
+ /* @__PURE__ */ jsx(View, { style: [styles.rangeThumb, { left: `${normalized * 100}%` }] })
2688
+ ]
2689
+ }
2690
+ ),
2691
+ valueLabel ? /* @__PURE__ */ jsx(Text$1, { style: styles.rangeValue, children: valueLabel }) : null
2692
+ ] });
2693
+ }
2694
+ function InspectorSection({
2695
+ label,
2696
+ children
2697
+ }) {
2698
+ return /* @__PURE__ */ jsxs(View, { style: styles.section, children: [
2699
+ /* @__PURE__ */ jsx(Text$1, { style: styles.sectionLabel, children: label }),
2700
+ children
2701
+ ] });
2702
+ }
2703
+ function SegmentControl({
2704
+ segments,
2705
+ value,
2706
+ onChange
2707
+ }) {
2708
+ return /* @__PURE__ */ jsx(View, { style: styles.segmentGroup, children: segments.map((segment) => {
2709
+ const selected = segment.value === value;
2710
+ return /* @__PURE__ */ jsxs(
2711
+ Pressable,
2712
+ {
2713
+ accessibilityRole: "button",
2714
+ accessibilityState: { selected },
2715
+ accessibilityLabel: segment.label,
2716
+ onPress: () => onChange(segment.value),
2717
+ style: [styles.segment, selected ? styles.segmentSelected : null],
2718
+ children: [
2719
+ segment.preview,
2720
+ /* @__PURE__ */ jsx(
2721
+ Text$1,
2722
+ {
2723
+ style: [
2724
+ styles.segmentLabel,
2725
+ selected ? styles.segmentLabelSelected : null
2726
+ ],
2727
+ children: segment.label
2728
+ }
2729
+ )
2730
+ ]
2731
+ },
2732
+ String(segment.value)
2733
+ );
2734
+ }) });
2735
+ }
2736
+ function NativeVectorStyleInspector({
2737
+ toolId,
2738
+ value,
2739
+ onChange,
2740
+ style
2741
+ }) {
2742
+ const hex = normalizeNativeStyleHex(value.stroke);
2743
+ const opacity = toolId === "marker" ? value.strokeOpacity ?? 0.5 : void 0;
2744
+ const dashSegments = [
2745
+ {
2746
+ value: "solid",
2747
+ label: "Cont\xEDnuo",
2748
+ preview: /* @__PURE__ */ jsx(DashPreview, { color: "#18181b", width: 4 })
2749
+ },
2750
+ {
2751
+ value: "dashed",
2752
+ label: "Tracejado",
2753
+ preview: /* @__PURE__ */ jsx(DashPreview, { color: "#18181b", width: 4, dashed: true })
2754
+ }
2755
+ ];
2756
+ const opacityPercent = Math.round((opacity ?? 1) * 100);
2757
+ return /* @__PURE__ */ jsxs(
2758
+ View,
2759
+ {
2760
+ pointerEvents: "auto",
2761
+ style: [styles.shell, style],
2762
+ accessibilityRole: "summary",
2763
+ accessibilityLabel: toolId === "marker" ? "Configura\xE7\xF5es do marcador" : "Configura\xE7\xF5es da caneta",
2764
+ children: [
2765
+ /* @__PURE__ */ jsx(InspectorSection, { label: "Cor", children: /* @__PURE__ */ jsx(View, { style: styles.palette, children: NATIVE_STYLE_PALETTE.map((color) => {
2766
+ const selected = hexesEqual(color.hex, hex);
2767
+ return /* @__PURE__ */ jsx(
2768
+ Pressable,
2769
+ {
2770
+ accessibilityRole: "button",
2771
+ accessibilityState: { selected },
2772
+ accessibilityLabel: color.name,
2773
+ onPress: () => onChange({ stroke: color.hex }),
2774
+ style: [
2775
+ styles.swatch,
2776
+ {
2777
+ backgroundColor: color.hex,
2778
+ borderWidth: selected ? 2 : 1,
2779
+ transform: [{ scale: selected ? 1.08 : 1 }],
2780
+ shadowOpacity: selected ? 0.08 : 0
2781
+ }
2782
+ ]
2783
+ },
2784
+ color.name
2785
+ );
2786
+ }) }) }),
2787
+ /* @__PURE__ */ jsx(InspectorSection, { label: "Grossura", children: /* @__PURE__ */ jsx(
2788
+ RangeControl,
2789
+ {
2790
+ value: value.strokeWidth,
2791
+ min: 1,
2792
+ max: 48,
2793
+ onChange: (strokeWidth) => onChange({ strokeWidth })
2794
+ }
2795
+ ) }),
2796
+ toolId === "draw" ? /* @__PURE__ */ jsx(InspectorSection, { label: "Tra\xE7o", children: /* @__PURE__ */ jsx(
2797
+ SegmentControl,
2798
+ {
2799
+ segments: dashSegments,
2800
+ value: value.strokeDash === "dashed" ? "dashed" : "solid",
2801
+ onChange: (strokeDash) => onChange({ strokeDash })
2802
+ }
2803
+ ) }) : /* @__PURE__ */ jsx(InspectorSection, { label: "Opacidade", children: /* @__PURE__ */ jsx(
2804
+ RangeControl,
2805
+ {
2806
+ value: opacityPercent,
2807
+ min: 10,
2808
+ max: 100,
2809
+ valueLabel: `${opacityPercent}%`,
2810
+ onChange: (nextOpacity) => onChange({ strokeOpacity: nextOpacity / 100 })
2811
+ }
2812
+ ) })
2813
+ ]
2814
+ }
2815
+ );
2816
+ }
2817
+ var styles = StyleSheet.create({
2818
+ shell: {
2819
+ minWidth: 240,
2820
+ paddingHorizontal: 14,
2821
+ paddingVertical: 12,
2822
+ borderRadius: 10,
2823
+ borderWidth: 1,
2824
+ borderColor: "rgba(0,0,0,0.1)",
2825
+ backgroundColor: "rgba(255,255,255,0.97)",
2826
+ shadowColor: "#000000",
2827
+ shadowOpacity: 0.06,
2828
+ shadowRadius: 16,
2829
+ shadowOffset: { width: 0, height: 4 },
2830
+ gap: 10
2831
+ },
2832
+ section: {
2833
+ gap: 6
2834
+ },
2835
+ sectionLabel: {
2836
+ fontSize: 11,
2837
+ fontWeight: "600",
2838
+ color: "#52525b",
2839
+ textTransform: "uppercase",
2840
+ letterSpacing: 0.22
2841
+ },
2842
+ palette: {
2843
+ flexDirection: "row",
2844
+ flexWrap: "wrap",
2845
+ columnGap: 6,
2846
+ rowGap: 6,
2847
+ maxWidth: 174
2848
+ },
2849
+ swatch: {
2850
+ width: 24,
2851
+ height: 24,
2852
+ borderRadius: 12,
2853
+ borderColor: "rgba(0,0,0,0.12)",
2854
+ shadowColor: "#000000",
2855
+ shadowRadius: 2,
2856
+ shadowOffset: { width: 0, height: 1 }
2857
+ },
2858
+ rangeRow: {
2859
+ minHeight: 28,
2860
+ flexDirection: "row",
2861
+ alignItems: "center",
2862
+ gap: 8
2863
+ },
2864
+ rangeTrack: {
2865
+ position: "relative",
2866
+ flex: 1,
2867
+ height: 28,
2868
+ justifyContent: "center"
2869
+ },
2870
+ rangeBase: {
2871
+ position: "absolute",
2872
+ left: 0,
2873
+ right: 0,
2874
+ height: 3,
2875
+ borderRadius: 2,
2876
+ backgroundColor: "rgba(24,24,27,0.12)"
2877
+ },
2878
+ rangeFill: {
2879
+ position: "absolute",
2880
+ left: 0,
2881
+ height: 3,
2882
+ borderRadius: 2,
2883
+ backgroundColor: "#18181b"
2884
+ },
2885
+ rangeThumb: {
2886
+ position: "absolute",
2887
+ width: 18,
2888
+ height: 18,
2889
+ marginLeft: -9,
2890
+ borderRadius: 9,
2891
+ borderWidth: 2,
2892
+ borderColor: "#18181b",
2893
+ backgroundColor: "#ffffff",
2894
+ shadowColor: "#000000",
2895
+ shadowOpacity: 0.12,
2896
+ shadowRadius: 3,
2897
+ shadowOffset: { width: 0, height: 1 }
2898
+ },
2899
+ rangeValue: {
2900
+ minWidth: 34,
2901
+ textAlign: "right",
2902
+ fontSize: 11,
2903
+ fontWeight: "500",
2904
+ color: "#71717a"
2905
+ },
2906
+ segmentGroup: {
2907
+ flexDirection: "row",
2908
+ alignItems: "center",
2909
+ gap: 3,
2910
+ padding: 3,
2911
+ borderRadius: 8,
2912
+ backgroundColor: "rgba(24,24,27,0.06)"
2913
+ },
2914
+ segment: {
2915
+ flex: 1,
2916
+ minWidth: 96,
2917
+ minHeight: 32,
2918
+ flexDirection: "row",
2919
+ alignItems: "center",
2920
+ justifyContent: "center",
2921
+ gap: 6,
2922
+ paddingHorizontal: 8,
2923
+ paddingVertical: 5,
2924
+ borderRadius: 6
2925
+ },
2926
+ segmentSelected: {
2927
+ backgroundColor: "#ffffff",
2928
+ shadowColor: "#000000",
2929
+ shadowOpacity: 0.1,
2930
+ shadowRadius: 2,
2931
+ shadowOffset: { width: 0, height: 1 }
2932
+ },
2933
+ segmentLabel: {
2934
+ fontSize: 11,
2935
+ fontWeight: "600",
2936
+ color: "#71717a",
2937
+ flexShrink: 0
2938
+ },
2939
+ segmentLabelSelected: {
2940
+ color: "#18181b"
2941
+ },
2942
+ dashPreviewTrack: {
2943
+ width: 26,
2944
+ height: 6,
2945
+ flexDirection: "row",
2946
+ alignItems: "center",
2947
+ justifyContent: "center",
2948
+ gap: 3
2949
+ },
2950
+ dashPreviewStroke: {
2951
+ flexShrink: 0
2952
+ }
2953
+ });
2236
2954
  var DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = [
2237
2955
  "rect",
2238
2956
  "ellipse",
@@ -2364,7 +3082,7 @@ function NativeVectorToolbar({
2364
3082
  View,
2365
3083
  {
2366
3084
  accessibilityLabel,
2367
- style: [styles.shell, style],
3085
+ style: [styles2.shell, style],
2368
3086
  pointerEvents: "box-none",
2369
3087
  children: [
2370
3088
  /* @__PURE__ */ jsxs(
@@ -2373,8 +3091,8 @@ function NativeVectorToolbar({
2373
3091
  horizontal: true,
2374
3092
  showsHorizontalScrollIndicator: false,
2375
3093
  contentContainerStyle: [
2376
- styles.content,
2377
- density === "comfortable" ? styles.comfortableContent : void 0,
3094
+ styles2.content,
3095
+ density === "comfortable" ? styles2.comfortableContent : void 0,
2378
3096
  contentContainerStyle
2379
3097
  ],
2380
3098
  children: [
@@ -2391,21 +3109,21 @@ function NativeVectorToolbar({
2391
3109
  disabled: toolLockDisabled,
2392
3110
  onPress: toggleToolLock,
2393
3111
  style: ({ pressed }) => [
2394
- styles.toolButton,
2395
- density === "comfortable" ? styles.comfortableToolButton : void 0,
2396
- toolLocked ? styles.activeToolButton : void 0,
2397
- pressed && !toolLockDisabled ? styles.pressedToolButton : void 0,
2398
- toolLockDisabled ? styles.disabledToolButton : void 0
3112
+ styles2.toolButton,
3113
+ density === "comfortable" ? styles2.comfortableToolButton : void 0,
3114
+ toolLocked ? styles2.activeToolButton : void 0,
3115
+ pressed && !toolLockDisabled ? styles2.pressedToolButton : void 0,
3116
+ toolLockDisabled ? styles2.disabledToolButton : void 0
2399
3117
  ],
2400
3118
  children: renderToolLockIcon?.({
2401
3119
  locked: toolLocked,
2402
3120
  disabled: toolLockDisabled,
2403
3121
  foregroundColor: "#18181b",
2404
3122
  onToggle: toggleToolLock
2405
- }) ?? /* @__PURE__ */ jsx(Text$1, { style: styles.lockGlyph, children: toolLocked ? "L" : "U" })
3123
+ }) ?? /* @__PURE__ */ jsx(Text$1, { style: styles2.lockGlyph, children: toolLocked ? "L" : "U" })
2406
3124
  }
2407
3125
  ),
2408
- /* @__PURE__ */ jsx(View, { style: styles.toolLockDivider })
3126
+ /* @__PURE__ */ jsx(View, { style: styles2.toolLockDivider })
2409
3127
  ] }) : null,
2410
3128
  toolbarTools.map(
2411
3129
  (tool) => renderNativeToolButton({
@@ -2423,7 +3141,7 @@ function NativeVectorToolbar({
2423
3141
  renderToolButton
2424
3142
  })
2425
3143
  ),
2426
- showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: styles.overflowSpacer }) : null,
3144
+ showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: styles2.overflowSpacer }) : null,
2427
3145
  showOverflowMenu ? /* @__PURE__ */ jsxs(
2428
3146
  Pressable,
2429
3147
  {
@@ -2437,27 +3155,27 @@ function NativeVectorToolbar({
2437
3155
  disabled,
2438
3156
  onPress: toggleOverflow,
2439
3157
  style: ({ pressed }) => [
2440
- styles.overflowTrigger,
2441
- overflowOpen || activeOverflowTool ? styles.activeToolButton : void 0,
2442
- pressed && !disabled ? styles.pressedToolButton : void 0,
2443
- disabled ? styles.disabledToolButton : void 0
3158
+ styles2.overflowTrigger,
3159
+ overflowOpen || activeOverflowTool ? styles2.activeToolButton : void 0,
3160
+ pressed && !disabled ? styles2.pressedToolButton : void 0,
3161
+ disabled ? styles2.disabledToolButton : void 0
2444
3162
  ],
2445
3163
  children: [
2446
- /* @__PURE__ */ jsx(View, { style: styles.iconSlot, children: activeOverflowTool ? renderToolIcon?.({
3164
+ /* @__PURE__ */ jsx(View, { style: styles2.iconSlot, children: activeOverflowTool ? renderToolIcon?.({
2447
3165
  tool: activeOverflowTool,
2448
3166
  selected: true,
2449
3167
  disabled,
2450
3168
  foregroundColor: "#18181b",
2451
3169
  onSelect: () => onChange(activeOverflowTool.id)
2452
- }) ?? renderNativeToolFallback(activeOverflowTool, "#18181b") : renderOverflowIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text$1, { style: styles.shapesGlyph, children: "S" }) }),
2453
- renderOverflowChevronIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text$1, { style: styles.chevronGlyph, children: overflowOpen ? "^" : "v" })
3170
+ }) ?? renderNativeToolFallback(activeOverflowTool, "#18181b") : renderOverflowIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text$1, { style: styles2.shapesGlyph, children: "S" }) }),
3171
+ renderOverflowChevronIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsx(Text$1, { style: styles2.chevronGlyph, children: overflowOpen ? "^" : "v" })
2454
3172
  ]
2455
3173
  }
2456
3174
  ) : null
2457
3175
  ]
2458
3176
  }
2459
3177
  ),
2460
- overflowOpen && showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: [styles.overflowPanel, overflowPanelStyle], children: remainingOverflowTools.map(
3178
+ overflowOpen && showOverflowMenu ? /* @__PURE__ */ jsx(View, { style: [styles2.overflowPanel, overflowPanelStyle], children: remainingOverflowTools.map(
2461
3179
  (tool) => renderNativeToolButton({
2462
3180
  tool,
2463
3181
  value,
@@ -2468,7 +3186,7 @@ function NativeVectorToolbar({
2468
3186
  disabled,
2469
3187
  disabledIds,
2470
3188
  density: "compact",
2471
- toolButtonStyle: styles.overflowToolButton,
3189
+ toolButtonStyle: styles2.overflowToolButton,
2472
3190
  activeToolButtonStyle,
2473
3191
  toolLabelStyle,
2474
3192
  activeToolLabelStyle,
@@ -2509,22 +3227,22 @@ function renderNativeToolButton(input) {
2509
3227
  disabled: toolDisabled,
2510
3228
  onPress: onSelect,
2511
3229
  style: ({ pressed }) => [
2512
- styles.toolButton,
2513
- input.density === "comfortable" ? styles.comfortableToolButton : void 0,
3230
+ styles2.toolButton,
3231
+ input.density === "comfortable" ? styles2.comfortableToolButton : void 0,
2514
3232
  input.toolButtonStyle,
2515
- selected ? styles.activeToolButton : void 0,
3233
+ selected ? styles2.activeToolButton : void 0,
2516
3234
  selected ? input.activeToolButtonStyle : void 0,
2517
- pressed && !toolDisabled ? styles.pressedToolButton : void 0,
2518
- toolDisabled ? styles.disabledToolButton : void 0
3235
+ pressed && !toolDisabled ? styles2.pressedToolButton : void 0,
3236
+ toolDisabled ? styles2.disabledToolButton : void 0
2519
3237
  ],
2520
3238
  children: [
2521
- /* @__PURE__ */ jsx(View, { style: styles.iconSlot, children: icon }),
3239
+ /* @__PURE__ */ jsx(View, { style: styles2.iconSlot, children: icon }),
2522
3240
  input.density === "comfortable" ? /* @__PURE__ */ jsx(
2523
3241
  Text$1,
2524
3242
  {
2525
3243
  numberOfLines: 1,
2526
3244
  style: [
2527
- styles.toolLabel,
3245
+ styles2.toolLabel,
2528
3246
  { color: foregroundColor },
2529
3247
  input.toolLabelStyle,
2530
3248
  selected ? input.activeToolLabelStyle : void 0
@@ -2538,9 +3256,9 @@ function renderNativeToolButton(input) {
2538
3256
  );
2539
3257
  }
2540
3258
  function renderNativeToolFallback(tool, foregroundColor, toolLabelStyle) {
2541
- return /* @__PURE__ */ jsx(Text$1, { style: [styles.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
3259
+ return /* @__PURE__ */ jsx(Text$1, { style: [styles2.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
2542
3260
  }
2543
- var styles = StyleSheet.create({
3261
+ var styles2 = StyleSheet.create({
2544
3262
  shell: {
2545
3263
  borderRadius: 8,
2546
3264
  borderWidth: StyleSheet.hairlineWidth,
@@ -2862,12 +3580,56 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
2862
3580
  }
2863
3581
  return ids;
2864
3582
  }
3583
+
3584
+ // src/native/native-tool-cursors.ts
3585
+ var ICON_SIZE = 24;
3586
+ var CENTER_HOTSPOT = { x: 12, y: 12 };
3587
+ function nativeCursorForVectorToolId(toolId) {
3588
+ switch (toolId) {
3589
+ case "rect":
3590
+ case "ellipse":
3591
+ case "architectural-cloud":
3592
+ case "line":
3593
+ case "arrow":
3594
+ case "laser":
3595
+ case "image":
3596
+ return { kind: "crosshair", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3597
+ case "draw":
3598
+ return { kind: "draw", size: ICON_SIZE, hotspot: { x: 4, y: 18 } };
3599
+ case "marker":
3600
+ return { kind: "marker", size: ICON_SIZE, hotspot: { x: 11, y: 14 } };
3601
+ case "eraser":
3602
+ return { kind: "eraser", size: ICON_SIZE, hotspot: { x: 12, y: 14 } };
3603
+ case "text":
3604
+ return { kind: "text", size: ICON_SIZE, hotspot: CENTER_HOTSPOT };
3605
+ default:
3606
+ return null;
3607
+ }
3608
+ }
2865
3609
  var MIN_PLACE_SIZE = 8;
2866
3610
  var MIN_ARROW_DRAG_PX = 8;
2867
3611
  var TAP_PX = 20;
3612
+ var MARKER_TOOL_STYLE = {
3613
+ stroke: "#fde047",
3614
+ strokeWidth: 16,
3615
+ strokeOpacity: 0.5
3616
+ };
2868
3617
  function isPlacementTool(toolId) {
2869
3618
  return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
2870
3619
  }
3620
+ function isDefaultMarkerToolStyle(style) {
3621
+ return style.stroke === MARKER_TOOL_STYLE.stroke && style.strokeWidth === MARKER_TOOL_STYLE.strokeWidth && style.strokeOpacity === MARKER_TOOL_STYLE.strokeOpacity;
3622
+ }
3623
+ function supportsNativeResizeHandles(item) {
3624
+ const k = item?.toolKind;
3625
+ if (k === "rect" || k === "ellipse" || k === "architectural-cloud" || k === "line" || k === "arrow" || k === "image" || k === "text") {
3626
+ return true;
3627
+ }
3628
+ if ((k === "draw" || k === "pencil" || k === "brush" || k === "marker") && item?.pathPointsLocal && item.pathPointsLocal.length > 0) {
3629
+ return true;
3630
+ }
3631
+ return k === "custom" && !!item?.customIntrinsicSize && !!item?.customInnerSvg;
3632
+ }
2871
3633
  function placementPreviewForTool(toolId, start, end) {
2872
3634
  if (toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud") {
2873
3635
  return { kind: toolId, rect: rectFromCorners(start, end) };
@@ -2904,6 +3666,12 @@ function collectIdsInRect(items, marquee) {
2904
3666
  }
2905
3667
  return out;
2906
3668
  }
3669
+ function screenPointFromPointerEvent(event) {
3670
+ const nativeEvent = event.nativeEvent;
3671
+ const x = Number.isFinite(nativeEvent.offsetX) ? nativeEvent.offsetX : nativeEvent.x;
3672
+ const y = Number.isFinite(nativeEvent.offsetY) ? nativeEvent.offsetY : nativeEvent.y;
3673
+ return { x, y };
3674
+ }
2907
3675
  function fitCameraToWorldRect(camera, viewportW, viewportH, worldRect, padding) {
2908
3676
  const r = normalizeRect(worldRect);
2909
3677
  if (r.width <= 0 || r.height <= 0 || viewportW <= 0 || viewportH <= 0) return;
@@ -2930,7 +3698,9 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
2930
3698
  onItemsChange,
2931
3699
  onToolChangeRequest,
2932
3700
  onCameraChange,
2933
- toolbar
3701
+ toolbar,
3702
+ showStyleInspector = false,
3703
+ styleInspectorPlacement = "bottom"
2934
3704
  }, ref) {
2935
3705
  const [size, setSize] = useState({ width: 0, height: 0 });
2936
3706
  const cameraRef = useRef(null);
@@ -2956,7 +3726,15 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
2956
3726
  );
2957
3727
  const [eraserTrail, setEraserTrail] = useState([]);
2958
3728
  const [laserTrail, setLaserTrail] = useState([]);
3729
+ const [toolCursorPoint, setToolCursorPoint] = useState(null);
2959
3730
  const laserClearTimerRef = useRef(null);
3731
+ const strokeStyleRef = useRef({ ...DEFAULT_STROKE_STYLE });
3732
+ const markerStrokeStyleRef = useRef({ ...MARKER_TOOL_STYLE });
3733
+ const styleBeforeMarkerRef = useRef({ ...DEFAULT_STROKE_STYLE });
3734
+ const lastToolIdRef = useRef(toolId);
3735
+ const [strokeStyleState, setStrokeStyleState] = useState({
3736
+ ...DEFAULT_STROKE_STYLE
3737
+ });
2960
3738
  useEffect(
2961
3739
  () => () => {
2962
3740
  if (laserClearTimerRef.current) {
@@ -2965,6 +3743,60 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
2965
3743
  },
2966
3744
  []
2967
3745
  );
3746
+ useEffect(() => {
3747
+ if (eraserTrail.length === 0 && laserTrail.length === 0) return;
3748
+ const timer = setInterval(() => {
3749
+ const now = Date.now();
3750
+ setEraserTrail(
3751
+ (prev) => freshTimedTrailPoints(prev, now, ERASER_TRAIL_MAX_AGE_MS)
3752
+ );
3753
+ setLaserTrail(
3754
+ (prev) => freshTimedTrailPoints(prev, now, LASER_TRAIL_MAX_AGE_MS)
3755
+ );
3756
+ }, 50);
3757
+ return () => clearInterval(timer);
3758
+ }, [eraserTrail.length, laserTrail.length]);
3759
+ useEffect(() => {
3760
+ const previousToolId = lastToolIdRef.current;
3761
+ const current = strokeStyleRef.current;
3762
+ let next = current;
3763
+ if (toolId === "marker") {
3764
+ if (previousToolId !== "marker") {
3765
+ styleBeforeMarkerRef.current = current;
3766
+ }
3767
+ next = markerStrokeStyleRef.current;
3768
+ } else if (previousToolId === "marker") {
3769
+ markerStrokeStyleRef.current = current;
3770
+ const restored = styleBeforeMarkerRef.current;
3771
+ next = {
3772
+ stroke: restored.stroke,
3773
+ strokeWidth: toolId === "draw" && restored.strokeWidth === DEFAULT_STROKE_STYLE.strokeWidth ? 10 : restored.strokeWidth,
3774
+ ...restored.strokeDash != null ? { strokeDash: restored.strokeDash } : {}
3775
+ };
3776
+ } else if (toolId === "draw") {
3777
+ next = {
3778
+ ...current,
3779
+ strokeWidth: current.strokeWidth === DEFAULT_STROKE_STYLE.strokeWidth ? 10 : current.strokeWidth
3780
+ };
3781
+ } else if (isDefaultMarkerToolStyle(current)) {
3782
+ next = {
3783
+ stroke: DEFAULT_STROKE_STYLE.stroke,
3784
+ strokeWidth: DEFAULT_STROKE_STYLE.strokeWidth,
3785
+ ...current.strokeDash != null ? { strokeDash: current.strokeDash } : {}
3786
+ };
3787
+ }
3788
+ strokeStyleRef.current = next;
3789
+ setStrokeStyleState(next);
3790
+ lastToolIdRef.current = toolId;
3791
+ }, [toolId]);
3792
+ const patchCurrentStrokeStyle = useCallback((patch) => {
3793
+ const next = { ...strokeStyleRef.current, ...patch };
3794
+ strokeStyleRef.current = next;
3795
+ if (toolIdRef.current === "marker") {
3796
+ markerStrokeStyleRef.current = next;
3797
+ }
3798
+ setStrokeStyleState(next);
3799
+ }, []);
2968
3800
  const [eraserPreviewIds, setEraserPreviewIds] = useState([]);
2969
3801
  const eraserPreviewIdSetRef = useRef(/* @__PURE__ */ new Set());
2970
3802
  const requestSelectToolAfterUse = useCallback(() => {
@@ -2993,6 +3825,23 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
2993
3825
  const { width, height } = e.nativeEvent.layout;
2994
3826
  setSize({ width, height });
2995
3827
  }, []);
3828
+ const updateToolCursorPoint = useCallback(
3829
+ (point) => {
3830
+ if (!interactive) return;
3831
+ if (!nativeCursorForVectorToolId(toolIdRef.current)) return;
3832
+ setToolCursorPoint(point);
3833
+ },
3834
+ [interactive]
3835
+ );
3836
+ const hideToolCursor = useCallback(() => {
3837
+ setToolCursorPoint(null);
3838
+ }, []);
3839
+ const handlePointerMove = useCallback(
3840
+ (event) => {
3841
+ updateToolCursorPoint(screenPointFromPointerEvent(event));
3842
+ },
3843
+ [updateToolCursorPoint]
3844
+ );
2996
3845
  const selectedItems = useMemo(
2997
3846
  () => items.filter((it) => selectedIds.includes(it.id)),
2998
3847
  [items, selectedIds]
@@ -3002,7 +3851,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3002
3851
  const hidden = new Set(eraserPreviewIds);
3003
3852
  return items.filter((it) => !hidden.has(it.id));
3004
3853
  }, [items, eraserPreviewIds]);
3005
- const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked;
3854
+ const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked && supportsNativeResizeHandles(selectedItems[0]);
3006
3855
  const lastPinchDist = useRef(null);
3007
3856
  const lastPanPoint = useRef(null);
3008
3857
  const panResponder = useMemo(
@@ -3016,9 +3865,11 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3016
3865
  const sx = evt.nativeEvent.locationX;
3017
3866
  const sy = evt.nativeEvent.locationY;
3018
3867
  if (touches && touches.length >= 2) {
3868
+ hideToolCursor();
3019
3869
  dragStateRef.current = { kind: "pan" };
3020
3870
  return;
3021
3871
  }
3872
+ updateToolCursorPoint({ x: sx, y: sy });
3022
3873
  if (!interactive) {
3023
3874
  dragStateRef.current = { kind: "pan" };
3024
3875
  return;
@@ -3077,7 +3928,14 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3077
3928
  clearTimeout(laserClearTimerRef.current);
3078
3929
  laserClearTimerRef.current = null;
3079
3930
  }
3080
- setLaserTrail([{ x: worldX, y: worldY }]);
3931
+ setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
3932
+ } else {
3933
+ setPlacementPreview({
3934
+ kind: "stroke",
3935
+ tool,
3936
+ points: [{ x: worldX, y: worldY }],
3937
+ style: { ...strokeStyleRef.current }
3938
+ });
3081
3939
  }
3082
3940
  return;
3083
3941
  }
@@ -3085,7 +3943,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3085
3943
  dragStateRef.current = { kind: "erase" };
3086
3944
  eraserPreviewIdSetRef.current = /* @__PURE__ */ new Set();
3087
3945
  setEraserPreviewIds([]);
3088
- setEraserTrail([{ x: worldX, y: worldY }]);
3946
+ setEraserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
3089
3947
  const toErase = collectEraserTargetsAtWorldPoint(
3090
3948
  itemsRef.current,
3091
3949
  worldX,
@@ -3137,6 +3995,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3137
3995
  const pageX = evt.nativeEvent.pageX;
3138
3996
  const pageY = evt.nativeEvent.pageY;
3139
3997
  if (touches && touches.length >= 2) {
3998
+ hideToolCursor();
3140
3999
  const t0 = touches[0];
3141
4000
  const t1 = touches[1];
3142
4001
  if (t0 && t1) {
@@ -3156,6 +4015,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3156
4015
  return;
3157
4016
  }
3158
4017
  lastPinchDist.current = null;
4018
+ updateToolCursorPoint({ x: sx, y: sy });
3159
4019
  const { worldX, worldY } = screenToWorld(sx, sy);
3160
4020
  const st = dragStateRef.current;
3161
4021
  if (st.kind === "pan") {
@@ -3176,18 +4036,25 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3176
4036
  const last = pts[pts.length - 1];
3177
4037
  const dx = worldX - (last?.x ?? worldX);
3178
4038
  const dy = worldY - (last?.y ?? worldY);
3179
- if (Math.hypot(dx, dy) > 0.5 / cam.zoom) {
4039
+ const shouldAppendPoint = Math.hypot(dx, dy) > 0.5 / cam.zoom;
4040
+ if (shouldAppendPoint) {
3180
4041
  pts.push({ x: worldX, y: worldY });
3181
4042
  }
3182
4043
  if (st.tool === "laser") {
3183
- setLaserTrail([...pts]);
3184
- } else {
3185
- setPlacementPreview({
3186
- kind: "stroke",
3187
- tool: st.tool,
3188
- points: [...pts]
3189
- });
4044
+ if (shouldAppendPoint) {
4045
+ setLaserTrail((prev) => [
4046
+ ...prev,
4047
+ { x: worldX, y: worldY, t: Date.now() }
4048
+ ]);
4049
+ }
4050
+ return;
3190
4051
  }
4052
+ setPlacementPreview({
4053
+ kind: "stroke",
4054
+ tool: st.tool,
4055
+ points: [...pts],
4056
+ style: { ...strokeStyleRef.current }
4057
+ });
3191
4058
  return;
3192
4059
  }
3193
4060
  if (st.kind === "move") {
@@ -3225,7 +4092,10 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3225
4092
  return;
3226
4093
  }
3227
4094
  if (st.kind === "erase") {
3228
- setEraserTrail((prev) => [...prev, { x: worldX, y: worldY }]);
4095
+ setEraserTrail((prev) => [
4096
+ ...prev,
4097
+ { x: worldX, y: worldY, t: Date.now() }
4098
+ ]);
3229
4099
  const toErase = collectEraserTargetsAtWorldPoint(
3230
4100
  itemsRef.current,
3231
4101
  worldX,
@@ -3251,6 +4121,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3251
4121
  onPanResponderRelease: (evt) => {
3252
4122
  lastPinchDist.current = null;
3253
4123
  lastPanPoint.current = null;
4124
+ hideToolCursor();
3254
4125
  const st = dragStateRef.current;
3255
4126
  if (st.kind === "draw") {
3256
4127
  dragStateRef.current = { kind: "idle" };
@@ -3270,7 +4141,12 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3270
4141
  const change = onItemsChangeRef.current;
3271
4142
  if (!change) return;
3272
4143
  const id = createShapeId();
3273
- const item = createFreehandStrokeItem(id, st.points, st.tool);
4144
+ const item = createFreehandStrokeItem(
4145
+ id,
4146
+ st.points,
4147
+ st.tool,
4148
+ strokeStyleRef.current
4149
+ );
3274
4150
  if (item) {
3275
4151
  change([...itemsRef.current, item]);
3276
4152
  }
@@ -3347,26 +4223,33 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3347
4223
  }
3348
4224
  }
3349
4225
  const id = createShapeId();
4226
+ const style = strokeStyleRef.current;
3350
4227
  if (st.tool === "rect") {
3351
- change([...itemsRef.current, createRectangleItem(id, raw)]);
4228
+ change([...itemsRef.current, createRectangleItem(id, raw, style)]);
3352
4229
  onSelectionChangeRef.current?.([id]);
3353
4230
  requestSelectToolAfterUse();
3354
4231
  return;
3355
4232
  }
3356
4233
  if (st.tool === "ellipse") {
3357
- change([...itemsRef.current, createEllipseItem(id, raw)]);
4234
+ change([...itemsRef.current, createEllipseItem(id, raw, style)]);
3358
4235
  onSelectionChangeRef.current?.([id]);
3359
4236
  requestSelectToolAfterUse();
3360
4237
  return;
3361
4238
  }
3362
4239
  if (st.tool === "architectural-cloud") {
3363
- change([...itemsRef.current, createArchitecturalCloudItem(id, raw)]);
4240
+ change([
4241
+ ...itemsRef.current,
4242
+ createArchitecturalCloudItem(id, raw, style)
4243
+ ]);
3364
4244
  onSelectionChangeRef.current?.([id]);
3365
4245
  requestSelectToolAfterUse();
3366
4246
  return;
3367
4247
  }
3368
4248
  const line = lineEndpointsToLocal(br, lineStart, lineEnd);
3369
- change([...itemsRef.current, createLineItem(id, br, line, st.tool)]);
4249
+ change([
4250
+ ...itemsRef.current,
4251
+ createLineItem(id, br, line, st.tool, style)
4252
+ ]);
3370
4253
  onSelectionChangeRef.current?.([id]);
3371
4254
  requestSelectToolAfterUse();
3372
4255
  return;
@@ -3389,7 +4272,7 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3389
4272
  height: 26
3390
4273
  },
3391
4274
  "Text",
3392
- void 0,
4275
+ strokeStyleRef.current,
3393
4276
  18
3394
4277
  );
3395
4278
  change([...itemsRef.current, item]);
@@ -3422,12 +4305,23 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3422
4305
  onPanResponderTerminate: () => {
3423
4306
  lastPinchDist.current = null;
3424
4307
  lastPanPoint.current = null;
4308
+ hideToolCursor();
3425
4309
  dragStateRef.current = { kind: "idle" };
3426
4310
  setPlacementPreview(null);
3427
4311
  setLaserTrail([]);
4312
+ setEraserTrail([]);
4313
+ setEraserPreviewIds([]);
4314
+ eraserPreviewIdSetRef.current.clear();
3428
4315
  }
3429
4316
  }),
3430
- [screenToWorld, requestRender, requestSelectToolAfterUse, interactive]
4317
+ [
4318
+ screenToWorld,
4319
+ requestRender,
4320
+ requestSelectToolAfterUse,
4321
+ interactive,
4322
+ updateToolCursorPoint,
4323
+ hideToolCursor
4324
+ ]
3431
4325
  );
3432
4326
  useImperativeHandle(
3433
4327
  ref,
@@ -3450,11 +4344,17 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3450
4344
  }),
3451
4345
  [requestRender, size]
3452
4346
  );
4347
+ const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
4348
+ const activeToolCursor = nativeCursorForVectorToolId(toolId);
4349
+ const toolCursor = activeToolCursor && toolCursorPoint ? { cursor: activeToolCursor, point: toolCursorPoint } : null;
3453
4350
  return /* @__PURE__ */ jsx(
3454
4351
  View,
3455
4352
  {
3456
4353
  style: { flex: 1, overflow: "hidden" },
3457
4354
  onLayout,
4355
+ onPointerMove: handlePointerMove,
4356
+ onPointerEnter: handlePointerMove,
4357
+ onPointerLeave: hideToolCursor,
3458
4358
  ...panResponder.panHandlers,
3459
4359
  children: size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
3460
4360
  /* @__PURE__ */ jsx(
@@ -3479,9 +4379,37 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3479
4379
  eraserTrail,
3480
4380
  eraserPreviewItems: items.filter(
3481
4381
  (it) => eraserPreviewIds.includes(it.id)
3482
- )
4382
+ ),
4383
+ previewStrokeStyle: strokeStyleState,
4384
+ toolCursor
3483
4385
  }
3484
4386
  ),
4387
+ interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsx(
4388
+ View,
4389
+ {
4390
+ pointerEvents: "box-none",
4391
+ style: styleInspectorPlacement === "top-left" ? {
4392
+ position: "absolute",
4393
+ left: 16,
4394
+ top: 104,
4395
+ alignItems: "flex-start"
4396
+ } : {
4397
+ position: "absolute",
4398
+ left: 16,
4399
+ right: 16,
4400
+ bottom: 84,
4401
+ alignItems: "center"
4402
+ },
4403
+ children: /* @__PURE__ */ jsx(
4404
+ NativeVectorStyleInspector,
4405
+ {
4406
+ toolId: activeStyleToolId,
4407
+ value: strokeStyleState,
4408
+ onChange: patchCurrentStrokeStyle
4409
+ }
4410
+ )
4411
+ }
4412
+ ) : null,
3485
4413
  toolbar && /* @__PURE__ */ jsx(
3486
4414
  View,
3487
4415
  {
@@ -3503,6 +4431,6 @@ var NativeVectorViewport = forwardRef(function NativeVectorViewport2({
3503
4431
  );
3504
4432
  });
3505
4433
 
3506
- export { DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NativeInteractionOverlay, NativeSceneRenderer, NativeShapeRenderer, NativeVectorToolbar, NativeVectorViewport, createFreehandStrokeItem, createImageItem, createShapeId, parseSvgFragment };
4434
+ export { DEFAULT_NATIVE_OVERFLOW_TOOL_IDS, DEFAULT_NATIVE_VECTOR_TOOLS, NATIVE_STYLE_PALETTE, NativeInteractionOverlay, NativeSceneRenderer, NativeShapeRenderer, NativeVectorStyleInspector, NativeVectorToolbar, NativeVectorViewport, createFreehandStrokeItem, createImageItem, createShapeId, nativeStyleColorWithOpacity, normalizeNativeStyleHex, parseSvgFragment };
3507
4435
  //# sourceMappingURL=native.js.map
3508
4436
  //# sourceMappingURL=native.js.map