@tapthrough/expo 0.0.0-stage → 0.1.0

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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -2
  3. package/app.plugin.d.ts +37 -0
  4. package/app.plugin.js +79 -0
  5. package/dist/Annotator.d.ts +14 -0
  6. package/dist/Annotator.d.ts.map +1 -0
  7. package/dist/Annotator.js +160 -0
  8. package/dist/Annotator.js.map +1 -0
  9. package/dist/Bubble.d.ts +27 -0
  10. package/dist/Bubble.d.ts.map +1 -0
  11. package/dist/Bubble.js +234 -0
  12. package/dist/Bubble.js.map +1 -0
  13. package/dist/Feedback.d.ts +14 -0
  14. package/dist/Feedback.d.ts.map +1 -0
  15. package/dist/Feedback.js +977 -0
  16. package/dist/Feedback.js.map +1 -0
  17. package/dist/Overlay.d.ts +13 -0
  18. package/dist/Overlay.d.ts.map +1 -0
  19. package/dist/Overlay.js +44 -0
  20. package/dist/Overlay.js.map +1 -0
  21. package/dist/Popover.d.ts +98 -0
  22. package/dist/Popover.d.ts.map +1 -0
  23. package/dist/Popover.js +531 -0
  24. package/dist/Popover.js.map +1 -0
  25. package/dist/Tapthrough.d.ts +27 -0
  26. package/dist/Tapthrough.d.ts.map +1 -0
  27. package/dist/Tapthrough.js +111 -0
  28. package/dist/Tapthrough.js.map +1 -0
  29. package/dist/core/annotations.d.ts +63 -0
  30. package/dist/core/annotations.d.ts.map +1 -0
  31. package/dist/core/annotations.js +76 -0
  32. package/dist/core/annotations.js.map +1 -0
  33. package/dist/core/bubble.d.ts +21 -0
  34. package/dist/core/bubble.d.ts.map +1 -0
  35. package/dist/core/bubble.js +25 -0
  36. package/dist/core/bubble.js.map +1 -0
  37. package/dist/core/controller.d.ts +46 -0
  38. package/dist/core/controller.d.ts.map +1 -0
  39. package/dist/core/controller.js +103 -0
  40. package/dist/core/controller.js.map +1 -0
  41. package/dist/core/diagnostics.d.ts +47 -0
  42. package/dist/core/diagnostics.d.ts.map +1 -0
  43. package/dist/core/diagnostics.js +101 -0
  44. package/dist/core/diagnostics.js.map +1 -0
  45. package/dist/core/feedback.d.ts +302 -0
  46. package/dist/core/feedback.d.ts.map +1 -0
  47. package/dist/core/feedback.js +345 -0
  48. package/dist/core/feedback.js.map +1 -0
  49. package/dist/core/flags.d.ts +13 -0
  50. package/dist/core/flags.d.ts.map +1 -0
  51. package/dist/core/flags.js +25 -0
  52. package/dist/core/flags.js.map +1 -0
  53. package/dist/core/ideas.d.ts +166 -0
  54. package/dist/core/ideas.d.ts.map +1 -0
  55. package/dist/core/ideas.js +159 -0
  56. package/dist/core/ideas.js.map +1 -0
  57. package/dist/core/launch.d.ts +43 -0
  58. package/dist/core/launch.d.ts.map +1 -0
  59. package/dist/core/launch.js +81 -0
  60. package/dist/core/launch.js.map +1 -0
  61. package/dist/core/reset.d.ts +17 -0
  62. package/dist/core/reset.d.ts.map +1 -0
  63. package/dist/core/reset.js +19 -0
  64. package/dist/core/reset.js.map +1 -0
  65. package/dist/core/review.d.ts +49 -0
  66. package/dist/core/review.d.ts.map +1 -0
  67. package/dist/core/review.js +92 -0
  68. package/dist/core/review.js.map +1 -0
  69. package/dist/core/url.d.ts +9 -0
  70. package/dist/core/url.d.ts.map +1 -0
  71. package/dist/core/url.js +23 -0
  72. package/dist/core/url.js.map +1 -0
  73. package/dist/hooks.d.ts +6 -0
  74. package/dist/hooks.d.ts.map +1 -0
  75. package/dist/hooks.js +13 -0
  76. package/dist/hooks.js.map +1 -0
  77. package/dist/index.d.ts +9 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +7 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/native-intent.d.ts +18 -0
  82. package/dist/native-intent.d.ts.map +1 -0
  83. package/dist/native-intent.js +32 -0
  84. package/dist/native-intent.js.map +1 -0
  85. package/dist/runtime.d.ts +24 -0
  86. package/dist/runtime.d.ts.map +1 -0
  87. package/dist/runtime.js +17 -0
  88. package/dist/runtime.js.map +1 -0
  89. package/dist/screenshot.d.ts +7 -0
  90. package/dist/screenshot.d.ts.map +1 -0
  91. package/dist/screenshot.js +48 -0
  92. package/dist/screenshot.js.map +1 -0
  93. package/dist/supabase.d.ts +20 -0
  94. package/dist/supabase.d.ts.map +1 -0
  95. package/dist/supabase.js +18 -0
  96. package/dist/supabase.js.map +1 -0
  97. package/dist/ui/native.d.ts +34 -0
  98. package/dist/ui/native.d.ts.map +1 -0
  99. package/dist/ui/native.js +76 -0
  100. package/dist/ui/native.js.map +1 -0
  101. package/dist/ui/parts.d.ts +62 -0
  102. package/dist/ui/parts.d.ts.map +1 -0
  103. package/dist/ui/parts.js +110 -0
  104. package/dist/ui/parts.js.map +1 -0
  105. package/dist/ui/theme.d.ts +90 -0
  106. package/dist/ui/theme.d.ts.map +1 -0
  107. package/dist/ui/theme.js +61 -0
  108. package/dist/ui/theme.js.map +1 -0
  109. package/package.json +82 -3
  110. package/src/Annotator.tsx +279 -0
  111. package/src/Bubble.tsx +331 -0
  112. package/src/Feedback.tsx +1739 -0
  113. package/src/Overlay.tsx +61 -0
  114. package/src/Popover.tsx +1393 -0
  115. package/src/Tapthrough.tsx +171 -0
  116. package/src/core/annotations.ts +116 -0
  117. package/src/core/bubble.ts +45 -0
  118. package/src/core/controller.ts +119 -0
  119. package/src/core/diagnostics.ts +128 -0
  120. package/src/core/feedback.ts +589 -0
  121. package/src/core/flags.ts +31 -0
  122. package/src/core/ideas.ts +337 -0
  123. package/src/core/launch.ts +119 -0
  124. package/src/core/reset.ts +28 -0
  125. package/src/core/review.ts +148 -0
  126. package/src/core/url.ts +21 -0
  127. package/src/hooks.ts +15 -0
  128. package/src/index.ts +8 -0
  129. package/src/native-intent.ts +36 -0
  130. package/src/runtime.ts +30 -0
  131. package/src/screenshot.ts +64 -0
  132. package/src/supabase.ts +27 -0
  133. package/src/ui/native.ts +116 -0
  134. package/src/ui/parts.tsx +249 -0
  135. package/src/ui/theme.ts +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tapthrough contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,106 @@
