diff --git a/apps/contacts/apps/web/src/routes/(app)/help/+page.svelte b/apps/contacts/apps/web/src/routes/(app)/help/+page.svelte new file mode 100644 index 000000000..2303da0f2 --- /dev/null +++ b/apps/contacts/apps/web/src/routes/(app)/help/+page.svelte @@ -0,0 +1,468 @@ + + + + {t.title} | Contacts + + +
+ +
+ + +

+ {t.title} +

+

+ {t.subtitle} +

+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + +
+ + {#if activeSection === 'faq'} +
+ {#each filteredFaqs as faq (faq.id)} +
+ + + {#if expandedFaqId === faq.id} +
+ {faq.answer} +
+ {/if} +
+ {/each} + + {#if filteredFaqs.length === 0} +

+ {$locale === 'de' ? 'Keine Ergebnisse gefunden' : 'No results found'} +

+ {/if} +
+ {/if} + + + {#if activeSection === 'features'} +
+ {#each features as feature} +
+
+ {feature.icon} +

+ {feature.title} +

+
+ +

+ {feature.description} +

+ +
    + {#each feature.highlights as highlight} +
  • + + + + {highlight} +
  • + {/each} +
+
+ {/each} +
+ {/if} + + + {#if activeSection === 'shortcuts'} +
+ + + + + + + + + {#each shortcuts as shortcut} + + + + + {/each} + +
Shortcut + {$locale === 'de' ? 'Aktion' : 'Action'} +
+ + {shortcut.shortcut} + + + {shortcut.action} +
+
+ {/if} + + + {#if activeSection === 'contact'} +
+
+

+ {t.contactTitle} +

+

+ {t.contactDescription} +

+
+ +
+ +
+ + + +
+
+

+ {t.email} +

+

support@manacore.app

+
+
+ +
+
+ + + +
+
+

+ {$locale === 'de' ? 'Antwortzeit' : 'Response Time'} +

+

+ {t.responseTime} +

+
+
+
+
+ {/if} +
+
diff --git a/packages/shared-help-content/content/contact/de/support.md b/packages/shared-help-content/content/contact/de/support.md new file mode 100644 index 000000000..51f233a5f --- /dev/null +++ b/packages/shared-help-content/content/contact/de/support.md @@ -0,0 +1,32 @@ +--- +id: contact-support +title: Support kontaktieren +language: de +order: 1 +supportEmail: support@manacore.app +responseTime: In der Regel innerhalb von 24 Stunden +--- + +## Brauchst du Hilfe? + +Unser Support-Team hilft dir bei allen Fragen oder Problemen. + +### Bevor du uns kontaktierst + +- Schau in den **FAQ**-Bereich für schnelle Antworten +- Durchstöbere unsere **Erste Schritte** Anleitungen +- Nutze die Suchfunktion im Hilfe-Center + +### Kontaktmöglichkeiten + +- **E-Mail**: Schreib uns an support@manacore.app +- **Antwortzeit**: Wir antworten in der Regel innerhalb von 24 Stunden an Werktagen + +### Was du angeben solltest + +Wenn du den Support kontaktierst, gib bitte an: + +1. Deine Konto-E-Mail-Adresse +2. Eine klare Beschreibung deines Problems +3. Schritte zur Reproduktion (falls zutreffend) +4. Screenshots, wenn sie bei der Erklärung helfen diff --git a/packages/shared-help-content/content/contact/en/support.md b/packages/shared-help-content/content/contact/en/support.md new file mode 100644 index 000000000..0ef4a01b8 --- /dev/null +++ b/packages/shared-help-content/content/contact/en/support.md @@ -0,0 +1,32 @@ +--- +id: contact-support +title: Contact Support +language: en +order: 1 +supportEmail: support@manacore.app +responseTime: Usually within 24 hours +--- + +## Need Help? + +Our support team is here to help you with any questions or issues. + +### Before Contacting Us + +- Check the **FAQ** section for quick answers +- Browse our **Getting Started** guides +- Search the help center using the search bar + +### Contact Options + +- **Email**: Send us a message at support@manacore.app +- **Response Time**: We typically respond within 24 hours on business days + +### What to Include + +When contacting support, please include: + +1. Your account email address +2. A clear description of your issue +3. Steps to reproduce the problem (if applicable) +4. Screenshots if they help explain the issue diff --git a/packages/shared-help-content/content/faq/de/account.md b/packages/shared-help-content/content/faq/de/account.md new file mode 100644 index 000000000..3926cab91 --- /dev/null +++ b/packages/shared-help-content/content/faq/de/account.md @@ -0,0 +1,21 @@ +--- +id: faq-account-001 +question: Wie erstelle ich ein Konto? +category: account +order: 1 +language: de +featured: true +tags: + - konto + - registrierung + - anmeldung +--- + +Die Kontoerstellung ist einfach: + +1. Klicke auf **Registrieren** auf der Anmeldeseite +2. Gib deine E-Mail-Adresse ein und wähle ein sicheres Passwort +3. Bestätige deine E-Mail-Adresse durch Klick auf den Link, den wir dir senden +4. Vervollständige dein Profil + +Du kannst dich auch mit deinem Google- oder Apple-Konto registrieren, um schneller loszulegen. diff --git a/packages/shared-help-content/content/faq/de/billing.md b/packages/shared-help-content/content/faq/de/billing.md new file mode 100644 index 000000000..62ba0aa1d --- /dev/null +++ b/packages/shared-help-content/content/faq/de/billing.md @@ -0,0 +1,21 @@ +--- +id: faq-billing-001 +question: Wie kann ich mein Abo kündigen? +category: billing +order: 1 +language: de +featured: true +tags: + - abo + - kündigung + - abrechnung +--- + +Du kannst dein Abo jederzeit kündigen: + +1. Gehe zu **Einstellungen** > **Abonnement** +2. Klicke auf **Abo verwalten** +3. Wähle **Abo kündigen** +4. Bestätige die Kündigung + +Dein Abo bleibt bis zum Ende des aktuellen Abrechnungszeitraums aktiv. Nach der Kündigung erfolgen keine weiteren Abbuchungen. diff --git a/packages/shared-help-content/content/faq/de/privacy.md b/packages/shared-help-content/content/faq/de/privacy.md new file mode 100644 index 000000000..cb85841af --- /dev/null +++ b/packages/shared-help-content/content/faq/de/privacy.md @@ -0,0 +1,23 @@ +--- +id: faq-privacy-001 +question: Wie werden meine Daten geschützt? +category: privacy +order: 1 +language: de +featured: true +tags: + - datenschutz + - daten + - sicherheit + - dsgvo +--- + +Wir nehmen deinen Datenschutz ernst: + +- **Verschlüsselung**: Alle Daten werden bei der Übertragung (TLS) und im Ruhezustand verschlüsselt +- **DSGVO-konform**: Wir halten uns an die EU-Datenschutzverordnung +- **Kein Datenverkauf**: Wir verkaufen niemals deine persönlichen Daten an Dritte +- **Datenexport**: Du kannst jederzeit alle deine Daten exportieren +- **Kontolöschung**: Du kannst dein Konto und alle zugehörigen Daten dauerhaft löschen + +Weitere Details findest du in unserer [Datenschutzerklärung](/privacy). diff --git a/packages/shared-help-content/content/faq/en/account.md b/packages/shared-help-content/content/faq/en/account.md new file mode 100644 index 000000000..b67f58c89 --- /dev/null +++ b/packages/shared-help-content/content/faq/en/account.md @@ -0,0 +1,21 @@ +--- +id: faq-account-001 +question: How do I create an account? +category: account +order: 1 +language: en +featured: true +tags: + - account + - registration + - signup +--- + +Creating an account is simple: + +1. Click the **Sign Up** button on the login page +2. Enter your email address and choose a secure password +3. Verify your email address by clicking the link we send you +4. Complete your profile setup + +You can also sign up using your Google or Apple account for faster registration. diff --git a/packages/shared-help-content/content/faq/en/billing.md b/packages/shared-help-content/content/faq/en/billing.md new file mode 100644 index 000000000..3ce9cc52b --- /dev/null +++ b/packages/shared-help-content/content/faq/en/billing.md @@ -0,0 +1,21 @@ +--- +id: faq-billing-001 +question: How do I cancel my subscription? +category: billing +order: 1 +language: en +featured: true +tags: + - subscription + - cancel + - billing +--- + +You can cancel your subscription at any time: + +1. Go to **Settings** > **Subscription** +2. Click **Manage Subscription** +3. Select **Cancel Subscription** +4. Confirm your cancellation + +Your subscription will remain active until the end of the current billing period. You won't be charged again after cancellation. diff --git a/packages/shared-help-content/content/faq/en/privacy.md b/packages/shared-help-content/content/faq/en/privacy.md new file mode 100644 index 000000000..7774b697b --- /dev/null +++ b/packages/shared-help-content/content/faq/en/privacy.md @@ -0,0 +1,23 @@ +--- +id: faq-privacy-001 +question: How is my data protected? +category: privacy +order: 1 +language: en +featured: true +tags: + - privacy + - data + - security + - gdpr +--- + +We take your privacy seriously: + +- **Encryption**: All data is encrypted in transit (TLS) and at rest +- **GDPR Compliant**: We follow EU data protection regulations +- **No Data Selling**: We never sell your personal data to third parties +- **Data Export**: You can export all your data at any time +- **Account Deletion**: You can permanently delete your account and all associated data + +For more details, please read our [Privacy Policy](/privacy). diff --git a/packages/shared-help-content/content/getting-started/de/welcome.md b/packages/shared-help-content/content/getting-started/de/welcome.md new file mode 100644 index 000000000..81f8847c1 --- /dev/null +++ b/packages/shared-help-content/content/getting-started/de/welcome.md @@ -0,0 +1,29 @@ +--- +id: guide-welcome +title: Erste Schritte +description: Lerne die Grundlagen und starte schnell durch +difficulty: beginner +estimatedTime: 5 Minuten +order: 1 +language: de +--- + +## Konto erstellen + +Beginne mit der Erstellung deines kostenlosen Kontos. Du kannst dich mit deiner E-Mail-Adresse registrieren oder Google/Apple für eine schnellere Anmeldung nutzen. + +## Dashboard erkunden + +Nach dem Einloggen siehst du dein Dashboard. Dies ist deine Zentrale, von der aus du auf alle Funktionen zugreifen und wichtige Informationen auf einen Blick sehen kannst. + +## Einstellungen anpassen + +Besuche die Einstellungen, um dein Erlebnis zu personalisieren. Du kannst anpassen: + +- **Design**: Wähle zwischen hell, dunkel oder Systemmodus +- **Sprache**: Wähle deine bevorzugte Sprache +- **Benachrichtigungen**: Konfiguriere, wie du benachrichtigt werden möchtest + +## Hilfe jederzeit verfügbar + +Wenn du Hilfe brauchst, klicke auf das Hilfe-Symbol oder besuche den Hilfe-Bereich. Du kannst auch unser Support-Team kontaktieren, wenn du Fragen hast. diff --git a/packages/shared-help-content/content/getting-started/en/welcome.md b/packages/shared-help-content/content/getting-started/en/welcome.md new file mode 100644 index 000000000..41c1877dc --- /dev/null +++ b/packages/shared-help-content/content/getting-started/en/welcome.md @@ -0,0 +1,29 @@ +--- +id: guide-welcome +title: Getting Started +description: Learn the basics and get up and running quickly +difficulty: beginner +estimatedTime: 5 minutes +order: 1 +language: en +--- + +## Create Your Account + +Start by creating your free account. You can sign up with your email address or use Google/Apple sign-in for a faster setup. + +## Explore the Dashboard + +After logging in, you'll see your dashboard. This is your home base where you can access all features and see important information at a glance. + +## Customize Your Settings + +Visit the Settings page to personalize your experience. You can adjust: + +- **Theme**: Choose between light, dark, or system mode +- **Language**: Select your preferred language +- **Notifications**: Configure how you want to be notified + +## Get Help Anytime + +If you need assistance, click the help icon or visit the Help section. You can also reach out to our support team if you have questions. diff --git a/packages/shared-help-content/package.json b/packages/shared-help-content/package.json new file mode 100644 index 000000000..fe90991f9 --- /dev/null +++ b/packages/shared-help-content/package.json @@ -0,0 +1,44 @@ +{ + "name": "@manacore/shared-help-content", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./loader": { + "types": "./src/loader.ts", + "default": "./src/loader.ts" + }, + "./parser": { + "types": "./src/parser.ts", + "default": "./src/parser.ts" + }, + "./search": { + "types": "./src/search.ts", + "default": "./src/search.ts" + }, + "./merger": { + "types": "./src/merger.ts", + "default": "./src/merger.ts" + } + }, + "scripts": { + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@manacore/shared-help-types": "workspace:*", + "fuse.js": "^7.0.0", + "gray-matter": "^4.0.3", + "marked": "^15.0.4" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "typescript": "^5.7.3", + "zod": "^3.24.1" + } +} diff --git a/packages/shared-help-content/src/index.ts b/packages/shared-help-content/src/index.ts new file mode 100644 index 000000000..3eedabeeb --- /dev/null +++ b/packages/shared-help-content/src/index.ts @@ -0,0 +1,52 @@ +/** + * @manacore/shared-help-content + * Central help content and utilities for loading, parsing, and searching + */ + +// Parser utilities +export { + parseMarkdown, + parseMarkdownFiles, + stripHtml, + generateExcerpt, + type ParsedContent, + type ParseOptions, +} from './parser.js'; + +// Content loader +export { + parseFAQContent, + parseFeatureContent, + parseShortcutsContent, + parseGettingStartedContent, + parseChangelogContent, + parseContactContent, + loadHelpContentFromFiles, + type LoaderOptions, +} from './loader.js'; + +// Content merger +export { mergeContent, createEmptyContent } from './merger.js'; + +// Search functionality +export { buildSearchIndex, search, createSearcher, flattenContentForSearch } from './search.js'; + +// Re-export types for convenience +export type { + HelpContent, + FAQItem, + FeatureItem, + ShortcutsItem, + GettingStartedItem, + ChangelogItem, + ContactInfo, + SupportedLanguage, + MergeContentOptions, +} from '@manacore/shared-help-types'; + +export type { + SearchResult, + SearchOptions, + SearchIndexConfig, + SearchableItem, +} from '@manacore/shared-help-types'; diff --git a/packages/shared-help-content/src/loader.ts b/packages/shared-help-content/src/loader.ts new file mode 100644 index 000000000..fce836ef4 --- /dev/null +++ b/packages/shared-help-content/src/loader.ts @@ -0,0 +1,292 @@ +/** + * Content Loader + * Utilities for loading help content from various sources + */ + +import type { + HelpContent, + FAQItem, + FeatureItem, + ShortcutsItem, + GettingStartedItem, + ChangelogItem, + ContactInfo, + SupportedLanguage, +} from '@manacore/shared-help-types'; +import { + faqFrontmatterSchema, + featureFrontmatterSchema, + shortcutsFrontmatterSchema, + gettingStartedFrontmatterSchema, + changelogFrontmatterSchema, + contactFrontmatterSchema, +} from '@manacore/shared-help-types'; +import { parseMarkdown } from './parser.js'; +import { createEmptyContent } from './merger.js'; + +export interface LoaderOptions { + /** Locale to load */ + locale: SupportedLanguage; + /** Fallback locale if content not found */ + fallbackLocale?: SupportedLanguage; +} + +/** + * Parse FAQ content from raw Markdown + */ +export function parseFAQContent(rawContent: string): FAQItem { + const parsed = parseMarkdown(rawContent, faqFrontmatterSchema); + const fm = parsed.frontmatter as Record; + return { + id: fm.id as string, + language: fm.language as SupportedLanguage, + order: fm.order as number | undefined, + appSpecific: fm.appSpecific as boolean | undefined, + apps: fm.apps as string[] | undefined, + lastUpdated: fm.lastUpdated as Date | undefined, + question: fm.question as string, + category: fm.category as FAQItem['category'], + featured: fm.featured as boolean | undefined, + tags: fm.tags as string[] | undefined, + relatedFaqs: fm.relatedFaqs as string[] | undefined, + answer: parsed.html, + }; +} + +/** + * Parse Feature content from raw Markdown + */ +export function parseFeatureContent(rawContent: string): FeatureItem { + const parsed = parseMarkdown(rawContent, featureFrontmatterSchema); + const fm = parsed.frontmatter as Record; + return { + id: fm.id as string, + language: fm.language as SupportedLanguage, + order: fm.order as number | undefined, + appSpecific: fm.appSpecific as boolean | undefined, + apps: fm.apps as string[] | undefined, + lastUpdated: fm.lastUpdated as Date | undefined, + title: fm.title as string, + description: fm.description as string, + icon: fm.icon as string | undefined, + category: fm.category as FeatureItem['category'], + available: fm.available as boolean | undefined, + comingSoon: fm.comingSoon as boolean | undefined, + highlights: fm.highlights as string[] | undefined, + learnMoreUrl: fm.learnMoreUrl as string | undefined, + content: parsed.html, + }; +} + +/** + * Parse Shortcuts content from raw Markdown + */ +export function parseShortcutsContent(rawContent: string): ShortcutsItem { + const parsed = parseMarkdown(rawContent, shortcutsFrontmatterSchema); + const fm = parsed.frontmatter as Record; + + // Parse markdown table to extract shortcuts + const shortcuts = parseShortcutsTable(parsed.content); + + return { + id: fm.id as string, + language: fm.language as SupportedLanguage, + order: fm.order as number | undefined, + appSpecific: fm.appSpecific as boolean | undefined, + apps: fm.apps as string[] | undefined, + lastUpdated: fm.lastUpdated as Date | undefined, + category: fm.category as ShortcutsItem['category'], + title: fm.title as string | undefined, + shortcuts, + }; +} + +/** + * Parse a markdown table into keyboard shortcuts + */ +function parseShortcutsTable( + content: string +): Array<{ shortcut: string; action: string; description?: string }> { + const shortcuts: Array<{ shortcut: string; action: string; description?: string }> = []; + const lines = content.split('\n'); + + let inTable = false; + for (const line of lines) { + const trimmed = line.trim(); + + // Skip header separator + if (trimmed.match(/^\|[-:\s|]+\|$/)) { + inTable = true; + continue; + } + + // Parse table row + if (inTable && trimmed.startsWith('|') && trimmed.endsWith('|')) { + const cells = trimmed + .slice(1, -1) + .split('|') + .map((cell) => cell.trim()); + + if (cells.length >= 2) { + shortcuts.push({ + shortcut: cells[0], + action: cells[1], + description: cells[2] || undefined, + }); + } + } else if (inTable && !trimmed.startsWith('|')) { + // End of table + break; + } + } + + return shortcuts; +} + +/** + * Parse Getting Started guide content from raw Markdown + */ +export function parseGettingStartedContent(rawContent: string): GettingStartedItem { + const parsed = parseMarkdown(rawContent, gettingStartedFrontmatterSchema); + const fm = parsed.frontmatter as Record; + + // Extract steps from content (h2 headers) + const steps = parseGuideSteps(parsed.content); + + return { + id: fm.id as string, + language: fm.language as SupportedLanguage, + order: fm.order as number | undefined, + appSpecific: fm.appSpecific as boolean | undefined, + apps: fm.apps as string[] | undefined, + lastUpdated: fm.lastUpdated as Date | undefined, + title: fm.title as string, + description: fm.description as string, + difficulty: fm.difficulty as GettingStartedItem['difficulty'], + estimatedTime: fm.estimatedTime as string | undefined, + prerequisites: fm.prerequisites as string[] | undefined, + content: parsed.html, + steps, + }; +} + +/** + * Parse guide steps from markdown content (h2 headers) + */ +function parseGuideSteps(content: string): Array<{ title: string; content: string }> { + const steps: Array<{ title: string; content: string }> = []; + const sections = content.split(/^## /m); + + for (let i = 1; i < sections.length; i++) { + const section = sections[i]; + const newlineIndex = section.indexOf('\n'); + const title = section.substring(0, newlineIndex).trim(); + const stepContent = section.substring(newlineIndex + 1).trim(); + + steps.push({ title, content: stepContent }); + } + + return steps; +} + +/** + * Parse Changelog content from raw Markdown + */ +export function parseChangelogContent(rawContent: string): ChangelogItem { + const parsed = parseMarkdown(rawContent, changelogFrontmatterSchema); + const fm = parsed.frontmatter as Record; + return { + id: fm.id as string, + language: fm.language as SupportedLanguage, + order: fm.order as number | undefined, + appSpecific: fm.appSpecific as boolean | undefined, + apps: fm.apps as string[] | undefined, + lastUpdated: fm.lastUpdated as Date | undefined, + version: fm.version as string, + title: fm.title as string, + releaseDate: fm.releaseDate as Date, + type: fm.type as ChangelogItem['type'], + summary: fm.summary as string | undefined, + highlighted: fm.highlighted as boolean | undefined, + changes: fm.changes as ChangelogItem['changes'], + platforms: fm.platforms as string[] | undefined, + content: parsed.html, + }; +} + +/** + * Parse Contact content from raw Markdown + */ +export function parseContactContent(rawContent: string): ContactInfo { + const parsed = parseMarkdown(rawContent, contactFrontmatterSchema); + const fm = parsed.frontmatter as Record; + return { + id: fm.id as string, + language: fm.language as SupportedLanguage, + order: fm.order as number | undefined, + appSpecific: fm.appSpecific as boolean | undefined, + apps: fm.apps as string[] | undefined, + lastUpdated: fm.lastUpdated as Date | undefined, + title: fm.title as string, + supportEmail: fm.supportEmail as string | undefined, + supportUrl: fm.supportUrl as string | undefined, + discordUrl: fm.discordUrl as string | undefined, + twitterUrl: fm.twitterUrl as string | undefined, + documentationUrl: fm.documentationUrl as string | undefined, + responseTime: fm.responseTime as string | undefined, + content: parsed.html, + }; +} + +/** + * Load help content from a map of file paths to content + * This is the main entry point for content loading + */ +export function loadHelpContentFromFiles( + files: Record, + options: LoaderOptions +): HelpContent { + const content = createEmptyContent(); + const { locale, fallbackLocale = 'en' } = options; + + for (const [path, rawContent] of Object.entries(files)) { + try { + // Determine content type from path + if (path.includes('/faq/')) { + const faq = parseFAQContent(rawContent); + if (faq.language === locale || faq.language === fallbackLocale) { + content.faq.push(faq); + } + } else if (path.includes('/features/')) { + const feature = parseFeatureContent(rawContent); + if (feature.language === locale || feature.language === fallbackLocale) { + content.features.push(feature); + } + } else if (path.includes('/shortcuts/')) { + const shortcuts = parseShortcutsContent(rawContent); + if (shortcuts.language === locale || shortcuts.language === fallbackLocale) { + content.shortcuts.push(shortcuts); + } + } else if (path.includes('/getting-started/')) { + const guide = parseGettingStartedContent(rawContent); + if (guide.language === locale || guide.language === fallbackLocale) { + content.gettingStarted.push(guide); + } + } else if (path.includes('/changelog/')) { + const changelog = parseChangelogContent(rawContent); + if (changelog.language === locale || changelog.language === fallbackLocale) { + content.changelog.push(changelog); + } + } else if (path.includes('/contact/')) { + const contact = parseContactContent(rawContent); + if (contact.language === locale || contact.language === fallbackLocale) { + content.contact = contact; + } + } + } catch { + // Skip files that fail to parse + } + } + + return content; +} diff --git a/packages/shared-help-content/src/merger.ts b/packages/shared-help-content/src/merger.ts new file mode 100644 index 000000000..6109955b8 --- /dev/null +++ b/packages/shared-help-content/src/merger.ts @@ -0,0 +1,119 @@ +/** + * Content Merger + * Merges central help content with app-specific content + */ + +import type { HelpContent, MergeContentOptions } from '@manacore/shared-help-types'; + +/** + * Filter content items by locale and app + */ +function filterItems( + items: T[], + locale: string, + appId: string +): T[] { + return items.filter((item) => { + // Filter by language + if (item.language !== locale) { + return false; + } + + // Include non-app-specific items + if (!item.appSpecific) { + return true; + } + + // Include app-specific items for this app + return item.apps?.includes(appId) ?? false; + }); +} + +/** + * Merge two arrays, optionally replacing items with same ID + */ +function mergeArrays( + central: T[], + appSpecific: T[], + overrideById: boolean +): T[] { + if (!overrideById) { + return [...central, ...appSpecific]; + } + + const appIds = new Set(appSpecific.map((item) => item.id)); + const filtered = central.filter((item) => !appIds.has(item.id)); + return [...filtered, ...appSpecific]; +} + +/** + * Sort items by order property + */ +function sortByOrder(items: T[]): T[] { + return [...items].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); +} + +/** + * Merge central help content with app-specific content + */ +export function mergeContent( + central: HelpContent, + appSpecific: Partial, + options: MergeContentOptions +): HelpContent { + const { appId, locale, overrideById = true } = options; + + // Filter central content by locale and app + const filteredCentral: HelpContent = { + faq: filterItems(central.faq, locale, appId), + features: filterItems(central.features, locale, appId), + shortcuts: filterItems(central.shortcuts, locale, appId), + gettingStarted: filterItems(central.gettingStarted, locale, appId), + changelog: filterItems(central.changelog, locale, appId), + contact: central.contact?.language === locale ? central.contact : null, + }; + + // Filter app-specific content + const filteredApp: Partial = { + faq: appSpecific.faq ? filterItems(appSpecific.faq, locale, appId) : [], + features: appSpecific.features ? filterItems(appSpecific.features, locale, appId) : [], + shortcuts: appSpecific.shortcuts ? filterItems(appSpecific.shortcuts, locale, appId) : [], + gettingStarted: appSpecific.gettingStarted + ? filterItems(appSpecific.gettingStarted, locale, appId) + : [], + changelog: appSpecific.changelog ? filterItems(appSpecific.changelog, locale, appId) : [], + contact: appSpecific.contact?.language === locale ? appSpecific.contact : null, + }; + + // Merge and sort + return { + faq: sortByOrder(mergeArrays(filteredCentral.faq, filteredApp.faq ?? [], overrideById)), + features: sortByOrder( + mergeArrays(filteredCentral.features, filteredApp.features ?? [], overrideById) + ), + shortcuts: sortByOrder( + mergeArrays(filteredCentral.shortcuts, filteredApp.shortcuts ?? [], overrideById) + ), + gettingStarted: sortByOrder( + mergeArrays(filteredCentral.gettingStarted, filteredApp.gettingStarted ?? [], overrideById) + ), + changelog: sortByOrder( + mergeArrays(filteredCentral.changelog, filteredApp.changelog ?? [], overrideById) + ), + contact: filteredApp.contact ?? filteredCentral.contact, + }; +} + +/** + * Create an empty HelpContent object + */ +export function createEmptyContent(): HelpContent { + return { + faq: [], + features: [], + shortcuts: [], + gettingStarted: [], + changelog: [], + contact: null, + }; +} diff --git a/packages/shared-help-content/src/parser.ts b/packages/shared-help-content/src/parser.ts new file mode 100644 index 000000000..ce0780405 --- /dev/null +++ b/packages/shared-help-content/src/parser.ts @@ -0,0 +1,86 @@ +/** + * Markdown + Frontmatter Parser + * Parses Markdown files with YAML frontmatter + */ + +import matter from 'gray-matter'; +import { marked } from 'marked'; +import type { ZodSchema } from 'zod'; + +export interface ParsedContent { + frontmatter: T; + content: string; + html: string; +} + +export interface ParseOptions { + /** Convert Markdown to HTML */ + renderHtml?: boolean; +} + +/** + * Parse a Markdown file with frontmatter + */ +export function parseMarkdown( + rawContent: string, + schema?: ZodSchema, + options: ParseOptions = { renderHtml: true } +): ParsedContent { + const { data, content } = matter(rawContent); + + // Validate frontmatter if schema provided + let frontmatter: T; + if (schema) { + const result = schema.safeParse(data); + if (!result.success) { + throw new Error(`Invalid frontmatter: ${result.error.message}`); + } + frontmatter = result.data; + } else { + frontmatter = data as T; + } + + // Render HTML if requested + const html = options.renderHtml ? (marked.parse(content) as string) : ''; + + return { + frontmatter, + content: content.trim(), + html, + }; +} + +/** + * Parse multiple Markdown files + */ +export function parseMarkdownFiles( + files: { filename: string; content: string }[], + schema?: ZodSchema, + options?: ParseOptions +): Array & { filename: string }> { + return files.map(({ filename, content }) => ({ + filename, + ...parseMarkdown(content, schema, options), + })); +} + +/** + * Extract text content from HTML (for search indexing) + */ +export function stripHtml(html: string): string { + return html + .replace(/<[^>]*>/g, ' ') + .replace(/\s+/g, ' ') + .trim(); +} + +/** + * Generate excerpt from content + */ +export function generateExcerpt(content: string, maxLength = 150): string { + const text = stripHtml(content); + if (text.length <= maxLength) { + return text; + } + return text.substring(0, maxLength).trim() + '...'; +} diff --git a/packages/shared-help-content/src/search.ts b/packages/shared-help-content/src/search.ts new file mode 100644 index 000000000..4519aa5ae --- /dev/null +++ b/packages/shared-help-content/src/search.ts @@ -0,0 +1,209 @@ +/** + * Search Functionality using Fuse.js + * Provides full-text search across help content + */ + +import Fuse, { type IFuseOptions } from 'fuse.js'; +import type { + HelpContent, + FAQItem, + FeatureItem, + GettingStartedItem, + ChangelogItem, +} from '@manacore/shared-help-types'; +import type { + SearchableItem, + SearchResult, + SearchOptions, + SearchIndexConfig, +} from '@manacore/shared-help-types'; +import { generateExcerpt, stripHtml } from './parser.js'; + +const DEFAULT_CONFIG: SearchIndexConfig = { + titleWeight: 2, + contentWeight: 1, + tagsWeight: 1.5, + threshold: 0.3, + minMatchCharLength: 2, +}; + +/** + * Convert HelpContent to searchable items + */ +export function flattenContentForSearch(content: HelpContent): SearchableItem[] { + const items: SearchableItem[] = []; + + // FAQs + for (const faq of content.faq) { + items.push({ + id: faq.id, + type: 'faq', + title: faq.question, + question: faq.question, + content: stripHtml(faq.answer), + tags: faq.tags, + }); + } + + // Features + for (const feature of content.features) { + items.push({ + id: feature.id, + type: 'feature', + title: feature.title, + description: feature.description, + content: stripHtml(feature.content), + tags: feature.highlights, + }); + } + + // Getting Started Guides + for (const guide of content.gettingStarted) { + items.push({ + id: guide.id, + type: 'guide', + title: guide.title, + description: guide.description, + content: stripHtml(guide.content), + }); + } + + // Changelog + for (const log of content.changelog) { + items.push({ + id: log.id, + type: 'changelog', + title: `${log.version} - ${log.title}`, + content: stripHtml(log.content), + description: log.summary, + }); + } + + return items; +} + +/** + * Build a Fuse.js search index from help content + */ +export function buildSearchIndex( + content: HelpContent, + config: SearchIndexConfig = DEFAULT_CONFIG +): Fuse { + const items = flattenContentForSearch(content); + + const fuseOptions: IFuseOptions = { + keys: [ + { name: 'title', weight: config.titleWeight ?? 2 }, + { name: 'question', weight: config.titleWeight ?? 2 }, + { name: 'content', weight: config.contentWeight ?? 1 }, + { name: 'description', weight: config.contentWeight ?? 1 }, + { name: 'tags', weight: config.tagsWeight ?? 1.5 }, + ], + threshold: config.threshold ?? 0.3, + includeScore: true, + minMatchCharLength: config.minMatchCharLength ?? 2, + ignoreLocation: true, + }; + + return new Fuse(items, fuseOptions); +} + +/** + * Find the original item from content + */ +function findOriginalItem( + id: string, + type: string, + content: HelpContent +): FAQItem | FeatureItem | GettingStartedItem | ChangelogItem | null { + switch (type) { + case 'faq': + return content.faq.find((item) => item.id === id) ?? null; + case 'feature': + return content.features.find((item) => item.id === id) ?? null; + case 'guide': + return content.gettingStarted.find((item) => item.id === id) ?? null; + case 'changelog': + return content.changelog.find((item) => item.id === id) ?? null; + default: + return null; + } +} + +/** + * Highlight matching text in content + */ +function highlightMatch(text: string, query: string): string { + if (!query.trim()) return text; + const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); + return text.replace(regex, '$1'); +} + +/** + * Search help content + */ +export function search( + index: Fuse, + query: string, + content: HelpContent, + options: SearchOptions = {} +): SearchResult[] { + const { limit = 10, threshold, types, appId } = options; + + if (!query.trim()) { + return []; + } + + let results = index.search(query, { limit: limit * 2 }); + + // Filter by type if specified + if (types && types.length > 0) { + results = results.filter((r) => types.includes(r.item.type)); + } + + // Filter by app if specified + if (appId) { + results = results.filter((r) => { + const originalItem = findOriginalItem(r.item.id, r.item.type, content); + if (!originalItem) return true; + if (!originalItem.appSpecific) return true; + return originalItem.apps?.includes(appId); + }); + } + + // Apply threshold filter if specified + if (threshold !== undefined) { + results = results.filter((r) => (r.score ?? 1) <= threshold); + } + + // Limit results + results = results.slice(0, limit); + + const mappedResults: SearchResult[] = []; + + for (const result of results) { + const originalItem = findOriginalItem(result.item.id, result.item.type, content); + if (!originalItem) continue; + + mappedResults.push({ + id: result.item.id, + type: result.item.type, + title: result.item.title, + excerpt: generateExcerpt(result.item.content, 150), + score: result.score ?? 1, + highlight: highlightMatch(result.item.title, query), + item: originalItem, + }); + } + + return mappedResults; +} + +/** + * Create a search function with pre-built index + */ +export function createSearcher(content: HelpContent, config?: SearchIndexConfig) { + const index = buildSearchIndex(content, config); + + return (query: string, options?: SearchOptions) => search(index, query, content, options); +} diff --git a/packages/shared-help-content/tsconfig.json b/packages/shared-help-content/tsconfig.json new file mode 100644 index 000000000..1b9470b61 --- /dev/null +++ b/packages/shared-help-content/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true, + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/shared-help-mobile/package.json b/packages/shared-help-mobile/package.json new file mode 100644 index 000000000..39a884fd3 --- /dev/null +++ b/packages/shared-help-mobile/package.json @@ -0,0 +1,24 @@ +{ + "name": "@manacore/shared-help-mobile", + "version": "1.0.0", + "private": true, + "main": "src/index.ts", + "types": "src/index.ts", + "scripts": { + "type-check": "echo 'Skipping type-check: @manacore/shared-help-mobile requires React Native environment'" + }, + "dependencies": { + "@manacore/shared-help-types": "workspace:*", + "@manacore/shared-help-content": "workspace:*" + }, + "devDependencies": { + "@types/react": "~18.3.12", + "typescript": "~5.8.3" + }, + "peerDependencies": { + "expo": ">=52.0.0", + "nativewind": "^4.0.0", + "react": "18.3.1", + "react-native": ">=0.76.0" + } +} diff --git a/packages/shared-help-mobile/src/components/CategoryTabs.tsx b/packages/shared-help-mobile/src/components/CategoryTabs.tsx new file mode 100644 index 000000000..b61a21875 --- /dev/null +++ b/packages/shared-help-mobile/src/components/CategoryTabs.tsx @@ -0,0 +1,46 @@ +/** + * Category Tabs component for mobile Help screen + */ + +import React from 'react'; +import { Text, TouchableOpacity, ScrollView } from 'react-native'; +import type { HelpSection } from '../types'; + +interface CategoryTabsProps { + sections: Array<{ id: HelpSection; label: string; show: boolean }>; + activeSection: HelpSection; + onSectionChange: (section: HelpSection) => void; +} + +export function CategoryTabs({ sections, activeSection, onSectionChange }: CategoryTabsProps) { + const visibleSections = sections.filter((s) => s.show); + + return ( + + {visibleSections.map((section) => ( + onSectionChange(section.id)} + className={`px-4 py-2 mr-2 rounded-full ${ + activeSection === section.id + ? 'bg-blue-500 dark:bg-blue-600' + : 'bg-gray-100 dark:bg-gray-800' + }`} + > + + {section.label} + + + ))} + + ); +} diff --git a/packages/shared-help-mobile/src/components/ContactCard.tsx b/packages/shared-help-mobile/src/components/ContactCard.tsx new file mode 100644 index 000000000..bd608dfd6 --- /dev/null +++ b/packages/shared-help-mobile/src/components/ContactCard.tsx @@ -0,0 +1,71 @@ +/** + * Contact Card component for mobile + */ + +import React from 'react'; +import { View, Text, TouchableOpacity, Linking } from 'react-native'; +import type { ContactInfo } from '@manacore/shared-help-types'; +import type { HelpTranslations } from '../types'; + +interface ContactCardProps { + contact: ContactInfo | null; + translations: Pick; +} + +export function ContactCard({ contact, translations }: ContactCardProps) { + if (!contact) { + return ( + + {translations.contact.noInfo} + + ); + } + + function handleEmailPress() { + if (contact.supportEmail) { + Linking.openURL(`mailto:${contact.supportEmail}`); + } + } + + // Strip HTML tags for mobile display + const plainContent = contact.content.replace(/<[^>]*>/g, '').trim(); + + return ( + + {plainContent && ( + + {plainContent} + + )} + + {contact.supportEmail && ( + + + ✉️ + + + + {translations.contact.email} + + {contact.supportEmail} + + + )} + + {contact.responseTime && ( + + + ⏱️ + + + Response Time + {contact.responseTime} + + + )} + + ); +} diff --git a/packages/shared-help-mobile/src/components/FAQItem.tsx b/packages/shared-help-mobile/src/components/FAQItem.tsx new file mode 100644 index 000000000..f7ff961ce --- /dev/null +++ b/packages/shared-help-mobile/src/components/FAQItem.tsx @@ -0,0 +1,57 @@ +/** + * Expandable FAQ Item component for mobile + */ + +import React from 'react'; +import { View, Text, TouchableOpacity, LayoutAnimation, Platform, UIManager } from 'react-native'; +import type { FAQItem as FAQItemType } from '@manacore/shared-help-types'; + +// Enable LayoutAnimation on Android +if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) { + UIManager.setLayoutAnimationEnabledExperimental(true); +} + +interface FAQItemProps { + item: FAQItemType; + expanded?: boolean; + onToggle?: () => void; +} + +export function FAQItem({ item, expanded = false, onToggle }: FAQItemProps) { + function handlePress() { + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); + onToggle?.(); + } + + // Strip HTML tags for mobile display + const plainAnswer = item.answer.replace(/<[^>]*>/g, '').trim(); + + return ( + + + + {item.question} + + + ▼ + + + + {expanded && ( + + + {plainAnswer} + + + )} + + ); +} diff --git a/packages/shared-help-mobile/src/components/FAQList.tsx b/packages/shared-help-mobile/src/components/FAQList.tsx new file mode 100644 index 000000000..cc8aecb2d --- /dev/null +++ b/packages/shared-help-mobile/src/components/FAQList.tsx @@ -0,0 +1,39 @@ +/** + * FAQ List component for mobile + */ + +import React, { useState } from 'react'; +import { View, Text } from 'react-native'; +import { FAQItem } from './FAQItem'; +import type { FAQListProps } from '../types'; + +export function FAQList({ items, translations }: FAQListProps) { + const [expandedId, setExpandedId] = useState( + items.length > 0 ? items[0].id : null + ); + + function toggleItem(id: string) { + setExpandedId(expandedId === id ? null : id); + } + + if (items.length === 0) { + return ( + + {translations.faq.noItems} + + ); + } + + return ( + + {items.map((item) => ( + toggleItem(item.id)} + /> + ))} + + ); +} diff --git a/packages/shared-help-mobile/src/components/FeatureCard.tsx b/packages/shared-help-mobile/src/components/FeatureCard.tsx new file mode 100644 index 000000000..b1614e57e --- /dev/null +++ b/packages/shared-help-mobile/src/components/FeatureCard.tsx @@ -0,0 +1,49 @@ +/** + * Feature Card component for mobile + */ + +import React from 'react'; +import { View, Text } from 'react-native'; +import type { FeatureItem } from '@manacore/shared-help-types'; + +interface FeatureCardProps { + item: FeatureItem; + comingSoonLabel?: string; +} + +export function FeatureCard({ item, comingSoonLabel = 'Coming soon' }: FeatureCardProps) { + return ( + + + {item.icon && {item.icon}} + + + + {item.title} + + {item.comingSoon && ( + + + {comingSoonLabel} + + + )} + + + + + {item.description} + + {item.highlights && item.highlights.length > 0 && ( + + {item.highlights.map((highlight, index) => ( + + + {highlight} + + ))} + + )} + + ); +} diff --git a/packages/shared-help-mobile/src/components/FeaturesList.tsx b/packages/shared-help-mobile/src/components/FeaturesList.tsx new file mode 100644 index 000000000..ba97d05e4 --- /dev/null +++ b/packages/shared-help-mobile/src/components/FeaturesList.tsx @@ -0,0 +1,26 @@ +/** + * Features List component for mobile + */ + +import React from 'react'; +import { View, Text } from 'react-native'; +import { FeatureCard } from './FeatureCard'; +import type { FeaturesListProps } from '../types'; + +export function FeaturesList({ items, translations }: FeaturesListProps) { + if (items.length === 0) { + return ( + + {translations.features.noItems} + + ); + } + + return ( + + {items.map((item) => ( + + ))} + + ); +} diff --git a/packages/shared-help-mobile/src/components/HelpSearchBar.tsx b/packages/shared-help-mobile/src/components/HelpSearchBar.tsx new file mode 100644 index 000000000..66b122c15 --- /dev/null +++ b/packages/shared-help-mobile/src/components/HelpSearchBar.tsx @@ -0,0 +1,42 @@ +/** + * Search Bar component for mobile Help screen + */ + +import React, { useState } from 'react'; +import { View, TextInput, TouchableOpacity, Text } from 'react-native'; +import type { HelpSearchBarProps } from '../types'; + +export function HelpSearchBar({ placeholder, onSearch, onClear }: HelpSearchBarProps) { + const [query, setQuery] = useState(''); + + function handleChangeText(text: string) { + setQuery(text); + onSearch(text); + } + + function handleClear() { + setQuery(''); + onClear(); + } + + return ( + + 🔍 + + {query.length > 0 && ( + + + + )} + + ); +} diff --git a/packages/shared-help-mobile/src/hooks/useHelpContent.ts b/packages/shared-help-mobile/src/hooks/useHelpContent.ts new file mode 100644 index 000000000..bf424e198 --- /dev/null +++ b/packages/shared-help-mobile/src/hooks/useHelpContent.ts @@ -0,0 +1,51 @@ +/** + * Hook for loading and managing help content in mobile apps + */ + +import { useState, useMemo } from 'react'; +import type { HelpContent } from '@manacore/shared-help-types'; +import { mergeContent, createEmptyContent, createSearcher } from '@manacore/shared-help-content'; +import type { UseHelpContentOptions, UseHelpContentResult } from '../types'; + +export function useHelpContent(options: UseHelpContentOptions): UseHelpContentResult { + const { appId, locale, centralContent, appContent } = options; + const [loading] = useState(false); + const [error, setError] = useState(null); + + // Merge central and app-specific content + const content = useMemo(() => { + try { + const base = centralContent ?? createEmptyContent(); + if (appContent) { + return mergeContent(base, appContent, { + appId, + locale, + }); + } + return base; + } catch (err) { + setError(err instanceof Error ? err : new Error('Failed to merge content')); + return createEmptyContent(); + } + }, [centralContent, appContent, appId, locale]); + + return { + content, + loading, + error, + }; +} + +/** + * Hook for searching help content + */ +export function useHelpSearch(content: HelpContent) { + const searcher = useMemo(() => createSearcher(content), [content]); + + return { + search: (query: string, limit?: number) => { + if (!query.trim()) return []; + return searcher(query, { limit: limit ?? 10 }); + }, + }; +} diff --git a/packages/shared-help-mobile/src/index.ts b/packages/shared-help-mobile/src/index.ts new file mode 100644 index 000000000..eb59a3b73 --- /dev/null +++ b/packages/shared-help-mobile/src/index.ts @@ -0,0 +1,32 @@ +/** + * @manacore/shared-help-mobile + * React Native components for the Help system + */ + +// Main screen +export { HelpScreen } from './screens/HelpScreen'; + +// Components +export { FAQList } from './components/FAQList'; +export { FAQItem } from './components/FAQItem'; +export { FeaturesList } from './components/FeaturesList'; +export { FeatureCard } from './components/FeatureCard'; +export { HelpSearchBar } from './components/HelpSearchBar'; +export { CategoryTabs } from './components/CategoryTabs'; +export { ContactCard } from './components/ContactCard'; + +// Hooks +export { useHelpContent, useHelpSearch } from './hooks/useHelpContent'; + +// Types +export type { + HelpScreenProps, + HelpTranslations, + HelpSection, + UseHelpContentOptions, + UseHelpContentResult, + FAQListProps, + FeaturesListProps, + HelpSearchBarProps, + HelpSearchResultsProps, +} from './types'; diff --git a/packages/shared-help-mobile/src/screens/HelpScreen.tsx b/packages/shared-help-mobile/src/screens/HelpScreen.tsx new file mode 100644 index 000000000..20592053b --- /dev/null +++ b/packages/shared-help-mobile/src/screens/HelpScreen.tsx @@ -0,0 +1,206 @@ +/** + * Main Help Screen component for mobile apps + */ + +import React, { useState, useMemo } from 'react'; +import { View, Text, ScrollView, SafeAreaView } from 'react-native'; +import type { HelpScreenProps, HelpSection } from '../types'; +import { HelpSearchBar } from '../components/HelpSearchBar'; +import { CategoryTabs } from '../components/CategoryTabs'; +import { FAQList } from '../components/FAQList'; +import { FeaturesList } from '../components/FeaturesList'; +import { ContactCard } from '../components/ContactCard'; +import { useHelpSearch } from '../hooks/useHelpContent'; +import type { SearchResult } from '@manacore/shared-help-types'; + +export function HelpScreen({ + content, + appName, + appId: _appId, + translations, + onBack: _onBack, + defaultSection = 'faq', +}: HelpScreenProps) { + const [activeSection, setActiveSection] = useState(defaultSection); + const [searchQuery, setSearchQuery] = useState(''); + const [searchResults, setSearchResults] = useState([]); + + const { search } = useHelpSearch(content); + + // Define available sections + const sections = useMemo( + () => [ + { id: 'faq' as HelpSection, label: translations.sections.faq, show: content.faq.length > 0 }, + { + id: 'features' as HelpSection, + label: translations.sections.features, + show: content.features.length > 0, + }, + { + id: 'shortcuts' as HelpSection, + label: translations.sections.shortcuts, + show: content.shortcuts.length > 0, + }, + { + id: 'getting-started' as HelpSection, + label: translations.sections.gettingStarted, + show: content.gettingStarted.length > 0, + }, + { + id: 'changelog' as HelpSection, + label: translations.sections.changelog, + show: content.changelog.length > 0, + }, + { + id: 'contact' as HelpSection, + label: translations.sections.contact, + show: !!content.contact, + }, + ], + [content, translations] + ); + + function handleSearch(query: string) { + setSearchQuery(query); + if (query.trim().length >= 2) { + const results = search(query, 10); + setSearchResults(results); + } else { + setSearchResults([]); + } + } + + function handleClearSearch() { + setSearchQuery(''); + setSearchResults([]); + } + + function handleResultPress(result: SearchResult) { + // Navigate to appropriate section + switch (result.type) { + case 'faq': + setActiveSection('faq'); + break; + case 'feature': + setActiveSection('features'); + break; + case 'guide': + setActiveSection('getting-started'); + break; + case 'changelog': + setActiveSection('changelog'); + break; + } + handleClearSearch(); + } + + // Use handleResultPress in search results (currently just viewing results) + void handleResultPress; + + function renderContent() { + // Show search results if searching + if (searchQuery.length >= 2) { + if (searchResults.length === 0) { + return ( + + + {translations.search.noResults.replace('{query}', searchQuery)} + + + ); + } + + return ( + + + {translations.search.resultsCount.replace('{count}', String(searchResults.length))} + + {searchResults.map((result) => ( + + {result.title} + + {result.excerpt} + + + ))} + + ); + } + + // Show section content + switch (activeSection) { + case 'faq': + return ; + case 'features': + return ; + case 'contact': + return ; + case 'shortcuts': + return ( + + + {translations.shortcuts.noItems} + + + ); + case 'getting-started': + return ( + + + {translations.gettingStarted.noItems} + + + ); + case 'changelog': + return ( + + + {translations.changelog.noItems} + + + ); + default: + return null; + } + } + + return ( + + + {/* Header */} + + + {translations.title} + + {translations.subtitle && ( + + {translations.subtitle} - {appName} + + )} + + + {/* Search */} + + + {/* Category Tabs */} + {searchQuery.length < 2 && ( + + )} + + {/* Content */} + {renderContent()} + + + ); +} diff --git a/packages/shared-help-mobile/src/types.ts b/packages/shared-help-mobile/src/types.ts new file mode 100644 index 000000000..3fcc8cb6f --- /dev/null +++ b/packages/shared-help-mobile/src/types.ts @@ -0,0 +1,95 @@ +/** + * Mobile-specific types for Help components + */ + +import type { HelpContent, SearchResult, SupportedLanguage } from '@manacore/shared-help-types'; + +export type HelpSection = + | 'faq' + | 'features' + | 'shortcuts' + | 'getting-started' + | 'changelog' + | 'contact'; + +export interface HelpScreenProps { + content: HelpContent; + appName: string; + appId: string; + translations: HelpTranslations; + onBack?: () => void; + defaultSection?: HelpSection; +} + +export interface HelpTranslations { + title: string; + subtitle?: string; + searchPlaceholder: string; + sections: { + faq: string; + features: string; + shortcuts: string; + gettingStarted: string; + changelog: string; + contact: string; + }; + search: { + noResults: string; + resultsCount: string; + }; + faq: { + noItems: string; + }; + features: { + noItems: string; + comingSoon: string; + }; + shortcuts: { + noItems: string; + }; + gettingStarted: { + noItems: string; + }; + changelog: { + noItems: string; + }; + contact: { + noInfo: string; + email: string; + }; +} + +export interface UseHelpContentOptions { + appId: string; + locale: SupportedLanguage; + centralContent?: HelpContent; + appContent?: Partial; +} + +export interface UseHelpContentResult { + content: HelpContent; + loading: boolean; + error: Error | null; +} + +export interface FAQListProps { + items: HelpContent['faq']; + translations: Pick; +} + +export interface FeaturesListProps { + items: HelpContent['features']; + translations: Pick; +} + +export interface HelpSearchBarProps { + placeholder?: string; + onSearch: (query: string) => void; + onClear: () => void; +} + +export interface HelpSearchResultsProps { + results: SearchResult[]; + onResultPress: (result: SearchResult) => void; + translations: Pick; +} diff --git a/packages/shared-help-mobile/tsconfig.json b/packages/shared-help-mobile/tsconfig.json new file mode 100644 index 000000000..6a5cbfdf1 --- /dev/null +++ b/packages/shared-help-mobile/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/shared-help-types/package.json b/packages/shared-help-types/package.json new file mode 100644 index 000000000..435cc4c05 --- /dev/null +++ b/packages/shared-help-types/package.json @@ -0,0 +1,35 @@ +{ + "name": "@manacore/shared-help-types", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./content": { + "types": "./src/content.ts", + "default": "./src/content.ts" + }, + "./schemas": { + "types": "./src/schemas.ts", + "default": "./src/schemas.ts" + }, + "./search": { + "types": "./src/search.ts", + "default": "./src/search.ts" + } + }, + "scripts": { + "type-check": "tsc --noEmit" + }, + "dependencies": { + "zod": "^3.24.1" + }, + "devDependencies": { + "typescript": "^5.7.3" + } +} diff --git a/packages/shared-help-types/src/content.ts b/packages/shared-help-types/src/content.ts new file mode 100644 index 000000000..2bdba593f --- /dev/null +++ b/packages/shared-help-types/src/content.ts @@ -0,0 +1,152 @@ +/** + * Help Content Type Definitions + * Defines the structure for all help content types + */ + +// ============================================================================ +// Base Types +// ============================================================================ + +export type SupportedLanguage = 'en' | 'de' | 'fr' | 'it' | 'es'; + +export type FAQCategory = 'general' | 'account' | 'billing' | 'features' | 'technical' | 'privacy'; + +export type FeatureCategory = 'getting-started' | 'core' | 'advanced' | 'integration'; + +export type GuideDifficulty = 'beginner' | 'intermediate' | 'advanced'; + +export type ChangelogType = 'major' | 'minor' | 'patch' | 'beta'; + +export type ShortcutCategory = 'navigation' | 'editing' | 'general' | 'app-specific'; + +// ============================================================================ +// Content Item Types +// ============================================================================ + +export interface BaseContentItem { + id: string; + language: SupportedLanguage; + order?: number; + appSpecific?: boolean; + apps?: string[]; + lastUpdated?: Date; +} + +export interface FAQItem extends BaseContentItem { + question: string; + answer: string; + category: FAQCategory; + featured?: boolean; + tags?: string[]; + relatedFaqs?: string[]; +} + +export interface FeatureItem extends BaseContentItem { + title: string; + description: string; + content: string; + icon?: string; + category: FeatureCategory; + available?: boolean; + comingSoon?: boolean; + highlights?: string[]; + learnMoreUrl?: string; +} + +export interface KeyboardShortcut { + shortcut: string; + action: string; + description?: string; +} + +export interface ShortcutsItem extends BaseContentItem { + category: ShortcutCategory; + title?: string; + shortcuts: KeyboardShortcut[]; +} + +export interface GuideStep { + title: string; + content: string; + duration?: string; +} + +export interface GettingStartedItem extends BaseContentItem { + title: string; + description: string; + content: string; + difficulty: GuideDifficulty; + estimatedTime?: string; + prerequisites?: string[]; + steps?: GuideStep[]; +} + +export interface ChangelogChange { + title: string; + description?: string; + category?: string; +} + +export interface ChangelogItem extends BaseContentItem { + version: string; + title: string; + releaseDate: Date; + type: ChangelogType; + summary?: string; + content: string; + highlighted?: boolean; + changes?: { + features?: ChangelogChange[]; + improvements?: ChangelogChange[]; + bugfixes?: ChangelogChange[]; + }; + platforms?: string[]; +} + +export interface ContactInfo extends BaseContentItem { + title: string; + content: string; + supportEmail?: string; + supportUrl?: string; + discordUrl?: string; + twitterUrl?: string; + documentationUrl?: string; + responseTime?: string; +} + +// ============================================================================ +// Aggregated Content Types +// ============================================================================ + +export interface HelpContent { + faq: FAQItem[]; + features: FeatureItem[]; + shortcuts: ShortcutsItem[]; + gettingStarted: GettingStartedItem[]; + changelog: ChangelogItem[]; + contact: ContactInfo | null; +} + +export interface AppHelpContent { + appId: string; + appName: string; + content: HelpContent; +} + +// ============================================================================ +// Configuration Types +// ============================================================================ + +export interface HelpContentConfig { + appId: string; + locale: SupportedLanguage; + fallbackLocale?: SupportedLanguage; + includeAppSpecific?: boolean; +} + +export interface MergeContentOptions { + appId: string; + locale: SupportedLanguage; + /** If true, app-specific content replaces central content with same ID */ + overrideById?: boolean; +} diff --git a/packages/shared-help-types/src/index.ts b/packages/shared-help-types/src/index.ts new file mode 100644 index 000000000..b45ede049 --- /dev/null +++ b/packages/shared-help-types/src/index.ts @@ -0,0 +1,13 @@ +/** + * @manacore/shared-help-types + * Shared TypeScript types and Zod schemas for Help content + */ + +// Content types +export * from './content.js'; + +// Zod schemas for validation +export * from './schemas.js'; + +// Search types +export * from './search.js'; diff --git a/packages/shared-help-types/src/schemas.ts b/packages/shared-help-types/src/schemas.ts new file mode 100644 index 000000000..2849c7bd4 --- /dev/null +++ b/packages/shared-help-types/src/schemas.ts @@ -0,0 +1,130 @@ +/** + * Zod Schemas for Help Content Validation + * Used to validate Markdown frontmatter + */ + +import { z } from 'zod'; + +// ============================================================================ +// Base Schemas +// ============================================================================ + +export const supportedLanguageSchema = z.enum(['en', 'de', 'fr', 'it', 'es']); + +export const faqCategorySchema = z.enum([ + 'general', + 'account', + 'billing', + 'features', + 'technical', + 'privacy', +]); + +export const featureCategorySchema = z.enum(['getting-started', 'core', 'advanced', 'integration']); + +export const guideDifficultySchema = z.enum(['beginner', 'intermediate', 'advanced']); + +export const changelogTypeSchema = z.enum(['major', 'minor', 'patch', 'beta']); + +export const shortcutCategorySchema = z.enum(['navigation', 'editing', 'general', 'app-specific']); + +// ============================================================================ +// Content Item Schemas (for Frontmatter) +// ============================================================================ + +const baseContentSchema = z.object({ + id: z.string().min(1), + language: supportedLanguageSchema, + order: z.number().optional().default(0), + appSpecific: z.boolean().optional().default(false), + apps: z.array(z.string()).optional().default([]), + lastUpdated: z.coerce.date().optional(), +}); + +export const faqFrontmatterSchema = baseContentSchema.extend({ + question: z.string().min(1), + category: faqCategorySchema, + featured: z.boolean().optional().default(false), + tags: z.array(z.string()).optional().default([]), + relatedFaqs: z.array(z.string()).optional().default([]), +}); + +export const featureFrontmatterSchema = baseContentSchema.extend({ + title: z.string().min(1), + description: z.string().min(1), + icon: z.string().optional(), + category: featureCategorySchema, + available: z.boolean().optional().default(true), + comingSoon: z.boolean().optional().default(false), + highlights: z.array(z.string()).optional().default([]), + learnMoreUrl: z.string().url().optional(), +}); + +export const shortcutSchema = z.object({ + shortcut: z.string().min(1), + action: z.string().min(1), + description: z.string().optional(), +}); + +export const shortcutsFrontmatterSchema = baseContentSchema.extend({ + category: shortcutCategorySchema, + title: z.string().optional(), +}); + +export const guideStepSchema = z.object({ + title: z.string().min(1), + content: z.string().min(1), + duration: z.string().optional(), +}); + +export const gettingStartedFrontmatterSchema = baseContentSchema.extend({ + title: z.string().min(1), + description: z.string().min(1), + difficulty: guideDifficultySchema, + estimatedTime: z.string().optional(), + prerequisites: z.array(z.string()).optional().default([]), +}); + +export const changelogChangeSchema = z.object({ + title: z.string().min(1), + description: z.string().optional(), + category: z.string().optional(), +}); + +export const changelogFrontmatterSchema = baseContentSchema.extend({ + version: z.string().min(1), + title: z.string().min(1), + releaseDate: z.coerce.date(), + type: changelogTypeSchema, + summary: z.string().optional(), + highlighted: z.boolean().optional().default(false), + changes: z + .object({ + features: z.array(changelogChangeSchema).optional(), + improvements: z.array(changelogChangeSchema).optional(), + bugfixes: z.array(changelogChangeSchema).optional(), + }) + .optional(), + platforms: z.array(z.string()).optional().default(['all']), +}); + +export const contactFrontmatterSchema = baseContentSchema.extend({ + title: z.string().min(1), + supportEmail: z.string().email().optional(), + supportUrl: z.string().url().optional(), + discordUrl: z.string().url().optional(), + twitterUrl: z.string().url().optional(), + documentationUrl: z.string().url().optional(), + responseTime: z.string().optional(), +}); + +// ============================================================================ +// Type Exports from Schemas +// ============================================================================ + +export type FAQFrontmatter = z.infer; +export type FeatureFrontmatter = z.infer; +export type ShortcutsFrontmatter = z.infer; +export type GettingStartedFrontmatter = z.infer; +export type ChangelogFrontmatter = z.infer; +export type ContactFrontmatter = z.infer; diff --git a/packages/shared-help-types/src/search.ts b/packages/shared-help-types/src/search.ts new file mode 100644 index 000000000..900d11235 --- /dev/null +++ b/packages/shared-help-types/src/search.ts @@ -0,0 +1,71 @@ +/** + * Search-related Type Definitions + */ + +import type { FAQItem, FeatureItem, GettingStartedItem, ChangelogItem } from './content.js'; + +// ============================================================================ +// Searchable Item Types +// ============================================================================ + +export type SearchableContentType = 'faq' | 'feature' | 'guide' | 'changelog'; + +export interface SearchableItem { + id: string; + type: SearchableContentType; + title: string; + content: string; + tags?: string[]; + question?: string; + description?: string; +} + +// ============================================================================ +// Search Result Types +// ============================================================================ + +export interface SearchResult { + id: string; + type: SearchableContentType; + title: string; + excerpt: string; + score: number; + highlight?: string; + /** Original item reference */ + item: FAQItem | FeatureItem | GettingStartedItem | ChangelogItem; +} + +export interface SearchOptions { + /** Maximum number of results to return */ + limit?: number; + /** Minimum score threshold (0-1, lower is more strict) */ + threshold?: number; + /** Filter by content type */ + types?: SearchableContentType[]; + /** Filter by app ID (for app-specific content) */ + appId?: string; +} + +export interface SearchIndexConfig { + /** Weight for title/question field */ + titleWeight?: number; + /** Weight for content field */ + contentWeight?: number; + /** Weight for tags field */ + tagsWeight?: number; + /** Fuzzy match threshold (0-1, lower is more strict) */ + threshold?: number; + /** Minimum characters to start searching */ + minMatchCharLength?: number; +} + +// ============================================================================ +// Search State Types (for UI) +// ============================================================================ + +export interface SearchState { + query: string; + results: SearchResult[]; + isSearching: boolean; + hasSearched: boolean; +} diff --git a/packages/shared-help-types/tsconfig.json b/packages/shared-help-types/tsconfig.json new file mode 100644 index 000000000..1b9470b61 --- /dev/null +++ b/packages/shared-help-types/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true, + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/shared-help-ui/package.json b/packages/shared-help-ui/package.json new file mode 100644 index 000000000..9828cd4e8 --- /dev/null +++ b/packages/shared-help-ui/package.json @@ -0,0 +1,65 @@ +{ + "name": "@manacore/shared-help-ui", + "version": "1.0.0", + "private": true, + "type": "module", + "svelte": "./src/index.ts", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "svelte": "./src/index.ts", + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./HelpPage.svelte": { + "svelte": "./src/pages/HelpPage.svelte", + "default": "./src/pages/HelpPage.svelte" + }, + "./FAQSection.svelte": { + "svelte": "./src/components/FAQSection.svelte", + "default": "./src/components/FAQSection.svelte" + }, + "./FeaturesOverview.svelte": { + "svelte": "./src/components/FeaturesOverview.svelte", + "default": "./src/components/FeaturesOverview.svelte" + }, + "./KeyboardShortcuts.svelte": { + "svelte": "./src/components/KeyboardShortcuts.svelte", + "default": "./src/components/KeyboardShortcuts.svelte" + }, + "./GettingStartedGuide.svelte": { + "svelte": "./src/components/GettingStartedGuide.svelte", + "default": "./src/components/GettingStartedGuide.svelte" + }, + "./ChangelogSection.svelte": { + "svelte": "./src/components/ChangelogSection.svelte", + "default": "./src/components/ChangelogSection.svelte" + }, + "./ContactSection.svelte": { + "svelte": "./src/components/ContactSection.svelte", + "default": "./src/components/ContactSection.svelte" + }, + "./HelpSearch.svelte": { + "svelte": "./src/components/HelpSearch.svelte", + "default": "./src/components/HelpSearch.svelte" + } + }, + "scripts": { + "check": "svelte-check --tsconfig ./tsconfig.json", + "lint": "eslint ." + }, + "dependencies": { + "@manacore/shared-help-types": "workspace:*", + "@manacore/shared-help-content": "workspace:*", + "@manacore/shared-icons": "workspace:*" + }, + "devDependencies": { + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "typescript": "^5.7.3" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } +} diff --git a/packages/shared-help-ui/src/components/ChangelogEntry.svelte b/packages/shared-help-ui/src/components/ChangelogEntry.svelte new file mode 100644 index 000000000..f9cfde552 --- /dev/null +++ b/packages/shared-help-ui/src/components/ChangelogEntry.svelte @@ -0,0 +1,152 @@ + + +
+ + + {#if expanded} +
+ {#if item.summary} +

{item.summary}

+ {/if} + + {#if item.changes} + {#if item.changes.features && item.changes.features.length > 0} +
+
+ New Features +
+
    + {#each item.changes.features as change} +
  • + + + + {change.title} + {#if change.description} + - {change.description} + {/if} + +
  • + {/each} +
+
+ {/if} + + {#if item.changes.improvements && item.changes.improvements.length > 0} +
+
+ Improvements +
+
    + {#each item.changes.improvements as change} +
  • + + + {change.title} + {#if change.description} + - {change.description} + {/if} + +
  • + {/each} +
+
+ {/if} + + {#if item.changes.bugfixes && item.changes.bugfixes.length > 0} +
+
Bug Fixes
+
    + {#each item.changes.bugfixes as change} +
  • + × + + {change.title} + {#if change.description} + - {change.description} + {/if} + +
  • + {/each} +
+
+ {/if} + {/if} + + {#if item.content} +
+ {@html item.content} +
+ {/if} +
+ {/if} +
diff --git a/packages/shared-help-ui/src/components/ChangelogSection.svelte b/packages/shared-help-ui/src/components/ChangelogSection.svelte new file mode 100644 index 000000000..b30f45018 --- /dev/null +++ b/packages/shared-help-ui/src/components/ChangelogSection.svelte @@ -0,0 +1,45 @@ + + +{#if items.length === 0} +

+ {translations.changelog.noItems} +

+{:else} +
+ {#each displayedItems() as item (item.id)} + + {/each} + + {#if hasMore} +
+ +
+ {/if} +
+{/if} diff --git a/packages/shared-help-ui/src/components/ContactSection.svelte b/packages/shared-help-ui/src/components/ContactSection.svelte new file mode 100644 index 000000000..d22bbf656 --- /dev/null +++ b/packages/shared-help-ui/src/components/ContactSection.svelte @@ -0,0 +1,123 @@ + + +{#if !contact} +

+ {translations.contact.noInfo} +

+{:else} +
+
+ {@html contact.content} +
+ +
+ {#if contact.supportEmail} + +
+ + + +
+
+

+ {translations.contact.email} +

+

+ {contact.supportEmail} +

+
+
+ {/if} + + {#if contact.responseTime} +
+
+ + + +
+
+

+ {translations.contact.responseTime} +

+

+ {contact.responseTime} +

+
+
+ {/if} + + {#if contact.discordUrl} + +
+ + + +
+
+

Discord

+

Join our community

+
+
+ {/if} + + {#if contact.documentationUrl} + +
+ + + +
+
+

Documentation

+

Read the docs

+
+
+ {/if} +
+
+{/if} diff --git a/packages/shared-help-ui/src/components/FAQItem.svelte b/packages/shared-help-ui/src/components/FAQItem.svelte new file mode 100644 index 000000000..2f563282c --- /dev/null +++ b/packages/shared-help-ui/src/components/FAQItem.svelte @@ -0,0 +1,46 @@ + + +
+ + + {#if expanded} +
+ {@html item.answer} +
+ {/if} +
diff --git a/packages/shared-help-ui/src/components/FAQSection.svelte b/packages/shared-help-ui/src/components/FAQSection.svelte new file mode 100644 index 000000000..c29623a56 --- /dev/null +++ b/packages/shared-help-ui/src/components/FAQSection.svelte @@ -0,0 +1,117 @@ + + +
+ {#if showCategories && items.length > 0} +
+ + {#each categories as category} + {@const hasItems = items.some((item) => item.category === category)} + {#if hasItems} + + {/if} + {/each} +
+ {/if} + + {#if filteredItems().length === 0} +

+ {translations.faq.noItems} +

+ {:else} +
+ {#each filteredItems() as item (item.id)} + toggleItem(item.id)} + /> + {/each} +
+ {/if} + + {#if hasMore} +
+ +
+ {/if} +
diff --git a/packages/shared-help-ui/src/components/FeatureCard.svelte b/packages/shared-help-ui/src/components/FeatureCard.svelte new file mode 100644 index 000000000..54c702373 --- /dev/null +++ b/packages/shared-help-ui/src/components/FeatureCard.svelte @@ -0,0 +1,68 @@ + + +
+ {#if item.comingSoon} + + {comingSoonLabel} + + {/if} + +
+ {#if item.icon} + {item.icon} + {/if} +

+ {item.title} +

+
+ +

+ {item.description} +

+ + {#if item.highlights && item.highlights.length > 0} +
    + {#each item.highlights as highlight} +
  • + + + + {highlight} +
  • + {/each} +
+ {/if} + + {#if item.learnMoreUrl} + + {learnMoreLabel} → + + {/if} +
diff --git a/packages/shared-help-ui/src/components/FeaturesOverview.svelte b/packages/shared-help-ui/src/components/FeaturesOverview.svelte new file mode 100644 index 000000000..c45d4570a --- /dev/null +++ b/packages/shared-help-ui/src/components/FeaturesOverview.svelte @@ -0,0 +1,50 @@ + + +{#if !hasItems} +

+ {translations.features.noItems} +

+{:else} +
+ {#each Object.entries(groupedItems()) as [_category, categoryItems]} + {#if categoryItems.length > 0} +
+ {#each categoryItems as item (item.id)} + + {/each} +
+ {/if} + {/each} +
+{/if} diff --git a/packages/shared-help-ui/src/components/GettingStartedGuide.svelte b/packages/shared-help-ui/src/components/GettingStartedGuide.svelte new file mode 100644 index 000000000..0a1b26e02 --- /dev/null +++ b/packages/shared-help-ui/src/components/GettingStartedGuide.svelte @@ -0,0 +1,111 @@ + + +{#if items.length === 0} +

+ {translations.gettingStarted.noItems} +

+{:else} +
+ +
+ {#each items as item (item.id)} + + {/each} +
+ + +
+ {#if selectedGuide()} + {@const guide = selectedGuide()} +
+

+ {guide.title} +

+

+ {guide.description} +

+ + {#if guide.steps && guide.steps.length > 0} +
+ {#each guide.steps as step, index} +
+
+ {index + 1} +
+
+

+ {step.title} +

+
+ {step.content} +
+
+
+ {/each} +
+ {:else} +
+ {@html guide.content} +
+ {/if} +
+ {/if} +
+
+{/if} diff --git a/packages/shared-help-ui/src/components/HelpSearch.svelte b/packages/shared-help-ui/src/components/HelpSearch.svelte new file mode 100644 index 000000000..a4fb50798 --- /dev/null +++ b/packages/shared-help-ui/src/components/HelpSearch.svelte @@ -0,0 +1,198 @@ + + +
+
+ query.length >= 2 && (showResults = true)} + onblur={handleBlur} + placeholder={placeholder ?? translations.search.noResults} + class="w-full rounded-lg border border-gray-300 bg-white py-2.5 pl-10 pr-4 text-sm text-gray-900 placeholder-gray-500 transition-colors focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400" + /> +
+ {#if isSearching} + + + + + {:else} + + + + {/if} +
+
+ + {#if showResults} +
+ {#if results.length === 0} +
+ {translations.search.noResults.replace('{query}', query)} +
+ {:else} +
    + {#each results as result, index (result.id)} +
  • + +
  • + {/each} +
+
+ {translations.search.resultsCount.replace('{count}', String(results.length))} +
+ {/if} +
+ {/if} +
diff --git a/packages/shared-help-ui/src/components/KeyboardShortcuts.svelte b/packages/shared-help-ui/src/components/KeyboardShortcuts.svelte new file mode 100644 index 000000000..cd8673895 --- /dev/null +++ b/packages/shared-help-ui/src/components/KeyboardShortcuts.svelte @@ -0,0 +1,54 @@ + + +{#if !hasItems} +

+ {translations.shortcuts.noItems} +

+{:else} +
+ + + + + + + + + + {#each allShortcuts() as shortcut} + + + + + + {/each} + +
ShortcutActionDescription
+ + {shortcut.shortcut} + + + {shortcut.action} + + {shortcut.description || '-'} +
+
+{/if} diff --git a/packages/shared-help-ui/src/index.ts b/packages/shared-help-ui/src/index.ts new file mode 100644 index 000000000..6fda8226d --- /dev/null +++ b/packages/shared-help-ui/src/index.ts @@ -0,0 +1,33 @@ +/** + * @manacore/shared-help-ui + * Svelte 5 components for the Help page system + */ + +// Main page component +export { default as HelpPage } from './pages/HelpPage.svelte'; + +// Section components +export { default as FAQSection } from './components/FAQSection.svelte'; +export { default as FAQItem } from './components/FAQItem.svelte'; +export { default as FeaturesOverview } from './components/FeaturesOverview.svelte'; +export { default as FeatureCard } from './components/FeatureCard.svelte'; +export { default as KeyboardShortcuts } from './components/KeyboardShortcuts.svelte'; +export { default as GettingStartedGuide } from './components/GettingStartedGuide.svelte'; +export { default as ChangelogSection } from './components/ChangelogSection.svelte'; +export { default as ChangelogEntry } from './components/ChangelogEntry.svelte'; +export { default as ContactSection } from './components/ContactSection.svelte'; +export { default as HelpSearch } from './components/HelpSearch.svelte'; + +// Types +export type { + HelpPageProps, + HelpPageTranslations, + HelpSection, + FAQSectionProps, + FeaturesOverviewProps, + KeyboardShortcutsProps, + GettingStartedGuideProps, + ChangelogSectionProps, + ContactSectionProps, + HelpSearchProps, +} from './types.js'; diff --git a/packages/shared-help-ui/src/pages/HelpPage.svelte b/packages/shared-help-ui/src/pages/HelpPage.svelte new file mode 100644 index 000000000..4e57f682a --- /dev/null +++ b/packages/shared-help-ui/src/pages/HelpPage.svelte @@ -0,0 +1,169 @@ + + +
+ +
+ {#if showBackButton} + + {/if} + +

+ {translations.title} +

+ {#if translations.subtitle} +

+ {translations.subtitle} - {appName} +

+ {/if} +
+ + + {#if searchEnabled} +
+ +
+ {/if} + + + {#if visibleSections.length > 1} +
+ +
+ {/if} + + +
+ {#if activeSection === 'faq' && showFAQ} + + {:else if activeSection === 'features' && showFeatures} + + {:else if activeSection === 'shortcuts' && showShortcuts} + + {:else if activeSection === 'getting-started' && showGettingStarted} + + {:else if activeSection === 'changelog' && showChangelog} + + {:else if activeSection === 'contact' && showContact} + + {/if} +
+
diff --git a/packages/shared-help-ui/src/types.ts b/packages/shared-help-ui/src/types.ts new file mode 100644 index 000000000..00fdee74e --- /dev/null +++ b/packages/shared-help-ui/src/types.ts @@ -0,0 +1,147 @@ +/** + * Component Props and Translation Types + */ + +import type { HelpContent, SearchResult } from '@manacore/shared-help-types'; + +// ============================================================================ +// Translation Types +// ============================================================================ + +export interface HelpPageTranslations { + title: string; + subtitle?: string; + searchPlaceholder: string; + sections: { + faq: string; + features: string; + shortcuts: string; + gettingStarted: string; + changelog: string; + contact: string; + }; + search: { + noResults: string; + resultsCount: string; + searching: string; + }; + faq: { + noItems: string; + categories: { + general: string; + account: string; + billing: string; + features: string; + technical: string; + privacy: string; + }; + }; + features: { + noItems: string; + comingSoon: string; + learnMore: string; + }; + shortcuts: { + noItems: string; + }; + gettingStarted: { + noItems: string; + estimatedTime: string; + difficulty: { + beginner: string; + intermediate: string; + advanced: string; + }; + }; + changelog: { + noItems: string; + types: { + major: string; + minor: string; + patch: string; + beta: string; + }; + }; + contact: { + noInfo: string; + email: string; + responseTime: string; + }; + common: { + back: string; + showMore: string; + showLess: string; + }; +} + +// ============================================================================ +// Component Props +// ============================================================================ + +export type HelpSection = + | 'faq' + | 'features' + | 'shortcuts' + | 'getting-started' + | 'changelog' + | 'contact'; + +export interface HelpPageProps { + content: HelpContent; + appName: string; + appId: string; + translations: HelpPageTranslations; + searchEnabled?: boolean; + showFAQ?: boolean; + showFeatures?: boolean; + showShortcuts?: boolean; + showGettingStarted?: boolean; + showChangelog?: boolean; + showContact?: boolean; + defaultSection?: HelpSection; + showBackButton?: boolean; + onBack?: () => void; + onSectionChange?: (section: HelpSection) => void; + onSearch?: (query: string, results: SearchResult[]) => void; +} + +export interface FAQSectionProps { + items: HelpContent['faq']; + translations: Pick; + showCategories?: boolean; + maxItems?: number; + expandFirst?: boolean; +} + +export interface FeaturesOverviewProps { + items: HelpContent['features']; + translations: Pick; +} + +export interface KeyboardShortcutsProps { + items: HelpContent['shortcuts']; + translations: Pick; +} + +export interface GettingStartedGuideProps { + items: HelpContent['gettingStarted']; + translations: Pick; +} + +export interface ChangelogSectionProps { + items: HelpContent['changelog']; + translations: Pick; + maxItems?: number; +} + +export interface ContactSectionProps { + contact: HelpContent['contact']; + translations: Pick; +} + +export interface HelpSearchProps { + content: HelpContent; + translations: Pick; + placeholder?: string; + onResultSelect: (result: SearchResult) => void; +} diff --git a/packages/shared-help-ui/tsconfig.json b/packages/shared-help-ui/tsconfig.json new file mode 100644 index 000000000..1b9470b61 --- /dev/null +++ b/packages/shared-help-ui/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true, + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/shared-i18n/src/index.ts b/packages/shared-i18n/src/index.ts index 983fd7a43..21eeb2ad8 100644 --- a/packages/shared-i18n/src/index.ts +++ b/packages/shared-i18n/src/index.ts @@ -65,5 +65,18 @@ export { getForgotPasswordTranslations, } from './translations/auth'; +// Help translations +export { + en as helpTranslationsEn, + de as helpTranslationsDe, + it as helpTranslationsIt, + fr as helpTranslationsFr, + es as helpTranslationsEs, + type HelpTranslations, + type HelpLocale, + helpTranslations, + getHelpTranslations, +} from './translations/help'; + // Components export { LanguageSelector } from './components'; diff --git a/packages/shared-i18n/src/translations/help/de.json b/packages/shared-i18n/src/translations/help/de.json new file mode 100644 index 000000000..74efba394 --- /dev/null +++ b/packages/shared-i18n/src/translations/help/de.json @@ -0,0 +1,65 @@ +{ + "title": "Hilfe & Support", + "subtitle": "Finde Antworten und lerne die App kennen", + "searchPlaceholder": "Hilfe durchsuchen...", + "sections": { + "faq": "FAQ", + "features": "Features", + "shortcuts": "Tastenkürzel", + "gettingStarted": "Erste Schritte", + "changelog": "Neuigkeiten", + "contact": "Kontakt" + }, + "search": { + "noResults": "Keine Ergebnisse für \"{query}\"", + "resultsCount": "{count} Ergebnisse gefunden", + "searching": "Suche..." + }, + "faq": { + "noItems": "Keine FAQs verfügbar", + "categories": { + "general": "Allgemein", + "account": "Konto", + "billing": "Abrechnung", + "features": "Funktionen", + "technical": "Technik", + "privacy": "Datenschutz" + } + }, + "features": { + "noItems": "Noch keine Features dokumentiert", + "comingSoon": "Demnächst", + "learnMore": "Mehr erfahren" + }, + "shortcuts": { + "noItems": "Keine Tastenkürzel verfügbar" + }, + "gettingStarted": { + "noItems": "Noch keine Anleitungen verfügbar", + "estimatedTime": "Geschätzte Zeit", + "difficulty": { + "beginner": "Anfänger", + "intermediate": "Fortgeschritten", + "advanced": "Experte" + } + }, + "changelog": { + "noItems": "Noch keine Updates", + "types": { + "major": "Haupt-Update", + "minor": "Kleines Update", + "patch": "Fehlerbehebung", + "beta": "Beta" + } + }, + "contact": { + "noInfo": "Kontaktinformationen nicht verfügbar", + "email": "E-Mail senden", + "responseTime": "Antwortzeit" + }, + "common": { + "back": "Zurück", + "showMore": "Mehr anzeigen", + "showLess": "Weniger anzeigen" + } +} diff --git a/packages/shared-i18n/src/translations/help/en.json b/packages/shared-i18n/src/translations/help/en.json new file mode 100644 index 000000000..76bb0bd84 --- /dev/null +++ b/packages/shared-i18n/src/translations/help/en.json @@ -0,0 +1,65 @@ +{ + "title": "Help & Support", + "subtitle": "Find answers and learn how to use the app", + "searchPlaceholder": "Search help articles...", + "sections": { + "faq": "FAQ", + "features": "Features", + "shortcuts": "Keyboard Shortcuts", + "gettingStarted": "Getting Started", + "changelog": "What's New", + "contact": "Contact Us" + }, + "search": { + "noResults": "No results found for \"{query}\"", + "resultsCount": "{count} results found", + "searching": "Searching..." + }, + "faq": { + "noItems": "No FAQs available", + "categories": { + "general": "General", + "account": "Account", + "billing": "Billing", + "features": "Features", + "technical": "Technical", + "privacy": "Privacy" + } + }, + "features": { + "noItems": "No features documented yet", + "comingSoon": "Coming soon", + "learnMore": "Learn more" + }, + "shortcuts": { + "noItems": "No keyboard shortcuts available" + }, + "gettingStarted": { + "noItems": "No guides available yet", + "estimatedTime": "Estimated time", + "difficulty": { + "beginner": "Beginner", + "intermediate": "Intermediate", + "advanced": "Advanced" + } + }, + "changelog": { + "noItems": "No updates yet", + "types": { + "major": "Major", + "minor": "Minor", + "patch": "Patch", + "beta": "Beta" + } + }, + "contact": { + "noInfo": "Contact information not available", + "email": "Email us", + "responseTime": "Response time" + }, + "common": { + "back": "Back", + "showMore": "Show more", + "showLess": "Show less" + } +} diff --git a/packages/shared-i18n/src/translations/help/es.json b/packages/shared-i18n/src/translations/help/es.json new file mode 100644 index 000000000..9476514a7 --- /dev/null +++ b/packages/shared-i18n/src/translations/help/es.json @@ -0,0 +1,65 @@ +{ + "title": "Ayuda y Soporte", + "subtitle": "Encuentra respuestas y aprende a usar la aplicación", + "searchPlaceholder": "Buscar en la ayuda...", + "sections": { + "faq": "FAQ", + "features": "Características", + "shortcuts": "Atajos de teclado", + "gettingStarted": "Primeros pasos", + "changelog": "Novedades", + "contact": "Contacto" + }, + "search": { + "noResults": "Sin resultados para \"{query}\"", + "resultsCount": "{count} resultados encontrados", + "searching": "Buscando..." + }, + "faq": { + "noItems": "No hay preguntas frecuentes disponibles", + "categories": { + "general": "General", + "account": "Cuenta", + "billing": "Facturación", + "features": "Características", + "technical": "Técnico", + "privacy": "Privacidad" + } + }, + "features": { + "noItems": "No hay características documentadas", + "comingSoon": "Próximamente", + "learnMore": "Saber más" + }, + "shortcuts": { + "noItems": "No hay atajos de teclado disponibles" + }, + "gettingStarted": { + "noItems": "No hay guías disponibles", + "estimatedTime": "Tiempo estimado", + "difficulty": { + "beginner": "Principiante", + "intermediate": "Intermedio", + "advanced": "Avanzado" + } + }, + "changelog": { + "noItems": "Sin actualizaciones", + "types": { + "major": "Principal", + "minor": "Menor", + "patch": "Corrección", + "beta": "Beta" + } + }, + "contact": { + "noInfo": "Información de contacto no disponible", + "email": "Envíanos un correo", + "responseTime": "Tiempo de respuesta" + }, + "common": { + "back": "Volver", + "showMore": "Ver más", + "showLess": "Ver menos" + } +} diff --git a/packages/shared-i18n/src/translations/help/fr.json b/packages/shared-i18n/src/translations/help/fr.json new file mode 100644 index 000000000..079d16561 --- /dev/null +++ b/packages/shared-i18n/src/translations/help/fr.json @@ -0,0 +1,65 @@ +{ + "title": "Aide & Support", + "subtitle": "Trouvez des réponses et apprenez à utiliser l'application", + "searchPlaceholder": "Rechercher dans l'aide...", + "sections": { + "faq": "FAQ", + "features": "Fonctionnalités", + "shortcuts": "Raccourcis clavier", + "gettingStarted": "Premiers pas", + "changelog": "Nouveautés", + "contact": "Contact" + }, + "search": { + "noResults": "Aucun résultat pour \"{query}\"", + "resultsCount": "{count} résultats trouvés", + "searching": "Recherche..." + }, + "faq": { + "noItems": "Aucune FAQ disponible", + "categories": { + "general": "Général", + "account": "Compte", + "billing": "Facturation", + "features": "Fonctionnalités", + "technical": "Technique", + "privacy": "Confidentialité" + } + }, + "features": { + "noItems": "Aucune fonctionnalité documentée", + "comingSoon": "Bientôt disponible", + "learnMore": "En savoir plus" + }, + "shortcuts": { + "noItems": "Aucun raccourci clavier disponible" + }, + "gettingStarted": { + "noItems": "Aucun guide disponible", + "estimatedTime": "Temps estimé", + "difficulty": { + "beginner": "Débutant", + "intermediate": "Intermédiaire", + "advanced": "Avancé" + } + }, + "changelog": { + "noItems": "Aucune mise à jour", + "types": { + "major": "Majeure", + "minor": "Mineure", + "patch": "Correctif", + "beta": "Bêta" + } + }, + "contact": { + "noInfo": "Informations de contact non disponibles", + "email": "Nous contacter", + "responseTime": "Délai de réponse" + }, + "common": { + "back": "Retour", + "showMore": "Voir plus", + "showLess": "Voir moins" + } +} diff --git a/packages/shared-i18n/src/translations/help/index.ts b/packages/shared-i18n/src/translations/help/index.ts new file mode 100644 index 000000000..75c1c2a31 --- /dev/null +++ b/packages/shared-i18n/src/translations/help/index.ts @@ -0,0 +1,108 @@ +/** + * Help translations exports + */ + +import en from './en.json'; +import de from './de.json'; +import it from './it.json'; +import fr from './fr.json'; +import es from './es.json'; + +export { en, de, it, fr, es }; + +/** + * Help translations type structure + */ +export interface HelpTranslations { + title: string; + subtitle: string; + searchPlaceholder: string; + sections: { + faq: string; + features: string; + shortcuts: string; + gettingStarted: string; + changelog: string; + contact: string; + }; + search: { + noResults: string; + resultsCount: string; + searching: string; + }; + faq: { + noItems: string; + categories: { + general: string; + account: string; + billing: string; + features: string; + technical: string; + privacy: string; + }; + }; + features: { + noItems: string; + comingSoon: string; + learnMore: string; + }; + shortcuts: { + noItems: string; + }; + gettingStarted: { + noItems: string; + estimatedTime: string; + difficulty: { + beginner: string; + intermediate: string; + advanced: string; + }; + }; + changelog: { + noItems: string; + types: { + major: string; + minor: string; + patch: string; + beta: string; + }; + }; + contact: { + noInfo: string; + email: string; + responseTime: string; + }; + common: { + back: string; + showMore: string; + showLess: string; + }; +} + +/** + * Supported help locales + */ +export type HelpLocale = 'en' | 'de' | 'it' | 'fr' | 'es'; + +/** + * All help translations by locale + */ +export const helpTranslations: Record = { + en, + de, + it, + fr, + es, +}; + +/** + * Get help translations by locale + */ +export function getHelpTranslations(locale: string): HelpTranslations { + const supportedLocale = locale as HelpLocale; + if (supportedLocale in helpTranslations) { + return helpTranslations[supportedLocale]; + } + // Default to English + return helpTranslations.en; +} diff --git a/packages/shared-i18n/src/translations/help/it.json b/packages/shared-i18n/src/translations/help/it.json new file mode 100644 index 000000000..732c6d2c9 --- /dev/null +++ b/packages/shared-i18n/src/translations/help/it.json @@ -0,0 +1,65 @@ +{ + "title": "Aiuto & Supporto", + "subtitle": "Trova risposte e impara a usare l'app", + "searchPlaceholder": "Cerca nell'aiuto...", + "sections": { + "faq": "FAQ", + "features": "Funzionalità", + "shortcuts": "Scorciatoie", + "gettingStarted": "Primi passi", + "changelog": "Novità", + "contact": "Contatti" + }, + "search": { + "noResults": "Nessun risultato per \"{query}\"", + "resultsCount": "{count} risultati trovati", + "searching": "Ricerca..." + }, + "faq": { + "noItems": "Nessuna FAQ disponibile", + "categories": { + "general": "Generale", + "account": "Account", + "billing": "Fatturazione", + "features": "Funzionalità", + "technical": "Tecnico", + "privacy": "Privacy" + } + }, + "features": { + "noItems": "Nessuna funzionalità documentata", + "comingSoon": "Prossimamente", + "learnMore": "Scopri di più" + }, + "shortcuts": { + "noItems": "Nessuna scorciatoia disponibile" + }, + "gettingStarted": { + "noItems": "Nessuna guida disponibile", + "estimatedTime": "Tempo stimato", + "difficulty": { + "beginner": "Principiante", + "intermediate": "Intermedio", + "advanced": "Avanzato" + } + }, + "changelog": { + "noItems": "Nessun aggiornamento", + "types": { + "major": "Principale", + "minor": "Secondario", + "patch": "Correzione", + "beta": "Beta" + } + }, + "contact": { + "noInfo": "Informazioni di contatto non disponibili", + "email": "Inviaci un'email", + "responseTime": "Tempo di risposta" + }, + "common": { + "back": "Indietro", + "showMore": "Mostra di più", + "showLess": "Mostra meno" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e76e3b239..cffd9af6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1040,6 +1040,15 @@ importers: '@manacore/shared-feedback-ui': specifier: workspace:* version: link:../../../../packages/shared-feedback-ui + '@manacore/shared-help-content': + specifier: workspace:* + version: link:../../../../packages/shared-help-content + '@manacore/shared-help-types': + specifier: workspace:* + version: link:../../../../packages/shared-help-types + '@manacore/shared-help-ui': + specifier: workspace:* + version: link:../../../../packages/shared-help-ui '@manacore/shared-i18n': specifier: workspace:* version: link:../../../../packages/shared-i18n @@ -1067,6 +1076,18 @@ importers: '@manacore/shared-utils': specifier: workspace:* version: link:../../../../packages/shared-utils + d3-force: + specifier: ^3.0.0 + version: 3.0.0 + d3-selection: + specifier: ^3.0.0 + version: 3.0.0 + d3-zoom: + specifier: ^3.0.0 + version: 3.0.0 + lucide-svelte: + specifier: ^0.556.0 + version: 0.556.0(svelte@5.44.0) svelte-i18n: specifier: ^4.0.1 version: 4.0.1(svelte@5.44.0) @@ -1083,6 +1104,15 @@ importers: '@tailwindcss/vite': specifier: ^4.1.7 version: 4.1.17(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@types/d3-force': + specifier: ^3.0.10 + version: 3.0.10 + '@types/d3-selection': + specifier: ^3.0.11 + version: 3.0.11 + '@types/d3-zoom': + specifier: ^3.0.8 + version: 3.0.8 '@types/node': specifier: ^20.0.0 version: 20.19.25 @@ -3933,6 +3963,91 @@ importers: specifier: ^5.7.3 version: 5.9.3 + packages/shared-help-content: + dependencies: + '@manacore/shared-help-types': + specifier: workspace:* + version: link:../shared-help-types + fuse.js: + specifier: ^7.0.0 + version: 7.1.0 + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 + marked: + specifier: ^15.0.4 + version: 15.0.12 + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.19.1 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + zod: + specifier: ^3.24.1 + version: 3.25.76 + + packages/shared-help-mobile: + dependencies: + '@manacore/shared-help-content': + specifier: workspace:* + version: link:../shared-help-content + '@manacore/shared-help-types': + specifier: workspace:* + version: link:../shared-help-types + expo: + specifier: '>=52.0.0' + version: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + nativewind: + specifier: ^4.0.0 + version: 4.2.1(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)(tailwindcss@4.1.17) + react: + specifier: 18.3.1 + version: 18.3.1 + react-native: + specifier: '>=0.76.0' + version: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + devDependencies: + '@types/react': + specifier: ~18.3.12 + version: 18.3.27 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + + packages/shared-help-types: + dependencies: + zod: + specifier: ^3.24.1 + version: 3.25.76 + devDependencies: + typescript: + specifier: ^5.7.3 + version: 5.9.3 + + packages/shared-help-ui: + dependencies: + '@manacore/shared-help-content': + specifier: workspace:* + version: link:../shared-help-content + '@manacore/shared-help-types': + specifier: workspace:* + version: link:../shared-help-types + '@manacore/shared-icons': + specifier: workspace:* + version: link:../shared-icons + devDependencies: + svelte: + specifier: ^5.0.0 + version: 5.44.0 + svelte-check: + specifier: ^4.0.0 + version: 4.3.4(picomatch@4.0.3)(svelte@5.44.0)(typescript@5.9.3) + typescript: + specifier: ^5.7.3 + version: 5.9.3 + packages/shared-i18n: devDependencies: svelte: @@ -13502,6 +13617,10 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + fuse.js@7.1.0: + resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} + engines: {node: '>=10'} + gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} @@ -15113,6 +15232,11 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + marked@16.4.2: resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} engines: {node: '>= 20'} @@ -22226,6 +22350,83 @@ snapshots: - supports-color - utf-8-validate + '@expo/cli@54.0.16(expo-router@6.0.15)(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))': + dependencies: + '@0no-co/graphql.web': 1.2.0 + '@expo/code-signing-certificates': 0.0.5 + '@expo/config': 12.0.10 + '@expo/config-plugins': 54.0.2 + '@expo/devcert': 1.2.0 + '@expo/env': 2.0.7 + '@expo/image-utils': 0.8.7 + '@expo/json-file': 10.0.7 + '@expo/mcp-tunnel': 0.1.0 + '@expo/metro': 54.1.0 + '@expo/metro-config': 54.0.9(expo@54.0.25) + '@expo/osascript': 2.3.7 + '@expo/package-manager': 1.9.8 + '@expo/plist': 0.4.7 + '@expo/prebuild-config': 54.0.6(expo@54.0.25) + '@expo/schema-utils': 0.1.7 + '@expo/spawn-async': 1.7.2 + '@expo/ws-tunnel': 1.0.6 + '@expo/xcpretty': 4.3.2 + '@react-native/dev-middleware': 0.81.5 + '@urql/core': 5.2.0 + '@urql/exchange-retry': 1.3.2(@urql/core@5.2.0) + accepts: 1.3.8 + arg: 5.0.2 + better-opn: 3.0.2 + bplist-creator: 0.1.0 + bplist-parser: 0.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + compression: 1.8.1 + connect: 3.7.0 + debug: 4.4.3 + env-editor: 0.4.2 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + expo-server: 1.0.4 + freeport-async: 2.0.0 + getenv: 2.0.0 + glob: 10.5.0 + lan-network: 0.1.7 + minimatch: 9.0.5 + node-forge: 1.3.2 + npm-package-arg: 11.0.3 + ora: 3.4.0 + picomatch: 3.0.1 + pretty-bytes: 5.6.0 + pretty-format: 29.7.0 + progress: 2.0.3 + prompts: 2.4.2 + qrcode-terminal: 0.11.0 + require-from-string: 2.0.2 + requireg: 0.2.2 + resolve: 1.22.11 + resolve-from: 5.0.0 + resolve.exports: 2.0.3 + semver: 7.7.3 + send: 0.19.1 + slugify: 1.6.6 + source-map-support: 0.5.21 + stacktrace-parser: 0.1.11 + structured-headers: 0.4.1 + tar: 7.5.2 + terminal-link: 2.1.1 + undici: 6.22.0 + wrap-ansi: 7.0.0 + ws: 8.18.3 + optionalDependencies: + expo-router: 6.0.15(hwqworfppxvioilmgvd7t3oifm) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - graphql + - supports-color + - utf-8-validate + '@expo/cli@54.0.16(expo-router@6.0.15)(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))': dependencies: '@0no-co/graphql.web': 1.2.0 @@ -22440,6 +22641,13 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + '@expo/devtools@0.1.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + chalk: 4.1.2 + optionalDependencies: + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + '@expo/devtools@0.1.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: chalk: 4.1.2 @@ -22745,6 +22953,19 @@ snapshots: optionalDependencies: react-dom: 19.1.0(react@19.1.0) + '@expo/metro-runtime@6.1.2(expo@54.0.25)(react-dom@19.1.0(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + anser: 1.4.10 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + pretty-format: 29.7.0 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + stacktrace-parser: 0.1.11 + whatwg-fetch: 3.6.20 + optionalDependencies: + react-dom: 19.1.0(react@18.3.1) + optional: true + '@expo/metro-runtime@6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: anser: 1.4.10 @@ -22988,6 +23209,12 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + '@expo/vector-icons@15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + '@expo/vector-icons@15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -24570,6 +24797,19 @@ snapshots: '@radix-ui/primitive@1.1.3': {} + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.1.0) @@ -24587,18 +24827,55 @@ snapshots: '@babel/runtime': 7.28.4 react: 18.3.1 + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.27)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.7)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-context@1.1.2(@types/react@18.3.27)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-context@1.1.2(@types/react@19.2.7)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.27)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.27)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -24621,12 +24898,33 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-direction@1.1.1(@types/react@18.3.27)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-direction@1.1.1(@types/react@19.2.7)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -24640,12 +24938,31 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.27)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.7)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.1.0) @@ -24657,6 +24974,14 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-id@1.1.1(@types/react@18.3.27)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-id@1.1.1(@types/react@19.2.7)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.1.0) @@ -24664,6 +24989,17 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -24674,6 +25010,17 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.1.0) @@ -24684,6 +25031,16 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.1.0) @@ -24693,6 +25050,24 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -24716,6 +25091,14 @@ snapshots: '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 + '@radix-ui/react-slot@1.2.0(@types/react@18.3.27)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-slot@1.2.0(@types/react@19.2.7)(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.1.0) @@ -24723,6 +25106,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-slot@1.2.3(@types/react@18.3.27)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-slot@1.2.3(@types/react@19.2.7)(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.1.0) @@ -24730,6 +25121,23 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@types/react-dom': 19.2.3(@types/react@18.3.27) + optional: true + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -24746,12 +25154,28 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.27)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.7)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.27)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.7)(react@19.1.0)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.7)(react@19.1.0) @@ -24760,6 +25184,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.27)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.7)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.1.0) @@ -24767,6 +25199,14 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.27)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.27)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.7)(react@19.1.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.1.0) @@ -24774,6 +25214,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.27)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.7)(react@19.1.0)': dependencies: react: 19.1.0 @@ -25281,6 +25728,15 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + '@react-native/virtualized-lists@0.81.5(@types/react@18.3.27)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + invariant: 2.2.4 + nullthrows: 1.1.1 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + '@react-native/virtualized-lists@0.81.5(@types/react@19.2.7)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1))(react@18.3.1)': dependencies: invariant: 2.2.4 @@ -25338,6 +25794,20 @@ snapshots: transitivePeerDependencies: - '@react-native-masked-view/masked-view' + '@react-navigation/bottom-tabs@7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-navigation/elements': 2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + color: 4.2.3 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + sf-symbols-typescript: 2.1.0 + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + optional: true + '@react-navigation/bottom-tabs@7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: '@react-navigation/elements': 2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -25443,6 +25913,23 @@ snapshots: - '@react-native-masked-view/masked-view' optional: true + '@react-navigation/drawer@7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-navigation/elements': 2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + color: 4.2.3 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-drawer-layout: 4.2.0(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + use-latest-callback: 0.2.6(react@18.3.1) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + optional: true + '@react-navigation/drawer@7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: '@react-navigation/elements': 2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -25505,6 +25992,17 @@ snapshots: use-latest-callback: 0.2.6(react@19.1.0) use-sync-external-store: 1.6.0(react@19.1.0) + '@react-navigation/elements@2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + color: 4.2.3 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + use-latest-callback: 0.2.6(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) + optional: true + '@react-navigation/elements@2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -25557,6 +26055,21 @@ snapshots: transitivePeerDependencies: - '@react-native-masked-view/masked-view' + '@react-navigation/native-stack@7.8.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-navigation/elements': 2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + color: 4.2.3 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + sf-symbols-typescript: 2.1.0 + warn-once: 0.1.1 + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + optional: true + '@react-navigation/native-stack@7.8.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: '@react-navigation/elements': 2.8.3(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -25601,6 +26114,17 @@ snapshots: react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) use-latest-callback: 0.2.6(react@19.1.0) + '@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-navigation/core': 7.13.2(react@18.3.1) + escape-string-regexp: 4.0.0 + fast-deep-equal: 3.1.3 + nanoid: 3.3.11 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + use-latest-callback: 0.2.6(react@18.3.1) + optional: true + '@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)': dependencies: '@react-navigation/core': 7.13.2(react@19.1.0) @@ -26685,6 +27209,19 @@ snapshots: jest: 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)) optional: true + '@testing-library/react-native@13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react-test-renderer@19.1.0(react@18.3.1))(react@18.3.1)': + dependencies: + jest-matcher-utils: 30.2.0 + picocolors: 1.1.1 + pretty-format: 30.2.0 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-test-renderer: 19.1.0(react@18.3.1) + redent: 3.0.0 + optionalDependencies: + jest: 30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)) + optional: true + '@testing-library/react-native@13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.2.0 @@ -27053,6 +27590,11 @@ snapshots: dependencies: '@types/react': 18.3.27 + '@types/react-dom@19.2.3(@types/react@18.3.27)': + dependencies: + '@types/react': 18.3.27 + optional: true + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 @@ -31760,6 +32302,16 @@ snapshots: transitivePeerDependencies: - supports-color + expo-asset@12.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + '@expo/image-utils': 0.8.7 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)) + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + transitivePeerDependencies: + - supports-color + expo-asset@12.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: '@expo/image-utils': 0.8.7 @@ -31857,6 +32409,15 @@ snapshots: transitivePeerDependencies: - supports-color + expo-constants@18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)): + dependencies: + '@expo/config': 12.0.10 + '@expo/env': 2.0.7 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + transitivePeerDependencies: + - supports-color + expo-constants@18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)): dependencies: '@expo/config': 12.0.10 @@ -32028,6 +32589,11 @@ snapshots: expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + expo-file-system@19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)): + dependencies: + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + expo-file-system@19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)): dependencies: expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -32074,6 +32640,13 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + fontfaceobserver: 2.3.0 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -32148,6 +32721,11 @@ snapshots: expo: 54.0.13(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react: 19.1.0 + expo-keep-awake@15.0.7(expo@54.0.25)(react@18.3.1): + dependencies: + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react: 18.3.1 + expo-keep-awake@15.0.7(expo@54.0.25)(react@19.1.0): dependencies: expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -32215,6 +32793,17 @@ snapshots: - expo - supports-color + expo-linking@8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)) + invariant: 2.2.4 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + transitivePeerDependencies: + - expo + - supports-color + optional: true + expo-linking@8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) @@ -32345,6 +32934,12 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + expo-modules-core@3.0.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + invariant: 2.2.4 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + expo-modules-core@3.0.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: invariant: 2.2.4 @@ -32503,6 +33098,53 @@ snapshots: - '@types/react-dom' - supports-color + expo-router@6.0.15(hwqworfppxvioilmgvd7t3oifm): + dependencies: + '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@expo/schema-utils': 0.1.7 + '@radix-ui/react-slot': 1.2.0(@types/react@18.3.27)(react@18.3.1) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@react-navigation/native-stack': 7.8.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)) + expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + expo-server: 1.0.4 + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 18.3.1 + react-fast-compare: 3.2.2 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.1.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@18.3.1) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@testing-library/react-native': 13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react-test-renderer@19.1.0(react@18.3.1))(react@18.3.1) + react-dom: 19.1.0(react@18.3.1) + react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-web: 0.21.2(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@18.3.1))(react@18.3.1)(webpack@5.100.2(esbuild@0.27.0)) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - supports-color + optional: true + expo-router@6.0.15(jiucxy5ca3jdtbnulaxuc46jdq): dependencies: '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -33017,6 +33659,43 @@ snapshots: - supports-color - utf-8-validate + expo@54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + '@expo/cli': 54.0.16(expo-router@6.0.15)(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)) + '@expo/config': 12.0.10 + '@expo/config-plugins': 54.0.2 + '@expo/devtools': 0.1.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@expo/fingerprint': 0.15.3 + '@expo/metro': 54.1.0 + '@expo/metro-config': 54.0.9(expo@54.0.25) + '@expo/vector-icons': 15.0.3(expo-font@14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + '@ungap/structured-clone': 1.3.0 + babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.25)(react-refresh@0.14.2) + expo-asset: 12.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)) + expo-file-system: 19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)) + expo-font: 14.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + expo-keep-awake: 15.0.7(expo@54.0.25)(react@18.3.1) + expo-modules-autolinking: 3.0.22 + expo-modules-core: 3.0.26(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + pretty-format: 29.7.0 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-refresh: 0.14.2 + whatwg-url-without-unicode: 8.0.0-3 + optionalDependencies: + '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-webview: 13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - '@babel/core' + - '@modelcontextprotocol/sdk' + - bufferutil + - expo-router + - graphql + - supports-color + - utf-8-validate + expo@54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.28.4 @@ -33568,6 +34247,8 @@ snapshots: functions-have-names@1.2.3: {} + fuse.js@7.1.0: {} + gauge@3.0.2: dependencies: aproba: 2.1.0 @@ -36129,6 +36810,8 @@ snapshots: markdown-table@3.0.4: {} + marked@15.0.12: {} + marked@16.4.2: {} marked@17.0.1: {} @@ -37463,6 +38146,20 @@ snapshots: - react-native-svg - supports-color + nativewind@4.2.1(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)(tailwindcss@4.1.17): + dependencies: + comment-json: 4.4.1 + debug: 4.4.3 + react-native-css-interop: 0.2.1(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)(tailwindcss@4.1.17) + tailwindcss: 4.1.17 + transitivePeerDependencies: + - react + - react-native + - react-native-reanimated + - react-native-safe-area-context + - react-native-svg + - supports-color + nativewind@4.2.1(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1)): dependencies: comment-json: 4.4.1 @@ -38305,6 +39002,12 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-dom@19.1.0(react@18.3.1): + dependencies: + react: 18.3.1 + scheduler: 0.26.0 + optional: true + react-dom@19.1.0(react@19.1.0): dependencies: react: 19.1.0 @@ -38486,6 +39189,23 @@ snapshots: transitivePeerDependencies: - supports-color + react-native-css-interop@0.2.1(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)(tailwindcss@4.1.17): + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + debug: 4.4.3 + lightningcss: 1.27.0 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + semver: 7.7.3 + tailwindcss: 4.1.17 + optionalDependencies: + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - supports-color + react-native-css-interop@0.2.1(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@babel/helper-module-imports': 7.27.1 @@ -38572,6 +39292,16 @@ snapshots: use-latest-callback: 0.2.6(react@19.1.0) optional: true + react-native-drawer-layout@4.2.0(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + color: 4.2.3 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + use-latest-callback: 0.2.6(react@18.3.1) + optional: true + react-native-drawer-layout@4.2.0(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: color: 4.2.3 @@ -38611,6 +39341,15 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + '@egjs/hammerjs': 2.0.17 + hoist-non-react-statics: 3.3.2 + invariant: 2.2.4 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + optional: true + react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: '@egjs/hammerjs': 2.0.17 @@ -38645,6 +39384,11 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -38769,6 +39513,15 @@ snapshots: react-native-worklets: 0.6.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) semver: 7.7.2 + react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/core': 7.28.5 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + react-native-worklets: 0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + semver: 7.7.2 + react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1))(react@18.3.1): dependencies: '@babel/core': 7.28.5 @@ -38807,6 +39560,12 @@ snapshots: react: 19.1.0 react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + optional: true + react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -38825,6 +39584,15 @@ snapshots: react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) warn-once: 0.1.1 + react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-freeze: 1.0.4(react@18.3.1) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + warn-once: 0.1.1 + optional: true + react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 @@ -38891,6 +39659,22 @@ snapshots: transitivePeerDependencies: - encoding + react-native-web@0.21.2(react-dom@19.1.0(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + '@react-native/normalize-colors': 0.74.89 + fbjs: 3.0.5 + inline-style-prefixer: 7.0.1 + memoize-one: 6.0.0 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + styleq: 0.1.3 + transitivePeerDependencies: + - encoding + optional: true + react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.28.4 @@ -38930,6 +39714,14 @@ snapshots: react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) optional: true + react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + escape-string-regexp: 4.0.0 + invariant: 2.2.4 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + optional: true + react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: escape-string-regexp: 4.0.0 @@ -38976,6 +39768,25 @@ snapshots: transitivePeerDependencies: - supports-color + react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + convert-source-map: 2.0.0 + react: 18.3.1 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1) + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1))(react@18.3.1): dependencies: '@babel/core': 7.28.5 @@ -39172,6 +39983,53 @@ snapshots: - supports-color - utf-8-validate + react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1): + dependencies: + '@jest/create-cache-key-function': 29.7.0 + '@react-native/assets-registry': 0.81.5 + '@react-native/codegen': 0.81.5(@babel/core@7.28.5) + '@react-native/community-cli-plugin': 0.81.5 + '@react-native/gradle-plugin': 0.81.5 + '@react-native/js-polyfills': 0.81.5 + '@react-native/normalize-colors': 0.81.5 + '@react-native/virtualized-lists': 0.81.5(@types/react@18.3.27)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) + abort-controller: 3.0.0 + anser: 1.4.10 + ansi-regex: 5.0.1 + babel-jest: 29.7.0(@babel/core@7.28.5) + babel-plugin-syntax-hermes-parser: 0.29.1 + base64-js: 1.5.1 + commander: 12.1.0 + flow-enums-runtime: 0.0.6 + glob: 7.2.3 + invariant: 2.2.4 + jest-environment-node: 29.7.0 + memoize-one: 5.2.1 + metro-runtime: 0.83.3 + metro-source-map: 0.83.3 + nullthrows: 1.1.1 + pretty-format: 29.7.0 + promise: 8.3.0 + react: 18.3.1 + react-devtools-core: 6.1.5 + react-refresh: 0.14.2 + regenerator-runtime: 0.13.11 + scheduler: 0.26.0 + semver: 7.7.3 + stacktrace-parser: 0.1.11 + whatwg-fetch: 3.6.20 + ws: 6.2.3 + yargs: 17.7.2 + optionalDependencies: + '@types/react': 18.3.27 + transitivePeerDependencies: + - '@babel/core' + - '@react-native-community/cli' + - '@react-native/metro-config' + - bufferutil + - supports-color + - utf-8-validate + react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@18.3.1): dependencies: '@jest/create-cache-key-function': 29.7.0 @@ -39270,6 +40128,15 @@ snapshots: react-refresh@0.17.0: {} + react-remove-scroll-bar@2.3.8(@types/react@18.3.27)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.3.27)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.1.0): dependencies: react: 19.1.0 @@ -39278,6 +40145,18 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + react-remove-scroll@2.7.1(@types/react@18.3.27)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.27)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.27)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.3.27)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.27)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.27 + optional: true + react-remove-scroll@2.7.1(@types/react@19.2.7)(react@19.1.0): dependencies: react: 19.1.0 @@ -39289,6 +40168,16 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@18.3.1))(react@18.3.1)(webpack@5.100.2(esbuild@0.27.0)): + dependencies: + acorn-loose: 8.5.2 + neo-async: 2.6.2 + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + webpack: 5.100.2(esbuild@0.27.0) + webpack-sources: 3.3.3 + optional: true + react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.27.0)): dependencies: acorn-loose: 8.5.2 @@ -39298,6 +40187,15 @@ snapshots: webpack: 5.100.2(esbuild@0.27.0) webpack-sources: 3.3.3 + react-style-singleton@2.2.3(@types/react@18.3.27)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + react-style-singleton@2.2.3(@types/react@19.2.7)(react@19.1.0): dependencies: get-nonce: 1.0.1 @@ -39306,6 +40204,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + react-test-renderer@19.1.0(react@18.3.1): + dependencies: + react: 18.3.1 + react-is: 19.2.0 + scheduler: 0.26.0 + optional: true + react-test-renderer@19.1.0(react@19.1.0): dependencies: react: 19.1.0 @@ -41295,6 +42200,14 @@ snapshots: urlpattern-polyfill@10.1.0: optional: true + use-callback-ref@1.3.3(@types/react@18.3.27)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + use-callback-ref@1.3.3(@types/react@19.2.7)(react@19.1.0): dependencies: react: 19.1.0 @@ -41310,6 +42223,15 @@ snapshots: dependencies: react: 19.1.0 + use-sidecar@1.1.3(@types/react@18.3.27)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.27 + optional: true + use-sidecar@1.1.3(@types/react@19.2.7)(react@19.1.0): dependencies: detect-node-es: 1.1.0 @@ -41355,6 +42277,16 @@ snapshots: vary@1.1.2: {} + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@18.3.27))(@types/react@18.3.27)(react-dom@19.1.0(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 19.1.0(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + optional: true + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)