diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..f5525a1 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,47 @@ +name: EAS Preview Update + +on: + push: + branches: + - claude/article-ideas-react-native-puYVn + - main + - master + +jobs: + update: + name: Publish EAS Update + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Setup Expo + uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + + - name: Initialize EAS project (creates if not exists) + run: eas init --non-interactive --force + env: + EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + + - name: Publish preview update + run: | + eas update \ + --channel preview \ + --message "$(git log -1 --pretty=%s)" \ + --platform ios \ + --non-interactive + env: + EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + EXPO_OFFLINE: "1" diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..521a9f7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/app.json b/app.json index 6e564ce..c269913 100644 --- a/app.json +++ b/app.json @@ -7,7 +7,7 @@ "orientation": "default", "icon": "./assets/icon.png", "userInterfaceStyle": "dark", - "newArchEnabled": true, + "newArchEnabled": false, "splash": { "image": "./assets/splash-icon.png", "resizeMode": "contain", @@ -28,6 +28,12 @@ "bundler": "metro", "favicon": "./assets/favicon.png" }, + "updates": { + "url": "https://u.expo.dev/6a1a260a-d832-47c9-bcf7-52cbef213b47" + }, + "runtimeVersion": { + "policy": "sdkVersion" + }, "plugins": [ "expo-router" ] diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index d6d36b1..03c2861 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState, useCallback } from 'react'; import { StyleSheet, Text, @@ -28,6 +28,22 @@ const STATUS_FILTERS: { label: string; value: IdeaStatus | 'all' }[] = [ { label: 'Published', value: 'published' }, ]; +function StatBadge({ value, label, accent }: { value: number; label: string; accent?: boolean }) { + return ( + + {value} + {label} + + ); +} + +const statStyles = StyleSheet.create({ + badge: { alignItems: 'center', flex: 1 }, + value: { color: Colors.textPrimary, fontSize: 22, fontWeight: '800', letterSpacing: -0.5 }, + valueAccent: { color: Colors.accentBright }, + label: { color: Colors.textMuted, fontSize: 11, marginTop: 1, fontWeight: '500' }, +}); + export default function HomeScreen() { const [ideas, setIdeas] = useState([]); const [search, setSearch] = useState(''); @@ -56,22 +72,18 @@ export default function HomeScreen() { const handleDelete = (idea: ArticleIdea) => { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning); - Alert.alert( - 'Delete Idea', - `Delete "${idea.title}"? This cannot be undone.`, - [ - { text: 'Cancel', style: 'cancel' }, - { - text: 'Delete', - style: 'destructive', - onPress: async () => { - await deleteIdea(idea.id); - await load(); - Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); - }, + Alert.alert('Delete Idea', `Delete "${idea.title}"? This cannot be undone.`, [ + { text: 'Cancel', style: 'cancel' }, + { + text: 'Delete', + style: 'destructive', + onPress: async () => { + await deleteIdea(idea.id); + await load(); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); }, - ] - ); + }, + ]); }; const filtered = ideas.filter((idea) => { @@ -84,6 +96,15 @@ export default function HomeScreen() { return matchesSearch && matchesFilter; }); + // Stats for hero + const aiCount = ideas.filter( + (i) => + i.aiContent?.outline || + i.aiContent?.intro || + i.aiContent?.improvedTitles?.length + ).length; + const publishedCount = ideas.filter((i) => i.status === 'published').length; + const renderItem = ({ item, index }: { item: ArticleIdea; index: number }) => ( ); - return ( - - {/* Header */} - - - Nova - - {ideas.length} idea{ideas.length !== 1 ? 's' : ''} - + const ListHeader = ( + <> + {/* ── Hero ── */} + + + + + Nova + + Article ideas, amplified by AI - - {/* Search */} + + + + + + + + + + {/* ── Search ── */} 🔍 @@ -126,7 +165,7 @@ export default function HomeScreen() { - {/* Filters */} + {/* ── Status filters ── */} + + ); - {/* Ideas list */} + return ( + item.id} numColumns={numColumns} - key={numColumns} // re-render when numColumns changes - contentContainerStyle={[ - styles.list, - filtered.length === 0 && styles.listEmpty, - ]} + key={numColumns} + ListHeaderComponent={ListHeader} + contentContainerStyle={[styles.list, filtered.length === 0 && styles.listEmpty]} renderItem={renderItem} refreshControl={ - + } ListEmptyComponent={} showsVerticalScrollIndicator={false} @@ -200,25 +236,52 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: Colors.bg, }, - header: { - flexDirection: 'row', - alignItems: 'flex-end', - justifyContent: 'space-between', + // Hero + hero: { paddingHorizontal: 20, - paddingTop: 8, - paddingBottom: 16, + paddingTop: 16, + paddingBottom: 24, + marginBottom: 4, + }, + heroTop: { + marginBottom: 20, + }, + heroTitleRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + marginBottom: 4, }, - appName: { + heroStar: { + color: Colors.accent, + fontSize: 22, + }, + heroName: { color: Colors.textPrimary, - fontSize: 34, - fontWeight: '800', - letterSpacing: -0.5, + fontSize: 40, + fontWeight: '900', + letterSpacing: -1, + lineHeight: 44, + }, + heroTagline: { + color: Colors.textSecondary, + fontSize: 14, + marginLeft: 30, }, - appSub: { - color: Colors.textMuted, - fontSize: 13, - marginTop: 2, + statsRow: { + flexDirection: 'row', + backgroundColor: 'rgba(255,255,255,0.04)', + borderRadius: 16, + borderWidth: 1, + borderColor: Colors.border, + paddingVertical: 14, }, + statDivider: { + width: StyleSheet.hairlineWidth, + backgroundColor: Colors.border, + marginVertical: 4, + }, + // Search searchRow: { paddingHorizontal: 16, marginBottom: 12, @@ -234,27 +297,17 @@ const styles = StyleSheet.create({ borderColor: Colors.border, gap: 8, }, - searchIcon: { - fontSize: 16, - }, + searchIcon: { fontSize: 16 }, searchInput: { flex: 1, color: Colors.textPrimary, fontSize: 15, padding: 0, }, - clearBtn: { - color: Colors.textMuted, - fontSize: 14, - }, - filterBar: { - flexGrow: 0, - marginBottom: 12, - }, - filterList: { - paddingHorizontal: 16, - gap: 8, - }, + clearBtn: { color: Colors.textMuted, fontSize: 14 }, + // Filters + filterBar: { flexGrow: 0, marginBottom: 12 }, + filterList: { paddingHorizontal: 16, gap: 8 }, filterChip: { paddingHorizontal: 14, paddingVertical: 7, @@ -267,36 +320,22 @@ const styles = StyleSheet.create({ backgroundColor: Colors.accentSoft, borderColor: Colors.accent, }, - filterLabel: { - color: Colors.textSecondary, - fontSize: 13, - fontWeight: '500', - }, - filterLabelActive: { - color: Colors.accentBright, - fontWeight: '600', - }, - list: { - paddingHorizontal: 16, - paddingBottom: 100, - }, - listEmpty: { - flex: 1, - }, - cardWrapper: { - flex: 1, - }, - cardWrapperLeft: { - marginRight: 6, - }, + filterLabel: { color: Colors.textSecondary, fontSize: 13, fontWeight: '500' }, + filterLabelActive: { color: Colors.accentBright, fontWeight: '600' }, + // List + list: { paddingHorizontal: 16, paddingBottom: 100 }, + listEmpty: { flex: 1 }, + cardWrapper: { flex: 1 }, + cardWrapperLeft: { marginRight: 6 }, + // FAB fab: { position: 'absolute', right: 20, bottom: 30, shadowColor: Colors.accent, shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.4, - shadowRadius: 12, + shadowOpacity: 0.45, + shadowRadius: 14, elevation: 8, }, fabGradient: { diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index ef9bfba..ef6a477 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -7,6 +7,7 @@ import { Pressable, ScrollView, Alert, + ActivityIndicator, Linking, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -14,6 +15,8 @@ import * as Haptics from 'expo-haptics'; import { Colors } from '../../src/constants/colors'; import { AppSettings } from '../../src/lib/types'; import { getSettings, saveSettings } from '../../src/lib/storage'; +import { generateOutline, AIDebugInfo } from '../../src/lib/ai'; +import AIDebugModal from '../../src/components/AIDebugModal'; interface SettingRowProps { label: string; @@ -95,12 +98,19 @@ const rowStyles = StyleSheet.create({ export default function SettingsScreen() { const [settings, setSettings] = useState({ - anthropicApiKey: '', + azureEndpoint: '', + azureApiKey: '', + azureDeployment: 'claude-opus-4-6', + imageEndpoint: '', + imageApiKey: '', + imageDeployment: 'dall-e-3', wordpressUrl: '', wordpressUsername: '', wordpressAppPassword: '', }); const [saved, setSaved] = useState(false); + const [testing, setTesting] = useState(false); + const [testModal, setTestModal] = useState<{ error: string; debugInfo?: AIDebugInfo } | null>(null); useEffect(() => { getSettings().then(setSettings); @@ -118,8 +128,35 @@ export default function SettingsScreen() { setTimeout(() => setSaved(false), 2000); }; + const handleTestConnection = async () => { + if (!settings.azureEndpoint || !settings.azureApiKey) { + Alert.alert('Missing Config', 'Enter an endpoint URL and API key first.'); + return; + } + setTesting(true); + const config = { + endpoint: settings.azureEndpoint, + apiKey: settings.azureApiKey, + deployment: settings.azureDeployment || 'claude-opus-4-6', + }; + const res = await generateOutline(config, 'Connection test', ''); + setTesting(false); + if (res.error) { + setTestModal({ error: res.error, debugInfo: res.debugInfo }); + } else { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + Alert.alert('Connection OK', 'Successfully connected to Azure AI Foundry.'); + } + }; + return ( + setTestModal(null)} + /> - AI — Anthropic + AI — Azure AI Foundry + + + Nova uses Claude via Azure AI Foundry. Your credentials are stored locally on your device. + + + + + + + {testing ? ( + + ) : ( + Test Connection + )} + + Linking.openURL('https://ai.azure.com')} + > + Open Azure AI Foundry → + + + + + {/* Image Generation Section */} + + + 🎨 + Image Generation — DALL·E 3 - Nova uses Claude to help you develop article ideas. Your API key is stored locally on your device. + Generates AI hero images for your articles using Azure OpenAI's DALL·E 3. Requires a separate Azure OpenAI resource (different from the Claude endpoint above). + + Linking.openURL('https://console.anthropic.com')} + onPress={() => Linking.openURL('https://oai.azure.com')} > - Get an API key → + Open Azure OpenAI Studio → @@ -264,6 +367,30 @@ const styles = StyleSheet.create({ lineHeight: 18, marginBottom: 16, }, + testRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginTop: 4, + }, + testBtn: { + backgroundColor: Colors.accentSoft, + borderRadius: 10, + borderWidth: 1, + borderColor: Colors.accent, + paddingHorizontal: 14, + paddingVertical: 9, + minWidth: 130, + alignItems: 'center', + }, + testBtnDisabled: { + opacity: 0.5, + }, + testBtnText: { + color: Colors.accentBright, + fontSize: 13, + fontWeight: '600', + }, linkBtn: { alignSelf: 'flex-start', marginTop: -4, diff --git a/app/idea/[id].tsx b/app/idea/[id].tsx index 04fe593..6fe8899 100644 --- a/app/idea/[id].tsx +++ b/app/idea/[id].tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; import { StyleSheet, Text, @@ -15,15 +15,188 @@ import { useLocalSearchParams, router } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { LinearGradient } from 'expo-linear-gradient'; import * as Haptics from 'expo-haptics'; +import { + RichText, + Toolbar, + useEditorBridge, + useBridgeState, + TenTapStartKit, +} from '@10play/tentap-editor'; import { Colors, StatusColors, StatusLabels } from '../../src/constants/colors'; import { ArticleIdea, IdeaStatus } from '../../src/lib/types'; import { getIdeas, saveIdea, deleteIdea } from '../../src/lib/storage'; import { getSettings } from '../../src/lib/storage'; import TagPill from '../../src/components/TagPill'; import AIPanel from '../../src/components/AIPanel'; +import HeroImagePanel from '../../src/components/HeroImagePanel'; +import AICollaborator from '../../src/components/AICollaborator'; + +// ─── Editor CSS ────────────────────────────────────────────────────────────── +const EDITOR_CSS = ` + * { background-color: #09090F !important; margin: 0; } + body { background-color: #09090F !important; } + .ProseMirror { + padding: 16px; + min-height: 280px; + font-size: 16px; + line-height: 1.75; + font-family: -apple-system, 'Helvetica Neue', 'Segoe UI', sans-serif; + color: #9090B8; + background-color: #09090F !important; + outline: none; + } + .ProseMirror p { color: #9090B8; margin-bottom: 10px; } + .ProseMirror h1 { + color: #F0EEFF; font-size: 26px; font-weight: 700; + margin: 20px 0 8px; line-height: 1.3; + } + .ProseMirror h2 { + color: #F0EEFF; font-size: 21px; font-weight: 700; + margin: 18px 0 6px; line-height: 1.35; + } + .ProseMirror h3 { + color: #F0EEFF; font-size: 17px; font-weight: 600; + margin: 14px 0 4px; line-height: 1.4; + } + .ProseMirror strong { color: #F0EEFF; font-weight: 700; } + .ProseMirror em { font-style: italic; } + .ProseMirror u { text-decoration: underline; } + .ProseMirror s { text-decoration: line-through; color: #5A5A78; } + .ProseMirror code { + background: #111119; color: #9B8DFF; + border-radius: 4px; padding: 2px 5px; + font-size: 14px; font-family: 'Menlo', 'Monaco', monospace; + } + .ProseMirror pre { + background: #111119; border-radius: 10px; + padding: 14px; margin: 12px 0; overflow: auto; + } + .ProseMirror pre code { background: transparent; padding: 0; font-size: 13px; color: #9B8DFF; } + .ProseMirror blockquote { + border-left: 3px solid #7C6AF7; + padding-left: 14px; margin: 12px 0 12px 0; + color: #9090B8; font-style: italic; + } + .ProseMirror ul, .ProseMirror ol { + color: #9090B8; padding-left: 22px; margin-bottom: 10px; + } + .ProseMirror li { margin-bottom: 4px; } + .ProseMirror hr { + border: none; border-top: 1px solid #252538; margin: 20px 0; + } + .ProseMirror a { color: #7C6AF7; text-decoration: underline; } + .ProseMirror p.is-editor-empty:first-child::before { + content: attr(data-placeholder); + float: left; color: #5A5A78; pointer-events: none; height: 0; + } + /* Task list */ + .ProseMirror ul[data-type="taskList"] { padding-left: 4px; } + .ProseMirror ul[data-type="taskList"] li { + display: flex; align-items: flex-start; gap: 8px; + } + .ProseMirror ul[data-type="taskList"] li > label { + margin-top: 2px; flex-shrink: 0; + } + .ProseMirror ul[data-type="taskList"] input[type="checkbox"] { + accent-color: #7C6AF7; width: 15px; height: 15px; + } +`; + +// ─── Toolbar theme ──────────────────────────────────────────────────────────── +const TOOLBAR_THEME = { + toolbar: { + toolbarBody: { + backgroundColor: '#1A1A2A', + borderTopColor: '#252538', + borderTopWidth: 1, + borderBottomWidth: 0, + minWidth: '100%' as const, + height: 44, + flex: 1, + }, + toolbarButton: { + backgroundColor: '#1A1A2A', + paddingHorizontal: 8, + alignItems: 'center' as const, + justifyContent: 'center' as const, + }, + icon: { + tintColor: '#9090B8', + height: 24, + width: 24, + }, + iconActive: { + tintColor: '#9B8DFF', + }, + iconDisabled: { + tintColor: '#5A5A78', + }, + iconWrapper: { + borderRadius: 6, + backgroundColor: '#1A1A2A', + }, + iconWrapperActive: { + backgroundColor: 'rgba(124, 106, 247, 0.15)', + borderRadius: 6, + }, + iconWrapperDisabled: { + opacity: 0.35, + }, + hidden: { + display: 'none' as const, + }, + keyboardAvoidingView: { + position: 'absolute' as const, + width: '100%' as const, + bottom: 0, + }, + }, + webview: { + backgroundColor: '#09090F', + }, +}; + +// ─── Helpers ────────────────────────────────────────────────────────────────── +/** Very simple markdown → HTML for inserting AI-generated text into the editor */ +function mdToHtml(md: string): string { + const blocks = md.split(/\n{2,}/); + return blocks + .map((block) => { + const t = block.trim(); + if (!t) return ''; + if (t.startsWith('### ')) return `

${t.slice(4)}

`; + if (t.startsWith('## ')) return `

${t.slice(3)}

`; + if (t.startsWith('# ')) return `

${t.slice(2)}

`; + const lines = t.split('\n'); + if (lines.every((l) => /^[-*]\s/.test(l.trim()))) { + const items = lines.map((l) => `
  • ${l.replace(/^[-*]\s/, '')}
  • `).join(''); + return `
      ${items}
    `; + } + if (lines.every((l) => /^\d+\.\s/.test(l.trim()))) { + const items = lines.map((l) => `
  • ${l.replace(/^\d+\.\s/, '')}
  • `).join(''); + return `
      ${items}
    `; + } + // Inline markdown within a paragraph + const html = t + .replace(/\*\*(.+?)\*\*/g, '$1') + .replace(/\*(.+?)\*/g, '$1') + .replace(/`(.+?)`/g, '$1') + .replace(/\n/g, '
    '); + return `

    ${html}

    `; + }) + .filter(Boolean) + .join(''); +} + +function wordCount(html: string): number { + if (!html || html === '

    ') return 0; + const text = html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); + return text ? text.split(' ').length : 0; +} const STATUSES: IdeaStatus[] = ['draft', 'outlined', 'in-progress', 'published']; +// ─── Component ──────────────────────────────────────────────────────────────── export default function IdeaDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const [idea, setIdea] = useState(null); @@ -32,13 +205,39 @@ export default function IdeaDetailScreen() { const [tags, setTags] = useState([]); const [status, setStatus] = useState('draft'); const [tagInput, setTagInput] = useState(''); + const [showDetails, setShowDetails] = useState(false); const [showAI, setShowAI] = useState(false); + const [showCollaborator, setShowCollaborator] = useState(false); const [showStatusPicker, setShowStatusPicker] = useState(false); const [isDirty, setIsDirty] = useState(false); - const [apiKey, setApiKey] = useState(''); + const [azureConfig, setAzureConfig] = useState({ endpoint: '', apiKey: '', deployment: '' }); + const [imageConfig, setImageConfig] = useState({ endpoint: '', apiKey: '', deployment: 'dall-e-3' }); + const [heroImageDataUri, setHeroImageDataUri] = useState(undefined); + const [htmlContent, setHtmlContent] = useState(''); + const cssInjected = useRef(false); const { width } = useWindowDimensions(); const isTablet = width >= 768; + const editor = useEditorBridge({ + autofocus: false, + dynamicHeight: true, + theme: TOOLBAR_THEME, + onChange: () => { + setIsDirty(true); + }, + }); + + const editorState = useBridgeState(editor); + + // Inject dark CSS + placeholder once editor is ready + useEffect(() => { + if (editorState.isReady && !cssInjected.current) { + cssInjected.current = true; + editor.injectCSS(EDITOR_CSS, 'nova-theme'); + editor.setPlaceholder('Start writing your article…'); + } + }, [editorState.isReady, editor]); + const load = useCallback(async () => { const all = await getIdeas(); const found = all.find((i) => i.id === id); @@ -48,14 +247,32 @@ export default function IdeaDetailScreen() { setNotes(found.notes); setTags(found.tags); setStatus(found.status); + setHeroImageDataUri(found.heroImageDataUri); + const initial = found.content || ''; + setHtmlContent(initial); + if (initial) { + editor.setContent(initial); + } } const settings = await getSettings(); - setApiKey(settings.anthropicApiKey); - }, [id]); + setAzureConfig({ + endpoint: settings.azureEndpoint, + apiKey: settings.azureApiKey, + deployment: settings.azureDeployment, + }); + setImageConfig({ + endpoint: settings.imageEndpoint || '', + apiKey: settings.imageApiKey || '', + deployment: settings.imageDeployment || 'dall-e-3', + }); + }, [id]); // eslint-disable-line react-hooks/exhaustive-deps + // Load once editor is ready so setContent works useEffect(() => { - load(); - }, [load]); + if (editorState.isReady) { + load(); + } + }, [editorState.isReady, load]); const markDirty = () => setIsDirty(true); @@ -65,16 +282,22 @@ export default function IdeaDetailScreen() { Alert.alert('Title required', 'Please add a title.'); return; } + const content = await editor.getHTML(); + const cleanContent = content === '

    ' ? '' : content; + const updated: ArticleIdea = { ...idea, title: title.trim(), notes: notes.trim(), + content: cleanContent, + heroImageDataUri, tags, status, updatedAt: new Date().toISOString(), }; await saveIdea(updated); setIdea(updated); + setHtmlContent(cleanContent); setIsDirty(false); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); }; @@ -113,123 +336,197 @@ export default function IdeaDetailScreen() { markDirty(); }; + const handleInsertContent = async (markdownText: string) => { + const current = await editor.getHTML(); + const isEmpty = !current || current === '

    '; + const newHtml = mdToHtml(markdownText); + editor.setContent(isEmpty ? newHtml : current + newHtml); + markDirty(); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + setTimeout(() => editor.focus('end'), 100); + }; + + const handleCollaboratorApply = (newHtml: string) => { + editor.setContent(newHtml); + markDirty(); + }; + + const handleHeroImageGenerated = (dataUri: string) => { + setHeroImageDataUri(dataUri); + markDirty(); + }; + if (!idea) { return ( - - Loading… + + Loading… ); } const statusColor = StatusColors[status]; + const words = wordCount(htmlContent); + const readMins = Math.max(1, Math.round(words / 200)); - const mainContent = ( - - {/* Title */} - { setTitle(v); markDirty(); }} - multiline - placeholder="Article title" - placeholderTextColor={Colors.textMuted} + const editorSection = ( + <> + {/* Hero image */} + - {/* Status picker */} + {/* Article section header */} + + ARTICLE + {words > 0 && ( + + + {words.toLocaleString()} words · {readMins} min read + + + )} + + + {/* The editor card */} + + + + + ); + + const detailsSection = ( + <> setShowStatusPicker((v) => !v)} + style={[styles.sectionToggle, showDetails && styles.sectionToggleOpen]} + onPress={() => { + setShowDetails((v) => !v); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + }} > - - - {StatusLabels[status]} + 📋 + + Details - {showStatusPicker ? '▲' : '▼'} + + + + {StatusLabels[status]} + + + {showDetails ? '▲' : '▼'} - {showStatusPicker && ( - - {STATUSES.map((s) => ( + {showDetails && ( + + {/* Status */} + + STATUS { - setStatus(s); - setShowStatusPicker(false); - markDirty(); - }} + style={[styles.statusRow, { borderColor: statusColor }]} + onPress={() => setShowStatusPicker((v) => !v)} > - - - {StatusLabels[s]} + + + {StatusLabels[status]} + {showStatusPicker ? '▲' : '▼'} - ))} - - )} + {showStatusPicker && ( + + {STATUSES.map((s) => ( + { + setStatus(s); + setShowStatusPicker(false); + markDirty(); + }} + > + + + {StatusLabels[s]} + + + ))} + + )} + - {/* Notes */} - { setNotes(v); markDirty(); }} - multiline - placeholder="Notes, context, rough outline…" - placeholderTextColor={Colors.textMuted} - textAlignVertical="top" - /> + {/* Notes / Brief */} + + BRIEF / NOTES + { setNotes(v); markDirty(); }} + multiline + placeholder="Context, target audience, key points to cover…" + placeholderTextColor={Colors.textMuted} + textAlignVertical="top" + /> + - {/* Tags */} - - Tags - - { if (tagInput.trim()) addTag(tagInput); }} - returnKeyType="done" - autoCapitalize="none" - /> - - {tags.length > 0 && ( - - {tags.map((tag) => ( - { - setTags((prev) => prev.filter((t) => t !== tag)); - markDirty(); - }} + {/* Tags */} + + TAGS + + { if (tagInput.trim()) addTag(tagInput); }} + returnKeyType="done" + autoCapitalize="none" /> - ))} + + {tags.length > 0 && ( + + {tags.map((tag) => ( + { + setTags((prev) => prev.filter((t) => t !== tag)); + markDirty(); + }} + /> + ))} + + )} - )} - + + )} + + ); - {/* AI Panel toggle */} + const aiSection = ( + <> + {/* AI Tools (outline, intro, tags, titles) */} { setShowAI((v) => !v); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }} > - - - {showAI ? 'Hide AI Tools' : 'AI Tools'} + + + AI Tools - {showAI ? '▲' : '▼'} + {showAI ? '▲' : '▼'} {showAI && ( @@ -237,11 +534,65 @@ export default function IdeaDetailScreen() { + + )} + + {/* AI Collaborator (revise + improve article) */} + { + setShowCollaborator((v) => !v); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + }} + > + 🤝 + + AI Collaborator + + {showCollaborator ? '▲' : '▼'} + + + {showCollaborator && ( + + editor.getHTML()} + onApplyChanges={handleCollaboratorApply} /> )} + + ); + + const mainContent = ( + + {/* Document title */} + { setTitle(v); markDirty(); }} + multiline + placeholder="Article title" + placeholderTextColor={Colors.textMuted} + /> + + {editorSection} + + + {detailsSection} + {aiSection} + {/* Danger zone */} @@ -254,15 +605,12 @@ export default function IdeaDetailScreen() { return ( - - {/* Nav bar */} - - router.back()}> - ‹ Back - + {/* Nav bar */} + + router.back()}> + ‹ Back + + {isDirty && ( )} + + {isTablet ? ( - // Tablet: side-by-side layout {mainContent} @@ -286,24 +638,43 @@ export default function IdeaDetailScreen() { + AI Collaborator + editor.getHTML()} + onApplyChanges={handleCollaboratorApply} /> ) : ( mainContent )} + ); } +// ─── Styles ─────────────────────────────────────────────────────────────────── const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: Colors.bg, }, + loadingContainer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + loadingText: { + color: Colors.textMuted, + }, navBar: { flexDirection: 'row', alignItems: 'center', @@ -320,6 +691,11 @@ const styles = StyleSheet.create({ color: Colors.accent, fontSize: 17, }, + navRight: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + }, saveBtn: { borderRadius: 20, overflow: 'hidden', @@ -333,32 +709,150 @@ const styles = StyleSheet.create({ fontSize: 15, fontWeight: '600', }, + keyboardView: { + flex: 1, + }, scroll: { flex: 1, }, scrollContent: { - padding: 20, - paddingBottom: 60, + paddingBottom: 80, }, + // Title titleInput: { color: Colors.textPrimary, - fontSize: 26, + fontSize: 28, fontWeight: '700', - lineHeight: 34, - marginBottom: 16, + lineHeight: 36, + paddingHorizontal: 20, + paddingTop: 20, + paddingBottom: 16, padding: 0, }, + // Editor section + sectionHeader: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 20, + paddingBottom: 10, + gap: 10, + }, + sectionHeaderText: { + color: Colors.textMuted, + fontSize: 11, + fontWeight: '700', + letterSpacing: 1.2, + }, + wordCountBadge: { + backgroundColor: Colors.surface, + borderRadius: 10, + paddingHorizontal: 8, + paddingVertical: 3, + borderWidth: StyleSheet.hairlineWidth, + borderColor: Colors.border, + }, + wordCountText: { + color: Colors.textMuted, + fontSize: 11, + fontWeight: '500', + }, + editorCard: { + marginHorizontal: 12, + marginBottom: 16, + borderRadius: 16, + borderWidth: 1, + borderColor: Colors.border, + backgroundColor: Colors.bg, + overflow: 'hidden', + }, + editorWebview: { + backgroundColor: Colors.bg, + minHeight: 280, + }, + // Meta sections wrapper + metaSections: { + paddingHorizontal: 12, + gap: 8, + }, + // Collapsible section toggle + sectionToggle: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + backgroundColor: Colors.surface, + borderRadius: 12, + borderWidth: 1, + borderColor: Colors.border, + paddingHorizontal: 14, + paddingVertical: 13, + }, + sectionToggleOpen: { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + borderBottomColor: 'transparent', + borderColor: Colors.border, + }, + sectionToggleIcon: { + fontSize: 16, + }, + sectionToggleText: { + flex: 1, + color: Colors.textSecondary, + fontSize: 15, + fontWeight: '600', + }, + sectionToggleTextOpen: { + color: Colors.textPrimary, + }, + sectionToggleMeta: { + flexDirection: 'row', + alignItems: 'center', + gap: 5, + }, + statusDotSmall: { + width: 6, + height: 6, + borderRadius: 3, + }, + statusLabelSmall: { + fontSize: 12, + fontWeight: '500', + }, + sectionChevron: { + color: Colors.textMuted, + fontSize: 11, + }, + // Details body + detailsBody: { + backgroundColor: Colors.surface, + borderBottomLeftRadius: 12, + borderBottomRightRadius: 12, + borderWidth: 1, + borderTopWidth: 0, + borderColor: Colors.border, + padding: 16, + gap: 16, + }, + fieldGroup: { + gap: 8, + }, + fieldLabel: { + color: Colors.textMuted, + fontSize: 11, + fontWeight: '700', + letterSpacing: 1.0, + }, + // Status statusRow: { flexDirection: 'row', alignItems: 'center', gap: 8, paddingHorizontal: 12, - paddingVertical: 8, + paddingVertical: 9, borderRadius: 10, borderWidth: 1, - backgroundColor: Colors.surface, + backgroundColor: Colors.elevated, alignSelf: 'flex-start', - marginBottom: 16, }, statusDot: { width: 8, @@ -376,10 +870,9 @@ const styles = StyleSheet.create({ }, statusMenu: { backgroundColor: Colors.elevated, - borderRadius: 12, + borderRadius: 10, borderWidth: 1, borderColor: Colors.border, - marginBottom: 16, overflow: 'hidden', }, statusOption: { @@ -387,7 +880,7 @@ const styles = StyleSheet.create({ alignItems: 'center', gap: 10, paddingHorizontal: 14, - paddingVertical: 12, + paddingVertical: 11, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: Colors.border, }, @@ -398,80 +891,52 @@ const styles = StyleSheet.create({ fontSize: 14, fontWeight: '500', }, + // Notes notesInput: { color: Colors.textSecondary, - fontSize: 16, - lineHeight: 24, - minHeight: 120, - padding: 0, - marginBottom: 24, - }, - section: { - marginBottom: 20, - }, - sectionLabel: { - color: Colors.textMuted, - fontSize: 12, - fontWeight: '600', - textTransform: 'uppercase', - letterSpacing: 0.8, - marginBottom: 10, + fontSize: 15, + lineHeight: 22, + minHeight: 90, + padding: 12, + backgroundColor: Colors.elevated, + borderRadius: 10, + borderWidth: 1, + borderColor: Colors.border, + textAlignVertical: 'top', }, + // Tags tagInputRow: { - backgroundColor: Colors.surface, - borderRadius: 12, + backgroundColor: Colors.elevated, + borderRadius: 10, borderWidth: 1, borderColor: Colors.border, - paddingHorizontal: 14, - paddingVertical: 10, + paddingHorizontal: 12, + paddingVertical: 9, }, tagInput: { color: Colors.textPrimary, - fontSize: 15, + fontSize: 14, padding: 0, }, tagsList: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, - marginTop: 12, }, - aiToggle: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, + // AI Panel + aiPanelWrapper: { backgroundColor: Colors.surface, - borderRadius: 12, + borderBottomLeftRadius: 12, + borderBottomRightRadius: 12, borderWidth: 1, + borderTopWidth: 0, borderColor: Colors.border, - padding: 14, - marginBottom: 12, - }, - aiToggleActive: { - borderColor: Colors.accent, - backgroundColor: Colors.accentSoft, - }, - aiToggleIcon: { - fontSize: 18, - }, - aiToggleText: { - flex: 1, - color: Colors.textSecondary, - fontSize: 15, - fontWeight: '600', - }, - aiToggleTextActive: { - color: Colors.accentBright, - }, - aiChevron: { - color: Colors.textMuted, - fontSize: 12, - }, - aiPanelWrapper: { - marginBottom: 20, + overflow: 'hidden', }, + // Danger dangerZone: { - marginTop: 20, + marginHorizontal: 12, + marginTop: 24, paddingTop: 20, borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: Colors.border, diff --git a/app/new.tsx b/app/new.tsx index 213d64c..9d4779f 100644 --- a/app/new.tsx +++ b/app/new.tsx @@ -9,6 +9,7 @@ import { KeyboardAvoidingView, Platform, Alert, + ActivityIndicator, } from 'react-native'; import { router } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -16,39 +17,102 @@ import { LinearGradient } from 'expo-linear-gradient'; import * as Haptics from 'expo-haptics'; import { Colors } from '../src/constants/colors'; import { saveIdea, getSettings } from '../src/lib/storage'; -import { ArticleIdea } from '../src/lib/types'; +import { ArticleIdea, AIContent } from '../src/lib/types'; +import { generateOutline, improveTitles, suggestTags, writeIntro, AIDebugInfo } from '../src/lib/ai'; import TagPill from '../src/components/TagPill'; -import AIPanel from '../src/components/AIPanel'; +import AIDebugModal from '../src/components/AIDebugModal'; function generateId(): string { return Date.now().toString(36) + Math.random().toString(36).slice(2); } +type AIAction = 'outline' | 'titles' | 'tags' | 'intro'; + +const AI_ACTIONS: { action: AIAction; icon: string; label: string }[] = [ + { action: 'outline', icon: '📋', label: 'Outline' }, + { action: 'titles', icon: '✏️', label: 'Titles' }, + { action: 'tags', icon: '🏷️', label: 'Tags' }, + { action: 'intro', icon: '🖊️', label: 'Intro' }, +]; + export default function NewIdeaScreen() { const [title, setTitle] = useState(''); const [notes, setNotes] = useState(''); const [tags, setTags] = useState([]); const [tagInput, setTagInput] = useState(''); - const [showAI, setShowAI] = useState(false); const [saving, setSaving] = useState(false); - const [apiKey, setApiKey] = useState(''); + const [azureConfig, setAzureConfig] = useState({ endpoint: '', apiKey: '', deployment: '' }); + + // AI state + const [loadingAction, setLoadingAction] = useState(null); + const [aiResults, setAiResults] = useState>>({}); + const [expandedResult, setExpandedResult] = useState(null); + const [errorModal, setErrorModal] = useState<{ error: string; debugInfo?: AIDebugInfo } | null>(null); + const titleRef = useRef(null); useEffect(() => { setTimeout(() => titleRef.current?.focus(), 300); - getSettings().then((s) => setApiKey(s.anthropicApiKey)); + getSettings().then((s) => setAzureConfig({ + endpoint: s.azureEndpoint, + apiKey: s.azureApiKey, + deployment: s.azureDeployment, + })); }, []); const addTag = (raw: string) => { const tag = raw.trim().toLowerCase().replace(/^#/, '').replace(/\s+/g, '-'); - if (tag && !tags.includes(tag)) { - setTags((prev) => [...prev, tag]); - } + if (tag && !tags.includes(tag)) setTags((prev) => [...prev, tag]); setTagInput(''); }; - const handleTagInputSubmit = () => { - if (tagInput.trim()) addTag(tagInput); + const runAI = async (action: AIAction) => { + if (!azureConfig.endpoint || !azureConfig.apiKey) { + Alert.alert('Azure Configuration Required', 'Add your Azure AI Foundry endpoint and API key in Settings.'); + return; + } + if (!title.trim()) { + Alert.alert('Add a title first', 'Give your idea a title before running AI tools.'); + return; + } + + setLoadingAction(action); + setExpandedResult(action); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + + let res; + switch (action) { + case 'outline': res = await generateOutline(azureConfig, title, notes); break; + case 'titles': res = await improveTitles(azureConfig, title, notes); break; + case 'tags': res = await suggestTags(azureConfig, title, notes); break; + case 'intro': res = await writeIntro(azureConfig, title, notes); break; + } + + setLoadingAction(null); + + if (res.error) { + setErrorModal({ error: res.error, debugInfo: res.debugInfo }); + return; + } + + setAiResults((prev) => ({ ...prev, [action]: res.content })); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + + // Auto-apply tags + if (action === 'tags') { + const suggested = res.content + .split(',') + .map((t: string) => t.trim().toLowerCase().replace(/^#/, '')) + .filter(Boolean) + .slice(0, 8); + setTags((prev) => { + const merged = [...prev]; + for (const tag of suggested) { + if (!merged.includes(tag)) merged.push(tag); + } + return merged.slice(0, 10); + }); + } }; const handleSave = async () => { @@ -59,13 +123,31 @@ export default function NewIdeaScreen() { setSaving(true); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + const aiContent: AIContent = {}; + if (aiResults.outline) aiContent.outline = aiResults.outline; + if (aiResults.intro) aiContent.intro = aiResults.intro; + if (aiResults.titles) { + aiContent.improvedTitles = aiResults.titles + .split('\n') + .filter((l: string) => l.trim()) + .slice(0, 5); + } + if (aiResults.tags) { + aiContent.suggestedTags = aiResults.tags + .split(',') + .map((t: string) => t.trim()) + .filter(Boolean); + } + const now = new Date().toISOString(); const idea: ArticleIdea = { id: generateId(), title: title.trim(), notes: notes.trim(), + content: '', tags, status: 'draft', + aiContent: Object.keys(aiContent).length ? aiContent : undefined, createdAt: now, updatedAt: now, }; @@ -75,22 +157,17 @@ export default function NewIdeaScreen() { router.back(); }; - const handleAITagsGenerated = (suggested: string[]) => { - setTags((prev) => { - const merged = [...prev]; - for (const tag of suggested) { - if (!merged.includes(tag)) merged.push(tag); - } - return merged.slice(0, 10); - }); - }; + const hasAnyResult = Object.keys(aiResults).length > 0; return ( - + setErrorModal(null)} + /> + {/* Nav bar */} router.back()}> @@ -119,7 +196,7 @@ export default function NewIdeaScreen() { keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false} > - {/* Title input */} + {/* Title */} - {/* Notes input */} + {/* Notes */} - {/* Tags section */} + {/* ── AI Quick Actions ── always visible, part of the flow */} + + + + AI + {(!azureConfig.endpoint || !azureConfig.apiKey) && ( + Configure Azure in Settings + )} + + + {AI_ACTIONS.map(({ action, icon, label }) => { + const isLoading = loadingAction === action; + const isDone = !!aiResults[action]; + const isExpanded = expandedResult === action && isDone; + return ( + { + if (isDone && expandedResult === action) { + setExpandedResult(null); + } else if (isDone) { + setExpandedResult(action); + } else { + runAI(action); + } + }} + disabled={isLoading} + > + {isLoading ? ( + + ) : ( + {isDone ? '✓' : icon} + )} + + {label} + + + ); + })} + + + {/* Expanded result */} + {expandedResult && aiResults[expandedResult] && ( + + {aiResults[expandedResult]} + + )} + + + {/* Tags */} Tags @@ -153,7 +284,7 @@ export default function NewIdeaScreen() { placeholderTextColor={Colors.textMuted} value={tagInput} onChangeText={setTagInput} - onSubmitEditing={handleTagInputSubmit} + onSubmitEditing={() => { if (tagInput.trim()) addTag(tagInput); }} returnKeyType="done" autoCapitalize="none" /> @@ -170,33 +301,6 @@ export default function NewIdeaScreen() { )} - - {/* AI toggle */} - { - setShowAI((v) => !v); - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - }} - > - - - {showAI ? 'Hide AI Tools' : 'AI Tools'} - - {showAI ? '▲' : '▼'} - - - {/* AI Panel */} - {showAI && ( - - - - )}
    @@ -217,42 +321,15 @@ const styles = StyleSheet.create({ borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: Colors.border, }, - cancelBtn: { - paddingVertical: 4, - paddingHorizontal: 4, - }, - cancelText: { - color: Colors.textSecondary, - fontSize: 16, - }, - navTitle: { - color: Colors.textPrimary, - fontSize: 16, - fontWeight: '600', - }, - saveBtn: { - borderRadius: 20, - overflow: 'hidden', - }, - saveBtnDisabled: { - opacity: 0.5, - }, - saveBtnGradient: { - paddingHorizontal: 18, - paddingVertical: 8, - }, - saveText: { - color: Colors.white, - fontSize: 15, - fontWeight: '600', - }, - scroll: { - flex: 1, - }, - scrollContent: { - padding: 20, - paddingBottom: 40, - }, + cancelBtn: { paddingVertical: 4, paddingHorizontal: 4 }, + cancelText: { color: Colors.textSecondary, fontSize: 16 }, + navTitle: { color: Colors.textPrimary, fontSize: 16, fontWeight: '600' }, + saveBtn: { borderRadius: 20, overflow: 'hidden' }, + saveBtnDisabled: { opacity: 0.5 }, + saveBtnGradient: { paddingHorizontal: 18, paddingVertical: 8 }, + saveText: { color: Colors.white, fontSize: 15, fontWeight: '600' }, + scroll: { flex: 1 }, + scrollContent: { padding: 20, paddingBottom: 40 }, titleInput: { color: Colors.textPrimary, fontSize: 26, @@ -265,75 +342,91 @@ const styles = StyleSheet.create({ color: Colors.textSecondary, fontSize: 16, lineHeight: 24, - minHeight: 100, + minHeight: 90, padding: 0, - marginBottom: 24, - }, - section: { marginBottom: 20, }, - sectionLabel: { - color: Colors.textMuted, - fontSize: 12, - fontWeight: '600', - textTransform: 'uppercase', - letterSpacing: 0.8, - marginBottom: 10, + // AI section + aiSection: { + backgroundColor: Colors.elevated, + borderRadius: 16, + borderWidth: 1, + borderColor: Colors.border, + padding: 14, + marginBottom: 20, }, - tagInputRow: { + aiSectionHeader: { flexDirection: 'row', alignItems: 'center', - backgroundColor: Colors.surface, - borderRadius: 12, - borderWidth: 1, - borderColor: Colors.border, - paddingHorizontal: 14, - paddingVertical: 10, + marginBottom: 12, + gap: 5, }, - tagInput: { - flex: 1, - color: Colors.textPrimary, - fontSize: 15, - padding: 0, + aiSectionIcon: { fontSize: 14 }, + aiSectionLabel: { + color: Colors.accentBright, + fontSize: 13, + fontWeight: '700', + letterSpacing: 0.5, + }, + aiKeyHint: { + color: Colors.textMuted, + fontSize: 12, }, - tagsList: { + aiActionsRow: { flexDirection: 'row', - flexWrap: 'wrap', gap: 8, - marginTop: 12, + flexWrap: 'wrap', }, - aiToggle: { + aiActionChip: { flexDirection: 'row', alignItems: 'center', - gap: 8, + gap: 5, + paddingHorizontal: 12, + paddingVertical: 8, + borderRadius: 20, backgroundColor: Colors.surface, - borderRadius: 12, borderWidth: 1, borderColor: Colors.border, - padding: 14, - marginBottom: 12, }, - aiToggleActive: { + aiActionChipDone: { borderColor: Colors.accent, backgroundColor: Colors.accentSoft, }, - aiToggleIcon: { - fontSize: 18, + aiActionChipExpanded: { + borderColor: Colors.accentBright, }, - aiToggleText: { - flex: 1, - color: Colors.textSecondary, - fontSize: 15, - fontWeight: '600', + aiActionIcon: { fontSize: 14 }, + aiActionLabel: { color: Colors.textSecondary, fontSize: 13, fontWeight: '500' }, + aiActionLabelDone: { color: Colors.accentBright, fontWeight: '600' }, + resultBox: { + marginTop: 12, + paddingTop: 12, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: Colors.border, }, - aiToggleTextActive: { - color: Colors.accentBright, + resultText: { + color: Colors.textSecondary, + fontSize: 14, + lineHeight: 22, }, - aiChevron: { + // Tags + section: { marginBottom: 20 }, + sectionLabel: { color: Colors.textMuted, fontSize: 12, + fontWeight: '600', + textTransform: 'uppercase', + letterSpacing: 0.8, + marginBottom: 10, }, - aiPanelWrapper: { - marginBottom: 20, + tagInputRow: { + backgroundColor: Colors.surface, + borderRadius: 12, + borderWidth: 1, + borderColor: Colors.border, + paddingHorizontal: 14, + paddingVertical: 10, }, + tagInput: { color: Colors.textPrimary, fontSize: 15, padding: 0 }, + tagsList: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 12 }, }); diff --git a/babel.config.js b/babel.config.js index d872de3..fb036fe 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,6 +2,6 @@ module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], - plugins: ['react-native-reanimated/plugin'], + plugins: [], }; }; diff --git a/eas.json b/eas.json new file mode 100644 index 0000000..f008913 --- /dev/null +++ b/eas.json @@ -0,0 +1,25 @@ +{ + "cli": { + "version": ">= 18.0.0", + "appVersionSource": "local" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal", + "ios": { + "simulator": true + } + }, + "preview": { + "distribution": "internal", + "ios": { + "resourceClass": "m-medium" + } + }, + "production": {} + }, + "submit": { + "production": {} + } +} diff --git a/package-lock.json b/package-lock.json index 1e580a5..593f467 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,26 +8,97 @@ "name": "nova", "version": "1.0.0", "dependencies": { - "@react-native-async-storage/async-storage": "2.1.2", + "@10play/tentap-editor": "^1.0.1", + "@anthropic-ai/sdk": "^0.79.0", + "@expo/metro-runtime": "~55.0.6", + "@react-native-async-storage/async-storage": "2.2.0", + "babel-preset-expo": "~55.0.11", "expo": "~55.0.6", - "expo-blur": "~14.1.4", - "expo-haptics": "~14.1.4", - "expo-linear-gradient": "~14.1.4", - "expo-router": "~5.0.7", + "expo-blur": "~55.0.9", + "expo-haptics": "~55.0.8", + "expo-linear-gradient": "~55.0.8", + "expo-linking": "~55.0.7", + "expo-router": "~55.0.5", "expo-status-bar": "~55.0.4", - "expo-symbols": "~0.4.3", + "expo-updates": "~55.0.13", "react": "19.2.0", "react-native": "0.83.2", - "react-native-gesture-handler": "~2.24.0", - "react-native-reanimated": "~3.17.4", - "react-native-safe-area-context": "5.4.0", - "react-native-screens": "~4.11.1" + "react-native-gesture-handler": "~2.30.0", + "react-native-markdown-display": "^7.0.2", + "react-native-safe-area-context": "~5.6.2", + "react-native-screens": "~4.23.0", + "react-native-webview": "^13.16.1" }, "devDependencies": { "@types/react": "~19.2.2", "typescript": "~5.9.2" } }, + "node_modules/@10play/tentap-editor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@10play/tentap-editor/-/tentap-editor-1.0.1.tgz", + "integrity": "sha512-tqBN0gqSxku50jk50e+rnB4DQdubRTdbgBrkMu7XE3kvHSbavCB5zJgrFjNhnU9A9dVRcBoEtZk3YJFnfDm9Ug==", + "license": "MIT", + "workspaces": [ + "example76", + "examplelatest", + "website" + ], + "dependencies": { + "@tiptap/core": "^3.11.0", + "@tiptap/extension-blockquote": "^3.11.0", + "@tiptap/extension-bold": "^3.11.0", + "@tiptap/extension-code": "^3.11.0", + "@tiptap/extension-code-block": "^3.11.0", + "@tiptap/extension-color": "^3.11.0", + "@tiptap/extension-document": "^3.11.0", + "@tiptap/extension-hard-break": "^3.11.0", + "@tiptap/extension-heading": "^3.11.0", + "@tiptap/extension-highlight": "^3.11.0", + "@tiptap/extension-horizontal-rule": "^3.11.0", + "@tiptap/extension-image": "^3.11.0", + "@tiptap/extension-italic": "^3.11.0", + "@tiptap/extension-link": "^3.11.0", + "@tiptap/extension-list": "^3.11.0", + "@tiptap/extension-strike": "^3.11.0", + "@tiptap/extension-text-style": "^3.11.0", + "@tiptap/extension-underline": "^3.11.0", + "@tiptap/extensions": "^3.11.0", + "@tiptap/pm": "^3.11.0", + "@tiptap/react": "^3.11.0", + "@tiptap/starter-kit": "^3.11.0", + "lodash": "^4.17.21", + "react-dom": "^18.2.0" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-webview": "*" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.79.0.tgz", + "integrity": "sha512-ietmtM6glcnnrWq26H+BZm8J07iay9Cob6hRzDTr/A9QWF1m2T//TQhFO4MTKcZht2/7LS8bG9wUYEhcizKRnA==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -1257,21 +1328,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-typescript": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", @@ -1431,6 +1487,12 @@ "node": ">=0.8.0" } }, + "node_modules/@expo-google-fonts/material-symbols": { + "version": "0.4.27", + "resolved": "https://registry.npmjs.org/@expo-google-fonts/material-symbols/-/material-symbols-0.4.27.tgz", + "integrity": "sha512-cnb3DZnWUWpezGFkJ8y4MT5f/lw6FcgDzeJzic+T+vpQHLHG1cg3SC3i1w1i8Bk4xKR4HPY3t9iIRNvtr5ml8A==", + "license": "MIT AND Apache-2.0" + }, "node_modules/@expo/code-signing-certificates": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz", @@ -1480,6 +1542,23 @@ "xml2js": "0.6.0" } }, + "node_modules/@expo/config-plugins/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@expo/config-plugins/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -1498,6 +1577,23 @@ "integrity": "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg==", "license": "MIT" }, + "node_modules/@expo/config/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@expo/config/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -1597,6 +1693,23 @@ "fingerprint": "bin/cli.js" } }, + "node_modules/@expo/fingerprint/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@expo/fingerprint/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -1656,6 +1769,23 @@ "chalk": "^4.1.2" } }, + "node_modules/@expo/log-box": { + "version": "55.0.7", + "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-55.0.7.tgz", + "integrity": "sha512-m7V1k2vlMp4NOj3fopjOg4zl/ANXyTRF3HMTMep2GZAKsPiDzgOQ41nm8CaU50/HlDIGXlCObss07gOn20UpHQ==", + "license": "MIT", + "dependencies": { + "@expo/dom-webview": "^55.0.3", + "anser": "^1.4.9", + "stacktrace-parser": "^0.1.10" + }, + "peerDependencies": { + "@expo/dom-webview": "^55.0.3", + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/@expo/metro": { "version": "54.2.0", "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.2.0.tgz", @@ -1679,12 +1809,27 @@ } }, "node_modules/@expo/metro-runtime": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-5.0.4.tgz", - "integrity": "sha512-r694MeO+7Vi8IwOsDIDzH/Q5RPMt1kUDYbiTJwnO15nIqiDwlE8HU55UlRhffKZy6s5FmxQsZ8HA+T8DqUW8cQ==", + "version": "55.0.6", + "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-55.0.6.tgz", + "integrity": "sha512-l8VvgKN9md+URjeQDB+DnHVmvpcWI6zFLH6yv7GTv4sfRDKyaZ5zDXYjTP1phYdgW6ea2NrRtCGNIxylWhsgtg==", "license": "MIT", + "dependencies": { + "@expo/log-box": "55.0.7", + "anser": "^1.4.9", + "pretty-format": "^29.7.0", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0" + }, "peerDependencies": { + "expo": "*", + "react": "*", + "react-dom": "*", "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/@expo/osascript": { @@ -1788,18 +1933,6 @@ "integrity": "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==", "license": "MIT" }, - "node_modules/@expo/server": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@expo/server/-/server-0.6.3.tgz", - "integrity": "sha512-Ea7NJn9Xk1fe4YeJ86rObHSv/bm3u/6WiQPXEqXJ2GrfYpVab2Swoh9/PnSM3KjR64JAgKjArDn1HiPjITCfHA==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "debug": "^4.3.4", - "source-map-support": "~0.5.21", - "undici": "^6.18.2 || ^7.0.0" - } - }, "node_modules/@expo/spawn-async": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", @@ -1856,6 +1989,34 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT", + "optional": true + }, "node_modules/@isaacs/ttlcache": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", @@ -2053,6 +2214,56 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", @@ -2068,67 +2279,441 @@ } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@react-native-async-storage/async-storage": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.1.2.tgz", - "integrity": "sha512-dvlNq4AlGWC+ehtH12p65+17V0Dx7IecOWl6WanF2ja38O1Dcjjvn7jVzkUHJ5oWkQBlyASurTPlTHgKXyYiow==", + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { - "merge-options": "^3.0.4" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { - "react-native": "^0.0.0-0 || >=0.65 <1.0" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-native/assets-registry": { - "version": "0.83.2", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.2.tgz", - "integrity": "sha512-9I5l3pGAKnlpQ15uVkeB9Mgjvt3cZEaEc8EDtdexvdtZvLSjtwBzgourrOW4yZUijbjJr8h3YO2Y0q+THwUHTA==", + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "license": "MIT", - "engines": { - "node": ">= 20.19.4" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.83.2", - "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.83.2.tgz", - "integrity": "sha512-XbcN/BEa64pVlb0Hb/E/Ph2SepjVN/FcNKrJcQvtaKZA6mBSO8pW8Eircdlr61/KBH94LihHbQoQDzkQFpeaTg==", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.3", - "@react-native/codegen": "0.83.2" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, - "engines": { - "node": ">= 20.19.4" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-native/babel-preset": { - "version": "0.83.2", - "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.83.2.tgz", - "integrity": "sha512-X/RAXDfe6W+om/Fw1i6htTxQXFhBJ2jgNOWx3WpI3KbjeIWbq7ib6vrpTeIAW2NUMg+K3mML1NzgD4dpZeqdjA==", + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", + "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==", + "license": "MIT", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || >=0.65 <1.0" + } + }, + "node_modules/@react-native/assets-registry": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.2.tgz", + "integrity": "sha512-9I5l3pGAKnlpQ15uVkeB9Mgjvt3cZEaEc8EDtdexvdtZvLSjtwBzgourrOW4yZUijbjJr8h3YO2Y0q+THwUHTA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/babel-plugin-codegen": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.83.2.tgz", + "integrity": "sha512-XbcN/BEa64pVlb0Hb/E/Ph2SepjVN/FcNKrJcQvtaKZA6mBSO8pW8Eircdlr61/KBH94LihHbQoQDzkQFpeaTg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.3", + "@react-native/codegen": "0.83.2" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/babel-preset": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.83.2.tgz", + "integrity": "sha512-X/RAXDfe6W+om/Fw1i6htTxQXFhBJ2jgNOWx3WpI3KbjeIWbq7ib6vrpTeIAW2NUMg+K3mML1NzgD4dpZeqdjA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", @@ -2179,6 +2764,15 @@ "@babel/core": "*" } }, + "node_modules/@react-native/babel-preset/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", + "license": "MIT", + "dependencies": { + "hermes-parser": "0.32.0" + } + }, "node_modules/@react-native/codegen": { "version": "0.83.2", "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.83.2.tgz", @@ -2200,59 +2794,10 @@ "@babel/core": "*" } }, - "node_modules/@react-native/codegen/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/@react-native/codegen/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@react-native/codegen/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@react-native/codegen/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@react-native/community-cli-plugin": { - "version": "0.83.2", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.83.2.tgz", - "integrity": "sha512-sTEF0eiUKtmImEP07Qo5c3Khvm1LIVX1Qyb6zWUqPL6W3MqFiXutZvKBjqLz6p49Szx8cplQLoXfLHT0bcDXKg==", + "node_modules/@react-native/community-cli-plugin": { + "version": "0.83.2", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.83.2.tgz", + "integrity": "sha512-sTEF0eiUKtmImEP07Qo5c3Khvm1LIVX1Qyb6zWUqPL6W3MqFiXutZvKBjqLz6p49Szx8cplQLoXfLHT0bcDXKg==", "license": "MIT", "dependencies": { "@react-native/dev-middleware": "0.83.2", @@ -2378,115 +2923,607 @@ "react-native-screens": ">= 4.0.0" } }, - "node_modules/@react-navigation/core": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.16.1.tgz", - "integrity": "sha512-xhquoyhKdqDfiL7LuupbwYnmauUGfVFGDEJO34m26k8zSN1eDjQ2stBZcHN8ILOI1PrG9885nf8ZmfaQxPS0ww==", + "node_modules/@react-navigation/core": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.16.1.tgz", + "integrity": "sha512-xhquoyhKdqDfiL7LuupbwYnmauUGfVFGDEJO34m26k8zSN1eDjQ2stBZcHN8ILOI1PrG9885nf8ZmfaQxPS0ww==", + "license": "MIT", + "dependencies": { + "@react-navigation/routers": "^7.5.3", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "query-string": "^7.1.3", + "react-is": "^19.1.0", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "react": ">= 18.2.0" + } + }, + "node_modules/@react-navigation/elements": { + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.10.tgz", + "integrity": "sha512-N8tuBekzTRb0pkMHFJGvmC6Q5OisSbt6gzvw7RHMnp4NDo5auVllT12sWFaTXf8mTduaLKNSrD/NZNaOqThCBg==", + "license": "MIT", + "dependencies": { + "color": "^4.2.3", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@react-native-masked-view/masked-view": ">= 0.2.0", + "@react-navigation/native": "^7.1.33", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0" + }, + "peerDependenciesMeta": { + "@react-native-masked-view/masked-view": { + "optional": true + } + } + }, + "node_modules/@react-navigation/native": { + "version": "7.1.33", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.33.tgz", + "integrity": "sha512-DpFdWGcgLajKZ1TuIvDNQsblN2QaUFWpTQaB8v7WRP9Mix8H/6TFoIrZd93pbymI2hybd6UYrD+lI408eWVcfw==", + "license": "MIT", + "dependencies": { + "@react-navigation/core": "^7.16.1", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "use-latest-callback": "^0.2.4" + }, + "peerDependencies": { + "react": ">= 18.2.0", + "react-native": "*" + } + }, + "node_modules/@react-navigation/native-stack": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.14.5.tgz", + "integrity": "sha512-NuyMf21kKk3jODvYgpcDA+HwyWr/KEj72ciqquyEupZlsmQ3WNUGgdaixEB3A19+iPOvHLQzDLcoTrrqZk8Leg==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.9.10", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0", + "warn-once": "^0.1.1" + }, + "peerDependencies": { + "@react-navigation/native": "^7.1.33", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/routers": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", + "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11" + } + }, + "node_modules/@remirror/core-constants": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==", + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tiptap/core": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.4.tgz", + "integrity": "sha512-3i/DG89TFY/b34T5P+j35UcjYuB5d3+9K8u6qID+iUqNPiza015HPIZLuPfE5elNwVdV3EXIoPo0LLeBLgXXAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.20.4.tgz", + "integrity": "sha512-9sskyyhYj2oKat//lyZVXCp9YrPt4oJAZnGHYWXS0xlskjsLElrfKKlM4vpbhGss3VrhQRoEGqWLnIaJYPF1zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.20.4.tgz", + "integrity": "sha512-Md7/mNAeJCY+VLJc8JRGI+8XkVPKiOGB1NgqQPdh3aYtxXQDChQOZoJEQl6TuudDxZ85bLZB67NjZlx3jo8/0g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.20.4.tgz", + "integrity": "sha512-EXywPlI8wjPcAb8ozymgVhjtMjFrnhtoyNTy8ZcObdpUi5CdO9j892Y7aPbKe5hLhlDpvJk7rMfir4FFKEmfng==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.20.4.tgz", + "integrity": "sha512-1RTGrur1EKoxfnLZ3M6xeNj8GITAz74jH2DHGcjLsd2Xr7Q7BozGaIq6GkkvKguMwbI1zCOxTHFCpUETXAIQQA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.20.4.tgz", + "integrity": "sha512-7j8Hi964bH1SZ9oLdZC1fkqWz27mliSDV7M8lmL/M14+Qw42D/VOAKS4Aw9OCFtHMlTsjLR6qsoVxL8Lpkt6NA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.20.4.tgz", + "integrity": "sha512-Zlw3FrXTy01+o1yISeX/LC+iJeHA+ym602bMXGmtA6lyl7QSOSO7WExweJ6xeJGhbCjldwT5al6fkRAs8iGJZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-color": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-3.20.4.tgz", + "integrity": "sha512-+OT9wWEJnqoWmzfqPYt0oWm8LZcH+D44Z3jA2TNzBj4tLGQ2YPxN2SyS12AlRi7MuguVT7utFy7qDXrfir8eUA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-text-style": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.20.4.tgz", + "integrity": "sha512-zF1CIFVLt8MfSpWWnPwtGyxPOsT0xYM2qJKcXf2yZcTG37wDKmUi6heG53vGigIavbQlLaAFvs+1mNdOu2x/0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.20.4.tgz", + "integrity": "sha512-TgMwvZ8myXYdmd6bUV7qkpZXv7ZUiSmX/8eo+iPEzYo2CnDLAGvDKgC50nfq/g87SDvfBgPuAiBfFvsMQQWaTw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.20.4.tgz", + "integrity": "sha512-AaPTFhoO8DBIElJyd/RTVJjkctvJuL+GHURX0npbtTxXq5HXbebVwf2ARNR7jMd/GThsmBaNJiGxZg4A2oeDqQ==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.20.4.tgz", + "integrity": "sha512-JJ6f1iQ1e0s4kISgq55U3UYGwWV/N9f0PYMtB6e3L+SBQjXnywaLK0g6vfN6IvTCC2vdIuqeSOX8VlSO97sJLw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.20.4.tgz", + "integrity": "sha512-gJbq58d8zB1gzyqVEopowej5CpW4/Fpg6oGJvlZxaCukqd0gJRWGC89K+jE62YA1Td4sfcKrekKvN7jm2y/ZUg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.20.4.tgz", + "integrity": "sha512-xsnkmTGggJc5P2iCwS1lv8KFG31xC/GNPJKoi/3UH67j/lKDhA3AdtshsLeyv2FKtTtYDb8oV0IqzHB1MM6a7w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-highlight": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.20.4.tgz", + "integrity": "sha512-CyTVPorVWwE4v89+k1nmaoAvjXLo7/fYWBsYlHW6b9Y1Un0iLANgKMFmmuapyfpaqpvg7V0Eg5ElG9U9+rogVA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.20.4.tgz", + "integrity": "sha512-y6joCi49haAA0bo3EGUY+dWUMHH1GPUc84hxrBY/0pMs+Bn+kQ1+DQJErZDTWGJrlHPWU/yekBZT72SNdp0DNA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-image": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.20.4.tgz", + "integrity": "sha512-57w2TevHQljTh6Xiry9duIm7NNOQAUSTwtwRn4GGLoKwHR8qXTxzp513ASrFOgR2kgs2TP471Au6RHf947P+jg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.20.4.tgz", + "integrity": "sha512-4ZqiWr7cmqPFux8tj1ZLiYytyWf343IvQemNX6AvVWvscrJcrfj3YX4Le2BA0RW3A3M6RpLQXXozuF8vxYFDeQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.20.4.tgz", + "integrity": "sha512-JNDSkWrVdb8NSvbQXwHWvK5tCMbTWwOHFOweknQZ1JPK4dei9FJVofYQaHyW4bJBdcCjds3NZSnXE8DM9iAWmg==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.4.tgz", + "integrity": "sha512-X+5plTKhOioNcQ4KsAFJJSb/3+zR8Xhdpow4HzXtoV1KcbdDey1fhZdpsfkbrzCL0s6/wAgwZuAchCK7HujurQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.20.4.tgz", + "integrity": "sha512-QoTc5RACXaZF+vIIBBxjGO7D0oWFUDgBKJCpvUZ0CoGGKosnfe4a9I5THFyLj4201cf0oUqgf1oZhTqETGxlVw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.20.4.tgz", + "integrity": "sha512-RIqXM649+8IP7p/KVfaGlJiwjCylm1m6OPlaoM3K8O7oEOGRQzNeexexECCD2jsXRxew4E+vBNMD2orXqJmu8A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.20.4.tgz", + "integrity": "sha512-3budNL8BgBon3TcXZ4hjT0YpFvx1Ka3uSIECKDxHgES+OQcR+6cagxSb60gFEccf3Dr0PIwcVTY6g14lC1qKRQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.4" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.20.4.tgz", + "integrity": "sha512-lm6fOScWuZAF/Sfp97igUwFd3L1QHIVLAWP5NVdh0DTLrEIt4rMBmsww+yOpMQRhvz2uTgMbMXynrimhzi/QVw==", "license": "MIT", - "dependencies": { - "@react-navigation/routers": "^7.5.3", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "query-string": "^7.1.3", - "react-is": "^19.1.0", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "react": ">= 18.2.0" + "@tiptap/core": "^3.20.4" } }, - "node_modules/@react-navigation/elements": { - "version": "2.9.10", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.10.tgz", - "integrity": "sha512-N8tuBekzTRb0pkMHFJGvmC6Q5OisSbt6gzvw7RHMnp4NDo5auVllT12sWFaTXf8mTduaLKNSrD/NZNaOqThCBg==", + "node_modules/@tiptap/extension-strike": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.20.4.tgz", + "integrity": "sha512-It1Px9uDGTsVqyyg6cy7DigLoenljpQwqdI0jssM7QclZrHnsrye9fZxBBiiuCzzV1305MxKgHvratkHwqmVNA==", "license": "MIT", - "dependencies": { - "color": "^4.2.3", - "use-latest-callback": "^0.2.4", - "use-sync-external-store": "^1.5.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@react-native-masked-view/masked-view": ">= 0.2.0", - "@react-navigation/native": "^7.1.33", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0" - }, - "peerDependenciesMeta": { - "@react-native-masked-view/masked-view": { - "optional": true - } + "@tiptap/core": "^3.20.4" } }, - "node_modules/@react-navigation/native": { - "version": "7.1.33", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.33.tgz", - "integrity": "sha512-DpFdWGcgLajKZ1TuIvDNQsblN2QaUFWpTQaB8v7WRP9Mix8H/6TFoIrZd93pbymI2hybd6UYrD+lI408eWVcfw==", + "node_modules/@tiptap/extension-text": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.20.4.tgz", + "integrity": "sha512-jchJcBZixDEO2J66Zx5dchsI2mA6IYsROqF8P1poxL4ienH7RVQRCTsBNnSfIeOtREKKWeOU/tEs5fcpvvGwIQ==", "license": "MIT", - "dependencies": { - "@react-navigation/core": "^7.16.1", - "escape-string-regexp": "^4.0.0", - "fast-deep-equal": "^3.1.3", - "nanoid": "^3.3.11", - "use-latest-callback": "^0.2.4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "react": ">= 18.2.0", - "react-native": "*" + "@tiptap/core": "^3.20.4" } }, - "node_modules/@react-navigation/native-stack": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.14.5.tgz", - "integrity": "sha512-NuyMf21kKk3jODvYgpcDA+HwyWr/KEj72ciqquyEupZlsmQ3WNUGgdaixEB3A19+iPOvHLQzDLcoTrrqZk8Leg==", + "node_modules/@tiptap/extension-text-style": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-3.20.4.tgz", + "integrity": "sha512-PvW0Ja7ahWpo4bRuR8YCCVv4PH8lXjzhzlBAa4bMbsumOg+GbhX8Su7fwqd+IIPrHqfPXz9HTBMApSfzP6/08A==", "license": "MIT", - "dependencies": { - "@react-navigation/elements": "^2.9.10", - "color": "^4.2.3", - "sf-symbols-typescript": "^2.1.0", - "warn-once": "^0.1.1" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@react-navigation/native": "^7.1.33", - "react": ">= 18.2.0", - "react-native": "*", - "react-native-safe-area-context": ">= 4.0.0", - "react-native-screens": ">= 4.0.0" + "@tiptap/core": "^3.20.4" } }, - "node_modules/@react-navigation/routers": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", - "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", + "node_modules/@tiptap/extension-underline": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.20.4.tgz", + "integrity": "sha512-0OjMc3FDujX16G+jhvqcY/mLot8SrNtDu8ggUwNLAfiI/QIvMVgk7giFD71DATC/4Nb8i/iwAEegTD8MxBIXCg==", "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" + "node_modules/@tiptap/extensions": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.4.tgz", + "integrity": "sha512-8p6hVT65DjuQjtEdlH6ewX9SOJHlVQAOee3sWIJQmeJNRnZNvqPIBLleebUqDiljNTpxBv6s6QWkSTKgf3btwg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.4.tgz", + "integrity": "sha512-rCHYSBToilBEuI6PtjziHDdRkABH/XqwJ7dG4Amn/SD3yGiZKYCiEApQlTUS2zZeo8DsLeuqqqB4vEOeD4OEPg==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.1", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.24.1", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.5.0", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.38.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "license": "BSD-3-Clause", + "node_modules/@tiptap/react": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.20.4.tgz", + "integrity": "sha512-1B8iWsHWwb5TeyVaUs8BRPzwWo4PsLQcl03urHaz0zTJ8DauopqvxzV3+lem1OkzRHn7wnrapDvwmIGoROCaQw==", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@types/use-sync-external-store": "^0.0.6", + "fast-equals": "^5.3.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.20.4", + "@tiptap/extension-floating-menu": "^3.20.4" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/pm": "^3.20.4", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "3.20.4", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.20.4.tgz", + "integrity": "sha512-WcyK6hsTl8eBsQhQ+d9Sq8fYZKOYdL+D45MyH3hz583elXqJlW3h3JPFYb0o87gddGxn8Mm57OA/gA1zEdeDMw==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.20.4", + "@tiptap/extension-blockquote": "^3.20.4", + "@tiptap/extension-bold": "^3.20.4", + "@tiptap/extension-bullet-list": "^3.20.4", + "@tiptap/extension-code": "^3.20.4", + "@tiptap/extension-code-block": "^3.20.4", + "@tiptap/extension-document": "^3.20.4", + "@tiptap/extension-dropcursor": "^3.20.4", + "@tiptap/extension-gapcursor": "^3.20.4", + "@tiptap/extension-hard-break": "^3.20.4", + "@tiptap/extension-heading": "^3.20.4", + "@tiptap/extension-horizontal-rule": "^3.20.4", + "@tiptap/extension-italic": "^3.20.4", + "@tiptap/extension-link": "^3.20.4", + "@tiptap/extension-list": "^3.20.4", + "@tiptap/extension-list-item": "^3.20.4", + "@tiptap/extension-list-keymap": "^3.20.4", + "@tiptap/extension-ordered-list": "^3.20.4", + "@tiptap/extension-paragraph": "^3.20.4", + "@tiptap/extension-strike": "^3.20.4", + "@tiptap/extension-text": "^3.20.4", + "@tiptap/extension-underline": "^3.20.4", + "@tiptap/extensions": "^3.20.4", + "@tiptap/pm": "^3.20.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" } }, "node_modules/@types/babel__core": { @@ -2569,10 +3606,26 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "license": "MIT" }, "node_modules/@types/node": { @@ -2588,7 +3641,7 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2600,6 +3653,12 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -2676,51 +3735,6 @@ "node": ">= 14" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/anser": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", @@ -2806,6 +3820,18 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2919,12 +3945,27 @@ "license": "MIT" }, "node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", - "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.1.tgz", + "integrity": "sha512-HgErPZTghW76Rkq9uqn5ESeiD97FbqpZ1V170T1RG2RDp+7pJVQV2pQJs7y5YzN0/gcT6GM5ci9apRnIwuyPdQ==", "license": "MIT", "dependencies": { - "hermes-parser": "0.32.0" + "hermes-parser": "0.32.1" + } + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-estree": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.1.tgz", + "integrity": "sha512-ne5hkuDxheNBAikDjqvCZCwihnz0vVu9YsBzAEO1puiyFR4F1+PAz/SiPHSsNTuOveCYGRMX8Xbx4LOubeC0Qg==", + "license": "MIT" + }, + "node_modules/babel-plugin-syntax-hermes-parser/node_modules/hermes-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.1.tgz", + "integrity": "sha512-175dz634X/W5AiwrpLdoMl/MOb17poLHyIqgyExlE8D9zQ1OPnoORnGMB5ltRKnpvQzBjMYvT2rN/sHeIfZW5Q==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.32.1" } }, "node_modules/babel-plugin-transform-flow-enums": { @@ -2962,6 +4003,54 @@ "@babel/core": "^7.0.0 || ^8.0.0-0" } }, + "node_modules/babel-preset-expo": { + "version": "55.0.11", + "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-55.0.11.tgz", + "integrity": "sha512-ti8t4xufD6gUQQh+qY+b+VT/1zyA0n1PBnwOzCkPUyEDiIVBpaOixR+BzVH68hqu9mH2wDfzoFuGgv+2LfRdqw==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.20.5", + "@babel/helper-module-imports": "^7.25.9", + "@babel/plugin-proposal-decorators": "^7.12.9", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@react-native/babel-preset": "0.83.2", + "babel-plugin-react-compiler": "^1.0.0", + "babel-plugin-react-native-web": "~0.21.0", + "babel-plugin-syntax-hermes-parser": "^0.32.0", + "babel-plugin-transform-flow-enums": "^0.0.2", + "debug": "^4.3.4", + "resolve-from": "^5.0.0" + }, + "peerDependencies": { + "@babel/runtime": "^7.20.0", + "expo": "*", + "expo-widgets": "^55.0.4", + "react-refresh": ">=0.14.0 <1.0.0" + }, + "peerDependenciesMeta": { + "@babel/runtime": { + "optional": true + }, + "expo": { + "optional": true + }, + "expo-widgets": { + "optional": true + } + } + }, "node_modules/babel-preset-jest": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", @@ -2979,13 +4068,10 @@ } }, "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -3090,6 +4176,15 @@ "node": "18 || 20 || >=22" } }, + "node_modules/brace-expansion/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -3171,6 +4266,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001780", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", @@ -3457,25 +4561,51 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" } }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -3562,6 +4692,12 @@ "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/dnssd-advertise": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dnssd-advertise/-/dnssd-advertise-1.1.3.tgz", @@ -3595,6 +4731,12 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "license": "BSD-2-Clause" + }, "node_modules/error-stack-parser": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", @@ -3717,9 +4859,9 @@ } }, "node_modules/expo-blur": { - "version": "14.1.5", - "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-14.1.5.tgz", - "integrity": "sha512-CCLJHxN4eoAl06ESKT3CbMasJ98WsjF9ZQEJnuxtDb9ffrYbZ+g9ru84fukjNUOTtc8A8yXE5z8NgY1l0OMrmQ==", + "version": "55.0.10", + "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-55.0.10.tgz", + "integrity": "sha512-W6iMVArvCKYqhhZW55/V2yA3t9WcZU718Jg7vyIX+XtpDMYirEu3vqesnodfFvwTS+K1JHNLN+PAjQPy38+Rlg==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -3741,19 +4883,76 @@ "react-native": "*" } }, + "node_modules/expo-eas-client": { + "version": "55.0.2", + "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-55.0.2.tgz", + "integrity": "sha512-fjOgSXaZFBK2Xmzn/uw0DTF3BsYv97JEa4PYXXqVCEvNJPwJB1cV1eX6Xyq6iKGIhMPH9k62sOc+oUdt094WCw==", + "license": "MIT" + }, + "node_modules/expo-font": { + "version": "55.0.4", + "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.4.tgz", + "integrity": "sha512-ZKeGTFffPygvY5dM/9ATM2p7QDkhsaHopH7wFAWgP2lKzqUMS9B/RxCvw5CaObr9Ro7x9YptyeRKX2HmgmMfrg==", + "license": "MIT", + "dependencies": { + "fontfaceobserver": "^2.1.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-glass-effect": { + "version": "55.0.8", + "resolved": "https://registry.npmjs.org/expo-glass-effect/-/expo-glass-effect-55.0.8.tgz", + "integrity": "sha512-IvUjHb/4t6r2H/LXDjcQ4uDoHrmO2cLOvEb9leLavQ4HX5+P4LRtQrMDMlkWAn5Wo5DkLcG8+1CrQU2nqgogTA==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-haptics": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-14.1.4.tgz", - "integrity": "sha512-QZdE3NMX74rTuIl82I+n12XGwpDWKb8zfs5EpwsnGi/D/n7O2Jd4tO5ivH+muEG/OCJOMq5aeaVDqqaQOhTkcA==", + "version": "55.0.9", + "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-55.0.9.tgz", + "integrity": "sha512-KCRyHr/uu4syXmoq3aIQ6ahuaX6FGtlPkWGlLlHJ836WF3nG+5+oCaCQiI7qMTpml+Tp/V/zP4ZaowM2KHgLNA==", "license": "MIT", "peerDependencies": { "expo": "*" } }, + "node_modules/expo-image": { + "version": "55.0.6", + "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-55.0.6.tgz", + "integrity": "sha512-TKuu0uBmgTZlhd91Glv+V4vSBMlfl0bdQxfl97oKKZUo3OBC13l3eLik7v3VNLJN7PZbiwOAiXkZkqSOBx/Xsw==", + "license": "MIT", + "dependencies": { + "sf-symbols-typescript": "^2.2.0" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*", + "react-native-web": "*" + }, + "peerDependenciesMeta": { + "react-native-web": { + "optional": true + } + } + }, + "node_modules/expo-json-utils": { + "version": "55.0.0", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-55.0.0.tgz", + "integrity": "sha512-aupt/o5PDAb8dXDCb0JcRdkqnTLxe/F+La7jrnyd/sXlYFfRgBJLFOa1SqVFXm1E/Xam1SE/yw6eAb+DGY7Arg==", + "license": "MIT" + }, "node_modules/expo-linear-gradient": { - "version": "14.1.5", - "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz", - "integrity": "sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ==", + "version": "55.0.9", + "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-55.0.9.tgz", + "integrity": "sha512-S82iF+CVoSBVHdwusLQGh6Th/kcWLHU47jZhBPwyTrYWnsHZtb0oCqU96YvhDYvhbTdsuOaKEi+Xu+r/I2R8ow==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -3766,7 +4965,6 @@ "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-55.0.7.tgz", "integrity": "sha512-MiGCedere1vzQTEi2aGrkzd7eh/rPSz4w6F3GMBuAJzYl+/0VhIuyhozpEGrueyDIXWfzaUVOcn3SfxVi+kwQQ==", "license": "MIT", - "peer": true, "dependencies": { "expo-constants": "~55.0.7", "invariant": "^2.2.4" @@ -3776,6 +4974,19 @@ "react-native": "*" } }, + "node_modules/expo-manifests": { + "version": "55.0.9", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-55.0.9.tgz", + "integrity": "sha512-i82j3X4hbxYDe6kxUw4u8WfvbvTj2w+9BD9WKuL0mFRy+MjvdzdyaqAjEViWCKo/alquP/hTApDTQBb3UmWhkg==", + "license": "MIT", + "dependencies": { + "@expo/config": "~55.0.8", + "expo-json-utils": "~55.0.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "55.0.9", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-55.0.9.tgz", @@ -3805,44 +5016,107 @@ } }, "node_modules/expo-router": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-5.0.7.tgz", - "integrity": "sha512-NlEgRXCKtseDuIHBp87UfkvqsuVrc0MYG+zg33dopaN6wik4RkrWWxUYdNPHub0s/7qMye6zZBY4ZCrXwd/xpA==", + "version": "55.0.5", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-55.0.5.tgz", + "integrity": "sha512-PzN545wLtznKuVQmJXnAKB/JFjSJJIPHatsjJe4Cl6bRADr/MbWv5d2fqOpqFD/C0ZGCRHY1uBalq7mb5IQ3ZQ==", "license": "MIT", "dependencies": { - "@expo/metro-runtime": "5.0.4", - "@expo/server": "^0.6.2", - "@radix-ui/react-slot": "1.2.0", - "@react-navigation/bottom-tabs": "^7.3.10", - "@react-navigation/native": "^7.1.6", - "@react-navigation/native-stack": "^7.3.10", + "@expo/metro-runtime": "^55.0.6", + "@expo/schema-utils": "^55.0.2", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-tabs": "^1.1.12", + "@react-navigation/bottom-tabs": "^7.10.1", + "@react-navigation/native": "^7.1.28", + "@react-navigation/native-stack": "^7.10.1", "client-only": "^0.0.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "expo-glass-effect": "^55.0.8", + "expo-image": "^55.0.6", + "expo-server": "^55.0.6", + "expo-symbols": "^55.0.5", + "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", + "nanoid": "^3.3.8", + "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", - "react-native-is-edge-to-edge": "^1.1.6", - "schema-utils": "^4.0.1", + "react-native-is-edge-to-edge": "^1.2.1", "semver": "~7.6.3", "server-only": "^0.0.1", - "shallowequal": "^1.1.0" + "sf-symbols-typescript": "^2.1.0", + "shallowequal": "^1.1.0", + "use-latest-callback": "^0.2.1", + "vaul": "^1.1.2" }, "peerDependencies": { - "@react-navigation/drawer": "^7.3.9", + "@expo/log-box": "55.0.7", + "@expo/metro-runtime": "^55.0.6", + "@react-navigation/drawer": "^7.7.2", + "@testing-library/react-native": ">= 13.2.0", "expo": "*", - "expo-constants": "*", - "expo-linking": "*", + "expo-constants": "^55.0.7", + "expo-linking": "^55.0.7", + "react": "*", + "react-dom": "*", + "react-native": "*", + "react-native-gesture-handler": "*", "react-native-reanimated": "*", - "react-native-safe-area-context": "*", - "react-native-screens": "*" + "react-native-safe-area-context": ">= 5.4.0", + "react-native-screens": "*", + "react-native-web": "*", + "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" }, "peerDependenciesMeta": { "@react-navigation/drawer": { "optional": true }, - "@testing-library/jest-native": { + "@testing-library/react-native": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native-gesture-handler": { "optional": true }, "react-native-reanimated": { "optional": true + }, + "react-native-web": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + } + } + }, + "node_modules/expo-router/node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true } } }, @@ -3880,19 +5154,90 @@ "react-native": "*" } }, + "node_modules/expo-structured-headers": { + "version": "55.0.0", + "resolved": "https://registry.npmjs.org/expo-structured-headers/-/expo-structured-headers-55.0.0.tgz", + "integrity": "sha512-udaNvuWb45/Sryq9FLC/blwgOChhznuqlTrUzVjC0T83pMdcmscKJX23lnNDW6hCec8p81Y3z1DIFwIyk0g/PQ==", + "license": "MIT" + }, "node_modules/expo-symbols": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-0.4.5.tgz", - "integrity": "sha512-ZbgvJfACPfWaJxJrUd0YzDmH9X0Ci7vb5m0/ZpDz/tnF1vQJlkovvpFEHLUmCDSLIN7/fNK8t696KSpzfm8/kg==", + "version": "55.0.5", + "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-55.0.5.tgz", + "integrity": "sha512-W/QYRvnYVes947ZYOHtuKL8Gobs7BUjeu9oknzbo4jGnou7Ks6bj1CwdT0ZWNBgaTopbS4/POXumJIkW4cTPSQ==", "license": "MIT", "dependencies": { + "@expo-google-fonts/material-symbols": "^0.4.1", "sf-symbols-typescript": "^2.0.0" }, "peerDependencies": { "expo": "*", + "expo-font": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/expo-updates": { + "version": "55.0.13", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-55.0.13.tgz", + "integrity": "sha512-GuOhcyLqeZxUB2cX9R2prInmUSnm3NUJ3l5ZDwU6TrGCtHm9JsZyYqG+3+YwVLSKzwj57uPo6U71Gl7xBNaECg==", + "license": "MIT", + "dependencies": { + "@expo/code-signing-certificates": "^0.0.6", + "@expo/plist": "^0.5.2", + "@expo/spawn-async": "^1.7.2", + "arg": "^4.1.0", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "expo-eas-client": "~55.0.2", + "expo-manifests": "~55.0.9", + "expo-structured-headers": "~55.0.0", + "expo-updates-interface": "~55.1.3", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "ignore": "^5.3.1", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-updates": "bin/cli.js" + }, + "peerDependencies": { + "expo": "*", + "react": "*", "react-native": "*" } }, + "node_modules/expo-updates-interface": { + "version": "55.1.3", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-55.1.3.tgz", + "integrity": "sha512-UVVIiZqymQZJL+o/jh65kXOI97xdkbqBJJM0LMabaPMNLFnc6/WvOMOzmQs7SPyKb8+0PeBaFd7tj5DzF6JeQg==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-updates/node_modules/arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "license": "MIT" + }, + "node_modules/expo-updates/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/expo/node_modules/@expo/cli": { "version": "55.0.16", "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-55.0.16.tgz", @@ -3974,32 +5319,6 @@ } } }, - "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/metro-runtime": { - "version": "55.0.6", - "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-55.0.6.tgz", - "integrity": "sha512-l8VvgKN9md+URjeQDB+DnHVmvpcWI6zFLH6yv7GTv4sfRDKyaZ5zDXYjTP1phYdgW6ea2NrRtCGNIxylWhsgtg==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@expo/log-box": "55.0.7", - "anser": "^1.4.9", - "pretty-format": "^29.7.0", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-dom": "*", - "react-native": "*" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, "node_modules/expo/node_modules/@expo/cli/node_modules/@expo/router-server": { "version": "55.0.10", "resolved": "https://registry.npmjs.org/@expo/router-server/-/router-server-55.0.10.tgz", @@ -4034,23 +5353,6 @@ } } }, - "node_modules/expo/node_modules/@expo/log-box": { - "version": "55.0.7", - "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-55.0.7.tgz", - "integrity": "sha512-m7V1k2vlMp4NOj3fopjOg4zl/ANXyTRF3HMTMep2GZAKsPiDzgOQ41nm8CaU50/HlDIGXlCObss07gOn20UpHQ==", - "license": "MIT", - "dependencies": { - "@expo/dom-webview": "^55.0.3", - "anser": "^1.4.9", - "stacktrace-parser": "^0.1.10" - }, - "peerDependencies": { - "@expo/dom-webview": "^55.0.3", - "expo": "*", - "react": "*", - "react-native": "*" - } - }, "node_modules/expo/node_modules/@expo/metro-config": { "version": "55.0.9", "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-55.0.9.tgz", @@ -4097,54 +5399,6 @@ "react-native": "*" } }, - "node_modules/expo/node_modules/babel-preset-expo": { - "version": "55.0.11", - "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-55.0.11.tgz", - "integrity": "sha512-ti8t4xufD6gUQQh+qY+b+VT/1zyA0n1PBnwOzCkPUyEDiIVBpaOixR+BzVH68hqu9mH2wDfzoFuGgv+2LfRdqw==", - "license": "MIT", - "dependencies": { - "@babel/generator": "^7.20.5", - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-proposal-decorators": "^7.12.9", - "@babel/plugin-proposal-export-default-from": "^7.24.7", - "@babel/plugin-syntax-export-default-from": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-flow-strip-types": "^7.25.2", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@react-native/babel-preset": "0.83.2", - "babel-plugin-react-compiler": "^1.0.0", - "babel-plugin-react-native-web": "~0.21.0", - "babel-plugin-syntax-hermes-parser": "^0.32.0", - "babel-plugin-transform-flow-enums": "^0.0.2", - "debug": "^4.3.4", - "resolve-from": "^5.0.0" - }, - "peerDependencies": { - "@babel/runtime": "^7.20.0", - "expo": "*", - "expo-widgets": "^55.0.4", - "react-refresh": ">=0.14.0 <1.0.0" - }, - "peerDependenciesMeta": { - "@babel/runtime": { - "optional": true - }, - "expo": { - "optional": true - }, - "expo-widgets": { - "optional": true - } - } - }, "node_modules/expo/node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -4185,20 +5439,6 @@ "react-native": "*" } }, - "node_modules/expo/node_modules/expo-font": { - "version": "55.0.4", - "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-55.0.4.tgz", - "integrity": "sha512-ZKeGTFffPygvY5dM/9ATM2p7QDkhsaHopH7wFAWgP2lKzqUMS9B/RxCvw5CaObr9Ro7x9YptyeRKX2HmgmMfrg==", - "license": "MIT", - "dependencies": { - "fontfaceobserver": "^2.1.0" - }, - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*" - } - }, "node_modules/expo/node_modules/expo-keep-awake": { "version": "55.0.4", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-55.0.4.tgz", @@ -4209,6 +5449,23 @@ "react": "*" } }, + "node_modules/expo/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/expo/node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", @@ -4266,28 +5523,21 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/fb-dotslash": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", @@ -4450,6 +5700,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -4469,22 +5728,48 @@ } }, "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4989,11 +6274,18 @@ "node": ">=6" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } }, "node_modules/json5": { "version": "2.2.3", @@ -5308,6 +6600,21 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -5320,6 +6627,12 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -5445,12 +6758,34 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, "node_modules/marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", "license": "Apache-2.0" }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "license": "MIT" + }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", @@ -5949,6 +7284,15 @@ "node": ">=20.19.4" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -6116,6 +7460,12 @@ "node": ">=4" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -6309,6 +7659,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -6381,6 +7737,283 @@ "node": ">= 6" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/prosemirror-changeset": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz", + "integrity": "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz", + "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz", + "integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==", + "license": "MIT", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-markdown/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/prosemirror-markdown/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/prosemirror-markdown/node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/prosemirror-markdown/node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/prosemirror-markdown/node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/prosemirror-markdown/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/prosemirror-menu": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.0.tgz", + "integrity": "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==", + "license": "MIT", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", + "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz", + "integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz", + "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.4" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", + "license": "MIT", + "dependencies": { + "@remirror/core-constants": "3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz", + "integrity": "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.7", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.7.tgz", + "integrity": "sha512-jUwKNCEIGiqdvhlS91/2QAg21e4dfU5bH2iwmSDQeosXJgKF7smG0YSplOWK0cjSNgIqXe7VXqo7EIfUFJdt3w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/query-string": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", @@ -6426,14 +8059,36 @@ "node": ">=0.10.0" } }, - "node_modules/react-devtools-core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", - "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { - "shell-quote": "^1.6.1", - "ws": "^7" + "loose-envify": "^1.1.0" } }, "node_modules/react-fast-compare": { @@ -6518,10 +8173,19 @@ } } }, + "node_modules/react-native-fit-image": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz", + "integrity": "sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg==", + "license": "Beerware", + "dependencies": { + "prop-types": "^15.5.10" + } + }, "node_modules/react-native-gesture-handler": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz", - "integrity": "sha512-ZdWyOd1C8axKJHIfYxjJKCcxjWEpUtUWgTOVY2wynbiveSQDm8X/PDyAKXSer/GOtIpjudUbACOndZXCN3vHsw==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.30.0.tgz", + "integrity": "sha512-5YsnKHGa0X9C8lb5oCnKm0fLUPM6CRduvUUw2Bav4RIj/C3HcFh4RIUnF8wgG6JQWCL1//gRx4v+LVWgcIQdGA==", "license": "MIT", "dependencies": { "@egjs/hammerjs": "^2.0.17", @@ -6543,60 +8207,54 @@ "react-native": "*" } }, - "node_modules/react-native-reanimated": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz", - "integrity": "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw==", + "node_modules/react-native-markdown-display": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-native-markdown-display/-/react-native-markdown-display-7.0.2.tgz", + "integrity": "sha512-Mn4wotMvMfLAwbX/huMLt202W5DsdpMO/kblk+6eUs55S57VVNni1gzZCh5qpznYLjIQELNh50VIozEfY6fvaQ==", "license": "MIT", "dependencies": { - "@babel/plugin-transform-arrow-functions": "^7.0.0-0", - "@babel/plugin-transform-class-properties": "^7.0.0-0", - "@babel/plugin-transform-classes": "^7.0.0-0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", - "@babel/plugin-transform-optional-chaining": "^7.0.0-0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", - "@babel/plugin-transform-template-literals": "^7.0.0-0", - "@babel/plugin-transform-unicode-regex": "^7.0.0-0", - "@babel/preset-typescript": "^7.16.7", - "convert-source-map": "^2.0.0", - "invariant": "^2.2.4", - "react-native-is-edge-to-edge": "1.1.7" + "css-to-react-native": "^3.0.0", + "markdown-it": "^10.0.0", + "prop-types": "^15.7.2", + "react-native-fit-image": "^1.5.5" }, "peerDependencies": { - "@babel/core": "^7.0.0-0", - "react": "*", - "react-native": "*" + "react": ">=16.2.0", + "react-native": ">=0.50.4" } }, - "node_modules/react-native-reanimated/node_modules/react-native-is-edge-to-edge": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz", - "integrity": "sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w==", + "node_modules/react-native-safe-area-context": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", + "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" } }, - "node_modules/react-native-safe-area-context": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.4.0.tgz", - "integrity": "sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA==", + "node_modules/react-native-screens": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.23.0.tgz", + "integrity": "sha512-XhO3aK0UeLpBn4kLecd+J+EDeRRJlI/Ro9Fze06vo1q163VeYtzfU9QS09/VyDFMWR1qxDC1iazCArTPSFFiPw==", "license": "MIT", + "dependencies": { + "react-freeze": "^1.0.0", + "warn-once": "^0.1.0" + }, "peerDependencies": { "react": "*", "react-native": "*" } }, - "node_modules/react-native-screens": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.11.1.tgz", - "integrity": "sha512-F0zOzRVa3ptZfLpD0J8ROdo+y1fEPw+VBFq1MTY/iyDu08al7qFUO5hLMd+EYMda5VXGaTFCa8q7bOppUszhJw==", + "node_modules/react-native-webview": { + "version": "13.16.1", + "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.1.tgz", + "integrity": "sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==", "license": "MIT", "dependencies": { - "react-freeze": "^1.0.0", - "react-native-is-edge-to-edge": "^1.1.7", - "warn-once": "^0.1.0" + "escape-string-regexp": "^4.0.0", + "invariant": "2.2.4" }, "peerDependencies": { "react": "*", @@ -6626,20 +8284,13 @@ } } }, - "node_modules/react-native/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/react-native/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "hermes-parser": "0.32.0" } }, "node_modules/react-native/node_modules/commander": { @@ -6651,39 +8302,6 @@ "node": ">=18" } }, - "node_modules/react-native/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/react-native/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/react-native/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -6705,6 +8323,75 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -6773,15 +8460,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -6846,55 +8524,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", "license": "MIT" }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -6930,25 +8565,6 @@ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -7411,12 +9027,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, "node_modules/test-exclude/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -7427,27 +9037,6 @@ "concat-map": "0.0.1" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -7499,6 +9088,18 @@ "integrity": "sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==", "license": "MIT" }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -7521,7 +9122,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -7531,14 +9132,11 @@ "node": ">=14.17" } }, - "node_modules/undici": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", - "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "license": "MIT" }, "node_modules/undici-types": { "version": "7.18.2", @@ -7625,6 +9223,27 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-latest-callback": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", @@ -7634,6 +9253,28 @@ "react": ">=16.8" } }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -7679,12 +9320,31 @@ "node": ">= 0.8" } }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", "license": "MIT" }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index f891787..e49124e 100644 --- a/package.json +++ b/package.json @@ -9,20 +9,26 @@ "web": "expo start --web" }, "dependencies": { - "@react-native-async-storage/async-storage": "2.1.2", + "@10play/tentap-editor": "^1.0.1", + "@anthropic-ai/sdk": "^0.79.0", + "@expo/metro-runtime": "~55.0.6", + "@react-native-async-storage/async-storage": "2.2.0", + "babel-preset-expo": "~55.0.11", "expo": "~55.0.6", - "expo-blur": "~14.1.4", - "expo-haptics": "~14.1.4", - "expo-linear-gradient": "~14.1.4", - "expo-router": "~5.0.7", + "expo-blur": "~55.0.9", + "expo-haptics": "~55.0.8", + "expo-linear-gradient": "~55.0.8", + "expo-linking": "~55.0.7", + "expo-router": "~55.0.5", "expo-status-bar": "~55.0.4", - "expo-symbols": "~0.4.3", + "expo-updates": "~55.0.13", "react": "19.2.0", "react-native": "0.83.2", - "react-native-gesture-handler": "~2.24.0", - "react-native-reanimated": "~3.17.4", - "react-native-safe-area-context": "5.4.0", - "react-native-screens": "~4.11.1" + "react-native-gesture-handler": "~2.30.0", + "react-native-markdown-display": "^7.0.2", + "react-native-safe-area-context": "~5.6.2", + "react-native-screens": "~4.23.0", + "react-native-webview": "^13.16.1" }, "devDependencies": { "@types/react": "~19.2.2", diff --git a/src/components/AICollaborator.tsx b/src/components/AICollaborator.tsx new file mode 100644 index 0000000..6e9ec48 --- /dev/null +++ b/src/components/AICollaborator.tsx @@ -0,0 +1,550 @@ +import React, { useState } from 'react'; +import { + View, + Text, + Pressable, + ScrollView, + TextInput, + ActivityIndicator, + StyleSheet, + Modal, +} from 'react-native'; +import { LinearGradient } from 'expo-linear-gradient'; +import * as Haptics from 'expo-haptics'; +import { Colors } from '../constants/colors'; +import { + collaborateOnArticle, + CollaboratorOperation, + AIDebugInfo, +} from '../lib/ai'; +import AIDebugModal from './AIDebugModal'; + +interface AzureConfig { + endpoint: string; + apiKey: string; + deployment: string; +} + +interface Operation { + id: CollaboratorOperation; + icon: string; + label: string; + description: string; + hasInput?: boolean; + inputPlaceholder?: string; +} + +const OPERATIONS: Operation[] = [ + { + id: 'improve', + icon: '✨', + label: 'Improve Writing', + description: 'Polish clarity, flow & style throughout', + }, + { + id: 'strengthen-intro', + icon: '🎯', + label: 'Strengthen Intro', + description: 'Rewrite the opening to hook readers immediately', + }, + { + id: 'medical-review', + icon: '🩺', + label: 'Medical Review', + description: 'Add clinical nuance, accuracy & appropriate caveats', + }, + { + id: 'seo-optimize', + icon: '📈', + label: 'SEO Optimize', + description: 'Sharpen headings & keywords for search', + }, + { + id: 'add-section', + icon: '➕', + label: 'Add Section', + description: 'Insert a new section about a topic', + hasInput: true, + inputPlaceholder: 'What should the new section cover?', + }, + { + id: 'custom', + icon: '💬', + label: 'Custom Instruction', + description: 'Tell the AI exactly what to change', + hasInput: true, + inputPlaceholder: 'e.g. "Add a clinical case example to the second section"', + }, +]; + +interface AICollaboratorProps { + title: string; + notes: string; + azureConfig: AzureConfig; + getArticleHtml: () => Promise; + onApplyChanges: (newHtml: string) => void; +} + +export default function AICollaborator({ + title, + notes, + azureConfig, + getArticleHtml, + onApplyChanges, +}: AICollaboratorProps) { + const [selectedOp, setSelectedOp] = useState(null); + const [instruction, setInstruction] = useState(''); + const [loading, setLoading] = useState(false); + const [previewHtml, setPreviewHtml] = useState(null); + const [previewVisible, setPreviewVisible] = useState(false); + const [errorModal, setErrorModal] = useState<{ + error: string; + debugInfo?: AIDebugInfo; + } | null>(null); + + const selectedOpDef = OPERATIONS.find((o) => o.id === selectedOp); + + const handleRun = async () => { + if (!selectedOp) return; + if (!azureConfig.endpoint || !azureConfig.apiKey) { + setErrorModal({ error: 'Azure AI is not configured. Go to Settings to add your credentials.' }); + return; + } + if (!title.trim()) { + setErrorModal({ error: 'Add an article title before using the AI Collaborator.' }); + return; + } + if (selectedOpDef?.hasInput && !instruction.trim()) { + setErrorModal({ error: `Please describe ${selectedOp === 'add-section' ? 'the new section' : 'your instruction'} before running.` }); + return; + } + + setLoading(true); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + + const html = await getArticleHtml(); + if (!html || html === '

    ') { + setLoading(false); + setErrorModal({ error: 'The article is empty. Write some content before collaborating with AI.' }); + return; + } + + const res = await collaborateOnArticle( + azureConfig, + selectedOp, + html, + title, + notes, + instruction.trim() || undefined + ); + + setLoading(false); + + if (res.error) { + setErrorModal({ error: res.error, debugInfo: res.debugInfo }); + return; + } + if (res.content) { + setPreviewHtml(res.content); + setPreviewVisible(true); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + } + }; + + const handleApply = () => { + if (previewHtml) { + onApplyChanges(previewHtml); + setPreviewVisible(false); + setPreviewHtml(null); + setInstruction(''); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + } + }; + + const handleDiscard = () => { + setPreviewVisible(false); + setPreviewHtml(null); + }; + + return ( + + setErrorModal(null)} + /> + + {/* Preview Modal */} + + + + + AI Revision Preview + + {selectedOpDef?.icon} {selectedOpDef?.label} + + + + Discard + + + + + + + 📝 This is the AI's revision of your article. Review it below, then apply or discard. + + + {/* Show raw HTML for preview — clean text extraction */} + + {previewHtml + ?.replace(/<\/?(h[1-6])[^>]*>/gi, (_, tag) => tag.toLowerCase().startsWith('h') ? '\n\n' : '\n') + .replace(//gi, '\n') + .replace(/<\/p>/gi, '\n\n') + .replace(/
  • /gi, '\n• ') + .replace(/<[^>]+>/g, '') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/ /g, ' ') + .replace(/\n{3,}/g, '\n\n') + .trim()} + + + + + + Discard + + + + ✓ Apply to Article + + + + + + + {/* Header */} + + 🤝 + + AI Collaborator + Revise, expand & refine your article + + + + {/* Operation grid */} + + {OPERATIONS.map((op) => { + const isSelected = selectedOp === op.id; + return ( + { + setSelectedOp(isSelected ? null : op.id); + setInstruction(''); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + }} + disabled={loading} + > + {op.icon} + + + {op.label} + + {op.description} + + {isSelected && } + + ); + })} + + + {/* Input for operations that need it */} + {selectedOp && selectedOpDef?.hasInput && ( + + + + )} + + {/* Run button */} + {selectedOp && ( + + + {loading ? ( + + + AI is revising… + + ) : ( + + + {selectedOpDef?.icon} Run {selectedOpDef?.label} + + + )} + + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + backgroundColor: Colors.elevated, + borderRadius: 20, + borderWidth: 1, + borderColor: Colors.border, + overflow: 'hidden', + }, + // Header + header: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + padding: 16, + borderBottomWidth: 1, + borderBottomColor: Colors.border, + }, + headerIcon: { + fontSize: 24, + }, + headerTitle: { + color: Colors.textPrimary, + fontSize: 16, + fontWeight: '700', + }, + headerSub: { + color: Colors.textMuted, + fontSize: 12, + marginTop: 1, + }, + // Operation grid + opsGrid: { + padding: 12, + gap: 8, + }, + opBtn: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + padding: 12, + borderRadius: 12, + backgroundColor: Colors.surface, + borderWidth: 1, + borderColor: Colors.border, + }, + opBtnSelected: { + borderColor: Colors.accent, + backgroundColor: Colors.accentSoft, + }, + opIcon: { + fontSize: 20, + width: 28, + textAlign: 'center', + }, + opText: { + flex: 1, + }, + opLabel: { + color: Colors.textPrimary, + fontSize: 14, + fontWeight: '600', + }, + opLabelSelected: { + color: Colors.accentBright, + }, + opDesc: { + color: Colors.textMuted, + fontSize: 12, + marginTop: 1, + }, + opCheck: { + color: Colors.accentBright, + fontSize: 14, + fontWeight: '700', + }, + // Instruction input + inputWrapper: { + marginHorizontal: 12, + marginBottom: 8, + }, + instructionInput: { + backgroundColor: Colors.surface, + borderRadius: 12, + borderWidth: 1, + borderColor: Colors.accent, + padding: 12, + color: Colors.textPrimary, + fontSize: 14, + lineHeight: 20, + minHeight: 70, + }, + // Run button + runRow: { + padding: 12, + paddingTop: 4, + }, + runBtn: { + borderRadius: 14, + overflow: 'hidden', + }, + runBtnDisabled: { + opacity: 0.6, + }, + runBtnGradient: { + paddingVertical: 14, + alignItems: 'center', + }, + runBtnInner: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 8, + backgroundColor: Colors.accentSoft, + paddingVertical: 14, + borderRadius: 14, + borderWidth: 1, + borderColor: Colors.accent, + }, + runBtnText: { + color: Colors.white, + fontSize: 15, + fontWeight: '700', + }, + // Preview modal + previewModal: { + flex: 1, + backgroundColor: Colors.bg, + }, + previewHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + padding: 20, + paddingTop: 24, + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: Colors.border, + }, + previewTitle: { + color: Colors.textPrimary, + fontSize: 20, + fontWeight: '700', + }, + previewSubtitle: { + color: Colors.textMuted, + fontSize: 13, + marginTop: 2, + }, + discardBtn: { + paddingHorizontal: 12, + paddingVertical: 6, + }, + discardBtnText: { + color: Colors.textMuted, + fontSize: 15, + }, + previewScroll: { + flex: 1, + }, + previewContent: { + padding: 20, + paddingBottom: 40, + }, + previewNote: { + backgroundColor: Colors.accentSoft, + borderRadius: 10, + padding: 12, + marginBottom: 16, + borderWidth: 1, + borderColor: Colors.accent, + }, + previewNoteText: { + color: Colors.accentBright, + fontSize: 13, + lineHeight: 18, + }, + previewText: { + color: Colors.textSecondary, + fontSize: 15, + lineHeight: 24, + }, + previewFooter: { + flexDirection: 'row', + gap: 10, + padding: 16, + paddingBottom: 30, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: Colors.border, + }, + discardFooterBtn: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 14, + backgroundColor: Colors.surface, + borderRadius: 14, + borderWidth: 1, + borderColor: Colors.border, + }, + discardFooterText: { + color: Colors.textSecondary, + fontSize: 15, + fontWeight: '600', + }, + applyBtn: { + flex: 2, + borderRadius: 14, + overflow: 'hidden', + }, + applyBtnGradient: { + paddingVertical: 14, + alignItems: 'center', + }, + applyBtnText: { + color: Colors.white, + fontSize: 15, + fontWeight: '700', + }, +}); diff --git a/src/components/AIDebugModal.tsx b/src/components/AIDebugModal.tsx new file mode 100644 index 0000000..fb58c41 --- /dev/null +++ b/src/components/AIDebugModal.tsx @@ -0,0 +1,263 @@ +import React from 'react'; +import { + Modal, + View, + Text, + Pressable, + ScrollView, + TextInput, + Share, + StyleSheet, +} from 'react-native'; +import { Colors } from '../constants/colors'; +import { AIDebugInfo } from '../lib/ai'; + +interface Props { + visible: boolean; + error: string; + debugInfo?: AIDebugInfo; + onClose: () => void; +} + +export default function AIDebugModal({ visible, error, debugInfo, onClose }: Props) { + const shareText = debugInfo + ? [ + `=== AI Request Failed ===`, + `Error: ${error}`, + ``, + `--- Request ---`, + `Endpoint: ${debugInfo.endpoint || '(empty)'}`, + `Deployment: ${debugInfo.deployment || '(empty)'}`, + `Key length: ${debugInfo.keyLength} chars`, + `Key prefix: ${debugInfo.keyLength > 8 ? '(see Settings)' : '(too short or empty)'}`, + ``, + `--- Response ---`, + `HTTP Status: ${debugInfo.status || 'N/A (network error)'}`, + `Body: ${debugInfo.rawResponse || '(none)'}`, + ].join('\n') + : error; + + return ( + + + e.stopPropagation()}> + {/* Header */} + + AI Request Failed + + + + + + + {/* Error summary */} + + {error} + + + {debugInfo && ( + <> + {/* Request section */} + REQUEST + + + + + {/* Response section */} + RESPONSE + = 400} /> + Body + + + )} + + + {/* Actions */} + + Share.share({ message: shareText })} + > + Share Debug Info + + + OK + + + + + + ); +} + +function Row({ label, value, highlight }: { label: string; value: string; highlight?: boolean }) { + return ( + + {label} + + + ); +} + +const rowStyles = StyleSheet.create({ + row: { + marginBottom: 8, + }, + label: { + color: Colors.textMuted, + fontSize: 11, + fontWeight: '600', + textTransform: 'uppercase', + letterSpacing: 0.5, + marginBottom: 3, + }, + value: { + color: Colors.textSecondary, + fontSize: 13, + fontFamily: 'monospace', + backgroundColor: Colors.surface, + borderRadius: 8, + borderWidth: 1, + borderColor: Colors.border, + paddingHorizontal: 10, + paddingVertical: 8, + }, + valueHighlight: { + borderColor: Colors.warning, + color: Colors.warning, + }, +}); + +const styles = StyleSheet.create({ + overlay: { + flex: 1, + backgroundColor: Colors.overlay, + justifyContent: 'center', + paddingHorizontal: 20, + }, + sheet: { + backgroundColor: Colors.modal, + borderRadius: 20, + borderWidth: 1, + borderColor: Colors.border, + maxHeight: '85%', + overflow: 'hidden', + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingTop: 20, + paddingBottom: 12, + borderBottomWidth: 1, + borderBottomColor: Colors.border, + }, + title: { + color: Colors.textPrimary, + fontSize: 17, + fontWeight: '700', + }, + closeBtn: { + padding: 4, + }, + closeText: { + color: Colors.textMuted, + fontSize: 16, + }, + scroll: { + padding: 20, + }, + errorBadge: { + backgroundColor: Colors.dangerSoft, + borderRadius: 10, + borderWidth: 1, + borderColor: Colors.danger, + paddingHorizontal: 12, + paddingVertical: 8, + marginBottom: 20, + }, + errorBadgeText: { + color: Colors.danger, + fontSize: 13, + fontWeight: '600', + }, + sectionLabel: { + color: Colors.textMuted, + fontSize: 11, + fontWeight: '700', + letterSpacing: 1, + marginBottom: 10, + marginTop: 4, + }, + responseLabel: { + color: Colors.textMuted, + fontSize: 11, + fontWeight: '600', + textTransform: 'uppercase', + letterSpacing: 0.5, + marginBottom: 3, + }, + responseBody: { + color: Colors.textSecondary, + fontSize: 12, + fontFamily: 'monospace', + backgroundColor: Colors.surface, + borderRadius: 8, + borderWidth: 1, + borderColor: Colors.border, + paddingHorizontal: 10, + paddingVertical: 8, + marginBottom: 8, + minHeight: 80, + }, + actions: { + flexDirection: 'row', + gap: 10, + padding: 16, + borderTopWidth: 1, + borderTopColor: Colors.border, + }, + shareBtn: { + flex: 1, + backgroundColor: Colors.accentSoft, + borderRadius: 12, + borderWidth: 1, + borderColor: Colors.accent, + paddingVertical: 13, + alignItems: 'center', + }, + shareBtnText: { + color: Colors.accentBright, + fontSize: 14, + fontWeight: '600', + }, + okBtn: { + flex: 1, + backgroundColor: Colors.surface, + borderRadius: 12, + borderWidth: 1, + borderColor: Colors.border, + paddingVertical: 13, + alignItems: 'center', + }, + okBtnText: { + color: Colors.textPrimary, + fontSize: 14, + fontWeight: '600', + }, +}); diff --git a/src/components/AIPanel.tsx b/src/components/AIPanel.tsx index 83893cf..ee5b8f6 100644 --- a/src/components/AIPanel.tsx +++ b/src/components/AIPanel.tsx @@ -17,7 +17,15 @@ import { improveTitles, suggestTags, writeIntro, + AIDebugInfo, } from '../lib/ai'; +import AIDebugModal from './AIDebugModal'; + +interface AzureConfig { + endpoint: string; + apiKey: string; + deployment: string; +} interface AIActionItem { action: AIAction; @@ -56,21 +64,23 @@ const AI_ACTIONS: AIActionItem[] = [ interface AIPanelProps { title: string; notes: string; - apiKey: string; + azureConfig: AzureConfig; onTagsGenerated?: (tags: string[]) => void; + onInsertContent?: (content: string) => void; } -export default function AIPanel({ title, notes, apiKey, onTagsGenerated }: AIPanelProps) { +export default function AIPanel({ title, notes, azureConfig, onTagsGenerated, onInsertContent }: AIPanelProps) { const [activeAction, setActiveAction] = useState(null); const [loading, setLoading] = useState(false); const [result, setResult] = useState(''); const [resultAction, setResultAction] = useState(null); + const [errorModal, setErrorModal] = useState<{ error: string; debugInfo?: AIDebugInfo } | null>(null); const runAction = async (action: AIAction) => { - if (!apiKey) { + if (!azureConfig.endpoint || !azureConfig.apiKey) { Alert.alert( - 'API Key Required', - 'Add your Anthropic API key in Settings to use AI features.', + 'Azure Configuration Required', + 'Add your Azure AI Foundry endpoint and API key in Settings to use AI features.', [{ text: 'OK' }] ); return; @@ -88,23 +98,23 @@ export default function AIPanel({ title, notes, apiKey, onTagsGenerated }: AIPan let res; switch (action) { case 'outline': - res = await generateOutline(apiKey, title, notes); + res = await generateOutline(azureConfig, title, notes); break; case 'titles': - res = await improveTitles(apiKey, title, notes); + res = await improveTitles(azureConfig, title, notes); break; case 'tags': - res = await suggestTags(apiKey, title, notes); + res = await suggestTags(azureConfig, title, notes); break; case 'intro': - res = await writeIntro(apiKey, title, notes); + res = await writeIntro(azureConfig, title, notes); break; } setLoading(false); if (res.error) { - Alert.alert('AI Error', res.error); + setErrorModal({ error: res.error, debugInfo: res.debugInfo }); setActiveAction(null); return; } @@ -126,6 +136,12 @@ export default function AIPanel({ title, notes, apiKey, onTagsGenerated }: AIPan return ( + setErrorModal(null)} + /> {/* Header */} a.action === resultAction)?.icon}{' '} {AI_ACTIONS.find((a) => a.action === resultAction)?.label} + {onInsertContent && (resultAction === 'outline' || resultAction === 'intro') && ( + { + onInsertContent(result); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + }} + > + ↙ Insert + + )} ', group: 4 }, + { action: 'hr', label: '\u2015', group: 4 }, +]; + +interface EditorToolbarProps { + onFormat: (action: FormatAction) => void; +} + +export default function EditorToolbar({ onFormat }: EditorToolbarProps) { + const handlePress = (action: FormatAction) => { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + onFormat(action); + }; + + return ( + + + {BUTTONS.map((btn, idx) => { + const prevGroup = idx > 0 ? BUTTONS[idx - 1].group : btn.group; + const showDivider = idx > 0 && prevGroup !== btn.group; + return ( + + {showDivider && } + [styles.btn, pressed && styles.btnPressed]} + onPress={() => handlePress(btn.action)} + > + + {btn.label} + + + + ); + })} + + + ); +} + +const styles = StyleSheet.create({ + wrapper: { + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: Colors.border, + backgroundColor: Colors.elevated, + }, + container: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 8, + paddingVertical: 6, + gap: 2, + }, + divider: { + width: StyleSheet.hairlineWidth, + height: 20, + backgroundColor: Colors.border, + marginHorizontal: 6, + }, + btn: { + minWidth: 36, + height: 34, + borderRadius: 8, + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 8, + }, + btnPressed: { + backgroundColor: Colors.accentSoft, + }, + btnText: { + color: Colors.textSecondary, + fontSize: 14, + fontWeight: '500', + }, + boldText: { + color: Colors.textPrimary, + fontWeight: '800', + fontSize: 15, + }, + italicText: { + color: Colors.textPrimary, + fontStyle: 'italic', + fontSize: 15, + }, + strikeText: { + color: Colors.textSecondary, + textDecorationLine: 'line-through', + fontSize: 15, + }, +}); diff --git a/src/components/HeroImagePanel.tsx b/src/components/HeroImagePanel.tsx new file mode 100644 index 0000000..d1eb869 --- /dev/null +++ b/src/components/HeroImagePanel.tsx @@ -0,0 +1,301 @@ +import React, { useState } from 'react'; +import { + View, + Text, + Image, + Pressable, + ActivityIndicator, + StyleSheet, + Alert, +} from 'react-native'; +import { LinearGradient } from 'expo-linear-gradient'; +import * as Haptics from 'expo-haptics'; +import { Colors } from '../constants/colors'; +import { generateHeroImage, HeroImageResponse } from '../lib/ai'; + +interface AzureConfig { + endpoint: string; + apiKey: string; + deployment: string; +} + +interface ImageConfig { + endpoint: string; + apiKey: string; + deployment: string; +} + +interface HeroImagePanelProps { + title: string; + notes: string; + claudeConfig: AzureConfig; + imageConfig: ImageConfig; + currentDataUri?: string; + onImageGenerated: (dataUri: string) => void; +} + +export default function HeroImagePanel({ + title, + notes, + claudeConfig, + imageConfig, + currentDataUri, + onImageGenerated, +}: HeroImagePanelProps) { + const [generating, setGenerating] = useState(false); + const [error, setError] = useState(null); + + const handleGenerate = async () => { + if (!title.trim()) { + Alert.alert('Title required', 'Add an article title before generating a hero image.'); + return; + } + if (!claudeConfig.endpoint || !claudeConfig.apiKey) { + Alert.alert('AI not configured', 'Configure your Azure AI settings in Settings first.'); + return; + } + if (!imageConfig.endpoint || !imageConfig.apiKey) { + Alert.alert( + 'Image generation not configured', + 'Add your Azure OpenAI endpoint and key in Settings → Image Generation.' + ); + return; + } + + setGenerating(true); + setError(null); + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + + const result: HeroImageResponse = await generateHeroImage( + claudeConfig, + imageConfig, + title, + notes + ); + + setGenerating(false); + + if (result.error) { + setError(result.error); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); + return; + } + if (result.dataUri) { + onImageGenerated(result.dataUri); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + } + }; + + return ( + + {currentDataUri ? ( + // Show existing hero image + + + + + + 🖼️ Hero Image + + + {generating ? ( + + ) : ( + ↺ Regenerate + )} + + + + ) : ( + // Empty state — prompt to generate + + {generating ? ( + <> + + Generating hero image… + + Claude is crafting a prompt, then DALL·E 3 is painting it + + + ) : ( + <> + + 🎨 + + Generate Hero Image + + Claude writes the prompt · DALL·E 3 generates the image + + + )} + + )} + + {error && ( + + ⚠ {error} + setError(null)}> + Dismiss + + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { + marginHorizontal: 12, + marginBottom: 4, + }, + // Image display + imageWrapper: { + borderRadius: 16, + overflow: 'hidden', + borderWidth: 1, + borderColor: Colors.border, + aspectRatio: 16 / 7, + }, + image: { + width: '100%', + height: '100%', + }, + imageGradient: { + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + height: '60%', + }, + imageOverlay: { + position: 'absolute', + bottom: 10, + left: 12, + right: 12, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + imageBadge: { + backgroundColor: 'rgba(0,0,0,0.5)', + borderRadius: 8, + paddingHorizontal: 8, + paddingVertical: 4, + }, + imageBadgeText: { + color: Colors.white, + fontSize: 12, + fontWeight: '600', + }, + regenBtn: { + backgroundColor: 'rgba(124, 106, 247, 0.85)', + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 5, + minWidth: 44, + alignItems: 'center', + }, + regenBtnDisabled: { + opacity: 0.6, + }, + regenBtnText: { + color: Colors.white, + fontSize: 12, + fontWeight: '700', + }, + // Empty state + emptyState: { + borderRadius: 16, + borderWidth: 1, + borderColor: Colors.border, + borderStyle: 'dashed', + backgroundColor: Colors.surface, + padding: 28, + alignItems: 'center', + gap: 10, + }, + emptyStateGenerating: { + borderStyle: 'solid', + borderColor: Colors.accent, + backgroundColor: Colors.accentSoft, + }, + emptyIcon: { + width: 52, + height: 52, + borderRadius: 16, + alignItems: 'center', + justifyContent: 'center', + }, + emptyIconText: { + fontSize: 26, + }, + emptyTitle: { + color: Colors.textPrimary, + fontSize: 16, + fontWeight: '700', + }, + emptySubtitle: { + color: Colors.textMuted, + fontSize: 13, + textAlign: 'center', + lineHeight: 18, + }, + generatingTitle: { + color: Colors.accentBright, + fontSize: 15, + fontWeight: '700', + marginTop: 8, + }, + generatingSubtitle: { + color: Colors.textMuted, + fontSize: 13, + textAlign: 'center', + lineHeight: 18, + }, + // Error + errorBox: { + flexDirection: 'row', + alignItems: 'flex-start', + justifyContent: 'space-between', + gap: 12, + backgroundColor: Colors.dangerSoft, + borderRadius: 10, + borderWidth: 1, + borderColor: 'rgba(255,107,107,0.3)', + padding: 12, + marginTop: 8, + }, + errorText: { + color: Colors.danger, + fontSize: 13, + lineHeight: 18, + flex: 1, + }, + errorDismiss: { + color: Colors.textMuted, + fontSize: 12, + fontWeight: '600', + }, +}); diff --git a/src/components/IdeaCard.tsx b/src/components/IdeaCard.tsx index 2776cb4..687fca2 100644 --- a/src/components/IdeaCard.tsx +++ b/src/components/IdeaCard.tsx @@ -30,38 +30,57 @@ function formatDate(isoString: string): string { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } +function estimateReadTime(idea: ArticleIdea): string { + if (idea.content) { + const text = idea.content.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); + const words = text.split(' ').filter(Boolean).length; + if (words > 0) { + const mins = Math.max(1, Math.round(words / 200)); + return `${words.toLocaleString()} words · ${mins} min`; + } + } + const text = [idea.title, idea.notes].join(' '); + const words = text.split(/\s+/).filter(Boolean).length; + const mins = Math.max(1, Math.round(words / 200)); + return `~${mins} min read`; +} + +interface AIChip { + label: string; +} + +function getAIChips(idea: ArticleIdea): AIChip[] { + const chips: AIChip[] = []; + if (idea.content && idea.content.replace(/<[^>]*>/g, '').trim()) { + chips.push({ label: '✍ Written' }); + } + if (idea.aiContent?.outline) chips.push({ label: 'Outline' }); + if (idea.aiContent?.intro) chips.push({ label: 'Intro' }); + if (idea.aiContent?.improvedTitles?.length) { + chips.push({ label: `${idea.aiContent.improvedTitles.length} titles` }); + } + if (idea.aiContent?.suggestedTags?.length) chips.push({ label: 'Tags' }); + return chips; +} + export default function IdeaCard({ idea, onPress, onLongPress }: IdeaCardProps) { const scale = useRef(new Animated.Value(1)).current; const { width } = useWindowDimensions(); const isTablet = width >= 600; const onPressIn = () => { - Animated.spring(scale, { - toValue: 0.97, - useNativeDriver: true, - tension: 300, - friction: 20, - }).start(); + Animated.spring(scale, { toValue: 0.97, useNativeDriver: true, tension: 300, friction: 20 }).start(); }; - const onPressOut = () => { - Animated.spring(scale, { - toValue: 1, - useNativeDriver: true, - tension: 300, - friction: 20, - }).start(); + Animated.spring(scale, { toValue: 1, useNativeDriver: true, tension: 300, friction: 20 }).start(); }; - const hasAI = !!( - idea.aiContent?.outline || - idea.aiContent?.intro || - idea.aiContent?.improvedTitles?.length - ); - const statusColor = StatusColors[idea.status] ?? Colors.statusDraft; + const aiChips = getAIChips(idea); + const hasAI = aiChips.length > 0; const visibleTags = idea.tags.slice(0, isTablet ? 4 : 3); const extraTags = idea.tags.length - visibleTags.length; + const readTime = estimateReadTime(idea); return ( @@ -72,21 +91,14 @@ export default function IdeaCard({ idea, onPress, onLongPress }: IdeaCardProps) onPressIn={onPressIn} onPressOut={onPressOut} > - {/* Status bar on left edge */} - + {/* Colored top border — status accent */} + - {/* Top row: title + AI badge */} - - - {idea.title} - - {hasAI && ( - - - - )} - + {/* Title */} + + {idea.title} + {/* Notes preview */} {!!idea.notes && ( @@ -95,6 +107,16 @@ export default function IdeaCard({ idea, onPress, onLongPress }: IdeaCardProps) )} + {/* AI content row — what has been generated */} + {hasAI && ( + + + + {aiChips.map((c) => c.label).join(' · ')} + + + )} + {/* Tags */} {idea.tags.length > 0 && ( @@ -107,7 +129,7 @@ export default function IdeaCard({ idea, onPress, onLongPress }: IdeaCardProps) )} - {/* Footer: status + date */} + {/* Footer */} @@ -115,7 +137,11 @@ export default function IdeaCard({ idea, onPress, onLongPress }: IdeaCardProps) {StatusLabels[idea.status]} - {formatDate(idea.updatedAt)} + + {readTime} + · + {formatDate(idea.updatedAt)} + @@ -132,56 +158,53 @@ const styles = StyleSheet.create({ borderRadius: 16, borderWidth: 1, borderColor: Colors.border, - flexDirection: 'row', overflow: 'hidden', }, - statusBar: { - width: 3, - borderRadius: 2, - margin: 12, - marginRight: 0, - borderTopLeftRadius: 2, - borderBottomLeftRadius: 2, - minHeight: 40, + topAccent: { + height: 3, }, content: { - flex: 1, padding: 14, - paddingLeft: 12, - }, - topRow: { - flexDirection: 'row', - alignItems: 'flex-start', - gap: 8, }, title: { - flex: 1, color: Colors.textPrimary, - fontSize: 16, - fontWeight: '600', - lineHeight: 22, + fontSize: 17, + fontWeight: '700', + lineHeight: 23, + letterSpacing: -0.2, + marginBottom: 6, + }, + notes: { + color: Colors.textSecondary, + fontSize: 13, + lineHeight: 18, + marginBottom: 10, }, - aiBadge: { + // AI content strip + aiRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 5, backgroundColor: Colors.accentSoft, borderRadius: 8, - paddingHorizontal: 6, - paddingVertical: 3, - marginTop: 1, + paddingHorizontal: 10, + paddingVertical: 5, + marginBottom: 10, + alignSelf: 'flex-start', }, - aiBadgeText: { + aiRowIcon: { fontSize: 12, }, - notes: { - color: Colors.textSecondary, - fontSize: 13, - lineHeight: 18, - marginTop: 5, + aiRowText: { + color: Colors.accentBright, + fontSize: 12, + fontWeight: '500', }, tagsRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 6, - marginTop: 10, + marginBottom: 12, }, moreTags: { color: Colors.textMuted, @@ -193,7 +216,6 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - marginTop: 12, }, statusPill: { flexDirection: 'row', @@ -214,6 +236,19 @@ const styles = StyleSheet.create({ fontSize: 11, fontWeight: '600', }, + footerRight: { + flexDirection: 'row', + alignItems: 'center', + gap: 5, + }, + readTime: { + color: Colors.textMuted, + fontSize: 12, + }, + dot: { + color: Colors.textMuted, + fontSize: 12, + }, date: { color: Colors.textMuted, fontSize: 12, diff --git a/src/lib/ai.ts b/src/lib/ai.ts index 20f8073..fec8eb9 100644 --- a/src/lib/ai.ts +++ b/src/lib/ai.ts @@ -1,42 +1,79 @@ -const ANTHROPIC_API = 'https://api.anthropic.com/v1/messages'; -const MODEL = 'claude-opus-4-6'; +import Anthropic from '@anthropic-ai/sdk'; + +export interface AIDebugInfo { + endpoint: string; + deployment: string; + keyLength: number; + status: number; + requestBody: object; + rawResponse: string; +} export interface AIResponse { content: string; error?: string; + debugInfo?: AIDebugInfo; } -async function callClaude(apiKey: string, prompt: string): Promise { - try { - const res = await fetch(ANTHROPIC_API, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': apiKey, - 'anthropic-version': '2023-06-01', - 'anthropic-dangerous-direct-browser-calls': 'true', - }, - body: JSON.stringify({ - model: MODEL, - max_tokens: 1500, - messages: [{ role: 'user', content: prompt }], - }), - }); +interface AzureConfig { + endpoint: string; // base URL, e.g. https://resource.services.ai.azure.com/anthropic/ + apiKey: string; + deployment: string; // model deployment name, e.g. claude-sonnet-4-6 +} - if (!res.ok) { - const err = await res.json().catch(() => ({})); - return { content: '', error: (err as any).error?.message ?? `API error ${res.status}` }; - } +function makeClient(config: AzureConfig): Anthropic { + // Strip trailing /v1/messages if user pasted the full URL + const baseURL = config.endpoint.trim().replace(/\/v1\/messages(\?.*)?$/, ''); + return new Anthropic({ + baseURL, + apiKey: config.apiKey, + // Azure AI Foundry uses 'api-key' header instead of Anthropic's 'x-api-key' + defaultHeaders: { 'api-key': config.apiKey }, + // Azure requires the api-version as a query param + defaultQuery: { 'api-version': '2023-06-01' }, + dangerouslyAllowBrowser: true, + }); +} + +async function callClaude(config: AzureConfig, prompt: string): Promise { + const client = makeClient(config); + const requestBody = { + model: config.deployment, + max_tokens: 1500, + messages: [{ role: 'user' as const, content: prompt }], + }; + const keyLength = config.apiKey?.length ?? 0; + console.log('[AI] baseURL:', config.endpoint); + console.log('[AI] deployment:', config.deployment); + console.log('[AI] apiKey length:', keyLength); - const data = await res.json(); - return { content: data.content[0].text }; + try { + const message = await client.messages.create(requestBody); + const text = message.content[0].type === 'text' ? message.content[0].text : ''; + return { content: text }; } catch (e: any) { - return { content: '', error: e.message ?? 'Network error' }; + // Anthropic SDK throws structured errors + const status = e.status ?? 0; + const rawResponse = e.message ?? String(e); + console.log('[AI] error status:', status); + console.log('[AI] error:', rawResponse); + return { + content: '', + error: `HTTP ${status || 'Network error'}`, + debugInfo: { + endpoint: config.endpoint, + deployment: config.deployment, + keyLength, + status, + requestBody, + rawResponse: e.error ? JSON.stringify(e.error) : rawResponse, + }, + }; } } export async function generateOutline( - apiKey: string, + config: AzureConfig, title: string, notes: string ): Promise { @@ -52,11 +89,11 @@ Generate a detailed article outline with: - A conclusion note Format it cleanly with markdown. Be specific and actionable.`; - return callClaude(apiKey, prompt); + return callClaude(config, prompt); } export async function improveTitles( - apiKey: string, + config: AzureConfig, title: string, notes: string ): Promise { @@ -72,11 +109,11 @@ Suggest 5 improved article titles. Make them: - Varied in style (list, how-to, question, statement, bold claim) Return just the 5 titles, numbered. No explanations.`; - return callClaude(apiKey, prompt); + return callClaude(config, prompt); } export async function suggestTags( - apiKey: string, + config: AzureConfig, title: string, notes: string ): Promise { @@ -87,11 +124,11 @@ ${notes ? `Notes: ${notes}` : ''} Suggest 6-8 relevant tags/categories. Consider topic, audience, content type, and industry. Return only a comma-separated list of short tags (1-3 words each). No explanations.`; - return callClaude(apiKey, prompt); + return callClaude(config, prompt); } export async function writeIntro( - apiKey: string, + config: AzureConfig, title: string, notes: string ): Promise { @@ -106,5 +143,219 @@ Write a compelling 2-3 paragraph introduction that: - Previews what they'll learn Keep it engaging and conversational but professional. About 150-200 words.`; - return callClaude(apiKey, prompt); + return callClaude(config, prompt); +} + +// ─── Image Generation ───────────────────────────────────────────────────────── + +interface ImageConfig { + endpoint: string; // Azure OpenAI endpoint e.g. https://resource.openai.azure.com/ + apiKey: string; + deployment: string; // e.g. dall-e-3 +} + +export interface HeroImageResponse { + dataUri?: string; // base64 data URI — persists across sessions + error?: string; +} + +/** Step 1: Ask Claude to craft an optimised DALL-E prompt for the article */ +async function buildImagePrompt( + config: AzureConfig, + title: string, + notes: string +): Promise { + const res = await callClaude(config, `You are creating a DALL-E 3 prompt for a hero image for an article on aipsychmd.com — a professional blog about AI and psychiatry. + +Article title: "${title}" +${notes ? `Notes: ${notes}` : ''} + +Write a single DALL-E 3 image generation prompt (2–4 sentences) for a wide-format (16:9) editorial hero image that: +- Captures the essence of the article topic visually +- Has a clean, modern, professional aesthetic suitable for a medical/tech publication +- Uses abstract or conceptual imagery — NO text, NO real faces, NO identifiable people +- Fits a dark, sophisticated palette (deep blues, purples, cool grays, with subtle warm accents) +- Could include: abstract neural networks, brain/mind imagery, flowing data visualisations, clinical symbols combined with technology motifs, calm yet intelligent atmosphere + +Return ONLY the prompt text, nothing else.`); + return res.content || `Abstract digital illustration representing ${title}, clinical and technological, deep blue and purple palette, wide format, no text, no people`; +} + +/** Step 2: Call Azure OpenAI DALL-E 3 API and download the image as a base64 data URI */ +export async function generateHeroImage( + claudeConfig: AzureConfig, + imageConfig: ImageConfig, + title: string, + notes: string +): Promise { + if (!imageConfig.endpoint || !imageConfig.apiKey) { + return { error: 'Image generation is not configured. Add your Azure OpenAI endpoint and key in Settings.' }; + } + + // Build prompt with Claude first + const prompt = await buildImagePrompt(claudeConfig, title, notes); + console.log('[Image] DALL-E prompt:', prompt); + + // Normalise endpoint + const base = imageConfig.endpoint.trim().replace(/\/$/, ''); + const url = `${base}/openai/deployments/${imageConfig.deployment}/images/generations?api-version=2024-02-01`; + + let imageUrl: string; + try { + const resp = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'api-key': imageConfig.apiKey, + }, + body: JSON.stringify({ + prompt, + n: 1, + size: '1792x1024', + quality: 'hd', + style: 'natural', + }), + }); + + if (!resp.ok) { + const text = await resp.text(); + console.log('[Image] API error:', resp.status, text); + return { error: `Image API error ${resp.status}: ${text.slice(0, 200)}` }; + } + + const data = await resp.json(); + imageUrl = data?.data?.[0]?.url; + if (!imageUrl) { + return { error: 'No image URL in response' }; + } + } catch (e: any) { + return { error: `Network error: ${e.message}` }; + } + + // Download and convert to base64 data URI so it persists + try { + const imgResp = await fetch(imageUrl); + const arrayBuffer = await imgResp.arrayBuffer(); + const uint8 = new Uint8Array(arrayBuffer); + let binary = ''; + const chunk = 8192; + for (let i = 0; i < uint8.length; i += chunk) { + binary += String.fromCharCode(...(uint8.subarray(i, i + chunk) as unknown as number[])); + } + const base64 = btoa(binary); + return { dataUri: `data:image/png;base64,${base64}` }; + } catch (e: any) { + // Fallback: return the temporary URL if download fails + console.log('[Image] download failed, returning URL:', e.message); + return { dataUri: imageUrl }; + } +} + +// ─── AI Collaborator ────────────────────────────────────────────────────────── + +export type CollaboratorOperation = + | 'improve' + | 'strengthen-intro' + | 'medical-review' + | 'seo-optimize' + | 'add-section' + | 'custom'; + +const COLLABORATOR_PROMPTS: Record = { + 'improve': `You are an expert medical writer and editor for aipsychmd.com, a professional blog about AI and psychiatry for clinicians and healthcare technology professionals. + +Improve the writing quality, clarity, flow, and style of the article. Fix grammar issues, strengthen sentence structure, smooth transitions, and make the prose more engaging and authoritative. +Do not change the structure or meaning — only improve how it reads.`, + + 'strengthen-intro': `You are an expert medical writer for aipsychmd.com, a professional blog about AI and psychiatry. + +Rewrite or significantly strengthen the introductory section (first paragraphs before the first heading). Make it immediately compelling: open with a striking fact, a clinical scenario, or a provocative question. Establish stakes quickly and hook the reader into reading further. +Keep the rest of the article exactly as-is.`, + + 'medical-review': `You are a board-certified psychiatrist and medical AI researcher reviewing a blog post for aipsychmd.com. + +Review the article and: +- Add clinical accuracy and nuance where needed +- Insert appropriate caveats about AI limitations in mental health contexts +- Add relevant clinical context or real-world applicability +- Ensure responsible framing (no hype, no unsubstantiated claims) +- The audience is informed professionals — no need to over-explain basic concepts`, + + 'seo-optimize': `You are an SEO specialist for healthcare content writing for aipsychmd.com. + +Optimise this article for search without compromising quality: +- Strengthen H1/H2/H3 headings to be more descriptive and keyword-rich +- Ensure the intro and meta-description area contains the core topic naturally +- Add a FAQ section at the end if it would help (max 3 questions) +- Naturally weave in semantic keywords related to the topic +- Target audience: psychiatrists, mental health professionals, healthcare technology researchers`, + + 'add-section': `You are an expert medical writer for aipsychmd.com, a professional blog about AI and psychiatry. + +Add a new, well-researched section to the article based on the instruction below. Place it where it fits logically in the flow. Use appropriate H2 or H3 heading.`, + + 'custom': `You are an expert medical writer and editor for aipsychmd.com, a professional blog about AI and psychiatry. + +Follow the instruction below precisely.`, +}; + +export async function collaborateOnArticle( + config: AzureConfig, + operation: CollaboratorOperation, + articleHtml: string, + title: string, + notes: string, + instruction?: string +): Promise { + const systemContext = COLLABORATOR_PROMPTS[operation]; + + const instructionBlock = instruction + ? `\n\nInstruction: ${instruction}` + : ''; + + const prompt = `${systemContext}${instructionBlock} + +Article title: "${title}" +${notes ? `Brief/notes: ${notes}` : ''} + +Current article HTML: +${articleHtml} + +IMPORTANT RULES: +- Return ONLY the complete, modified HTML for the article body +- Do NOT wrap in markdown code blocks +- Do NOT include explanations, preamble, or notes — just the HTML +- Preserve all existing formatting tags unless the operation requires changing them +- The HTML will be set directly into a TipTap editor`; + + // Use higher token limit for full document rewrites + const client = makeClient(config); + const requestBody = { + model: config.deployment, + max_tokens: 4000, + messages: [{ role: 'user' as const, content: prompt }], + }; + + try { + const message = await client.messages.create(requestBody); + const text = message.content[0].type === 'text' ? message.content[0].text : ''; + // Strip accidental markdown fences if Claude adds them despite instructions + const clean = text.replace(/^```html\s*/i, '').replace(/\s*```$/i, '').trim(); + return { content: clean }; + } catch (e: any) { + const status = e.status ?? 0; + const rawResponse = e.message ?? String(e); + return { + content: '', + error: `HTTP ${status || 'Network error'}`, + debugInfo: { + endpoint: config.endpoint, + deployment: config.deployment, + keyLength: config.apiKey?.length ?? 0, + status, + requestBody, + rawResponse: e.error ? JSON.stringify(e.error) : rawResponse, + }, + }; + } } diff --git a/src/lib/storage.ts b/src/lib/storage.ts index b29e62c..0410d5f 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -31,7 +31,12 @@ export async function getSettings(): Promise { return raw ? JSON.parse(raw) : { - anthropicApiKey: '', + azureEndpoint: '', + azureApiKey: '', + azureDeployment: 'claude-opus-4-6', + imageEndpoint: '', + imageApiKey: '', + imageDeployment: 'dall-e-3', wordpressUrl: '', wordpressUsername: '', wordpressAppPassword: '', diff --git a/src/lib/types.ts b/src/lib/types.ts index d873fb3..9e6f366 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -11,6 +11,8 @@ export interface ArticleIdea { id: string; title: string; notes: string; + content: string; + heroImageDataUri?: string; // base64 data URI — persists without expiry tags: string[]; status: IdeaStatus; aiContent?: AIContent; @@ -19,7 +21,13 @@ export interface ArticleIdea { } export interface AppSettings { - anthropicApiKey: string; + azureEndpoint: string; + azureApiKey: string; + azureDeployment: string; + // Image generation (Azure OpenAI DALL-E 3) + imageEndpoint: string; // e.g. https://resource.openai.azure.com/ + imageApiKey: string; + imageDeployment: string; // e.g. dall-e-3 wordpressUrl: string; wordpressUsername: string; wordpressAppPassword: string;