1
- # Temporary Holding Version
1
+ # @tapthrough/expo
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The [Tapthrough](https://github.com/tapthrough/tapthrough) SDK for Expo Router apps. When a reviewer opens
4
+ a pull request's preview link, the app:
5
+
6
+ 1. sends the link token to your [seed hook](../seed), which creates a fresh test user in staging
7
+ 2. clears local state: query caches, persisted keys, stores
8
+ 3. logs in as that user
9
+ 4. applies the scenario's feature flags
10
+ 5. opens the screen the PR changed
11
+
12
+ When the app opens any other way, the SDK does nothing.
13
+
14
+ ```sh
15
+ npx expo install @tapthrough/expo
16
+ ```
17
+
18
+ ## Setup
19
+
20
+ ### 1. Config plugin
21
+
22
+ ```ts
23
+ // app.config.ts
24
+ export default {
25
+ scheme: 'acmenotes',
26
+ plugins: [
27
+ [
28
+ '@tapthrough/expo',
29
+ {
30
+ enabled: process.env.APP_VARIANT === 'preview',
31
+ seedHookUrl: 'https://<ref>.supabase.co/functions/v1/tapthrough-seed',
32
+ },
33
+ ],
34
+ ],
35
+ }
36
+ ```
37
+
38
+ The SDK ignores launch links unless `enabled` is true, so keep it off for store builds. The plugin
39
+ requires a `scheme`, because links open `<scheme>://tapthrough/launch?t=<token>`.
40
+
41
+ ### 2. Wrap the root layout
42
+
43
+ ```tsx
44
+ // app/_layout.tsx
45
+ import { resetQueryClient, resetStorage, Tapthrough } from '@tapthrough/expo'
46
+ import { supabaseAuth } from '@tapthrough/expo/supabase'
47
+ import AsyncStorage from '@react-native-async-storage/async-storage'
48
+ import { Stack } from 'expo-router'
49
+
50
+ const auth = supabaseAuth(supabase)
51
+ const reset = [resetQueryClient(queryClient), resetStorage(AsyncStorage, ['notes-store'])]
52
+
53
+ export default function RootLayout() {
54
+ return (
55
+ <QueryClientProvider client={queryClient}>
56
+ <Tapthrough auth={auth} reset={reset}>
57
+ <Stack />
58
+ </Tapthrough>
59
+ </QueryClientProvider>
60
+ )
61
+ }
62
+ ```
63
+
64
+ Define `auth` and `reset` outside the component, or memoize them, so they don't change on every render.
65
+
66
+ `reset` runs **before** sign-in, so a listed key can't wipe the new session. List only your app's own
67
+ keys, not the auth client's.
68
+
69
+ ### 3. Hand launch links to the SDK
70
+
71
+ ```ts
72
+ // app/+native-intent.ts
73
+ export { redirectSystemPath } from '@tapthrough/expo'
74
+ ```
75
+
76
+ Without this file, Expo Router tries to open `/tapthrough/launch` as a screen and shows its "not found"
77
+ page. If you already have a `redirectSystemPath`, call `handleLaunchPath(path)` first and return `'/'`
78
+ (or `null` when the app was already open) when it returns true.
79
+
80
+ ### 4. Read flag overrides
81
+
82
+ ```ts
83
+ import { useFlagOverride } from '@tapthrough/expo'
84
+
85
+ const newEditor = useFlagOverride('newEditor') ?? flags.newEditor
86
+ ```
87
+
88
+ Outside React, use `getFlagOverride(name)`.
89
+
90
+ ## API
91
+
92
+ | Export | Does |
93
+ | --- | --- |
94
+ | `<Tapthrough auth reset? seedHookUrl? enabled?>` | Runs the launch sequence and shows a loading screen, or an error card the reviewer can dismiss. `seedHookUrl` and `enabled` override the plugin. |
95
+ | `redirectSystemPath`, `handleLaunchPath(path)` | For `+native-intent`, as above. |
96
+ | `supabaseAuth(client)` from `@tapthrough/expo/supabase` | Logs in with the `{ access_token, refresh_token }` that `createTestUser` returns. |
97
+ | `resetQueryClient(client)`, `resetStorage(storage, keys)`, `resetStores(stores)` | Reset targets for TanStack Query, AsyncStorage or MMKV, and Zustand. A reset target is any `() => void \| Promise<void>`. |
98
+ | `useFlagOverride(name)`, `getFlagOverride(name)`, `getFlagOverrides()` | The scenario's flag values. `undefined` means the scenario leaves that flag alone. |
99
+ | `useLaunchState()` | `idle`, `launching`, `done` (with `path`) or `error` (with `error.code` and `error.message`). |
100
+
101
+ Any other auth provider works through `auth={{ signIn: async (session) => { … } }}`, where `session` is
102
+ whatever your seed hook returns.
103
+
104
+ ## License
105
+
106
+ MIT
@@ -0,0 +1,37 @@
1
+ export interface TapthroughPluginProps {
2
+ /** Turns the SDK on for this build. Defaults to true; set false for store builds. */
3
+ enabled?: boolean
4
+ /** Your seed hook, e.g. `https://<ref>.supabase.co/functions/v1/tapthrough-seed`. */
5
+ seedHookUrl?: string
6
+ /** Tapthrough's base URL, where in-app reports go. Defaults to `https://tapthrough.dev`. */
7
+ apiUrl?: string
8
+ /**
9
+ * Staging and TestFlight builds: shows a feedback button outside PR previews,
10
+ * to report issues and share ideas. Never turn it on for store builds.
11
+ */
12
+ staff?: boolean
13
+ /** The repo's app key from `npx @tapthrough/cli app-key`; required with `staff`. */
14
+ appKey?: string
15
+ }
16
+
17
+ type ExpoConfig = { scheme?: string | string[]; extra?: Record<string, unknown> } & Record<
18
+ string,
19
+ unknown
20
+ >
21
+
22
+ declare function withTapthrough<T extends ExpoConfig>(
23
+ config: T,
24
+ props?: TapthroughPluginProps,
25
+ ): T & {
26
+ extra: NonNullable<T['extra']> & {
27
+ tapthrough: {
28
+ enabled: boolean
29
+ seedHookUrl: string | undefined
30
+ apiUrl?: string
31
+ staff?: boolean
32
+ appKey?: string
33
+ }
34
+ }
35
+ }
36
+
37
+ export = withTapthrough
package/app.plugin.js ADDED
@@ -0,0 +1,79 @@
1
+ // @ts-check
2
+ /**
3
+ * Expo config plugin. Writes the SDK's build-time settings into `extra.tapthrough`.
4
+ *
5
+ * plugins: [['@tapthrough/expo', {
6
+ * // PR previews: launch links, test data, the checklist.
7
+ * enabled: process.env.APP_VARIANT === 'development',
8
+ * seedHookUrl: 'https://<ref>.supabase.co/functions/v1/tapthrough-seed',
9
+ * // Staging and TestFlight builds: report issues and share ideas.
10
+ * staff: process.env.APP_VARIANT === 'staging',
11
+ * appKey: process.env.TAPTHROUGH_APP_KEY,
12
+ * }]]
13
+ *
14
+ * Keep both off for store builds. With `enabled` off the SDK ignores launch
15
+ * links; with `staff` off there's no feedback button outside PR previews.
16
+ *
17
+ * @param {{ expo?: unknown } & Record<string, any>} config
18
+ * @param {{ enabled?: boolean, seedHookUrl?: string, apiUrl?: string, staff?: boolean, appKey?: string }} [props]
19
+ */
20
+ function withTapthrough(config, props = {}) {
21
+ const enabled = props.enabled ?? true
22
+ const staff = props.staff === true
23
+ const seedHookUrl = props.seedHookUrl
24
+ const apiUrl = props.apiUrl ?? 'https://tapthrough.dev'
25
+
26
+ if (enabled || staff) {
27
+ if (!isAllowedUrl(apiUrl)) {
28
+ throw new Error(
29
+ `[@tapthrough/expo] \`apiUrl\` must be https (http is allowed only for localhost): ${apiUrl}`,
30
+ )
31
+ }
32
+ }
33
+ if (enabled) {
34
+ // Optional: without it, links open the right screen but can't add test data.
35
+ if (seedHookUrl && !isAllowedUrl(seedHookUrl)) {
36
+ throw new Error(
37
+ `[@tapthrough/expo] \`seedHookUrl\` must be https (http is allowed only for localhost): ${seedHookUrl}`,
38
+ )
39
+ }
40
+ if (!config.scheme) {
41
+ throw new Error(
42
+ '[@tapthrough/expo] Set `scheme` in your app config; launch links open `<scheme>://tapthrough/launch`.',
43
+ )
44
+ }
45
+ }
46
+ if (staff && !props.appKey) {
47
+ throw new Error(
48
+ '[@tapthrough/expo] `staff` needs an `appKey`. Make one with `npx @tapthrough/cli app-key` and pass it from an environment variable.',
49
+ )
50
+ }
51
+
52
+ return {
53
+ ...config,
54
+ extra: {
55
+ ...config.extra,
56
+ tapthrough: {
57
+ enabled,
58
+ seedHookUrl: enabled ? seedHookUrl : undefined,
59
+ ...(enabled || staff ? { apiUrl } : {}),
60
+ // The key only goes into builds that use it.
61
+ ...(staff ? { staff, appKey: props.appKey } : {}),
62
+ },
63
+ },
64
+ }
65
+ }
66
+
67
+ /** @param {string} url */
68
+ function isAllowedUrl(url) {
69
+ try {
70
+ const { protocol, hostname } = new URL(url)
71
+ if (protocol === 'https:') return true
72
+ // 10.0.2.2 is the host machine from the Android emulator.
73
+ return protocol === 'http:' && ['localhost', '127.0.0.1', '10.0.2.2'].includes(hostname)
74
+ } catch {
75
+ return false
76
+ }
77
+ }
78
+
79
+ module.exports = withTapthrough
@@ -0,0 +1,14 @@
1
+ import { type Annotation } from './core/annotations.js';
2
+ import type { Screenshot } from './screenshot.js';
3
+ /**
4
+ * Full-screen markup for a report's screenshot: Draw to circle or point at
5
+ * something freehand, Box to outline it, Blur to hide personal data. The
6
+ * server applies the marks before storing the image.
7
+ */
8
+ export declare function Annotator({ screenshot, initial, onDone, onCancel, }: {
9
+ screenshot: Screenshot;
10
+ initial: Annotation[];
11
+ onDone: (marks: Annotation[]) => void;
12
+ onCancel: () => void;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=Annotator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Annotator.d.ts","sourceRoot":"","sources":["../src/Annotator.tsx"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,UAAU,EAOhB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAUjD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACxB,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAQ,GACT,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAA;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,+BA0JA"}
@@ -0,0 +1,160 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Animated, Image, PanResponder, Pressable, StyleSheet, Text, useWindowDimensions, View, } from 'react-native';
4
+ import { addStrokePoint, boxFromDrag, finishStroke, MAX_ANNOTATIONS, strokeSegments, } from './core/annotations.js';
5
+ import { useBackPress } from './Overlay.js';
6
+ const MODES = [
7
+ { mode: 'draw', title: 'Draw', hint: 'Draw on the screenshot to circle or point at something.' },
8
+ { mode: 'highlight', title: 'Box', hint: 'Drag on the screenshot to draw a box.' },
9
+ { mode: 'blur', title: 'Blur', hint: 'Drag over anything private to blur it.' },
10
+ ];
11
+ /**
12
+ * Full-screen markup for a report's screenshot: Draw to circle or point at
13
+ * something freehand, Box to outline it, Blur to hide personal data. The
14
+ * server applies the marks before storing the image.
15
+ */
16
+ export function Annotator({ screenshot, initial, onDone, onCancel, }) {
17
+ const window = useWindowDimensions();
18
+ const [boxes, setBoxes] = useState(initial);
19
+ const [kind, setKind] = useState('draw');
20
+ const [draft, setDraft] = useState(null);
21
+ const [aspect, setAspect] = useState(window.width / window.height);
22
+ const uri = `data:${screenshot.contentType};base64,${screenshot.base64}`;
23
+ const shown = useRef(new Animated.Value(0)).current;
24
+ useBackPress(onCancel);
25
+ useEffect(() => {
26
+ Animated.timing(shown, { toValue: 1, duration: 180, useNativeDriver: true }).start();
27
+ }, [shown]);
28
+ // The image is shown at the screenshot's aspect ratio, so fractions map 1:1.
29
+ const maxWidth = window.width - 32;
30
+ const maxHeight = window.height - 200;
31
+ const width = Math.min(maxWidth, maxHeight * aspect);
32
+ const height = width / aspect;
33
+ const state = useRef({
34
+ start: { x: 0, y: 0 },
35
+ draft: null,
36
+ points: [],
37
+ kind,
38
+ boxes,
39
+ });
40
+ state.current.kind = kind;
41
+ state.current.boxes = boxes;
42
+ const responder = useMemo(() => PanResponder.create({
43
+ onStartShouldSetPanResponder: () => true,
44
+ onMoveShouldSetPanResponder: () => true,
45
+ onPanResponderGrant: (event) => {
46
+ const start = { x: event.nativeEvent.locationX, y: event.nativeEvent.locationY };
47
+ state.current.start = start;
48
+ state.current.draft = null;
49
+ state.current.points =
50
+ state.current.kind === 'draw' ? addStrokePoint([], start, { width, height }) : [];
51
+ },
52
+ onPanResponderMove: (_event, gesture) => {
53
+ const { start, kind: mode } = state.current;
54
+ const at = { x: start.x + gesture.dx, y: start.y + gesture.dy };
55
+ if (mode === 'draw') {
56
+ const points = addStrokePoint(state.current.points, at, { width, height });
57
+ if (points === state.current.points)
58
+ return;
59
+ state.current.points = points;
60
+ const stroke = { kind: 'draw', points };
61
+ state.current.draft = stroke;
62
+ setDraft(stroke);
63
+ return;
64
+ }
65
+ const next = boxFromDrag(mode, start, at, { width, height });
66
+ state.current.draft = next;
67
+ setDraft(next);
68
+ },
69
+ onPanResponderRelease: () => {
70
+ const { draft: current } = state.current;
71
+ const finished = current?.kind === 'draw' ? finishStroke(current.points) : current;
72
+ if (finished && state.current.boxes.length < MAX_ANNOTATIONS) {
73
+ setBoxes([...state.current.boxes, finished]);
74
+ }
75
+ state.current.draft = null;
76
+ setDraft(null);
77
+ },
78
+ }), [width, height]);
79
+ return (_jsx(Animated.View, { style: [StyleSheet.absoluteFill, { opacity: shown }], accessibilityViewIsModal: true, children: _jsxs(View, { style: styles.screen, children: [_jsxs(View, { style: styles.toolbar, children: [_jsx(Pressable, { onPress: onCancel, accessibilityRole: "button", hitSlop: 8, children: _jsx(Text, { style: styles.toolbarText, children: "Cancel" }) }), _jsx(View, { style: styles.modes, children: MODES.map(({ mode, title }) => (_jsx(Pressable, { onPress: () => setKind(mode), style: [styles.mode, kind === mode && styles.modeActive], accessibilityRole: "radio", accessibilityState: { selected: kind === mode }, children: _jsx(Text, { style: [styles.modeText, kind === mode && styles.modeTextActive], children: title }) }, mode))) }), _jsx(Pressable, { onPress: () => onDone(boxes), accessibilityRole: "button", hitSlop: 8, children: _jsx(Text, { style: [styles.toolbarText, styles.done], children: "Done" }) })] }), _jsxs(View, { style: { width, height }, ...responder.panHandlers, children: [_jsx(Image, { source: { uri }, style: { width, height }, onLoad: (event) => {
80
+ const { width: w, height: h } = event.nativeEvent.source;
81
+ if (w && h)
82
+ setAspect(w / h);
83
+ }, accessibilityLabel: "Screenshot to mark up" }), [...boxes, ...(draft ? [draft] : [])].map((box, index) => box.kind === 'draw' ? (
84
+ // biome-ignore lint/suspicious/noArrayIndexKey: marks have no identity beyond their order
85
+ _jsx(Stroke, { points: box.points, width: width, height: height }, index)) : (_jsx(View
86
+ // biome-ignore lint/suspicious/noArrayIndexKey: boxes have no identity beyond their order
87
+ , { pointerEvents: "none", style: [
88
+ styles.box,
89
+ box.kind === 'blur' ? styles.blurBox : styles.highlightBox,
90
+ {
91
+ left: box.x * width,
92
+ top: box.y * height,
93
+ width: box.w * width,
94
+ height: box.h * height,
95
+ },
96
+ ], children: box.kind === 'blur' ? _jsx(Text, { style: styles.blurLabel, children: "Blurred" }) : null }, index)))] }), _jsxs(View, { style: styles.footer, children: [_jsx(Text, { style: styles.hint, children: boxes.length === 0
97
+ ? MODES.find((m) => m.mode === kind)?.hint
98
+ : boxes.length >= MAX_ANNOTATIONS
99
+ ? `${MAX_ANNOTATIONS} marks is the most. Undo one to add another.`
100
+ : `${boxes.length} mark${boxes.length === 1 ? '' : 's'}` }), boxes.length > 0 ? (_jsx(Pressable, { onPress: () => setBoxes(boxes.slice(0, -1)), accessibilityRole: "button", hitSlop: 8, children: _jsx(Text, { style: styles.toolbarText, children: "Undo" }) })) : null] })] }) }));
101
+ }
102
+ const STROKE_WIDTH = 4;
103
+ /** A freehand mark, drawn as short rotated bars so it needs no SVG library. */
104
+ function Stroke({ points, width, height, }) {
105
+ return (_jsx(_Fragment, { children: strokeSegments(points, { width, height }).map((segment, i) => (_jsx(View
106
+ // biome-ignore lint/suspicious/noArrayIndexKey: segments are positional
107
+ , { pointerEvents: "none", style: [
108
+ styles.segment,
109
+ {
110
+ left: segment.left,
111
+ top: segment.top - STROKE_WIDTH / 2,
112
+ // A little longer than the gap, so joints don't show.
113
+ width: segment.length + STROKE_WIDTH / 2,
114
+ transform: [{ rotate: `${segment.angle}rad` }],
115
+ },
116
+ ] }, i))) }));
117
+ }
118
+ const styles = StyleSheet.create({
119
+ segment: {
120
+ position: 'absolute',
121
+ height: STROKE_WIDTH,
122
+ borderRadius: STROKE_WIDTH / 2,
123
+ backgroundColor: '#e5484d',
124
+ },
125
+ screen: {
126
+ flex: 1,
127
+ alignItems: 'center',
128
+ justifyContent: 'center',
129
+ gap: 16,
130
+ backgroundColor: '#111114',
131
+ paddingVertical: 48,
132
+ },
133
+ toolbar: {
134
+ width: '100%',
135
+ paddingHorizontal: 16,
136
+ flexDirection: 'row',
137
+ alignItems: 'center',
138
+ justifyContent: 'space-between',
139
+ },
140
+ toolbarText: { color: '#ffffff', fontSize: 16 },
141
+ done: { fontWeight: '700', color: '#8ab4f8' },
142
+ modes: { flexDirection: 'row', borderRadius: 999, backgroundColor: '#2a2b31', padding: 3 },
143
+ mode: { paddingVertical: 6, paddingHorizontal: 14, borderRadius: 999 },
144
+ modeActive: { backgroundColor: '#ffffff' },
145
+ modeText: { color: '#d4d4d8', fontSize: 14, fontWeight: '600' },
146
+ modeTextActive: { color: '#111114' },
147
+ box: { position: 'absolute', borderRadius: 6, alignItems: 'center', justifyContent: 'center' },
148
+ highlightBox: { borderWidth: 3, borderColor: '#e5484d' },
149
+ blurBox: { backgroundColor: 'rgba(40, 40, 46, 0.85)' },
150
+ blurLabel: { color: '#ffffff', fontSize: 12, fontWeight: '600' },
151
+ footer: {
152
+ width: '100%',
153
+ paddingHorizontal: 16,
154
+ flexDirection: 'row',
155
+ alignItems: 'center',
156
+ justifyContent: 'space-between',
157
+ },
158
+ hint: { color: '#a1a1aa', fontSize: 14 },
159
+ });
160
+ //# sourceMappingURL=Annotator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Annotator.js","sourceRoot":"","sources":["../src/Annotator.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { type Bounds, type Point } from './core/bubble.js';
2
+ import type { Badge } from './core/feedback.js';
3
+ /** The screen, less conservative insets for the status bar and home indicator. */
4
+ export declare function screenBounds(width: number, height: number): Bounds;
5
+ /**
6
+ * A small, draggable Review button. Drag it anywhere; it snaps to the nearer
7
+ * side. It fades while idle so it stays out of the way of the app under test.
8
+ */
9
+ export declare function Bubble({ onPress, badge, busy, active, onRest, }: {
10
+ onPress: () => void;
11
+ badge: Badge | null;
12
+ busy: boolean;
13
+ /** The popover is open: show ✕, stay fully visible and hide the badge. */
14
+ active?: boolean;
15
+ /** Where the button settles, so the popover can open beside it. */
16
+ onRest?: (at: Point) => void;
17
+ }): import("react").JSX.Element;
18
+ export declare const COLORS: {
19
+ ink: string;
20
+ tape: string;
21
+ tapeInk: string;
22
+ /** Steps left: the iOS system blue, the SDK's one tint. */
23
+ count: string;
24
+ pass: string;
25
+ fail: string;
26
+ };
27
+ //# sourceMappingURL=Bubble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Bubble.d.ts","sourceRoot":"","sources":["../src/Bubble.tsx"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,MAAM,EAIX,KAAK,KAAK,EAEX,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAW/C,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQlE;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,KAAK,EACL,IAAI,EACJ,MAAc,EACd,MAAM,GACP,EAAE;IACD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,0EAA0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,KAAK,IAAI,CAAA;CAC7B,+BA0JA;AAuDD,eAAO,MAAM,MAAM;;;;IAIjB,2DAA2D;;;;CAI5D,CAAA"}