@agimon-ai/po-native-components 0.0.0-alpha.67 → 0.0.0-alpha.69

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.
@@ -15,6 +15,8 @@ export interface AnnotationMark {
15
15
  x: number;
16
16
  y: number;
17
17
  }[];
18
+ /** A translucent, thick rose stroke over the content. */
19
+ highlight?: boolean;
18
20
  }
19
21
  export interface AnnotationSurfaceProps {
20
22
  /** The content being marked up: an image, a page, a block of text. */
@@ -36,6 +38,16 @@ export interface AnnotationSurfaceProps {
36
38
  accessibilityLabel: string;
37
39
  /** Image surfaces can pinch and move with two fingers on both native platforms. */
38
40
  zoomable?: boolean;
41
+ /** Explicit one-finger pan instead of placing or drawing marks. */
42
+ navigation?: boolean;
43
+ /** Controlled transform. Origins are unscaled RN layout positions, not raster pixels. */
44
+ viewport?: {
45
+ zoom: number;
46
+ originX: number;
47
+ originY: number;
48
+ };
49
+ /** Render the in-progress stroke as a highlight. */
50
+ drawingHighlight?: boolean;
39
51
  /** Viewport dimensions and source-layout-to-viewport transform, all in RN layout units, not raster pixels. */
40
52
  onViewportChange?: (viewport: {
41
53
  width: number;
@@ -52,6 +64,6 @@ export interface AnnotationSurfaceProps {
52
64
  * content, so text and controls inside stay readable, and activating it places a
53
65
  * note at its centre.
54
66
  */
55
- export declare function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable, onViewportChange, style }: AnnotationSurfaceProps): import("react").JSX.Element;
67
+ export declare function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable, onViewportChange, navigation, viewport: controlledViewport, drawingHighlight, style }: AnnotationSurfaceProps): import("react").JSX.Element;
56
68
  //#endregion
57
69
  //# sourceMappingURL=AnnotationSurface.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnnotationSurface.d.mts","names":[],"sources":["../../src/components/AnnotationSurface.tsx"],"mappings":";;;;iBAQiB;EACf;EACA;EACA;;EAEA;;EAEA;;EAEA;IAAoB;IAAW;;;iBAGhB;;EAEf,UAAU;EACV,gBAAgB;EAChB;;EAEA,WAAW;IAAS;IAAW;;;EAE/B,UAAU;IAAU;IAAW;;EAC/B,gBAAgB;;EAEhB;;EAEA;;EAEA,oBAAoB;IAClB;IACA;IACA;IACA;IACA;;EAEF,QAAQ,UAAU;;;;;;;;wBAyCJ,oBACd,UACA,OACA,YACA,SACA,QACA,cACA,oBACA,UACA,kBACA,SACC,yCAAsB,IAAA"}
1
+ {"version":3,"file":"AnnotationSurface.d.mts","names":[],"sources":["../../src/components/AnnotationSurface.tsx"],"mappings":";;;;iBAQiB;EACf;EACA;EACA;;EAEA;;EAEA;;EAEA;IAAoB;IAAW;;;EAE/B;;iBAGe;;EAEf,UAAU;EACV,gBAAgB;EAChB;;EAEA,WAAW;IAAS;IAAW;;;EAE/B,UAAU;IAAU;IAAW;;EAC/B,gBAAgB;;EAEhB;;EAEA;;EAEA;;EAEA;IAAa;IAAc;IAAiB;;;EAE5C;;EAEA,oBAAoB;IAClB;IACA;IACA;IACA;IACA;;EAEF,QAAQ,UAAU;;;;;;;;wBAqDJ,oBACd,UACA,OACA,YACA,SACA,QACA,cACA,oBACA,UACA,kBACA,YACA,UAAU,oBACV,kBACA,SACC,yCAAsB,IAAA"}
@@ -3,14 +3,15 @@ import { selectionTick } from "../lib/haptics.mjs";
3
3
  import { View } from "react-native-unistyles/components/native/View";
4
4
  import { Text } from "react-native-unistyles/components/native/Text";
5
5
  import { StyleSheet } from "react-native-unistyles";
6
- import { useEffect, useRef, useState } from "react";
6
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
9
9
 
10
10
  //#region src/components/AnnotationSurface.tsx
11
- const clamp = (value) => Math.min(1, Math.max(0, value));
11
+ const clamp = (value) => Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0;
12
12
  const MAX_POINTS = 128;
13
- function Stroke({ points, width, height }) {
13
+ function Stroke({ points, width, height, highlight = false }) {
14
+ const thickness = highlight ? 16 : 3;
14
15
  const bounded = points.slice(0, MAX_POINTS);
15
16
  return bounded.map((point, index) => {
16
17
  const previous = bounded[index - 1];
@@ -25,12 +26,16 @@ function Stroke({ points, width, height }) {
25
26
  const dx = clamp(point.x) * width - x;
26
27
  const dy = clamp(point.y) * height - y;
27
28
  const length = Math.hypot(dx, dy);
28
- return /* @__PURE__ */ jsx(View, { style: [styles.segment, {
29
- left: x + dx / 2 - length / 2,
30
- top: y + dy / 2 - 1.5,
31
- width: length,
32
- transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }]
33
- }] }, index);
29
+ return /* @__PURE__ */ jsx(View, { style: [
30
+ styles.segment,
31
+ highlight && styles.highlight,
32
+ {
33
+ left: x + dx / 2 - length / 2,
34
+ top: y + dy / 2 - thickness / 2,
35
+ width: length,
36
+ transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }]
37
+ }
38
+ ] }, index);
34
39
  });
35
40
  }
36
41
  /**
@@ -39,7 +44,7 @@ function Stroke({ points, width, height }) {
39
44
  * content, so text and controls inside stay readable, and activating it places a
40
45
  * note at its centre.
41
46
  */
