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 ``;
+ }
+ 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;