import { useState } from 'react'; import { Alert, Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native'; import * as Clipboard from 'expo-clipboard'; import type { MobileWorkspace } from '../../api/workspaces'; import { useNotesStore, type NotesState } from '../../store/notes-store'; import { useWorkspaceStore, type WorkspaceState } from '../../store/workspace-store'; import { extractFromUrl, copilotTransform } from '../../api/note-prompts'; import { colors } from '../../theme'; type CaptureMode = 'text' | 'photo' | 'voice' | 'url' | 'scan' | 'paste'; const CAPTURE_MODES: { mode: CaptureMode; label: string; icon: string; description: string }[] = [ { mode: 'text', label: 'Text', icon: '✏️', description: 'Type a quick note' }, { mode: 'photo', label: 'Photo', icon: '📷', description: 'Capture from camera' }, { mode: 'voice', label: 'Voice', icon: '🎙️', description: 'Record & transcribe' }, { mode: 'url', label: 'URL', icon: '🔗', description: 'Extract from web page' }, { mode: 'scan', label: 'Scan', icon: '📄', description: 'Scan multi-page doc' }, { mode: 'paste', label: 'Paste', icon: '📋', description: 'Paste & clean up' }, ]; export default function CaptureScreen() { const [mode, setMode] = useState('text'); const [title, setTitle] = useState(''); const [body, setBody] = useState(''); const [urlInput, setUrlInput] = useState(''); const [saved, setSaved] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const saveDraft = useNotesStore((state: NotesState) => state.saveDraft); const workspaces = useWorkspaceStore((state: WorkspaceState) => state.workspaces); const activeWorkspaceId = useWorkspaceStore((state: WorkspaceState) => state.activeWorkspaceId); const setActiveWorkspace = useWorkspaceStore((state: WorkspaceState) => state.setActiveWorkspace); const activeWorkspaceName = workspaces.find((workspace: MobileWorkspace) => workspace.id === activeWorkspaceId)?.name ?? 'Drafts'; const resetForm = () => { setTitle(''); setBody(''); setUrlInput(''); setSaved(false); setError(null); }; const handleSave = async () => { if (!activeWorkspaceId) return; setBusy(true); try { const didSave = await saveDraft(activeWorkspaceId, title, body); setSaved(didSave); if (didSave) resetForm(); } catch (e) { setError(e instanceof Error ? e.message : 'Save failed'); } finally { setBusy(false); } }; const handleUrlExtract = async () => { if (!activeWorkspaceId || !urlInput.trim()) return; setBusy(true); setError(null); try { const result = await extractFromUrl(urlInput.trim(), activeWorkspaceId); setTitle(result.title); setBody(result.content); } catch (e) { setError(e instanceof Error ? e.message : 'URL extraction failed'); } finally { setBusy(false); } }; const handlePasteAndClean = async () => { if (!activeWorkspaceId) return; setBusy(true); setError(null); try { const clipText = await Clipboard.getStringAsync(); if (!clipText?.trim()) { setError('Clipboard is empty'); setBusy(false); return; } // Check if it looks like a URL if (/^https?:\/\//.test(clipText.trim())) { setUrlInput(clipText.trim()); setMode('url'); setBusy(false); return; } setBody(clipText); setTitle('Pasted note'); } catch (e) { setError(e instanceof Error ? e.message : 'Paste failed'); } finally { setBusy(false); } }; const handleVoiceCapture = () => { Alert.alert('Voice Capture', 'Voice recording requires expo-av. Install expo-av and grant microphone permission to enable this feature.', [{ text: 'OK' }]); }; const handlePhotoCapture = () => { Alert.alert('Photo Capture', 'Camera capture requires expo-image-picker. Install the package and grant camera permission to enable this feature.', [{ text: 'OK' }]); }; const handleScanCapture = () => { Alert.alert('Document Scan', 'Multi-page scanning requires expo-image-picker with continuous mode. Install the package to enable this feature.', [{ text: 'OK' }]); }; return ( Quick capture {/* Workspace selector */} {workspaces.map((workspace: MobileWorkspace) => { const isActive = workspace.id === activeWorkspaceId; return ( setActiveWorkspace(workspace.id)} style={[styles.workspaceChip, isActive ? styles.workspaceChipActive : null]} > {workspace.name} ); })} {/* Capture mode selector — 6 modes */} {CAPTURE_MODES.map(({ mode: m, label, icon, description }) => { const isActive = m === mode; return ( { setMode(m); resetForm(); }} style={[styles.modeCard, isActive ? styles.modeCardActive : null]} > {icon} {label} ); })} {/* Mode-specific content */} {mode === 'text' && ( <> )} {mode === 'url' && ( <> {busy ? 'Extracting...' : 'Extract & Summarize'} {body ? ( <> ) : null} )} {mode === 'paste' && ( <> {busy ? 'Reading clipboard...' : 'Paste & Clean'} {body ? ( <> ) : null} )} {mode === 'voice' && ( Voice-to-Note Record audio and transcribe to text. Requires expo-av for audio recording. Start Recording )} {mode === 'photo' && ( Screenshot-to-Note Take a photo or select from gallery. Uses vision AI for OCR and text extraction. Open Camera )} {mode === 'scan' && ( Document Scan Photograph multiple pages of a document. Each page is processed with vision AI and combined into a single note. Start Scanning )} {/* Error display */} {error ? {error} : null} {/* Save button (shown when we have content to save) */} {(mode === 'text' || body) && ( {busy ? 'Saving...' : activeWorkspaceId ? 'Save draft' : 'Select workspace'} )} {saved ? Draft saved to the product backend. : null} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.bgCanvas, }, contentContainer: { padding: 20, gap: 14, }, title: { color: colors.textPrimary, fontSize: 28, fontWeight: '700', }, input: { borderWidth: 1, borderColor: colors.borderDefault, borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, color: colors.textPrimary, backgroundColor: colors.surfaceCard, }, bodyInput: { minHeight: 160, }, button: { backgroundColor: colors.accentPrimary, borderRadius: 12, paddingVertical: 14, alignItems: 'center', }, buttonText: { color: colors.textPrimary, fontWeight: '700', }, buttonDisabled: { opacity: 0.6, }, saved: { color: colors.success, fontSize: 14, fontWeight: '600', }, error: { color: colors.danger, fontSize: 14, fontWeight: '500', }, card: { backgroundColor: colors.surfaceCard, borderRadius: 14, borderWidth: 1, borderColor: colors.borderDefault, padding: 14, gap: 10, }, workspaceRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, }, workspaceChip: { borderRadius: 999, borderWidth: 1, borderColor: colors.borderDefault, paddingHorizontal: 12, paddingVertical: 8, backgroundColor: colors.surfaceCard, }, workspaceChipActive: { backgroundColor: colors.accentPrimary, borderColor: colors.accentPrimary, }, workspaceChipText: { color: colors.textSecondary, fontSize: 13, fontWeight: '600', }, workspaceChipTextActive: { color: colors.textPrimary, }, modeGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, }, modeCard: { width: '30%', backgroundColor: colors.surfaceCard, borderRadius: 14, borderWidth: 1, borderColor: colors.borderDefault, padding: 12, alignItems: 'center', gap: 4, }, modeCardActive: { backgroundColor: colors.accentPrimary, borderColor: colors.accentPrimary, }, modeIcon: { fontSize: 24, }, modeLabel: { color: colors.textSecondary, fontSize: 12, fontWeight: '700', }, modeLabelActive: { color: colors.textPrimary, }, cardTitle: { color: colors.textPrimary, fontSize: 16, fontWeight: '700', }, cardBody: { color: colors.textSecondary, fontSize: 14, lineHeight: 20, }, });