42
- function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable = false, onViewportChange, style }) {
47
+ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable = false, onViewportChange, navigation = false, viewport: controlledViewport, drawingHighlight = false, style }) {
43
48
  const [size, setSize] = useState({
44
49
  width: 0,
45
50
  height: 0
@@ -52,6 +57,8 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
52
57
  const transform = useRef(viewport);
53
58
  const pinch = useRef(null);
54
59
  const suppressTap = useRef(false);
60
+ const pressedPoint = useRef(void 0);
61
+ const navigating = useRef(false);
55
62
  const reportViewport = (next, dimensions = size) => {
56
63
  onViewportChange?.({
57
64
  ...dimensions,
@@ -60,26 +67,42 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
60
67
  originY: dimensions.height * (1 - next.scale) / 2 + next.y
61
68
  });
62
69
  };
63
- const resetViewport = () => {
64
- pinch.current = null;
65
- transform.current = {
66
- scale: 1,
67
- x: 0,
68
- y: 0
70
+ const boundViewport = (request, dimensions = size) => {
71
+ const scale = zoomable && Number.isFinite(request.zoom) ? Math.max(1, Math.min(4, request.zoom)) : 1;
72
+ const maxX = dimensions.width * (scale - 1) / 2;
73
+ const maxY = dimensions.height * (scale - 1) / 2;
74
+ return {
75
+ scale,
76
+ x: Math.max(-maxX, Math.min(maxX, (Number.isFinite(request.originX) ? request.originX : 0) + maxX)),
77
+ y: Math.max(-maxY, Math.min(maxY, (Number.isFinite(request.originY) ? request.originY : 0) + maxY))
69
78
  };
79
+ };
80
+ const resetViewport = (dimensions = size) => {
81
+ pinch.current = null;
82
+ transform.current = boundViewport(controlledViewport ?? {
83
+ zoom: 1,
84
+ originX: 0,
85
+ originY: 0
86
+ }, dimensions);
70
87
  setViewport(transform.current);
71
- reportViewport(transform.current);
88
+ reportViewport(transform.current, dimensions);
72
89
  };
73
- useEffect(() => {
74
- resetViewport();
75
- }, [zoomable]);
76
- useEffect(() => {
77
- reportViewport(viewport);
90
+ useLayoutEffect(() => {
91
+ const next = boundViewport(controlledViewport ?? {
92
+ zoom: 1,
93
+ originX: 0,
94
+ originY: 0
95
+ });
96
+ if (next.scale !== transform.current.scale || Math.abs(next.x - transform.current.x) > 1e-6 || Math.abs(next.y - transform.current.y) > 1e-6) resetViewport();
78
97
  }, [
79
- viewport,
80
- size,
81
- onViewportChange
98
+ zoomable,
99
+ controlledViewport?.zoom,
100
+ controlledViewport?.originX,
101
+ controlledViewport?.originY
82
102
  ]);
103
+ useLayoutEffect(() => {
104
+ reportViewport(transform.current);
105
+ }, [size, onViewportChange]);
83
106
  const sourcePoint = (x, y) => {
84
107
  const current = transform.current;
85
108
  return {
@@ -88,14 +111,18 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
88
111
  };
89
112
  };
90
113
  const moveViewport = (event) => {
91
- const [first, second] = event.nativeEvent.touches;
92
- if (!zoomable || !first || !second || size.width <= 0 || size.height <= 0) {
114
+ const { touches } = event.nativeEvent;
115
+ const [first, second] = touches;
116
+ const count = touches.length;
117
+ if (!zoomable || !first || count !== 2 && !(navigation && count === 1) || size.width <= 0 || size.height <= 0) {
93
118
  pinch.current = null;
94
119
  return;
95
120
  }
96
- const x = (first.pageX + second.pageX) / 2;
97
- const y = (first.pageY + second.pageY) / 2;
98
- const distance = Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY);
121
+ cancel();
122
+ suppressTap.current = true;
123
+ const x = second ? (first.pageX + second.pageX) / 2 : first.pageX;
124
+ const y = second ? (first.pageY + second.pageY) / 2 : first.pageY;
125
+ const distance = second ? Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY) : 1;
99
126
  if (![
100
127
  x,
101
128
  y,
@@ -104,8 +131,13 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
104
131
  pinch.current = null;
105
132
  return;
106
133
  }
107
- if (!pinch.current) {
134
+ if (!pinch.current || pinch.current.count !== count) {
135
+ const focalX = Number.isFinite(first.locationX) ? first.locationX + x - first.pageX : x;
136
+ const focalY = Number.isFinite(first.locationY) ? first.locationY + y - first.pageY : y;
108
137
  pinch.current = {
138
+ count,
139
+ focalX,
140
+ focalY,
109
141
  x,
110
142
  y,
111
143
  distance,
@@ -117,12 +149,13 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
117
149
  }
118
150
  const start = pinch.current;
119
151
  const scale = Math.max(1, Math.min(4, start.scale * distance / start.distance));
152
+ const ratio = scale / start.scale;
120
153
  const maxX = size.width * (scale - 1) / 2;
121
154
  const maxY = size.height * (scale - 1) / 2;
122
155
  transform.current = {
123
156
  scale,
124
- x: Math.max(-maxX, Math.min(maxX, start.offsetX + x - start.x)),
125
- y: Math.max(-maxY, Math.min(maxY, start.offsetY + y - start.y))
157
+ x: Math.max(-maxX, Math.min(maxX, start.offsetX * ratio + (start.focalX - size.width / 2) * (1 - ratio) + x - start.x)),
158
+ y: Math.max(-maxY, Math.min(maxY, start.offsetY * ratio + (start.focalY - size.height / 2) * (1 - ratio) + y - start.y))
126
159
  };
127
160
  reportViewport(transform.current);
128
161
  setViewport(transform.current);
@@ -134,6 +167,7 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
134
167
  const drawing = onDraw !== void 0;
135
168
  const cancel = () => {
136
169
  stroke.current = null;
170
+ pressedPoint.current = void 0;
137
171
  setLive([]);
138
172
  };
139
173
  useEffect(() => {
@@ -142,7 +176,14 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
142
176
  return () => {
143
177
  stroke.current = null;
144
178
  };
145
- }, [drawing]);
179
+ }, [
180
+ drawing,
181
+ navigation,
182
+ drawingHighlight,
183
+ controlledViewport?.zoom,
184
+ controlledViewport?.originX,
185
+ controlledViewport?.originY
186
+ ]);
146
187
  const append = (event) => {
147
188
  if (!stroke.current || !draw.current) return cancel();
148
189
  const { locationX, locationY, touches } = event.nativeEvent;
@@ -160,14 +201,15 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
160
201
  setLive([...stroke.current]);
161
202
  };
162
203
  const place = (event) => {
163
- if (!onPlace || onDraw || suppressTap.current) return;
204
+ if (!onPlace || onDraw || navigation || suppressTap.current) return;
164
205
  const { locationX, locationY } = event.nativeEvent;
165
- const known = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY);
166
- selectionTick();
167
- onPlace(known ? sourcePoint(locationX, locationY) : {
206
+ const point = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY) ? sourcePoint(locationX, locationY) : pressedPoint.current ?? {
168
207
  x: .5,
169
208
  y: .5
170
- });
209
+ };
210
+ pressedPoint.current = void 0;
211
+ selectionTick();
212
+ onPlace(point);
171
213
  };
172
214
  return /* @__PURE__ */ jsxs(View, {
173
215
  style: [
@@ -175,30 +217,41 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
175
217
  zoomable && { overflow: "hidden" },
176
218
  style
177
219
  ],
178
- onStartShouldSetResponderCapture: (event) => zoomable && event.nativeEvent.touches.length > 1,
179
- onMoveShouldSetResponderCapture: (event) => zoomable && event.nativeEvent.touches.length > 1,
220
+ onStartShouldSetResponderCapture: (event) => zoomable && (navigation || event.nativeEvent.touches.length > 1),
221
+ onMoveShouldSetResponderCapture: (event) => zoomable && (navigation || event.nativeEvent.touches.length > 1),
180
222
  onResponderGrant: zoomable ? (event) => {
181
223
  cancel();
224
+ navigating.current = true;
182
225
  suppressTap.current = true;
183
226
  moveViewport(event);
184
227
  } : void 0,
228
+ onResponderStart: zoomable ? (event) => {
229
+ cancel();
230
+ moveViewport(event);
231
+ } : void 0,
232
+ onResponderEnd: zoomable ? (event) => {
233
+ cancel();
234
+ moveViewport(event);
235
+ } : void 0,
185
236
  onResponderMove: zoomable ? moveViewport : void 0,
186
237
  onResponderRelease: zoomable ? () => {
238
+ navigating.current = false;
187
239
  pinch.current = null;
188
240
  } : void 0,
189
241
  onResponderTerminationRequest: () => true,
190
242
  onResponderTerminate: zoomable ? () => {
243
+ navigating.current = false;
191
244
  pinch.current = null;
192
245
  cancel();
193
246
  } : void 0,
194
247
  onLayout: (event) => {
195
248
  cancel();
196
- resetViewport();
249
+ const { width, height } = event.nativeEvent.layout;
197
250
  const dimensions = {
198
- width: event.nativeEvent.layout.width,
199
- height: event.nativeEvent.layout.height
251
+ width: Number.isFinite(width) ? Math.max(0, width) : 0,
252
+ height: Number.isFinite(height) ? Math.max(0, height) : 0
200
253
  };
201
- reportViewport(transform.current, dimensions);
254
+ resetViewport(dimensions);
202
255
  setSize(dimensions);
203
256
  },
204
257
  children: [
@@ -223,9 +276,13 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
223
276
  }),
224
277
  /* @__PURE__ */ jsx(Pressable, {
225
278
  accessible: false,
226
- disabled: !onPlace || !!onDraw,
227
- onPressIn: () => {
228
- if (!pinch.current) suppressTap.current = false;
279
+ disabled: !onPlace || !!onDraw || navigation,
280
+ onPressIn: (event) => {
281
+ pressedPoint.current = void 0;
282
+ if (navigating.current) return;
283
+ suppressTap.current = false;
284
+ const { locationX, locationY } = event.nativeEvent;
285
+ if (size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY)) pressedPoint.current = sourcePoint(locationX, locationY);
229
286
  },
230
287
  onPress: place,
231
288
  children: /* @__PURE__ */ jsxs(View, {
@@ -242,16 +299,18 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
242
299
  children: [marks.map((mark) => mark.points && /* @__PURE__ */ jsx(Stroke, {
243
300
  points: mark.points,
244
301
  width: size.width,
245
- height: size.height
302
+ height: size.height,
303
+ highlight: mark.highlight
246
304
  }, mark.id)), onDraw && /* @__PURE__ */ jsx(Stroke, {
247
305
  points: live,
248
306
  width: size.width,
249
- height: size.height
307
+ height: size.height,
308
+ highlight: drawingHighlight
250
309
  })]
251
310
  })]
252
311
  })
253
312
  }),
254
- onDraw && /* @__PURE__ */ jsx(View, {
313
+ onDraw && !navigation && /* @__PURE__ */ jsx(View, {
255
314
  accessible: false,
256
315
  collapsable: false,
257
316
  style: styles.overlay,
@@ -320,6 +379,11 @@ const styles = StyleSheet.create((theme) => ({
320
379
  backgroundColor: theme.colors.magenta,
321
380
  uni__dependencies: [0]
322
381
  },
382
+ highlight: {
383
+ height: 16,
384
+ borderRadius: 8,
385
+ opacity: .3
386
+ },
323
387
  hit: {
324
388
  position: "absolute",
325
389
  width: HIT,
@@ -1 +1 @@
1
- {"version":3,"file":"AnnotationSurface.mjs","names":["ReactNode","useEffect","useRef","useState","GestureResponderEvent","StyleProp","ViewStyle","StyleSheet","fontOf","selectionTick","AnnotationMark","id","x","y","label","description","points","AnnotationSurfaceProps","children","marks","selectedId","onPlace","point","onDraw","onSelectMark","accessibilityLabel","zoomable","onViewportChange","viewport","width","height","zoom","originX","originY","style","clamp","value","Math","min","max","MAX_POINTS","Point","Stroke","bounded","slice","map","index","previous","every","Number","isFinite","dx","dy","length","hypot","styles","segment","left","top","transform","rotate","atan2","AnnotationSurface","size","setSize","setViewport","scale","pinch","distance","offsetX","offsetY","suppressTap","reportViewport","next","dimensions","resetViewport","current","sourcePoint","moveViewport","event","first","second","nativeEvent","touches","pageX","pageY","start","maxX","maxY","live","setLive","stroke","draw","drawing","undefined","cancel","append","locationX","locationY","last","push","place","known","frame","overflow","layout","overlay","name","actionName","translateX","translateY","mark","some","selected","hit","pin","pinSelected","labelSelected","HIT","PIN","create","theme","position","alignSelf","right","bottom","borderRadius","backgroundColor","colors","magenta","uni__dependencies","marginLeft","marginTop","alignItems","justifyContent","borderWidth","borderColor","bg","color","fontFamily","sans","fontSize","sm","fontWeight","semibold"],"sources":["../../src/components/AnnotationSurface.tsx"],"sourcesContent":["import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport { type GestureResponderEvent, Pressable, type StyleProp, Text, View, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { selectionTick } from '../lib/haptics';\n\n/** One note pinned to the content, with an optional stroke, in fractions (0 to 1) of the surface's width and height. */\nexport interface AnnotationMark {\n id: string;\n x: number;\n y: number;\n /** The short text drawn on the pin, such as \"1\". */\n label: string;\n /** What a screen reader announces for the pin, such as the note text. */\n description?: string;\n /** Optional normalized freehand stroke. At most 128 finite points are rendered. */\n points?: readonly { x: number; y: number }[];\n}\n\nexport interface AnnotationSurfaceProps {\n /** The content being marked up: an image, a page, a block of text. */\n children: ReactNode;\n marks: readonly AnnotationMark[];\n selectedId?: string;\n /** A tap places a note there. Without onPlace or onDraw the surface is read-only. */\n onPlace?: (point: { x: number; y: number }) => void;\n /** Enables drawing instead of tap placement. Receives 2 to 128 finite [0,1] points only on a completed stroke. */\n onDraw?: (points: { x: number; y: number }[]) => void;\n onSelectMark?: (id: string) => void;\n /** What the content is, such as \"hero.png\". */\n accessibilityLabel: string;\n /** Image surfaces can pinch and move with two fingers on both native platforms. */\n zoomable?: boolean;\n /** Viewport dimensions and source-layout-to-viewport transform, all in RN layout units, not raster pixels. */\n onViewportChange?: (viewport: {\n width: number;\n height: number;\n zoom: number;\n originX: number;\n originY: number;\n }) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst clamp = (value: number) => Math.min(1, Math.max(0, value));\nconst MAX_POINTS = 128;\ntype Point = { x: number; y: number };\n\nfunction Stroke({ points, width, height }: { points: readonly Point[]; width: number; height: number }) {\n // Slice before processing so even externally supplied strokes have bounded work.\n const bounded = points.slice(0, MAX_POINTS);\n return bounded.map((point, index) => {\n const previous = bounded[index - 1];\n if (!previous || ![point.x, point.y, previous.x, previous.y].every(Number.isFinite)) return null;\n const x = clamp(previous.x) * width;\n const y = clamp(previous.y) * height;\n const dx = clamp(point.x) * width - x;\n const dy = clamp(point.y) * height - y;\n const length = Math.hypot(dx, dy);\n return (\n <View\n key={index}\n style={[\n styles.segment,\n {\n left: x + dx / 2 - length / 2,\n top: y + dy / 2 - 1.5,\n width: length,\n transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }],\n },\n ]}\n />\n );\n });\n}\n\n/**\n * Tap to pin a note on whatever it wraps. Each pin is its own 44pt button, so a\n * screen reader can reach every note; the surface's own element sits behind the\n * content, so text and controls inside stay readable, and activating it places a\n * note at its centre.\n */\nexport function AnnotationSurface({\n children,\n marks,\n selectedId,\n onPlace,\n onDraw,\n onSelectMark,\n accessibilityLabel,\n zoomable = false,\n onViewportChange,\n style,\n}: AnnotationSurfaceProps) {\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [viewport, setViewport] = useState({ scale: 1, x: 0, y: 0 });\n const transform = useRef(viewport);\n const pinch = useRef<{\n distance: number;\n x: number;\n y: number;\n scale: number;\n offsetX: number;\n offsetY: number;\n } | null>(null);\n const suppressTap = useRef(false);\n const reportViewport = (next: typeof viewport, dimensions = size) => {\n onViewportChange?.({\n ...dimensions,\n zoom: next.scale,\n originX: (dimensions.width * (1 - next.scale)) / 2 + next.x,\n originY: (dimensions.height * (1 - next.scale)) / 2 + next.y,\n });\n };\n const resetViewport = () => {\n pinch.current = null;\n transform.current = { scale: 1, x: 0, y: 0 };\n setViewport(transform.current);\n reportViewport(transform.current);\n };\n useEffect(() => {\n resetViewport();\n }, [zoomable]);\n useEffect(() => {\n reportViewport(viewport);\n }, [viewport, size, onViewportChange]);\n const sourcePoint = (x: number, y: number) => {\n const current = transform.current;\n return {\n x: clamp((x - size.width / 2 - current.x) / current.scale / size.width + 0.5),\n y: clamp((y - size.height / 2 - current.y) / current.scale / size.height + 0.5),\n };\n };\n const moveViewport = (event: GestureResponderEvent) => {\n const [first, second] = event.nativeEvent.touches;\n if (!zoomable || !first || !second || size.width <= 0 || size.height <= 0) {\n pinch.current = null;\n return;\n }\n const x = (first.pageX + second.pageX) / 2;\n const y = (first.pageY + second.pageY) / 2;\n const distance = Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY);\n if (![x, y, distance].every(Number.isFinite) || distance <= 0) {\n pinch.current = null;\n return;\n }\n if (!pinch.current) {\n pinch.current = {\n x,\n y,\n distance,\n scale: transform.current.scale,\n offsetX: transform.current.x,\n offsetY: transform.current.y,\n };\n return;\n }\n const start = pinch.current;\n const scale = Math.max(1, Math.min(4, (start.scale * distance) / start.distance));\n const maxX = (size.width * (scale - 1)) / 2;\n const maxY = (size.height * (scale - 1)) / 2;\n transform.current = {\n scale,\n x: Math.max(-maxX, Math.min(maxX, start.offsetX + x - start.x)),\n y: Math.max(-maxY, Math.min(maxY, start.offsetY + y - start.y)),\n };\n // Publish synchronously so capture can reject a gesture even before React renders it.\n reportViewport(transform.current);\n setViewport(transform.current);\n };\n const [live, setLive] = useState<readonly Point[]>([]);\n const stroke = useRef<Point[] | null>(null);\n // Callers pass inline callbacks, so a new identity is not a new tool: only switching drawing off or on cancels.\n const draw = useRef(onDraw);\n draw.current = onDraw;\n const drawing = onDraw !== undefined;\n const cancel = () => {\n stroke.current = null;\n setLive([]);\n };\n useEffect(() => {\n stroke.current = null;\n setLive([]);\n return () => {\n stroke.current = null;\n };\n }, [drawing]);\n const append = (event: GestureResponderEvent) => {\n if (!stroke.current || !draw.current) return cancel();\n const { locationX, locationY, touches } = event.nativeEvent;\n if (\n touches.length > 1 ||\n ![locationX, locationY, size.width, size.height].every(Number.isFinite) ||\n size.width <= 0 ||\n size.height <= 0\n )\n return cancel();\n const point = sourcePoint(locationX, locationY);\n const last = stroke.current[stroke.current.length - 1];\n if (last?.x === point.x && last.y === point.y) return;\n // Keep the start and a bounded prefix; update the last point once full.\n if (stroke.current.length < MAX_POINTS) stroke.current.push(point);\n else stroke.current[MAX_POINTS - 1] = point;\n setLive([...stroke.current]);\n };\n const place = (event: GestureResponderEvent) => {\n if (!onPlace || onDraw || suppressTap.current) return;\n const { locationX, locationY } = event.nativeEvent;\n const known = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY);\n selectionTick();\n onPlace(known ? sourcePoint(locationX, locationY) : { x: 0.5, y: 0.5 });\n };\n return (\n <View\n style={[styles.frame, zoomable && { overflow: 'hidden' }, style]}\n onStartShouldSetResponderCapture={(event) => zoomable && event.nativeEvent.touches.length > 1}\n onMoveShouldSetResponderCapture={(event) => zoomable && event.nativeEvent.touches.length > 1}\n onResponderGrant={\n zoomable\n ? (event) => {\n cancel();\n suppressTap.current = true;\n moveViewport(event);\n }\n : undefined\n }\n onResponderMove={zoomable ? moveViewport : undefined}\n onResponderRelease={\n zoomable\n ? () => {\n pinch.current = null;\n }\n : undefined\n }\n onResponderTerminationRequest={() => true}\n onResponderTerminate={\n zoomable\n ? () => {\n pinch.current = null;\n cancel();\n }\n : undefined\n }\n onLayout={(event) => {\n cancel();\n resetViewport();\n const dimensions = { width: event.nativeEvent.layout.width, height: event.nativeEvent.layout.height };\n reportViewport(transform.current, dimensions);\n setSize(dimensions);\n }}\n >\n <View\n pointerEvents=\"none\"\n style={styles.overlay}\n accessible\n accessibilityRole={onPlace && !onDraw ? 'button' : 'image'}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={\n onPlace && !onDraw\n ? 'Double tap to add a note here. Use the Note on whole image action to mark the whole image.'\n : undefined\n }\n accessibilityActions={\n onPlace\n ? [...(onDraw ? [] : [{ name: 'activate' }]), { name: 'noteWholeImage', label: 'Note on whole image' }]\n : undefined\n }\n onAccessibilityAction={(event) => {\n const { actionName } = event.nativeEvent;\n if (actionName === 'noteWholeImage' || (actionName === 'activate' && !onDraw)) onPlace?.({ x: 0.5, y: 0.5 });\n }}\n />\n <Pressable\n accessible={false}\n disabled={!onPlace || !!onDraw}\n onPressIn={() => {\n if (!pinch.current) suppressTap.current = false;\n }}\n onPress={place}\n >\n {/* Zoomed content is only a picture, so every touch maps through the frame. At 1x it keeps its own controls, such as PDF page buttons. */}\n <View\n pointerEvents={zoomable && viewport.scale > 1 ? 'none' : 'auto'}\n style={\n zoomable\n ? { transform: [{ translateX: viewport.x }, { translateY: viewport.y }, { scale: viewport.scale }] }\n : undefined\n }\n >\n {children}\n <View pointerEvents=\"none\" accessible={false} style={styles.overlay}>\n {marks.map(\n (mark) =>\n mark.points && <Stroke key={mark.id} points={mark.points} width={size.width} height={size.height} />,\n )}\n {onDraw && <Stroke points={live} width={size.width} height={size.height} />}\n </View>\n </View>\n </Pressable>\n {onDraw && (\n <View\n accessible={false}\n // Only JS responder props, which Fabric may flatten out of the native tree; keep it hit-testable.\n collapsable={false}\n style={styles.overlay}\n onStartShouldSetResponder={() => true}\n onResponderGrant={(event) => {\n stroke.current = [];\n append(event);\n }}\n onResponderStart={(event) => {\n if (event.nativeEvent.touches.length > 1) cancel();\n }}\n onResponderMove={append}\n onResponderRelease={(event) => {\n append(event);\n const points = stroke.current;\n cancel();\n if (\n points &&\n points.length >= 2 &&\n points.some((point) => point.x !== points[0].x || point.y !== points[0].y)\n ) {\n draw.current?.(points);\n }\n }}\n onResponderTerminationRequest={() => true}\n onResponderTerminate={cancel}\n />\n )}\n {marks.map((mark) => {\n const selected = mark.id === selectedId;\n return (\n <Pressable\n key={mark.id}\n accessibilityRole=\"button\"\n accessibilityLabel={`Note ${mark.label}${mark.description ? `: ${mark.description}` : ''}`}\n accessibilityState={{ selected }}\n onPress={() => onSelectMark?.(mark.id)}\n style={[\n styles.hit,\n zoomable\n ? {\n left: size.width / 2 + (clamp(mark.x) - 0.5) * size.width * viewport.scale + viewport.x,\n top: size.height / 2 + (clamp(mark.y) - 0.5) * size.height * viewport.scale + viewport.y,\n }\n : { left: `${clamp(mark.x) * 100}%`, top: `${clamp(mark.y) * 100}%` },\n ]}\n >\n <View style={[styles.pin, selected && styles.pinSelected]}>\n <Text style={[styles.label, selected && styles.labelSelected]}>{mark.label}</Text>\n </View>\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nconst HIT = 44;\nconst PIN = 28;\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { position: 'relative', alignSelf: 'stretch' },\n overlay: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 },\n segment: { position: 'absolute', height: 3, borderRadius: 1.5, backgroundColor: theme.colors.magenta },\n hit: {\n position: 'absolute',\n width: HIT,\n height: HIT,\n marginLeft: -HIT / 2,\n marginTop: -HIT / 2,\n alignItems: 'center',\n justifyContent: 'center',\n },\n pin: {\n width: PIN,\n height: PIN,\n borderRadius: PIN / 2,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderColor: theme.colors.magenta,\n backgroundColor: theme.colors.bg,\n },\n pinSelected: { backgroundColor: theme.colors.magenta },\n label: {\n color: theme.colors.magenta,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n fontWeight: theme.fontWeight.semibold,\n },\n labelSelected: { color: theme.colors.bg },\n}));\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnnotationSurface.mjs","names":["ReactNode","useEffect","useLayoutEffect","useRef","useState","GestureResponderEvent","StyleProp","ViewStyle","StyleSheet","fontOf","selectionTick","AnnotationMark","id","x","y","label","description","points","highlight","AnnotationSurfaceProps","children","marks","selectedId","onPlace","point","onDraw","onSelectMark","accessibilityLabel","zoomable","navigation","viewport","zoom","originX","originY","drawingHighlight","onViewportChange","width","height","style","clamp","value","Number","isFinite","Math","min","max","MAX_POINTS","Point","Stroke","thickness","bounded","slice","map","index","previous","every","dx","dy","length","hypot","styles","segment","left","top","transform","rotate","atan2","AnnotationSurface","controlledViewport","size","setSize","setViewport","scale","pinch","count","focalX","focalY","distance","offsetX","offsetY","suppressTap","pressedPoint","undefined","navigating","reportViewport","next","dimensions","boundViewport","request","NonNullable","maxX","maxY","resetViewport","current","abs","sourcePoint","moveViewport","event","touches","nativeEvent","first","second","cancel","pageX","pageY","locationX","locationY","start","ratio","live","setLive","stroke","draw","drawing","append","last","push","place","known","frame","overflow","layout","overlay","name","actionName","translateX","translateY","mark","some","selected","hit","pin","pinSelected","labelSelected","HIT","PIN","create","theme","position","alignSelf","right","bottom","borderRadius","backgroundColor","colors","magenta","uni__dependencies","opacity","marginLeft","marginTop","alignItems","justifyContent","borderWidth","borderColor","bg","color","fontFamily","sans","fontSize","sm","fontWeight","semibold"],"sources":["../../src/components/AnnotationSurface.tsx"],"sourcesContent":["import { type ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { type GestureResponderEvent, Pressable, type StyleProp, Text, View, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { selectionTick } from '../lib/haptics';\n\n/** One note pinned to the content, with an optional stroke, in fractions (0 to 1) of the surface's width and height. */\nexport interface AnnotationMark {\n id: string;\n x: number;\n y: number;\n /** The short text drawn on the pin, such as \"1\". */\n label: string;\n /** What a screen reader announces for the pin, such as the note text. */\n description?: string;\n /** Optional normalized freehand stroke. At most 128 finite points are rendered. */\n points?: readonly { x: number; y: number }[];\n /** A translucent, thick rose stroke over the content. */\n highlight?: boolean;\n}\n\nexport interface AnnotationSurfaceProps {\n /** The content being marked up: an image, a page, a block of text. */\n children: ReactNode;\n marks: readonly AnnotationMark[];\n selectedId?: string;\n /** A tap places a note there. Without onPlace or onDraw the surface is read-only. */\n onPlace?: (point: { x: number; y: number }) => void;\n /** Enables drawing instead of tap placement. Receives 2 to 128 finite [0,1] points only on a completed stroke. */\n onDraw?: (points: { x: number; y: number }[]) => void;\n onSelectMark?: (id: string) => void;\n /** What the content is, such as \"hero.png\". */\n accessibilityLabel: string;\n /** Image surfaces can pinch and move with two fingers on both native platforms. */\n zoomable?: boolean;\n /** Explicit one-finger pan instead of placing or drawing marks. */\n navigation?: boolean;\n /** Controlled transform. Origins are unscaled RN layout positions, not raster pixels. */\n viewport?: { zoom: number; originX: number; originY: number };\n /** Render the in-progress stroke as a highlight. */\n drawingHighlight?: boolean;\n /** Viewport dimensions and source-layout-to-viewport transform, all in RN layout units, not raster pixels. */\n onViewportChange?: (viewport: {\n width: number;\n height: number;\n zoom: number;\n originX: number;\n originY: number;\n }) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst clamp = (value: number) => (Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0);\nconst MAX_POINTS = 128;\ntype Point = { x: number; y: number };\n\nfunction Stroke({\n points,\n width,\n height,\n highlight = false,\n}: {\n points: readonly Point[];\n width: number;\n height: number;\n highlight?: boolean;\n}) {\n const thickness = highlight ? 16 : 3;\n // Slice before processing so even externally supplied strokes have bounded work.\n const bounded = points.slice(0, MAX_POINTS);\n return bounded.map((point, index) => {\n const previous = bounded[index - 1];\n if (!previous || ![point.x, point.y, previous.x, previous.y].every(Number.isFinite)) return null;\n const x = clamp(previous.x) * width;\n const y = clamp(previous.y) * height;\n const dx = clamp(point.x) * width - x;\n const dy = clamp(point.y) * height - y;\n const length = Math.hypot(dx, dy);\n return (\n <View\n key={index}\n style={[\n styles.segment,\n highlight && styles.highlight,\n {\n left: x + dx / 2 - length / 2,\n top: y + dy / 2 - thickness / 2,\n width: length,\n transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }],\n },\n ]}\n />\n );\n });\n}\n\n/**\n * Tap to pin a note on whatever it wraps. Each pin is its own 44pt button, so a\n * screen reader can reach every note; the surface's own element sits behind the\n * content, so text and controls inside stay readable, and activating it places a\n * note at its centre.\n */\nexport function AnnotationSurface({\n children,\n marks,\n selectedId,\n onPlace,\n onDraw,\n onSelectMark,\n accessibilityLabel,\n zoomable = false,\n onViewportChange,\n navigation = false,\n viewport: controlledViewport,\n drawingHighlight = false,\n style,\n}: AnnotationSurfaceProps) {\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [viewport, setViewport] = useState({ scale: 1, x: 0, y: 0 });\n const transform = useRef(viewport);\n const pinch = useRef<{\n count: number;\n focalX: number;\n focalY: number;\n distance: number;\n x: number;\n y: number;\n scale: number;\n offsetX: number;\n offsetY: number;\n } | null>(null);\n const suppressTap = useRef(false);\n const pressedPoint = useRef<Point | undefined>(undefined);\n const navigating = useRef(false);\n const reportViewport = (next: typeof viewport, dimensions = size) => {\n onViewportChange?.({\n ...dimensions,\n zoom: next.scale,\n originX: (dimensions.width * (1 - next.scale)) / 2 + next.x,\n originY: (dimensions.height * (1 - next.scale)) / 2 + next.y,\n });\n };\n const boundViewport = (request: NonNullable<AnnotationSurfaceProps['viewport']>, dimensions = size) => {\n const scale = zoomable && Number.isFinite(request.zoom) ? Math.max(1, Math.min(4, request.zoom)) : 1;\n const maxX = (dimensions.width * (scale - 1)) / 2;\n const maxY = (dimensions.height * (scale - 1)) / 2;\n return {\n scale,\n x: Math.max(-maxX, Math.min(maxX, (Number.isFinite(request.originX) ? request.originX : 0) + maxX)),\n y: Math.max(-maxY, Math.min(maxY, (Number.isFinite(request.originY) ? request.originY : 0) + maxY)),\n };\n };\n const resetViewport = (dimensions = size) => {\n pinch.current = null;\n transform.current = boundViewport(controlledViewport ?? { zoom: 1, originX: 0, originY: 0 }, dimensions);\n setViewport(transform.current);\n reportViewport(transform.current, dimensions);\n };\n // Layout effects publish programmatic zoom/Fit/focus before a parent's capture effect.\n useLayoutEffect(() => {\n const next = boundViewport(controlledViewport ?? { zoom: 1, originX: 0, originY: 0 });\n // A controlled parent's gesture echo is not a new navigation command.\n if (\n next.scale !== transform.current.scale ||\n Math.abs(next.x - transform.current.x) > 1e-6 ||\n Math.abs(next.y - transform.current.y) > 1e-6\n )\n resetViewport();\n }, [zoomable, controlledViewport?.zoom, controlledViewport?.originX, controlledViewport?.originY]);\n useLayoutEffect(() => {\n reportViewport(transform.current);\n }, [size, onViewportChange]);\n const sourcePoint = (x: number, y: number) => {\n const current = transform.current;\n return {\n x: clamp((x - size.width / 2 - current.x) / current.scale / size.width + 0.5),\n y: clamp((y - size.height / 2 - current.y) / current.scale / size.height + 0.5),\n };\n };\n const moveViewport = (event: GestureResponderEvent) => {\n const { touches } = event.nativeEvent;\n const [first, second] = touches;\n const count = touches.length;\n if (!zoomable || !first || (count !== 2 && !(navigation && count === 1)) || size.width <= 0 || size.height <= 0) {\n pinch.current = null;\n return;\n }\n cancel();\n suppressTap.current = true;\n const x = second ? (first.pageX + second.pageX) / 2 : first.pageX;\n const y = second ? (first.pageY + second.pageY) / 2 : first.pageY;\n const distance = second ? Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY) : 1;\n if (![x, y, distance].every(Number.isFinite) || distance <= 0) {\n pinch.current = null;\n return;\n }\n if (!pinch.current || pinch.current.count !== count) {\n // Touch locations are frame-local; page positions are used only for movement deltas.\n const focalX = Number.isFinite(first.locationX) ? first.locationX + x - first.pageX : x;\n const focalY = Number.isFinite(first.locationY) ? first.locationY + y - first.pageY : y;\n pinch.current = {\n count,\n focalX,\n focalY,\n x,\n y,\n distance,\n scale: transform.current.scale,\n offsetX: transform.current.x,\n offsetY: transform.current.y,\n };\n return;\n }\n const start = pinch.current;\n const scale = Math.max(1, Math.min(4, (start.scale * distance) / start.distance));\n const ratio = scale / start.scale;\n const maxX = (size.width * (scale - 1)) / 2;\n const maxY = (size.height * (scale - 1)) / 2;\n transform.current = {\n scale,\n x: Math.max(\n -maxX,\n Math.min(maxX, start.offsetX * ratio + (start.focalX - size.width / 2) * (1 - ratio) + x - start.x),\n ),\n y: Math.max(\n -maxY,\n Math.min(maxY, start.offsetY * ratio + (start.focalY - size.height / 2) * (1 - ratio) + y - start.y),\n ),\n };\n // Publish synchronously so capture can reject a gesture even before React renders it.\n reportViewport(transform.current);\n setViewport(transform.current);\n };\n const [live, setLive] = useState<readonly Point[]>([]);\n const stroke = useRef<Point[] | null>(null);\n // Callers pass inline callbacks, so a new identity is not a new tool: only switching drawing off or on cancels.\n const draw = useRef(onDraw);\n draw.current = onDraw;\n const drawing = onDraw !== undefined;\n const cancel = () => {\n stroke.current = null;\n pressedPoint.current = undefined;\n setLive([]);\n };\n useEffect(() => {\n stroke.current = null;\n setLive([]);\n return () => {\n stroke.current = null;\n };\n }, [\n drawing,\n navigation,\n drawingHighlight,\n controlledViewport?.zoom,\n controlledViewport?.originX,\n controlledViewport?.originY,\n ]);\n const append = (event: GestureResponderEvent) => {\n if (!stroke.current || !draw.current) return cancel();\n const { locationX, locationY, touches } = event.nativeEvent;\n if (\n touches.length > 1 ||\n ![locationX, locationY, size.width, size.height].every(Number.isFinite) ||\n size.width <= 0 ||\n size.height <= 0\n )\n return cancel();\n const point = sourcePoint(locationX, locationY);\n const last = stroke.current[stroke.current.length - 1];\n if (last?.x === point.x && last.y === point.y) return;\n // Keep the start and a bounded prefix; update the last point once full.\n if (stroke.current.length < MAX_POINTS) stroke.current.push(point);\n else stroke.current[MAX_POINTS - 1] = point;\n setLive([...stroke.current]);\n };\n const place = (event: GestureResponderEvent) => {\n if (!onPlace || onDraw || navigation || suppressTap.current) return;\n const { locationX, locationY } = event.nativeEvent;\n const known = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY);\n const point = known ? sourcePoint(locationX, locationY) : (pressedPoint.current ?? { x: 0.5, y: 0.5 });\n pressedPoint.current = undefined;\n selectionTick();\n onPlace(point);\n };\n return (\n <View\n style={[styles.frame, zoomable && { overflow: 'hidden' }, style]}\n onStartShouldSetResponderCapture={(event) => zoomable && (navigation || event.nativeEvent.touches.length > 1)}\n onMoveShouldSetResponderCapture={(event) => zoomable && (navigation || event.nativeEvent.touches.length > 1)}\n onResponderGrant={\n zoomable\n ? (event) => {\n cancel();\n navigating.current = true;\n suppressTap.current = true;\n moveViewport(event);\n }\n : undefined\n }\n onResponderStart={\n zoomable\n ? (event) => {\n cancel();\n moveViewport(event);\n }\n : undefined\n }\n onResponderEnd={\n zoomable\n ? (event) => {\n cancel();\n moveViewport(event);\n }\n : undefined\n }\n onResponderMove={zoomable ? moveViewport : undefined}\n onResponderRelease={\n zoomable\n ? () => {\n navigating.current = false;\n pinch.current = null;\n }\n : undefined\n }\n onResponderTerminationRequest={() => true}\n onResponderTerminate={\n zoomable\n ? () => {\n navigating.current = false;\n pinch.current = null;\n cancel();\n }\n : undefined\n }\n onLayout={(event) => {\n cancel();\n const { width, height } = event.nativeEvent.layout;\n const dimensions = {\n width: Number.isFinite(width) ? Math.max(0, width) : 0,\n height: Number.isFinite(height) ? Math.max(0, height) : 0,\n };\n resetViewport(dimensions);\n setSize(dimensions);\n }}\n >\n <View\n pointerEvents=\"none\"\n style={styles.overlay}\n accessible\n accessibilityRole={onPlace && !onDraw ? 'button' : 'image'}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={\n onPlace && !onDraw\n ? 'Double tap to add a note here. Use the Note on whole image action to mark the whole image.'\n : undefined\n }\n accessibilityActions={\n onPlace\n ? [...(onDraw ? [] : [{ name: 'activate' }]), { name: 'noteWholeImage', label: 'Note on whole image' }]\n : undefined\n }\n onAccessibilityAction={(event) => {\n const { actionName } = event.nativeEvent;\n if (actionName === 'noteWholeImage' || (actionName === 'activate' && !onDraw)) onPlace?.({ x: 0.5, y: 0.5 });\n }}\n />\n <Pressable\n accessible={false}\n disabled={!onPlace || !!onDraw || navigation}\n onPressIn={(event) => {\n pressedPoint.current = undefined;\n if (navigating.current) return;\n suppressTap.current = false;\n // react-native-web onPress is a MouseEvent; its responder start retains the native location.\n const { locationX, locationY } = event.nativeEvent;\n if (size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY))\n pressedPoint.current = sourcePoint(locationX, locationY);\n }}\n onPress={place}\n >\n {/* Zoomed content is only a picture, so every touch maps through the frame. At 1x it keeps its own controls, such as PDF page buttons. */}\n <View\n pointerEvents={zoomable && viewport.scale > 1 ? 'none' : 'auto'}\n style={\n zoomable\n ? { transform: [{ translateX: viewport.x }, { translateY: viewport.y }, { scale: viewport.scale }] }\n : undefined\n }\n >\n {children}\n <View pointerEvents=\"none\" accessible={false} style={styles.overlay}>\n {marks.map(\n (mark) =>\n mark.points && (\n <Stroke\n key={mark.id}\n points={mark.points}\n width={size.width}\n height={size.height}\n highlight={mark.highlight}\n />\n ),\n )}\n {onDraw && <Stroke points={live} width={size.width} height={size.height} highlight={drawingHighlight} />}\n </View>\n </View>\n </Pressable>\n {onDraw && !navigation && (\n <View\n accessible={false}\n // Only JS responder props, which Fabric may flatten out of the native tree; keep it hit-testable.\n collapsable={false}\n style={styles.overlay}\n onStartShouldSetResponder={() => true}\n onResponderGrant={(event) => {\n stroke.current = [];\n append(event);\n }}\n onResponderStart={(event) => {\n if (event.nativeEvent.touches.length > 1) cancel();\n }}\n onResponderMove={append}\n onResponderRelease={(event) => {\n append(event);\n const points = stroke.current;\n cancel();\n if (\n points &&\n points.length >= 2 &&\n points.some((point) => point.x !== points[0].x || point.y !== points[0].y)\n ) {\n draw.current?.(points);\n }\n }}\n onResponderTerminationRequest={() => true}\n onResponderTerminate={cancel}\n />\n )}\n {marks.map((mark) => {\n const selected = mark.id === selectedId;\n return (\n <Pressable\n key={mark.id}\n accessibilityRole=\"button\"\n accessibilityLabel={`Note ${mark.label}${mark.description ? `: ${mark.description}` : ''}`}\n accessibilityState={{ selected }}\n onPress={() => onSelectMark?.(mark.id)}\n style={[\n styles.hit,\n zoomable\n ? {\n left: size.width / 2 + (clamp(mark.x) - 0.5) * size.width * viewport.scale + viewport.x,\n top: size.height / 2 + (clamp(mark.y) - 0.5) * size.height * viewport.scale + viewport.y,\n }\n : { left: `${clamp(mark.x) * 100}%`, top: `${clamp(mark.y) * 100}%` },\n ]}\n >\n <View style={[styles.pin, selected && styles.pinSelected]}>\n <Text style={[styles.label, selected && styles.labelSelected]}>{mark.label}</Text>\n </View>\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nconst HIT = 44;\nconst PIN = 28;\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { position: 'relative', alignSelf: 'stretch' },\n overlay: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 },\n segment: { position: 'absolute', height: 3, borderRadius: 1.5, backgroundColor: theme.colors.magenta },\n highlight: { height: 16, borderRadius: 8, opacity: 0.3 },\n hit: {\n position: 'absolute',\n width: HIT,\n height: HIT,\n marginLeft: -HIT / 2,\n marginTop: -HIT / 2,\n alignItems: 'center',\n justifyContent: 'center',\n },\n pin: {\n width: PIN,\n height: PIN,\n borderRadius: PIN / 2,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderColor: theme.colors.magenta,\n backgroundColor: theme.colors.bg,\n },\n pinSelected: { backgroundColor: theme.colors.magenta },\n label: {\n color: theme.colors.magenta,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n fontWeight: theme.fontWeight.semibold,\n },\n labelSelected: { color: theme.colors.bg },\n}));\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-native-components",
3
- "version": "0.0.0-alpha.67",
3
+ "version": "0.0.0-alpha.69",
4
4
  "description": "Shared React Native components for Po and its native plugins: the web component library's look and API on native parts, tuned to the Po palette.",
5
5
  "keywords": [
6
6
  "ai",
@@ -45,11 +45,11 @@
45
45
  "access": "public"
46
46
  },
47
47
  "dependencies": {
48
- "@agimon-ai/po-web-components": "0.0.0-alpha.67"
48
+ "@agimon-ai/po-web-components": "0.0.0-alpha.69"
49
49
  },
50
50
  "devDependencies": {
51
- "@agimon-ai/po-build": "0.0.0-alpha.67",
52
- "@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.67",
51
+ "@agimon-ai/po-build": "0.0.0-alpha.69",
52
+ "@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.69",
53
53
  "@babel/core": "7.29.7",
54
54
  "@expo/vector-icons": "15.1.1",
55
55
  "@fontsource-variable/ibm-plex-sans": "5.3.0",