@np-dev/ui-ai-anotation 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 (52) hide show
  1. package/README.md +245 -0
  2. package/dist/cjs/index.cjs +1550 -0
  3. package/dist/cjs/index.cjs.map +7 -0
  4. package/dist/cjs/index.native.cjs +1004 -0
  5. package/dist/cjs/index.native.cjs.map +7 -0
  6. package/dist/cjs/index.web.cjs +83 -0
  7. package/dist/cjs/index.web.cjs.map +7 -0
  8. package/dist/esm/index.js +1524 -0
  9. package/dist/esm/index.js.map +7 -0
  10. package/dist/esm/index.native.js +1012 -0
  11. package/dist/esm/index.native.js.map +7 -0
  12. package/dist/esm/index.web.js +62 -0
  13. package/dist/esm/index.web.js.map +7 -0
  14. package/dist/types/components/AnnotationInput.d.ts +8 -0
  15. package/dist/types/components/AnnotationList.d.ts +1 -0
  16. package/dist/types/components/Draggable.d.ts +10 -0
  17. package/dist/types/components/Highlighter.d.ts +1 -0
  18. package/dist/types/components/Toolbar.d.ts +1 -0
  19. package/dist/types/index.d.ts +20 -0
  20. package/dist/types/index.web.d.ts +69 -0
  21. package/dist/types/store.d.ts +66 -0
  22. package/dist/types/utils/fiber.d.ts +51 -0
  23. package/dist/types/utils/platform.d.ts +8 -0
  24. package/dist/types/utils/screenshot.d.ts +28 -0
  25. package/package.json +115 -0
  26. package/src/components/AnnotationInput.tsx +269 -0
  27. package/src/components/AnnotationList.tsx +248 -0
  28. package/src/components/Draggable.tsx +73 -0
  29. package/src/components/Highlighter.tsx +497 -0
  30. package/src/components/Toolbar.tsx +213 -0
  31. package/src/components/native/AnnotationInput.tsx +227 -0
  32. package/src/components/native/AnnotationList.tsx +157 -0
  33. package/src/components/native/Draggable.tsx +65 -0
  34. package/src/components/native/Highlighter.tsx +239 -0
  35. package/src/components/native/Toolbar.tsx +192 -0
  36. package/src/components/native/index.ts +6 -0
  37. package/src/components/web/AnnotationInput.tsx +150 -0
  38. package/src/components/web/AnnotationList.tsx +117 -0
  39. package/src/components/web/Draggable.tsx +74 -0
  40. package/src/components/web/Highlighter.tsx +329 -0
  41. package/src/components/web/Toolbar.tsx +198 -0
  42. package/src/components/web/index.ts +6 -0
  43. package/src/extension.tsx +15 -0
  44. package/src/index.native.tsx +50 -0
  45. package/src/index.tsx +41 -0
  46. package/src/index.web.tsx +124 -0
  47. package/src/store.tsx +120 -0
  48. package/src/utils/fiber.native.ts +90 -0
  49. package/src/utils/fiber.ts +255 -0
  50. package/src/utils/platform.ts +33 -0
  51. package/src/utils/screenshot.native.ts +139 -0
  52. package/src/utils/screenshot.ts +162 -0
@@ -0,0 +1,139 @@
1
+ import { RefObject } from 'react';
2
+ import { View, Platform } from 'react-native';
3
+
4
+ export interface ScreenshotOptions {
5
+ /** Scale factor for higher resolution (default: 2) */
6
+ scale?: number;
7
+ /** Image format (default: 'png') */
8
+ format?: 'png' | 'jpg';
9
+ /** Quality for jpg format (0-1, default: 0.9) */
10
+ quality?: number;
11
+ /** Whether to copy to clipboard (default: true, requires expo-clipboard) */
12
+ copyToClipboard?: boolean;
13
+ /** Whether to save to camera roll (default: false, requires expo-media-library) */
14
+ saveToCameraRoll?: boolean;
15
+ }
16
+
17
+ export interface ScreenshotResult {
18
+ success: boolean;
19
+ uri?: string;
20
+ base64?: string;
21
+ error?: string;
22
+ }
23
+
24
+ /**
25
+ * Captures a screenshot of a React Native View using react-native-view-shot
26
+ *
27
+ * Requirements:
28
+ * - react-native-view-shot must be installed
29
+ * - Optionally: expo-clipboard for clipboard support
30
+ * - Optionally: expo-media-library for camera roll support
31
+ *
32
+ * @param viewRef - Reference to the View to capture
33
+ * @param options - Screenshot options
34
+ */
35
+ export async function captureScreenshot(
36
+ viewRef: RefObject<View>,
37
+ options: ScreenshotOptions = {}
38
+ ): Promise<ScreenshotResult> {
39
+ const {
40
+ scale = 2,
41
+ format = 'png',
42
+ quality = 0.9,
43
+ copyToClipboard = false,
44
+ saveToCameraRoll = false,
45
+ } = options;
46
+
47
+ try {
48
+ // Dynamic import to avoid issues if not installed
49
+ let captureRef: any;
50
+ try {
51
+ const viewShot = await import('react-native-view-shot');
52
+ captureRef = viewShot.captureRef;
53
+ } catch {
54
+ return {
55
+ success: false,
56
+ error: 'react-native-view-shot is not installed. Run: npm install react-native-view-shot',
57
+ };
58
+ }
59
+
60
+ if (!viewRef.current) {
61
+ return {
62
+ success: false,
63
+ error: 'View reference is null',
64
+ };
65
+ }
66
+
67
+ // Capture the view
68
+ const uri = await captureRef(viewRef, {
69
+ format,
70
+ quality,
71
+ result: 'tmpfile',
72
+ snapshotContentContainer: false,
73
+ });
74
+
75
+ // Get base64 if needed
76
+ let base64: string | undefined;
77
+ if (copyToClipboard) {
78
+ const base64Result = await captureRef(viewRef, {
79
+ format,
80
+ quality,
81
+ result: 'base64',
82
+ snapshotContentContainer: false,
83
+ });
84
+ base64 = base64Result;
85
+ }
86
+
87
+ // Copy to clipboard if requested
88
+ if (copyToClipboard && base64) {
89
+ try {
90
+ const Clipboard = await import('expo-clipboard');
91
+ // expo-clipboard doesn't support image directly,
92
+ // we'll copy the base64 string
93
+ await Clipboard.setStringAsync(base64);
94
+ } catch {
95
+ console.warn('expo-clipboard not available for clipboard support');
96
+ }
97
+ }
98
+
99
+ // Save to camera roll if requested
100
+ if (saveToCameraRoll) {
101
+ try {
102
+ const MediaLibrary = await import('expo-media-library');
103
+ const { status } = await MediaLibrary.requestPermissionsAsync();
104
+ if (status === 'granted') {
105
+ await MediaLibrary.saveToLibraryAsync(uri);
106
+ }
107
+ } catch {
108
+ console.warn('expo-media-library not available for saving to camera roll');
109
+ }
110
+ }
111
+
112
+ return {
113
+ success: true,
114
+ uri,
115
+ base64,
116
+ };
117
+ } catch (error) {
118
+ const message = error instanceof Error ? error.message : 'Unknown error';
119
+ console.error('Screenshot capture failed:', message);
120
+ return {
121
+ success: false,
122
+ error: message,
123
+ };
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Placeholder for web compatibility
129
+ * The actual web implementation is in screenshot.ts
130
+ */
131
+ export function captureScreenshotWeb(
132
+ _element: any,
133
+ _options: any
134
+ ): Promise<ScreenshotResult> {
135
+ return Promise.resolve({
136
+ success: false,
137
+ error: 'Use the web-specific captureScreenshot function',
138
+ });
139
+ }
@@ -0,0 +1,162 @@
1
+ import html2canvas from 'html2canvas';
2
+
3
+ export interface ScreenshotOptions {
4
+ /** Scale factor for higher resolution (default: 2) */
5
+ scale?: number;
6
+ /** Background color (default: transparent) */
7
+ backgroundColor?: string | null;
8
+ /** Whether to copy to clipboard (default: true) */
9
+ copyToClipboard?: boolean;
10
+ /** Whether to download as file (default: false) */
11
+ download?: boolean;
12
+ /** Filename when downloading (default: 'screenshot.png') */
13
+ filename?: string;
14
+ }
15
+
16
+ export interface ScreenshotResult {
17
+ success: boolean;
18
+ dataUrl?: string;
19
+ blob?: Blob;
20
+ error?: string;
21
+ }
22
+
23
+ // Tauri internals type for checking environment
24
+ declare global {
25
+ interface Window {
26
+ __TAURI_INTERNALS__?: unknown;
27
+ __TAURI_CLIPBOARD_WRITE_IMAGE__?: (bytes: Uint8Array) => Promise<void>;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Check if running in Tauri environment
33
+ */
34
+ function isTauriEnv(): boolean {
35
+ return typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window;
36
+ }
37
+
38
+ /**
39
+ * Copy image to clipboard using Tauri's clipboard plugin
40
+ * Uses a global function injected by the main app to avoid bundler issues
41
+ */
42
+ async function copyToClipboardTauri(blob: Blob): Promise<void> {
43
+ // Check if the main app has injected the clipboard function
44
+ if (typeof window.__TAURI_CLIPBOARD_WRITE_IMAGE__ === 'function') {
45
+ const arrayBuffer = await blob.arrayBuffer();
46
+ const bytes = new Uint8Array(arrayBuffer);
47
+ await window.__TAURI_CLIPBOARD_WRITE_IMAGE__(bytes);
48
+ return;
49
+ }
50
+
51
+ // Fallback: throw error so browser clipboard API is tried
52
+ throw new Error('Tauri clipboard function not available');
53
+ }
54
+
55
+ /**
56
+ * Copy image to clipboard using browser's Clipboard API
57
+ */
58
+ async function copyToClipboardBrowser(blob: Blob, dataUrl: string): Promise<void> {
59
+ try {
60
+ await navigator.clipboard.write([
61
+ new ClipboardItem({
62
+ 'image/png': blob,
63
+ }),
64
+ ]);
65
+ } catch (clipboardError) {
66
+ console.warn('Failed to copy to clipboard:', clipboardError);
67
+ // Fallback: try copying as text (data URL)
68
+ try {
69
+ await navigator.clipboard.writeText(dataUrl);
70
+ } catch {
71
+ // Ignore fallback failure
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Captures a screenshot of the given HTML element
78
+ */
79
+ export async function captureScreenshot(
80
+ element: HTMLElement,
81
+ options: ScreenshotOptions = {}
82
+ ): Promise<ScreenshotResult> {
83
+ const {
84
+ scale = 2,
85
+ backgroundColor = null,
86
+ copyToClipboard = true,
87
+ download = false,
88
+ filename = 'screenshot.png',
89
+ } = options;
90
+
91
+ try {
92
+ // Capture the element
93
+ const canvas = await html2canvas(element, {
94
+ scale,
95
+ backgroundColor,
96
+ logging: false,
97
+ useCORS: true,
98
+ allowTaint: true,
99
+ // Capture just the element
100
+ x: window.scrollX,
101
+ y: window.scrollY,
102
+ scrollX: -window.scrollX,
103
+ scrollY: -window.scrollY,
104
+ windowWidth: document.documentElement.scrollWidth,
105
+ windowHeight: document.documentElement.scrollHeight,
106
+ // Ignore annotation UI elements
107
+ ignoreElements: (el) => {
108
+ return el.hasAttribute('data-ai-annotation-ui');
109
+ },
110
+ });
111
+
112
+ // Convert to blob
113
+ const blob = await new Promise<Blob>((resolve, reject) => {
114
+ canvas.toBlob((b) => {
115
+ if (b) resolve(b);
116
+ else reject(new Error('Failed to create blob'));
117
+ }, 'image/png');
118
+ });
119
+
120
+ const dataUrl = canvas.toDataURL('image/png');
121
+
122
+ // Copy to clipboard if requested
123
+ if (copyToClipboard) {
124
+ if (isTauriEnv()) {
125
+ try {
126
+ // Use Tauri's clipboard plugin for proper image clipboard support
127
+ await copyToClipboardTauri(blob);
128
+ } catch (tauriErr) {
129
+ console.warn('Tauri clipboard failed, falling back to browser API:', tauriErr);
130
+ // Fallback to browser's Clipboard API
131
+ await copyToClipboardBrowser(blob, dataUrl);
132
+ }
133
+ } else {
134
+ // Fallback to browser's Clipboard API
135
+ await copyToClipboardBrowser(blob, dataUrl);
136
+ }
137
+ }
138
+
139
+ // Download if requested
140
+ if (download) {
141
+ const link = document.createElement('a');
142
+ link.href = dataUrl;
143
+ link.download = filename;
144
+ document.body.appendChild(link);
145
+ link.click();
146
+ document.body.removeChild(link);
147
+ }
148
+
149
+ return {
150
+ success: true,
151
+ dataUrl,
152
+ blob,
153
+ };
154
+ } catch (error) {
155
+ const message = error instanceof Error ? error.message : 'Unknown error';
156
+ console.error('Screenshot capture failed:', message);
157
+ return {
158
+ success: false,
159
+ error: message,
160
+ };
161
+ }
162
+ }