diff --git a/.gitignore b/.gitignore index dc11ae93e..383005d19 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,10 @@ apps/mobile/generated/ apps/mobile/src/runtime/config/bundled.generated.ios.ts apps/mobile/src/runtime/config/bundled.generated.android.ts +# gradle caches inside tracked local expo modules (the app's android/ dir is already ignored) +apps/mobile/modules/*/android/.gradle/ +apps/mobile/modules/*/android/build/ + # standalone daemon distribution (pnpm --prod deploy output) apps/daemon/standalone/ diff --git a/apps/mobile/AGENTS.md b/apps/mobile/AGENTS.md index 8a79f8a73..860a84961 100644 --- a/apps/mobile/AGENTS.md +++ b/apps/mobile/AGENTS.md @@ -1,6 +1,6 @@ # apps/mobile — Expo / React Native client -Expo + React Native. List/form screens render **`@expo/ui`** (real SwiftUI); **HeroUI Native** +Expo + React Native. List/form screens render **`@expo/ui`** (SwiftUI on iOS, Jetpack Compose on Android); **HeroUI Native** covers the RN surfaces that cannot cross over ("What deliberately stays React Native" below). Reaches the host through the `server` tunnel; business data still travels over `transport` + `@linkcode/schema`, the same contract as every other client. @@ -44,6 +44,27 @@ Native. Mobile consumes `@linkcode/ui` only through its **native** components under Node, suspect this class first and probe the runtime rather than reasoning from the API's documentation. +## Native UI boundaries + +Platform views share `runtime/` hooks for data and actions. Keep `.ios.tsx` responsible for +SwiftUI and the Android implementation responsible for Compose; shared routes and runtime must +not import either platform's UI package. Android hosts use `ThemedHost` so Material colors follow +the app's appearance preference. Each visible list row must be a direct `LazyColumn` child. + +Expo's Compose `Switch` always receives a native callback, even without a JS handler. Wire +`onCheckedChange` alongside a toggleable row; a tap on the thumb is consumed by the switch. +Single-choice rows use `selectable` and their parent uses `selectableGroup` for TalkBack. + +List-item management actions use SwiftUI `SwipeActions` on iOS (horizontal row swipes) and +`RowActions` on Android (Compose long-press dropdown menus under `MaterialExpressiveTheme`). +Keep a row's normal tap for navigation; rows without a primary action may also open the menu on +tap. Destructive actions belong in these native action surfaces, not permanent trailing buttons. +Children of `RowActions` must not add their own click handler, which would consume the gesture. + +Android question batches open from a compact transcript dock into a Compose modal sheet. Closing +the sheet preserves drafts; cancelling the request requires confirmation. Keep permission actions +inline with one-time approval emphasized, and allow the action row to wrap for longer labels. + ## `@expo/ui` (SwiftUI) — its layout rules are not RN's Settings, terminal appearance, and connect render a real `Form` inside a `Host` (`style={{flex:1}}` @@ -80,13 +101,16 @@ only by driving the simulator: RN→SwiftUI is the supported direction; going back needs `RNHostView`, whose bidirectional nesting is the very thing 57.0.5 had to fix. So anything whose indispensable part is an RN view cannot cross over: **sign-in** (`AppleAuthenticationButton` is an RN view and `@expo/ui` has no Sign in with -Apple), the **conversation surface** — timeline, composer, and the screen holding them (excluded by -the redesign decision; the composer also rides `react-native-keyboard-controller`), the **terminal +Apple), the **conversation layout** — virtualized Markdown timeline and controlled multiline input +with `react-native-keyboard-controller` (its controls, disclosures, prompts, and transcript records +use SwiftUI / Compose), the **terminal canvas** (`expo-libghostty`), the **startup splash** (`BrandMark` is a bundled RN image), and the -**navigation header** (react-navigation). Two smaller losses are accepted rather than worked +**navigation header** (react-navigation). One smaller loss is accepted rather than worked around: `Image` takes SF Symbols, asset-catalog names, and local file URIs but **never a remote -URL**, so the account avatar is an SF Symbol; and the agent brand marks are RN SVG components, so -thread rows and the new-thread picker name the agent in text instead. +URL**, so the account avatar is an SF Symbol. The agent brand marks (RN SVG) do cross into rows +now: the iOS thread row hosts `AgentIcon` through `RNHostView` (`matchContents`, pointer events +off), and the Android row uses vector-drawable copies of the lobe glyphs in `assets/icons/` — +but the new-thread picker and selector sheets still name the agent in text. ## Styling & version pins @@ -161,6 +185,9 @@ thread rows and the new-thread picker name the agent in text instead. ## Build & run +- Expo SDK 57 requires Xcode 26.4 or newer. Older Xcode can fail while compiling + `expo-modules-jsi`'s Swift/C++ bridging annotations before app code is built. + - **Dev builds, not Expo Go.** Cloud sign-in needs the real `linkcode://` scheme: production HQ trusts only `https://linkcode.ai,linkcode://` (`TRUSTED_ORIGINS`), and the `@better-auth/expo` server plugin auto-trusts `exp://` only under `NODE_ENV=development`, so "Sign in" from Expo Go @@ -182,6 +209,8 @@ thread rows and the new-thread picker name the agent in text instead. `scripts.mobile` in `devenv.nix` unsets both groups (and sets `SENTRY_DISABLE_AUTO_UPLOAD`, since the Sentry Xcode phase otherwise fails the build without org/project credentials). Outside devenv the shell is already clean and `pnpm -F @linkcode/mobile ios` works as-is. + CocoaPods comes from devenv with its own Ruby runtime; a user-installed `pod` using env Ruby + breaks when this runner puts `/usr/bin` first and resolves Apple's older system Ruby. Symptoms are misleading: the first thing to fail is usually the `[RNDeps] Replace React Native Dependencies` script phase, which is collateral — that script runs fine on its own. diff --git a/apps/mobile/app.json b/apps/mobile/app.json index f13eaa364..9324eea55 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -87,6 +87,7 @@ ], "expo-apple-authentication", "expo-background-task", + "expo-font", "expo-sqlite", "expo-secure-store", "expo-status-bar", diff --git a/apps/mobile/assets/android-icon-foreground.png b/apps/mobile/assets/android-icon-foreground.png index c3e2ca447..0fb671470 100644 Binary files a/apps/mobile/assets/android-icon-foreground.png and b/apps/mobile/assets/android-icon-foreground.png differ diff --git a/apps/mobile/assets/android-icon-monochrome.png b/apps/mobile/assets/android-icon-monochrome.png index 8f5dc150f..ed5e3132f 100644 Binary files a/apps/mobile/assets/android-icon-monochrome.png and b/apps/mobile/assets/android-icon-monochrome.png differ diff --git a/apps/mobile/assets/icons/add.xml b/apps/mobile/assets/icons/add.xml new file mode 100644 index 000000000..fb14723ce --- /dev/null +++ b/apps/mobile/assets/icons/add.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/mobile/assets/icons/agent-claude-code.xml b/apps/mobile/assets/icons/agent-claude-code.xml new file mode 100644 index 000000000..ebe5d9b60 --- /dev/null +++ b/apps/mobile/assets/icons/agent-claude-code.xml @@ -0,0 +1,10 @@ + + + diff --git a/apps/mobile/assets/icons/agent-codex.xml b/apps/mobile/assets/icons/agent-codex.xml new file mode 100644 index 000000000..7ad436a4b --- /dev/null +++ b/apps/mobile/assets/icons/agent-codex.xml @@ -0,0 +1,10 @@ + + + diff --git a/apps/mobile/assets/icons/agent-opencode.xml b/apps/mobile/assets/icons/agent-opencode.xml new file mode 100644 index 000000000..6d807e4e0 --- /dev/null +++ b/apps/mobile/assets/icons/agent-opencode.xml @@ -0,0 +1,10 @@ + + + diff --git a/apps/mobile/assets/icons/arrow-up.xml b/apps/mobile/assets/icons/arrow-up.xml new file mode 100644 index 000000000..496043063 --- /dev/null +++ b/apps/mobile/assets/icons/arrow-up.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/assets/icons/check.xml b/apps/mobile/assets/icons/check.xml new file mode 100644 index 000000000..935007004 --- /dev/null +++ b/apps/mobile/assets/icons/check.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/mobile/assets/icons/chevron-left.xml b/apps/mobile/assets/icons/chevron-left.xml new file mode 100644 index 000000000..ddb20ec23 --- /dev/null +++ b/apps/mobile/assets/icons/chevron-left.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/assets/icons/chevron-right.xml b/apps/mobile/assets/icons/chevron-right.xml new file mode 100644 index 000000000..60f1b2f8b --- /dev/null +++ b/apps/mobile/assets/icons/chevron-right.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/assets/icons/close.xml b/apps/mobile/assets/icons/close.xml new file mode 100644 index 000000000..fdfeebfe8 --- /dev/null +++ b/apps/mobile/assets/icons/close.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/assets/icons/expand-less.xml b/apps/mobile/assets/icons/expand-less.xml new file mode 100644 index 000000000..e08b770eb --- /dev/null +++ b/apps/mobile/assets/icons/expand-less.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/mobile/assets/icons/expand-more.xml b/apps/mobile/assets/icons/expand-more.xml new file mode 100644 index 000000000..93d859cfe --- /dev/null +++ b/apps/mobile/assets/icons/expand-more.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/mobile/assets/icons/more-vert.xml b/apps/mobile/assets/icons/more-vert.xml new file mode 100644 index 000000000..9c2a0c227 --- /dev/null +++ b/apps/mobile/assets/icons/more-vert.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/mobile/assets/icons/search.xml b/apps/mobile/assets/icons/search.xml new file mode 100644 index 000000000..83a9d154c --- /dev/null +++ b/apps/mobile/assets/icons/search.xml @@ -0,0 +1,9 @@ + + + diff --git a/apps/mobile/assets/icons/shield.xml b/apps/mobile/assets/icons/shield.xml new file mode 100644 index 000000000..7c9446905 --- /dev/null +++ b/apps/mobile/assets/icons/shield.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/assets/icons/stop.xml b/apps/mobile/assets/icons/stop.xml new file mode 100644 index 000000000..5541768c6 --- /dev/null +++ b/apps/mobile/assets/icons/stop.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 8d64cec35..1e216bb74 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@better-auth/expo": "1.7.0-rc.2", - "@expo/ui": "57.0.7", + "@expo/ui": "57.0.11", "@gorhom/bottom-sheet": "^5.2.14", "@linkcode/client-core": "workspace:*", "@linkcode/common": "workspace:*", @@ -29,34 +29,35 @@ "@react-native-community/netinfo": "12.0.1", "@sentry/react-native": "~7.11.0", "better-auth": "1.7.0-rc.2", - "expo": "~57.0.4", + "expo": "~57.0.14", "expo-apple-authentication": "~57.0.1", - "expo-audio": "~57.0.0", - "expo-background-task": "~57.0.2", - "expo-camera": "~57.0.1", - "expo-clipboard": "~57.0.0", - "expo-constants": "~57.0.3", + "expo-audio": "~57.0.3", + "expo-background-task": "~57.0.11", + "expo-camera": "~57.0.3", + "expo-clipboard": "~57.0.1", + "expo-constants": "~57.0.12", "expo-crypto": "~57.0.1", - "expo-dev-client": "~57.0.5", - "expo-device": "~57.0.0", - "expo-document-picker": "~57.0.0", - "expo-file-system": "~57.0.0", - "expo-font": "~57.0.0", - "expo-haptics": "~57.0.0", - "expo-image-picker": "~57.0.2", + "expo-dev-client": "~57.0.13", + "expo-device": "~57.0.1", + "expo-document-picker": "~57.0.1", + "expo-file-system": "~57.0.4", + "expo-font": "~57.0.1", + "expo-haptics": "~57.0.1", + "expo-image-picker": "~57.0.11", "expo-libghostty": "^0.8.1", - "expo-linking": "~57.0.2", - "expo-local-authentication": "~57.0.0", + "expo-linking": "~57.0.6", + "expo-local-authentication": "~57.0.2", "expo-network": "~57.0.1", - "expo-notifications": "~57.0.3", - "expo-router": "~57.0.4", - "expo-secure-store": "~57.0.0", - "expo-sharing": "~57.0.3", - "expo-sqlite": "~57.0.0", - "expo-status-bar": "~57.0.0", - "expo-task-manager": "~57.0.2", - "expo-updates": "~57.0.6", - "expo-web-browser": "~57.0.0", + "expo-notifications": "~57.0.12", + "expo-router": "~57.0.14", + "expo-secure-store": "~57.0.1", + "expo-sharing": "~57.0.13", + "expo-sqlite": "~57.0.1", + "expo-status-bar": "~57.0.1", + "expo-symbols": "~57.0.2", + "expo-task-manager": "~57.0.11", + "expo-updates": "~57.0.15", + "expo-web-browser": "~57.0.2", "foxact": "^0.3.10", "foxts": "^5.9.1", "heroui-native": "1.0.6", @@ -64,16 +65,16 @@ "posthog-react-native": "^4.60.0", "react": "19.2.3", "react-dom": "19.2.3", - "react-native": "0.86.0", + "react-native": "0.86.2", "react-native-gesture-handler": "~2.32.0", "react-native-keyboard-controller": "1.21.9", - "react-native-reanimated": "4.5.0", + "react-native-reanimated": "4.5.1", "react-native-safe-area-context": "~5.7.0", - "react-native-screens": "4.25.2", + "react-native-screens": "4.26.2", "react-native-svg": "15.15.4", "react-native-web": "^0.21.2", "react-native-webview": "13.16.1", - "react-native-worklets": "0.10.0", + "react-native-worklets": "0.10.1", "tailwind-merge": "^3.6.0", "tailwind-variants": "^3.3.0", "uniwind": "1.10.0", diff --git a/apps/mobile/src/app/(tabs)/_layout.tsx b/apps/mobile/src/app/(tabs)/_layout.tsx index 9280c9a34..33598124f 100644 --- a/apps/mobile/src/app/(tabs)/_layout.tsx +++ b/apps/mobile/src/app/(tabs)/_layout.tsx @@ -32,12 +32,14 @@ function TabsNavigator(): React.ReactNode { return ( + {/* `md` is the Android glyph — without it the converter leaves the icon undefined and the + tab renders label-only; `sf` is never read there. */} - + {tThreads('title')} - + {tTerminals('title')} {/* iOS 26's separated tab-bar slot (the `search` role) carries the focused tab's primary diff --git a/apps/mobile/src/app/(tabs)/terminals/index.tsx b/apps/mobile/src/app/(tabs)/terminals/index.tsx index acdd0f672..300375f1e 100644 --- a/apps/mobile/src/app/(tabs)/terminals/index.tsx +++ b/apps/mobile/src/app/(tabs)/terminals/index.tsx @@ -1,43 +1,25 @@ -import { - Button, - ContentUnavailableView, - Form, - Host, - ProgressView, - Section, - Text, -} from '@expo/ui/swift-ui'; -import { foregroundStyle, refreshable } from '@expo/ui/swift-ui/modifiers'; -import { useLinkCodeClient } from '@linkcode/client-core'; -import type { TerminalMetadata } from '@linkcode/schema'; -import { repositoryLabel } from '@linkcode/ui/native'; -import { NavigationRow } from '@mobile/components/form/navigation-row'; import { HostClientGate } from '@mobile/components/host/host-client-gate'; -import { useHostMenuItems } from '@mobile/components/host/use-host-menu-items'; +import { useHostHeaderOptions } from '@mobile/components/host/use-host-header-options'; import type { PrimaryAction } from '@mobile/components/shell/primary-action'; import { usePrimaryAction } from '@mobile/components/shell/primary-action'; +import { PrimaryActionFab } from '@mobile/components/shell/primary-action-fab'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; import { useTrailingActions } from '@mobile/components/shell/use-trailing-actions'; -import { NewTerminalSheet } from '@mobile/components/terminal/new-terminal-sheet'; +import { TerminalsScreen } from '@mobile/components/terminal/terminals-screen'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; import { useHostConnection } from '@mobile/runtime/host-connection'; -import { Stack, useFocusEffect, useRouter } from 'expo-router'; -import { extractErrorMessage } from 'foxts/extract-error-message'; +import { Stack } from 'expo-router'; import { PlusIcon } from 'lucide-react-native'; -import { useCallback, useState } from 'react'; -import { Platform, View } from 'react-native'; +import { useState } from 'react'; +import { View } from 'react-native'; import { useTranslations } from 'use-intl'; -const INITIAL_TERMINAL_SIZE = { cols: 80, rows: 24 }; - -const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); -const SUPPORTS_CONTENT_UNAVAILABLE_VIEW = - Platform.OS === 'ios' && Number.parseInt(Platform.Version, 10) >= 17; - /** Header above the gate, for the same reason as the threads tab: the host switcher has to stay * reachable when the host is not. */ export default function TerminalsRoute(): React.ReactNode { const t = useTranslations('mobile.terminals'); - const hostMenuItems = useHostMenuItems(); + const hostHeaderOptions = useHostHeaderOptions(); + const palette = useNativePalette(); const connection = useHostConnection(); const [sheetOpen, setSheetOpen] = useState(false); @@ -56,12 +38,12 @@ export default function TerminalsRoute(): React.ReactNode { // The flex container is load-bearing: a SwiftUI host left as the screen's direct child is // proposed the whole window and paints straight over the navigation header. return ( - + hostMenuItems, + ...hostHeaderOptions, ...trailingActions, }} /> @@ -72,159 +54,7 @@ export default function TerminalsRoute(): React.ReactNode { onSheetOpenChange={setSheetOpen} /> + ); } - -/** Host terminal inbox: attach to a running PTY or start a new one on the host. */ -function TerminalsScreen({ - sheetOpen, - onSheetOpenChange, -}: { - sheetOpen: boolean; - onSheetOpenChange: (open: boolean) => void; -}): React.ReactNode { - const t = useTranslations('mobile.terminals'); - const router = useRouter(); - const client = useLinkCodeClient(); - const [terminals, setTerminals] = useState([]); - const [loading, setLoading] = useState(true); - const [loadError, setLoadError] = useState(null); - const [createError, setCreateError] = useState(null); - const [creating, setCreating] = useState(false); - - const load = useCallback( - async () => (await client.listTerminals()).sort((a, b) => b.createdAt - a.createdAt), - [client], - ); - - useFocusEffect( - useCallback(() => { - let active = true; - setLoadError(null); - void load() - .then((nextTerminals) => { - if (active) setTerminals(nextTerminals); - }) - .catch((error_: unknown) => { - if (active) setLoadError(extractErrorMessage(error_, false) ?? 'Unknown error'); - }) - .finally(() => { - if (active) setLoading(false); - }); - return () => { - active = false; - }; - }, [load]), - ); - - // Drives SwiftUI's own pull-to-refresh, which keeps its spinner until this resolves. - const onRefresh = async () => { - setLoadError(null); - try { - setTerminals(await load()); - } catch (error_) { - setLoadError(extractErrorMessage(error_, false) ?? 'Unknown error'); - } - }; - - const openTerminal = (terminalId: string, takeControl = false) => { - const query = takeControl ? '?takeover=1' : ''; - router.push(`/terminal/${encodeURIComponent(terminalId)}${query}`); - }; - - const onCreate = async (cwd: string): Promise => { - if (creating) return false; - setCreating(true); - setCreateError(null); - try { - const terminalId = await client.openTerminal({ - ...INITIAL_TERMINAL_SIZE, - cwd: cwd || undefined, - }); - client.detachTerminal(terminalId); - onSheetOpenChange(false); - openTerminal(terminalId, true); - return true; - } catch (error_) { - setCreateError(extractErrorMessage(error_, false) ?? 'Unknown error'); - return false; - } finally { - setCreating(false); - } - }; - - return ( - <> - {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} - - {loading ? ( - - ) : loadError && terminals.length === 0 ? ( -
-
- - {t('loadError', { error: loadError })} - -
-
- ) : terminals.length === 0 ? ( - SUPPORTS_CONTENT_UNAVAILABLE_VIEW ? ( - - ) : ( -
-
- {t('emptyHint')} -
-
- ) - ) : ( -
- {loadError ? ( -
- - {t('loadError', { error: loadError })} - -
- ) : null} -
- {terminals.map((terminal) => ( - openTerminal(terminal.terminalId)} - /> - ))} -
-
- )} -
- { - if (!open) setCreateError(null); - onSheetOpenChange(open); - }} - creating={creating} - error={createError} - onCreate={onCreate} - /> - - ); -} diff --git a/apps/mobile/src/app/(tabs)/threads/index.tsx b/apps/mobile/src/app/(tabs)/threads/index.tsx index 052d99180..3c90224c6 100644 --- a/apps/mobile/src/app/(tabs)/threads/index.tsx +++ b/apps/mobile/src/app/(tabs)/threads/index.tsx @@ -1,54 +1,38 @@ -import { - Form, - Host, - ProgressView, - Section, - Button as UIButton, - Text as UIText, -} from '@expo/ui/swift-ui'; -import { useSessions } from '@linkcode/client-core'; -import type { SessionInfo } from '@linkcode/schema'; -import type { ThreadGroup } from '@linkcode/ui/native'; -import { - AGENT_LABELS, - groupThreadsByWorkspace, - repositoryLabel, - withoutAutomationSessions, -} from '@linkcode/ui/native'; -import { SECONDARY } from '@mobile/components/form/styles'; +import { useSearchHeaderOptions } from '@mobile/components/form/use-search-header-options'; import { HostClientGate } from '@mobile/components/host/host-client-gate'; -import { ThreadList } from '@mobile/components/host/thread-list/thread-list'; -import { useHostMenuItems } from '@mobile/components/host/use-host-menu-items'; +import { ThreadsScreen } from '@mobile/components/host/threads-screen'; +import { useHostHeaderOptions } from '@mobile/components/host/use-host-header-options'; import type { PrimaryAction } from '@mobile/components/shell/primary-action'; import { usePrimaryAction } from '@mobile/components/shell/primary-action'; +import { PrimaryActionFab } from '@mobile/components/shell/primary-action-fab'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; import { useTrailingActions } from '@mobile/components/shell/use-trailing-actions'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; import { useHostConnection } from '@mobile/runtime/host-connection'; -import { useWorkspaces } from '@mobile/runtime/use-workspaces'; import { Stack, useRouter } from 'expo-router'; import { SquarePenIcon } from 'lucide-react-native'; import { useCallback, useState } from 'react'; -import { View } from 'react-native'; +import { Platform, View } from 'react-native'; import { useTranslations } from 'use-intl'; -/** Taken from the search bar itself: RN's own replacement for the event it declares carries no text. */ -type SearchBarChangeEvent = Parameters< - NonNullable['onChangeText']> ->[0]; - -/** The title a thread is listed and searched under — the same fallback the row renders. */ -function threadTitle(session: SessionInfo): string { - return session.title ?? `${AGENT_LABELS[session.kind]} in ${repositoryLabel(session.cwd)}`; -} - /** The header outlives the connection: it carries the host switcher, which is the way out of a host * that cannot be reached, so it is mounted above the gate rather than inside it. New-thread needs a * client, so its entry points are dropped until the connection is ready. */ export default function ThreadsRoute(): React.ReactNode { const t = useTranslations('mobile.sessions'); + const tChat = useTranslations('mobile.chat'); const router = useRouter(); - const hostMenuItems = useHostMenuItems(); + const hostHeaderOptions = useHostHeaderOptions(); + const palette = useNativePalette(); const connection = useHostConnection(); + // Android-only affordance: iOS search lives in the screen's native search bar instead. + const [searchOpen, setSearchOpen] = useState(false); + // Toggling the search resets the filter: the field remounts empty, so the query must too. + const [query, setQuery] = useState(''); + const closeSearch = useCallback(() => { + setSearchOpen(false); + setQuery(''); + }, []); const primaryAction: PrimaryAction | null = connection?.status === 'ready' @@ -60,108 +44,44 @@ export default function ThreadsRoute(): React.ReactNode { } : null; usePrimaryAction('threads', primaryAction); - const trailingActions = useTrailingActions(primaryAction); + const trailingActions = useTrailingActions(primaryAction, { + ...(Platform.OS === 'android' && { + onSearchPress() { + setQuery(''); + setSearchOpen((open) => !open); + }, + }), + }); + const searchHeaderOptions = useSearchHeaderOptions({ + open: searchOpen, + placeholder: t('searchPlaceholder'), + closeLabel: tChat('cancel'), + onQueryChange: setQuery, + onClose: closeSearch, + }); return ( - + hostMenuItems, + ...hostHeaderOptions, ...trailingActions, + // The search view swaps the top app bar's content, replacing the switcher and actions; + // the toolbar itself stays mounted so the header never changes height (no flicker). + ...searchHeaderOptions, }} /> - + + ); } - -/** Threads inbox: sessions grouped by workspace (project) under collapsible headers, with the - * native search bar stacked below the navigation bar. Empty workspace groups are hidden — the - * new-thread page is where they surface. */ -function ThreadsScreen(): React.ReactNode { - const t = useTranslations('mobile.sessions'); - const router = useRouter(); - const { sessions, refresh, loading } = useSessions(); - const { workspaces, refresh: refreshWorkspaces } = useWorkspaces(); - - const [query, setQuery] = useState(''); - - // Stable so the search bar's options object survives a keystroke without re-registering. - const onSearchChange = useCallback( - (event: SearchBarChangeEvent) => setQuery(event.nativeEvent.text), - [], - ); - - const needle = query.trim().toLowerCase(); - const groups = groupThreadsByWorkspace(withoutAutomationSessions(sessions), workspaces).reduce< - ThreadGroup[] - >((kept, group) => { - const matched = - needle === '' - ? group.sessions - : group.sessions.filter((session) => threadTitle(session).toLowerCase().includes(needle)); - if (matched.length > 0) kept.push({ ...group, sessions: matched }); - return kept; - }, []); - - const groupLabel = (group: ThreadGroup): string => { - if (group.isChat) return t('chats'); - if (!group.workspace) return t('otherThreads'); - return group.workspace.name ?? repositoryLabel(group.workspace.cwd); - }; - - const onRefresh = async () => { - await Promise.all([refresh(), refreshWorkspaces()]); - }; - - return ( - <> - {/* `stacked` keeps the field below the inline title instead of moving into the iOS 26 toolbar. */} - - {/* The list needs the viewport as its proposed size, otherwise SwiftUI collapses it. */} - - {loading ? ( -
-
- -
-
- ) : groups.length === 0 ? ( - // A query that matched nothing is not an empty inbox: saying "no threads yet" there - // reads as though the existing threads were lost, and offering to start one is no - // remedy for a bad search. -
- {needle === '' ? ( -
{t('emptyHint')}}> - {t('emptyTitle')} - router.push('/new-thread')} /> -
- ) : ( -
- {t('searchEmpty')} -
- )} -
- ) : ( - router.push(`/session/${sessionId}`)} - onRefresh={onRefresh} - /> - )} -
- - ); -} diff --git a/apps/mobile/src/app/account.tsx b/apps/mobile/src/app/account.tsx index fb11505b1..b3ebf289f 100644 --- a/apps/mobile/src/app/account.tsx +++ b/apps/mobile/src/app/account.tsx @@ -1,15 +1,10 @@ -import { Button, Form, Host, ProgressView, Section } from '@expo/ui/swift-ui'; -import { DeleteAccountSection } from '@mobile/components/account/delete-account-section'; -import { DevicesSection } from '@mobile/components/account/devices-section'; -import { ProfileRow } from '@mobile/components/account/profile-row'; +import { AccountScreen } from '@mobile/components/account/account-screen'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; -import { signOutOfCloud, useCloudAccount } from '@mobile/runtime/cloud/account'; +import { useCloudAccount } from '@mobile/runtime/cloud/account'; import { Redirect, Stack } from 'expo-router'; -import { Alert } from 'react-native'; import { useTranslations } from 'use-intl'; -/** Account screen: profile, the account's device registry, and sign-out. */ -export default function AccountScreen(): React.ReactNode { +export default function AccountRoute(): React.ReactNode { const t = useTranslations('mobile.account'); const account = useCloudAccount(); @@ -18,29 +13,7 @@ export default function AccountScreen(): React.ReactNode { return ( <> - {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} - -
- {account.status === 'loading' ? ( - - ) : ( - <> -
- -
- -
-
- - - )} - -
+ ); } diff --git a/apps/mobile/src/app/connect.tsx b/apps/mobile/src/app/connect.tsx index e6199a053..bd7eb7e14 100644 --- a/apps/mobile/src/app/connect.tsx +++ b/apps/mobile/src/app/connect.tsx @@ -1,18 +1,10 @@ -import { Form, Host } from '@expo/ui/swift-ui'; -import { DiscoveredHostsSection } from '@mobile/components/connect/discovered-hosts-section'; -import { MyMachinesSection } from '@mobile/components/connect/my-machines-section'; -import { SavedHostsSection } from '@mobile/components/connect/saved-hosts-section'; -import { SignInSection } from '@mobile/components/connect/sign-in-section'; +import { ConnectScreen } from '@mobile/components/connect/connect-screen'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; -import { useCloudAccount } from '@mobile/runtime/cloud/account'; -import { useHostRegistryStore } from '@mobile/stores/host-store'; import { Stack } from 'expo-router'; import { useTranslations } from 'use-intl'; -export default function ConnectScreen(): React.ReactNode { +export default function ConnectRoute(): React.ReactNode { const t = useTranslations('mobile.connect'); - const account = useCloudAccount(); - const hosts = useHostRegistryStore((state) => state.hosts); return ( <> @@ -22,20 +14,7 @@ export default function ConnectScreen(): React.ReactNode { title: t('title'), }} /> - {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} - -
- {account.status === 'signed-in' ? ( - - ) : account.status === 'signed-out' ? ( - - ) : null} - - {hosts.length > 0 ? : null} - - - -
+ ); } diff --git a/apps/mobile/src/app/index.tsx b/apps/mobile/src/app/index.tsx index e8de4c245..11c64fe31 100644 --- a/apps/mobile/src/app/index.tsx +++ b/apps/mobile/src/app/index.tsx @@ -1,10 +1,8 @@ -import { BrandMark } from '@mobile/components/shell/brand-mark'; +import { AppLoadingScreen } from '@mobile/components/shell/app-loading-screen'; import { useCloudAccount } from '@mobile/runtime/cloud/account'; import { resolveStartupTarget } from '@mobile/runtime/startup'; import { useHostRegistryHydrated, useHostRegistryStore } from '@mobile/stores/host-store'; import { Redirect } from 'expo-router'; -import { Spinner } from 'heroui-native'; -import { View } from 'react-native'; /** * Startup router: waits for the persisted registry and the account state, @@ -19,12 +17,7 @@ export default function StartupScreen() { // Only first-run routing waits on account state — LAN/direct users with // saved hosts must not block on a slow or offline cloud session check. if (!hydrated || (hosts.length === 0 && account.status === 'loading')) { - return ( - - - - - ); + return ; } const target = resolveStartupTarget({ diff --git a/apps/mobile/src/app/new-thread.tsx b/apps/mobile/src/app/new-thread.tsx index e30fe5230..2cfa8b4f0 100644 --- a/apps/mobile/src/app/new-thread.tsx +++ b/apps/mobile/src/app/new-thread.tsx @@ -1,13 +1,9 @@ -import { Composer } from '@mobile/components/conversation/composer'; import { HostClientGate } from '@mobile/components/host/host-client-gate'; -import { AgentSelectorChip, ApprovalChip } from '@mobile/components/host/new-thread/draft-tools'; -import { ProjectRow } from '@mobile/components/host/new-thread/project-row'; +import { NewThreadScreen } from '@mobile/components/host/new-thread/new-thread-screen'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; -import { useNewThreadDraft } from '@mobile/runtime/use-new-thread-draft'; -import { Stack, useRouter } from 'expo-router'; -import { noop } from 'foxact/noop'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { Stack } from 'expo-router'; import { View } from 'react-native'; -import { KeyboardStickyView } from 'react-native-keyboard-controller'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; /** Composer-first new-thread page, the mobile shape of the desktop draft surface: the start @@ -15,9 +11,13 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; * starts on the host with the prompt riding behind it. A root push, so it covers the tab bar. */ export default function NewThreadRoute(): React.ReactNode { const insets = useSafeAreaInsets(); + const palette = useNativePalette(); return ( - + {/* Title-less on purpose: the composer says everything, and the bare back chevron keeps the page reading as a sheet of options rather than a destination. */} @@ -27,32 +27,3 @@ export default function NewThreadRoute(): React.ReactNode { ); } - -function NewThreadScreen(): React.ReactNode { - const router = useRouter(); - const { text, setText, start, creating, sendBlocked, error, project, approval, selector } = - useNewThreadDraft((sessionId) => router.replace(`/session/${sessionId}`)); - - return ( - <> - - - - { - void start(text); - }} - onStop={noop} - isRunning={false} - disabled={creating} - sendBlocked={sendBlocked} - error={error} - tools={} - trailing={} - /> - - - ); -} diff --git a/apps/mobile/src/app/session/[sessionId].tsx b/apps/mobile/src/app/session/[sessionId].tsx index f5dca6630..b9374e551 100644 --- a/apps/mobile/src/app/session/[sessionId].tsx +++ b/apps/mobile/src/app/session/[sessionId].tsx @@ -3,6 +3,7 @@ import type { SessionId, ToolCall } from '@linkcode/schema'; import { SessionIdSchema } from '@linkcode/schema'; import { AGENT_LABELS, + conversationFlowItems, EFFORT_OPTIONS_BY_ID, EmptyState, effortOptionsForModel, @@ -22,20 +23,18 @@ import { import { TimelineItem } from '@mobile/components/conversation/timeline-item'; import { ToolDetailSheet } from '@mobile/components/conversation/tool-detail-sheet/tool-detail-sheet'; import { HostClientGate } from '@mobile/components/host/host-client-gate'; -import { USES_IOS_26_NAVIGATION } from '@mobile/components/shell/ios-26-navigation'; +import { HeaderMenuButton } from '@mobile/components/shell/header-menu-button'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; import { useAccountModels } from '@mobile/runtime/use-account-models'; import { useSeededConversation } from '@mobile/runtime/use-seeded-conversation'; import { useSessionActions } from '@mobile/runtime/use-session-actions'; import { useSessionAutoResume } from '@mobile/runtime/use-session-auto-resume'; import * as Clipboard from 'expo-clipboard'; import { Stack, useLocalSearchParams, useRouter } from 'expo-router'; -import { useHeaderHeight } from 'expo-router/react-navigation'; import { noop } from 'foxact/noop'; -import { useThemeColor } from 'heroui-native'; -import { EllipsisIcon } from 'lucide-react-native'; import { useEffect, useRef, useState } from 'react'; -import { Alert, FlatList, Pressable, View } from 'react-native'; +import { FlatList, View } from 'react-native'; import { KeyboardStickyView } from 'react-native-keyboard-controller'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useTranslations } from 'use-intl'; @@ -59,8 +58,7 @@ function SessionScreen(): React.ReactNode { const tChat = useTranslations('mobile.chat'); const tSettings = useTranslations('mobile.settings'); const insets = useSafeAreaInsets(); - const headerHeight = useHeaderHeight(); - const muted = useThemeColor('muted'); + const palette = useNativePalette(); const router = useRouter(); const { sessionId: rawSessionId, autoResume } = useLocalSearchParams<{ sessionId: string; @@ -85,9 +83,6 @@ function SessionScreen(): React.ReactNode { const actions = useSessionActions(sessionId, conversation.status); const { stop } = useSessionAutoResume(sessionId, session?.status, autoResumeSuppressed); const [openToolCallId, setOpenToolCallId] = useState(null); - // Measured height of the floating composer block, fed back to the list as its bottom inset so - // resting content clears the card while scrolling still flows under the glass. - const [dockHeight, setDockHeight] = useState(0); const title = session ? (session.title ?? `${AGENT_LABELS[session.kind]} in ${repositoryLabel(session.cwd)}`) @@ -133,27 +128,23 @@ function SessionScreen(): React.ReactNode { if (sessionId) void Clipboard.setStringAsync(sessionId); }; - // Android fallback only — native bar items are iOS-only, so the menu degrades to an alert. - const showMenu = (): void => { - if (!sessionId) return; - Alert.alert(title, undefined, [ - { text: tChat('stopThread'), style: 'destructive', onPress: stopThread }, - { text: tChat('copyThreadId'), onPress: copyThreadId }, - { text: tChat('cancel'), style: 'cancel' }, - ]); - }; - // Inverted list: index 0 renders at the visual bottom, so newest items pin there. - const reversed = [...conversation.items].reverse(); + const reversed = conversationFlowItems(conversation.items).reverse(); return ( , ...(process.env.EXPO_OS === 'ios' @@ -185,14 +176,18 @@ function SessionScreen(): React.ReactNode { } : { headerRight: () => ( - - - + ), }), }} @@ -212,29 +207,11 @@ function SessionScreen(): React.ReactNode { onPressTool={(toolCall) => setOpenToolCallId(toolCall.toolCallId)} /> )} - ListFooterComponent={ - process.env.EXPO_OS === 'ios' ? : null - } - // Inverted list: the header renders at the visual bottom — the clearance that keeps - // resting content out from under the floating composer. - ListHeaderComponent={ - USES_IOS_26_NAVIGATION && dockHeight > 0 ? ( - - ) : null - } contentContainerStyle={{ paddingHorizontal: 16, paddingVertical: 12, gap: 12 }} className="flex-1" /> )} - {/* Sticky rather than an avoiding view: the inverted list already pins to the bottom, so - the composer only has to ride the keyboard instead of resizing the whole screen. On - iOS 26 the block floats over the list so content scrolls under the glass. */} - setDockHeight(event.nativeEvent.layout.height)} - > + ; if (appleAvailable === null || account.status === 'loading') { - return ( - - - - - ); + return ; } const run = async (flow: () => Promise) => { @@ -153,8 +115,8 @@ export default function SignInScreen() { return ( LinkCode {t('tagline')} @@ -189,16 +150,16 @@ export default function SignInScreen() { {busy ? ( ) : failed ? ( {t('error')} @@ -222,42 +183,22 @@ export default function SignInScreen() { }} /> ) : null} - - + /> diff --git a/apps/mobile/src/app/terminal-appearance.tsx b/apps/mobile/src/app/terminal-appearance.tsx index 65fa7d439..ce1099fa1 100644 --- a/apps/mobile/src/app/terminal-appearance.tsx +++ b/apps/mobile/src/app/terminal-appearance.tsx @@ -1,118 +1,15 @@ -import { - Circle, - Form, - Host, - HStack, - Picker, - Section, - Text, - VStack, - ZStack, -} from '@expo/ui/swift-ui'; -import { - font, - foregroundStyle, - frame, - pickerStyle, - strokeBorder, - tag, -} from '@expo/ui/swift-ui/modifiers'; +import { TerminalAppearanceScreen } from '@mobile/components/settings/terminal-appearance-screen'; import { VISIBLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; -import { - resolveTerminalTheme, - TERMINAL_COLOR_SCHEMES, - TERMINAL_FONT_SIZES, - useTerminalPrefsStore, -} from '@mobile/stores/terminal-prefs-store'; import { Stack } from 'expo-router'; import { useTranslations } from 'use-intl'; -const SWATCH = 24; - -const HIERARCHICAL_SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); - -/** Renders a theme's own colours so the row previews what it selects. `auto` has no theme - * of its own — it defers to ghostty's defaults — so it shows a neutral placeholder. */ -function ThemeSwatch({ theme }: { theme?: { background?: string; foreground?: string } }) { - return ( - - - {theme?.foreground ? ( - - a - - ) : null} - - ); -} - -/** Client-side terminal appearance: font size and color scheme. */ -export default function TerminalAppearanceScreen(): React.ReactNode { +export default function TerminalAppearanceRoute(): React.ReactNode { const t = useTranslations('mobile.terminalAppearance'); - const fontSize = useTerminalPrefsStore((state) => state.fontSize); - const colorScheme = useTerminalPrefsStore((state) => state.colorScheme); - const setFontSize = useTerminalPrefsStore((state) => state.setFontSize); - const setColorScheme = useTerminalPrefsStore((state) => state.setColorScheme); return ( <> - -
-
- - {TERMINAL_FONT_SIZES.map((size) => ( - - {String(size)} - - ))} - -
- - {/* An inline picker draws the selection checkmarks itself, so the rows only - have to carry the swatch and the label. */} -
- - {TERMINAL_COLOR_SCHEMES.map((scheme) => { - const theme = resolveTerminalTheme(scheme); - return ( - - - {scheme === 'auto' ? ( - - {t('colorSchemeAuto')} - - {t('colorSchemeAutoHint')} - - - ) : ( - {scheme} - )} - - ); - })} - -
-
-
+ ); } diff --git a/apps/mobile/src/app/terminal/[terminalId].tsx b/apps/mobile/src/app/terminal/[terminalId].tsx index 2e4457248..2d42a0fca 100644 --- a/apps/mobile/src/app/terminal/[terminalId].tsx +++ b/apps/mobile/src/app/terminal/[terminalId].tsx @@ -1,11 +1,12 @@ import { TerminalIdSchema } from '@linkcode/schema'; +import { ActionButton } from '@mobile/components/form/action-button'; import { HostClientGate } from '@mobile/components/host/host-client-gate'; import TerminalRenderer from '@mobile/components/terminal/terminal-renderer'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; import { useTerminalSession } from '@mobile/runtime/use-terminal-session'; import { resolveTerminalTheme, useTerminalPrefsStore } from '@mobile/stores/terminal-prefs-store'; import { useLocalSearchParams, useRouter } from 'expo-router'; -import { Button, Chip, Spinner } from 'heroui-native'; -import { Text, View } from 'react-native'; +import { ActivityIndicator, Text, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useTranslations } from 'use-intl'; @@ -24,6 +25,7 @@ function TerminalScreen(): React.ReactNode { const t = useTranslations('mobile.terminal'); const router = useRouter(); const insets = useSafeAreaInsets(); + const palette = useNativePalette(); const params = useLocalSearchParams<{ terminalId: string; takeover?: string }>(); const parsed = TerminalIdSchema.safeParse(params.terminalId); const terminalId = parsed.success ? parsed.data : null; @@ -55,49 +57,66 @@ function TerminalScreen(): React.ReactNode { return ( - + - + {terminal?.cwd ?? t('title')} {status === 'ready' ? ( - - {canControl ? t('controlling') : t('readOnly')} - + + {canControl ? t('controlling') : t('readOnly')} + ) : null} {truncated ? ( - {t('truncated')} + + {t('truncated')} + ) : null} {error ? ( - + {t('error', { error })} ) : null} {exit ? ( - + {exit.code === null ? t('exitedSignal') : t('exited', { code: exit.code })} ) : null} {status === 'attaching' ? ( - - {t('attaching')} + + + {t('attaching')} + ) : status === 'error' ? ( - + ) : ( {canControl ? ( - + + + ) : terminal?.managed ? ( - + {t('managedReadOnly')} ) : ( - + + + )} ) : null} diff --git a/apps/mobile/src/assets.d.ts b/apps/mobile/src/assets.d.ts index 7f063b4ca..16123cab9 100644 --- a/apps/mobile/src/assets.d.ts +++ b/apps/mobile/src/assets.d.ts @@ -2,3 +2,9 @@ declare module '*.woff2' { const url: string; export default url; } + +// Android XML vector drawables, served as Metro assets for the Compose `Icon` component. +declare module '*.xml' { + const assetId: number; + export default assetId; +} diff --git a/apps/mobile/src/components/account/account-screen.ios.tsx b/apps/mobile/src/components/account/account-screen.ios.tsx new file mode 100644 index 000000000..a41ac800f --- /dev/null +++ b/apps/mobile/src/components/account/account-screen.ios.tsx @@ -0,0 +1,41 @@ +import { Button, Form, Host, Section } from '@expo/ui/swift-ui'; +import { DeleteAccountSection } from '@mobile/components/account/delete-account-section'; +import { DevicesSection } from '@mobile/components/account/devices-section'; +import { ProfileRow } from '@mobile/components/account/profile-row'; +import { LoadingView } from '@mobile/components/form/loading-view.ios'; +import { signOutOfCloud, useCloudAccount } from '@mobile/runtime/cloud/account'; +import { Alert } from 'react-native'; +import { useTranslations } from 'use-intl'; + +/** Account body: profile, the account's device registry, and sign-out. The route shell owns the + * signed-out redirect; a transient non-signed-in status renders as loading. */ +export function AccountScreen(): React.ReactNode { + const t = useTranslations('mobile.account'); + const account = useCloudAccount(); + + return ( + // Form needs the viewport as its proposed size, otherwise it collapses to its content. + + {account.status === 'signed-in' ? ( +
+
+ +
+ +
+
+ + + ) : ( + + )} +
+ ); +} diff --git a/apps/mobile/src/components/account/account-screen.tsx b/apps/mobile/src/components/account/account-screen.tsx new file mode 100644 index 000000000..244a15381 --- /dev/null +++ b/apps/mobile/src/components/account/account-screen.tsx @@ -0,0 +1,45 @@ +import { ListItem, Text } from '@expo/ui/jetpack-compose'; +import { clickable } from '@expo/ui/jetpack-compose/modifiers'; +import { DeleteAccountSection } from '@mobile/components/account/delete-account-section'; +import { DevicesSection } from '@mobile/components/account/devices-section'; +import { ProfileRow } from '@mobile/components/account/profile-row'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { FormList } from '@mobile/components/form/list.android'; +import { LoadingView } from '@mobile/components/form/loading-view.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { signOutOfCloud, useCloudAccount } from '@mobile/runtime/cloud/account'; +import { Alert } from 'react-native'; +import { useTranslations } from 'use-intl'; + +/** Android account body, mirroring `account-screen.ios.tsx`. Sign-out is a plain error-colored + * row — MD3's shape for a destructive entry in a settings list. */ +export function AccountScreen(): React.ReactNode { + const t = useTranslations('mobile.account'); + const colors = useAppMaterialColors(); + const account = useCloudAccount(); + + if (account.status !== 'signed-in') return ; + + return ( + + + + + + + { + void signOutOfCloud().catch(() => Alert.alert(t('signOutError'))); + }), + ]} + > + + {t('signOut')} + + + + + + ); +} diff --git a/apps/mobile/src/components/account/delete-account-section.ios.tsx b/apps/mobile/src/components/account/delete-account-section.ios.tsx new file mode 100644 index 000000000..a81e3a0c0 --- /dev/null +++ b/apps/mobile/src/components/account/delete-account-section.ios.tsx @@ -0,0 +1,25 @@ +import { Button, Section } from '@expo/ui/swift-ui'; +import { disabled } from '@expo/ui/swift-ui/modifiers'; +import { useDeleteAccount } from '@mobile/runtime/use-delete-account'; +import { useTranslations } from 'use-intl'; + +/** + * Permanent, in-app account deletion (App Store Guideline 5.1.1(v)). Its own + * Section, below Sign out, `Button role="destructive"` — not hidden behind + * any secondary menu, matching `DevicesSection`'s destructive-row precedent. + */ +export function DeleteAccountSection(): React.ReactNode { + const t = useTranslations('mobile.account'); + const { busy, confirmDelete } = useDeleteAccount(); + + return ( +
+
+ ); +} diff --git a/apps/mobile/src/components/account/delete-account-section.tsx b/apps/mobile/src/components/account/delete-account-section.tsx index aee324d1f..afbeeeada 100644 --- a/apps/mobile/src/components/account/delete-account-section.tsx +++ b/apps/mobile/src/components/account/delete-account-section.tsx @@ -1,89 +1,29 @@ -import { Button, Section } from '@expo/ui/swift-ui'; -import { disabled } from '@expo/ui/swift-ui/modifiers'; -import { deleteAccount, runAccountDeletionTeardown } from '@mobile/runtime/cloud/deletion'; -import { useState } from 'react'; -import { Alert } from 'react-native'; +import { ListItem, Text } from '@expo/ui/jetpack-compose'; +import { clickable } from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { useDeleteAccount } from '@mobile/runtime/use-delete-account'; import { useTranslations } from 'use-intl'; -/** - * Permanent, in-app account deletion (App Store Guideline 5.1.1(v)). Its own - * Section, below Sign out, `Button role="destructive"` — not hidden behind - * any secondary menu, matching `DevicesSection`'s destructive-row precedent. - */ +/** Android delete row. Compose has no button `role`, so the label uses the error color. */ export function DeleteAccountSection(): React.ReactNode { const t = useTranslations('mobile.account'); - const [busy, setBusy] = useState(false); - - const failureMessage = (code: string | undefined): string => { - if (code === 'ACCOUNT_DELETION_EMERGENCY_AUDIT_HOLD') return t('deleteEmergencyHold'); - if (code === 'ACCOUNT_DELETION_SOLE_ORGANIZATION_OWNER') return t('deleteSoleOwner'); - return t('deleteFailed'); - }; - - const run = async () => { - setBusy(true); - try { - const outcome = await deleteAccount(); - if (outcome.kind === 'reauthentication-failed') { - Alert.alert(t('deleteReauthenticationFailed')); - return; - } - if (outcome.kind === 'apple-device-required') { - Alert.alert(t('deleteAppleDeviceRequired')); - return; - } - if (outcome.kind === 'account-mismatch') { - Alert.alert(t('deleteAccountMismatch')); - return; - } - if (outcome.kind === 'unknown') { - Alert.alert(t('deleteUnknown')); - return; - } - if (outcome.kind === 'failed') { - Alert.alert(failureMessage(outcome.code)); - return; - } - - // Both remaining outcomes (`pending` and `completed`) mean the server - // already accepted the deletion — local teardown runs regardless. - await runAccountDeletionTeardown(); - if (outcome.kind === 'pending') { - Alert.alert(t('deletePending')); - return; - } - // A failed revocation still leaves deletion successful and needs manual Apple follow-up. - if (outcome.authorizationRevocation === 'failed') { - Alert.alert(t('deleteRevocationFailedTitle'), t('deleteRevocationFailedMessage')); - } else { - Alert.alert(t('deleteCompleted')); - } - } finally { - setBusy(false); - } - }; - - const confirmDelete = () => { - Alert.alert(t('deleteTitle'), t('deleteMessage'), [ - { text: t('deleteCancel'), style: 'cancel' }, - { - text: t('deleteConfirm'), - style: 'destructive', - onPress() { - void run(); - }, - }, - ]); - }; + const colors = useAppMaterialColors(); + const { busy, confirmDelete } = useDeleteAccount(); return ( -
-
+ + { + if (!busy) confirmDelete(); + }), + ]} + > + + {t('deleteAccount')} + + + ); } diff --git a/apps/mobile/src/components/account/devices-section.ios.tsx b/apps/mobile/src/components/account/devices-section.ios.tsx new file mode 100644 index 000000000..e27180e3e --- /dev/null +++ b/apps/mobile/src/components/account/devices-section.ios.tsx @@ -0,0 +1,60 @@ +import { Button, HStack, Section, Spacer, SwipeActions, Text, VStack } from '@expo/ui/swift-ui'; +import { badge, buttonStyle, disabled, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; +import { FormLoadingRow } from '@mobile/components/form/loading-view.ios'; +import { FOOTNOTE, SECONDARY } from '@mobile/components/form/styles.ios'; +import { useDevicesSection } from '@mobile/runtime/use-devices-section'; +import { useTranslations } from 'use-intl'; + +/** The account's registered devices; the view model lives in `use-devices-section`. */ +export function DevicesSection(): React.ReactNode { + const t = useTranslations('mobile.account'); + const { devices, devicesError, enrolledId, busyId, refresh, confirmRevoke, describeDevice } = + useDevicesSection(); + + return ( + // A titled Section can't also carry an action, so the header is drawn by hand. +
+ {t('devices')} + +
+ ); +} diff --git a/apps/mobile/src/components/account/devices-section.tsx b/apps/mobile/src/components/account/devices-section.tsx index d5c370d05..b1cb0db53 100644 --- a/apps/mobile/src/components/account/devices-section.tsx +++ b/apps/mobile/src/components/account/devices-section.tsx @@ -1,140 +1,60 @@ -import { - Button, - HStack, - ProgressView, - Section, - Spacer, - SwipeActions, - Text, - VStack, -} from '@expo/ui/swift-ui'; -import { badge, buttonStyle, disabled, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; -import { FOOTNOTE, SECONDARY } from '@mobile/components/form/styles'; -import { signOutOfCloud } from '@mobile/runtime/cloud/account'; -import type { CloudDevice } from '@mobile/runtime/cloud/devices'; -import { fetchDevices, getEnrolledDeviceId, revokeDevice } from '@mobile/runtime/cloud/devices'; -import { formatRelativeShort } from '@mobile/utils/relative-time'; -import { noop } from 'foxact/noop'; -import { useCallback, useEffect, useState } from 'react'; -import { Alert } from 'react-native'; +import { ListItem, Text, TextButton } from '@expo/ui/jetpack-compose'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { RowActions } from '@mobile/components/form/row-actions.android'; +import { FormHint, FormLoadingRow } from '@mobile/components/form/rows.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { useDevicesSection } from '@mobile/runtime/use-devices-section'; import { useTranslations } from 'use-intl'; -/** - * The account's registered devices. Revoking cuts access to new tunnel tokens; - * revoking this phone also signs it out (the cloud kills its sessions). - */ export function DevicesSection(): React.ReactNode { const t = useTranslations('mobile.account'); - - const [devices, setDevices] = useState(null); - const [devicesError, setDevicesError] = useState(false); - const [enrolledId, setEnrolledId] = useState(null); - const [busyId, setBusyId] = useState(null); - - const load = useCallback(() => { - fetchDevices() - .then(setDevices) - .catch(() => setDevicesError(true)); - }, []); - - useEffect(() => { - getEnrolledDeviceId().then(setEnrolledId).catch(noop); - load(); - }, [load]); - - const refresh = () => { - setDevicesError(false); - setDevices(null); - load(); - }; - - const revoke = async (device: CloudDevice) => { - setBusyId(device.id); - try { - await revokeDevice(device.id); - if (device.id === enrolledId) { - // The device revoke already removed its push token and killed this phone's sessions. - await signOutOfCloud({ revokePushToken: false }).catch(noop); - return; - } - refresh(); - } catch { - Alert.alert(t('revokeError')); - } finally { - setBusyId(null); - } - }; - - const confirmRevoke = (device: CloudDevice) => { - Alert.alert( - t('revokeTitle', { name: device.name }), - device.id === enrolledId ? t('revokeThisDeviceMessage') : t('revokeMessage'), - [ - { text: t('revokeCancel'), style: 'cancel' }, - { - text: t('revoke'), - style: 'destructive', - onPress() { - void revoke(device); - }, - }, - ], - ); - }; - - const describeDevice = (device: CloudDevice): string => { - const kind = t(`deviceKind.${device.kind}`); - const platform = device.platform ? `${kind} · ${device.platform}` : kind; - return device.lastSeenAt - ? `${platform} · ${t('lastSeen', { time: formatRelativeShort(new Date(device.lastSeenAt).getTime()) })}` - : platform; - }; + const colors = useAppMaterialColors(); + const { devices, devicesError, enrolledId, busyId, refresh, confirmRevoke, describeDevice } = + useDevicesSection(); return ( - // A titled Section can't also carry an action, so the header is drawn by hand. -
- {t('devices')} - -
+ ); } diff --git a/apps/mobile/src/components/account/profile-row.ios.tsx b/apps/mobile/src/components/account/profile-row.ios.tsx new file mode 100644 index 000000000..93d095e17 --- /dev/null +++ b/apps/mobile/src/components/account/profile-row.ios.tsx @@ -0,0 +1,20 @@ +import { HStack, Image, Spacer, Text, VStack } from '@expo/ui/swift-ui'; +import { font } from '@expo/ui/swift-ui/modifiers'; +import { FOOTNOTE, SECONDARY } from '@mobile/components/form/styles.ios'; +import type { CloudUser } from '@mobile/runtime/cloud/account'; + +/** SF Symbol rather than the account's picture: `@expo/ui`'s `Image` takes SF Symbols, asset + * catalog names, and local files — never a remote URL. Apple sign-in supplies no picture + * anyway, so this is the fallback the old avatar already showed in the common case. */ +export function ProfileRow({ user }: { user: CloudUser }): React.ReactNode { + return ( + + + + {user.name || user.email} + {user.email} + + + + ); +} diff --git a/apps/mobile/src/components/account/profile-row.tsx b/apps/mobile/src/components/account/profile-row.tsx index 59c45e5b4..959870707 100644 --- a/apps/mobile/src/components/account/profile-row.tsx +++ b/apps/mobile/src/components/account/profile-row.tsx @@ -1,20 +1,22 @@ -import { HStack, Image, Spacer, Text, VStack } from '@expo/ui/swift-ui'; -import { font } from '@expo/ui/swift-ui/modifiers'; -import { FOOTNOTE, SECONDARY } from '@mobile/components/form/styles'; +import { ListItem, Text } from '@expo/ui/jetpack-compose'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; import type { CloudUser } from '@mobile/runtime/cloud/account'; -/** SF Symbol rather than the account's picture: `@expo/ui`'s `Image` takes SF Symbols, asset - * catalog names, and local files — never a remote URL. Apple sign-in supplies no picture - * anyway, so this is the fallback the old avatar already showed in the common case. */ +/** Android profile row. No avatar: MD3 needs no placeholder glyph, and Apple sign-in supplies no + * picture anyway (the iOS variant shows an SF Symbol for the same reason). */ export function ProfileRow({ user }: { user: CloudUser }): React.ReactNode { + const colors = useAppMaterialColors(); + return ( - - - - {user.name || user.email} - {user.email} - - - + + + {user.name || user.email} + + + + {user.email} + + + ); } diff --git a/apps/mobile/src/components/connect/add-host-screen.ios.tsx b/apps/mobile/src/components/connect/add-host-screen.ios.tsx new file mode 100644 index 000000000..4f9f6edc1 --- /dev/null +++ b/apps/mobile/src/components/connect/add-host-screen.ios.tsx @@ -0,0 +1,86 @@ +import { Form, Host, HStack, Section, Text, TextField, useNativeState } from '@expo/ui/swift-ui'; +import { + autocorrectionDisabled, + keyboardType, + onSubmit, + submitLabel, + textContentType, + textInputAutocapitalization, +} from '@expo/ui/swift-ui/modifiers'; +import { useAddHost } from '@mobile/runtime/use-add-host'; +import { Stack, useRouter } from 'expo-router'; +import { useTranslations } from 'use-intl'; + +export function AddHostScreen(): React.ReactNode { + const t = useTranslations('mobile.connect'); + const router = useRouter(); + const name = useNativeState(''); + const url = useNativeState(''); + const { urlInvalid, urlValid, onUrlChange, submit } = useAddHost(); + + const submitFields = () => submit(name.get(), url.get()); + + return ( + <> + [ + { + type: 'button', + label: t('cancel'), + accessibilityLabel: t('cancel'), + icon: { type: 'sfSymbol', name: 'xmark' }, + onPress: () => router.dismiss(), + }, + ], + unstable_headerRightItems: () => [ + { + type: 'button', + label: t('add'), + accessibilityLabel: t('add'), + icon: { type: 'sfSymbol', name: 'checkmark' }, + variant: 'prominent', + disabled: !urlValid, + onPress: submitFields, + }, + ], + }} + /> + {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} + +
+
{urlInvalid ? t('invalidUrl') : t('emptyHint')}}> + {/* `LabeledContent` only gives the field its intrinsic width; the stack fills the row. */} + + {t('nameLabel')} + + + + {t('urlLabel')} + + +
+
+
+ + ); +} diff --git a/apps/mobile/src/components/connect/add-host-screen.tsx b/apps/mobile/src/components/connect/add-host-screen.tsx index 427f265c0..48d8206f4 100644 --- a/apps/mobile/src/components/connect/add-host-screen.tsx +++ b/apps/mobile/src/components/connect/add-host-screen.tsx @@ -1,102 +1,71 @@ -import { Form, Host, HStack, Section, Text, TextField, useNativeState } from '@expo/ui/swift-ui'; -import { - autocorrectionDisabled, - keyboardType, - onSubmit, - submitLabel, - textContentType, - textInputAutocapitalization, -} from '@expo/ui/swift-ui/modifiers'; -import { HostUrlSchema, useHostRegistryStore } from '@mobile/stores/host-store'; -import { Stack, useRouter } from 'expo-router'; -import { useState } from 'react'; +import { Button, Column, OutlinedTextField, Text, useNativeState } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, padding, testID } from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import { useAddHost } from '@mobile/runtime/use-add-host'; +import { Stack } from 'expo-router'; import { useTranslations } from 'use-intl'; +/** Android add-host form, reached only by deep link — the connect screen opens the in-place + * bottom sheet instead. Pushed with the standard toolbar (Android's formSheet drops the header); + * the iOS header bar items (`unstable_header*Items`) don't exist here, so submit is an in-form + * button and dismissal is the toolbar back arrow. */ export function AddHostScreen(): React.ReactNode { const t = useTranslations('mobile.connect'); - const router = useRouter(); - const addHost = useHostRegistryStore((state) => state.addHost); - const setLastActiveHostId = useHostRegistryStore((state) => state.setLastActiveHostId); + const colors = useAppMaterialColors(); const name = useNativeState(''); const url = useNativeState(''); - const [urlInvalid, setUrlInvalid] = useState(false); - const [urlValid, setUrlValid] = useState(false); + const { urlInvalid, urlValid, onUrlChange, submit } = useAddHost(); - const submit = () => { - const trimmedUrl = url.get().trim(); - if (!HostUrlSchema.safeParse(trimmedUrl).success) { - setUrlInvalid(true); - return; - } - const profile = addHost({ name: name.get().trim() || t('namePlaceholder'), url: trimmedUrl }); - setLastActiveHostId(profile.id); - router.dismissTo('/threads'); - }; + const submitFields = () => submit(name.get(), url.get()); return ( <> - [ - { - type: 'button', - label: t('cancel'), - accessibilityLabel: t('cancel'), - icon: { type: 'sfSymbol', name: 'xmark' }, - onPress: () => router.dismiss(), - }, - ], - unstable_headerRightItems: () => [ - { - type: 'button', - label: t('add'), - accessibilityLabel: t('add'), - icon: { type: 'sfSymbol', name: 'checkmark' }, - variant: 'prominent', - disabled: !urlValid, - onPress: submit, - }, - ], - }} - /> - {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} - -
-
{urlInvalid ? t('invalidUrl') : t('emptyHint')}}> - {/* `LabeledContent` only gives the field its intrinsic width; the stack fills the row. */} - + + + + + {t('nameLabel')} - - - + + + {t('namePlaceholder')} + + + + {t('urlLabel')} - { - setUrlInvalid(false); - setUrlValid(HostUrlSchema.safeParse(text.trim()).success); - }} - modifiers={[ - textInputAutocapitalization('never'), - autocorrectionDisabled(), - keyboardType('url'), - textContentType('URL'), - submitLabel('go'), - onSubmit(submit), - ]} - /> - -
-
-
+ + + {t('urlPlaceholder')} + + + {urlInvalid ? t('invalidUrl') : t('emptyHint')} + + + + + ); } diff --git a/apps/mobile/src/components/connect/add-host-sheet.android.tsx b/apps/mobile/src/components/connect/add-host-sheet.android.tsx new file mode 100644 index 000000000..5dc9f8cbd --- /dev/null +++ b/apps/mobile/src/components/connect/add-host-sheet.android.tsx @@ -0,0 +1,88 @@ +import { + Button, + Column, + ModalBottomSheet, + OutlinedTextField, + Text, + useNativeState, +} from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, imePadding, padding, testID } from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import { useAddHost } from '@mobile/runtime/use-add-host'; +import { useTranslations } from 'use-intl'; + +/** Android add-host form as an MD3 bottom sheet — the same modal family as the host and model + * switchers. A successful submit dismisses to the threads tab, which unmounts the sheet. */ +export function AddHostSheet({ + isPresented, + onIsPresentedChange, +}: { + isPresented: boolean; + onIsPresentedChange: (isPresented: boolean) => void; +}): React.ReactNode { + const t = useTranslations('mobile.connect'); + const colors = useAppMaterialColors(); + const name = useNativeState(''); + const url = useNativeState(''); + const { urlInvalid, urlValid, onUrlChange, submit } = useAddHost(); + + if (!isPresented) return null; + + const submitFields = () => submit(name.get(), url.get()); + + return ( + + onIsPresentedChange(false)}> + + + {t('add')} + + + + {t('nameLabel')} + + + {t('namePlaceholder')} + + + + + {t('urlLabel')} + + + {t('urlPlaceholder')} + + + {urlInvalid ? t('invalidUrl') : t('emptyHint')} + + + + + + + ); +} diff --git a/apps/mobile/src/components/connect/connect-screen.ios.tsx b/apps/mobile/src/components/connect/connect-screen.ios.tsx new file mode 100644 index 000000000..400930360 --- /dev/null +++ b/apps/mobile/src/components/connect/connect-screen.ios.tsx @@ -0,0 +1,13 @@ +import { Form, Host } from '@expo/ui/swift-ui'; +import { ConnectSections } from '@mobile/components/connect/connect-sections'; + +export function ConnectScreen(): React.ReactNode { + return ( + // Form needs the viewport as its proposed size, otherwise it collapses to its content. + +
+ + +
+ ); +} diff --git a/apps/mobile/src/components/connect/connect-screen.tsx b/apps/mobile/src/components/connect/connect-screen.tsx new file mode 100644 index 000000000..e41320645 --- /dev/null +++ b/apps/mobile/src/components/connect/connect-screen.tsx @@ -0,0 +1,10 @@ +import { ConnectSections } from '@mobile/components/connect/connect-sections'; +import { FormList } from '@mobile/components/form/list.android'; + +export function ConnectScreen(): React.ReactNode { + return ( + + + + ); +} diff --git a/apps/mobile/src/components/connect/connect-sections.tsx b/apps/mobile/src/components/connect/connect-sections.tsx new file mode 100644 index 000000000..48c8b4a94 --- /dev/null +++ b/apps/mobile/src/components/connect/connect-sections.tsx @@ -0,0 +1,27 @@ +import { DiscoveredHostsSection } from '@mobile/components/connect/discovered-hosts-section'; +import { MyMachinesSection } from '@mobile/components/connect/my-machines-section'; +import { SavedHostsSection } from '@mobile/components/connect/saved-hosts-section'; +import { SignInSection } from '@mobile/components/connect/sign-in-section'; +import { useCloudAccount } from '@mobile/runtime/cloud/account'; +import { useHostRegistryStore } from '@mobile/stores/host-store'; + +/** The connect screen's section stack, single-sourced: each section resolves to its platform + * implementation through the Metro extension seam, so only the scaffold around this differs. */ +export function ConnectSections(): React.ReactNode { + const account = useCloudAccount(); + const hosts = useHostRegistryStore((state) => state.hosts); + + return ( + <> + {account.status === 'signed-in' ? ( + + ) : account.status === 'signed-out' ? ( + + ) : null} + + {hosts.length > 0 ? : null} + + + + ); +} diff --git a/apps/mobile/src/components/connect/discovered-hosts-section.ios.tsx b/apps/mobile/src/components/connect/discovered-hosts-section.ios.tsx new file mode 100644 index 000000000..ea07aa40e --- /dev/null +++ b/apps/mobile/src/components/connect/discovered-hosts-section.ios.tsx @@ -0,0 +1,40 @@ +import { HStack, ProgressView, Section, Text } from '@expo/ui/swift-ui'; +import { accessibilityLabel, controlSize, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; +import { ManualHostRow } from '@mobile/components/connect/manual-host-row'; +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { useDiscoveredHosts } from '@mobile/runtime/use-discovered-hosts'; +import { useTranslations } from 'use-intl'; + +export function DiscoveredHostsSection(): React.ReactNode { + const t = useTranslations('mobile.connect'); + const { discoveredHosts, status, saveAndOpen } = useDiscoveredHosts(); + + return ( +
+ {t('discovery.title')} + {status === 'error' ? null : ( + + )} + + } + > + {status === 'error' ? ( + {t('discovery.error')} + ) : ( + discoveredHosts.map((host) => ( + saveAndOpen(host)} + /> + )) + )} + +
+ ); +} diff --git a/apps/mobile/src/components/connect/discovered-hosts-section.tsx b/apps/mobile/src/components/connect/discovered-hosts-section.tsx index 7d600f8b3..31721a4f2 100644 --- a/apps/mobile/src/components/connect/discovered-hosts-section.tsx +++ b/apps/mobile/src/components/connect/discovered-hosts-section.tsx @@ -1,46 +1,23 @@ -import { HStack, ProgressView, Section, Text } from '@expo/ui/swift-ui'; -import { accessibilityLabel, controlSize, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; +import { LoadingIndicator } from '@expo/ui/jetpack-compose'; +import { size } from '@expo/ui/jetpack-compose/modifiers'; import { ManualHostRow } from '@mobile/components/connect/manual-host-row'; import { NavigationRow } from '@mobile/components/form/navigation-row'; -import { canonicalDirectHostUrl } from '@mobile/runtime/daemon-discovery'; -import { useDaemonDiscovery } from '@mobile/runtime/use-daemon-discovery'; -import { useOpenHost } from '@mobile/runtime/use-open-host'; -import { useHostRegistryStore } from '@mobile/stores/host-store'; +import { FormHint } from '@mobile/components/form/rows.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { useDiscoveredHosts } from '@mobile/runtime/use-discovered-hosts'; import { useTranslations } from 'use-intl'; export function DiscoveredHostsSection(): React.ReactNode { const t = useTranslations('mobile.connect'); - const { hosts, status } = useDaemonDiscovery(); - const openHost = useOpenHost(); - const savedHosts = useHostRegistryStore((state) => state.hosts); - const addHost = useHostRegistryStore((state) => state.addHost); - const savedUrls = new Set(); - for (let i = 0, len = savedHosts.length; i < len; i++) { - const host = savedHosts[i]; - if ('url' in host) savedUrls.add(canonicalDirectHostUrl(host.url)); - } - const discoveredHosts = hosts.filter((host) => !savedUrls.has(canonicalDirectHostUrl(host.url))); - - const saveAndOpen = (host: (typeof discoveredHosts)[number]) => { - const profile = addHost({ name: host.name, url: host.url }); - openHost(profile.id); - }; + const { discoveredHosts, status, saveAndOpen } = useDiscoveredHosts(); return ( -
- {t('discovery.title')} - {status === 'error' ? null : ( - - )} - - } + } > {status === 'error' ? ( - {t('discovery.error')} + {t('discovery.error')} ) : ( discoveredHosts.map((host) => ( -
+ ); } diff --git a/apps/mobile/src/components/connect/manual-host-row.ios.tsx b/apps/mobile/src/components/connect/manual-host-row.ios.tsx new file mode 100644 index 000000000..af814f644 --- /dev/null +++ b/apps/mobile/src/components/connect/manual-host-row.ios.tsx @@ -0,0 +1,10 @@ +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { useRouter } from 'expo-router'; +import { useTranslations } from 'use-intl'; + +export function ManualHostRow(): React.ReactNode { + const t = useTranslations('mobile.connect'); + const router = useRouter(); + + return router.push('/add-host')} />; +} diff --git a/apps/mobile/src/components/connect/manual-host-row.tsx b/apps/mobile/src/components/connect/manual-host-row.tsx index af814f644..e19bf92eb 100644 --- a/apps/mobile/src/components/connect/manual-host-row.tsx +++ b/apps/mobile/src/components/connect/manual-host-row.tsx @@ -1,10 +1,18 @@ +import { AddHostSheet } from '@mobile/components/connect/add-host-sheet.android'; import { NavigationRow } from '@mobile/components/form/navigation-row'; -import { useRouter } from 'expo-router'; +import { useState } from 'react'; import { useTranslations } from 'use-intl'; +/** Android opens the add-host form as a bottom sheet in place — the same modal family as the + * host and model switchers — instead of pushing the form-sheet route iOS uses. */ export function ManualHostRow(): React.ReactNode { const t = useTranslations('mobile.connect'); - const router = useRouter(); + const [open, setOpen] = useState(false); - return router.push('/add-host')} />; + return ( + <> + setOpen(true)} /> + + + ); } diff --git a/apps/mobile/src/components/connect/my-machines-section.ios.tsx b/apps/mobile/src/components/connect/my-machines-section.ios.tsx new file mode 100644 index 000000000..a0d6a675c --- /dev/null +++ b/apps/mobile/src/components/connect/my-machines-section.ios.tsx @@ -0,0 +1,48 @@ +import { Button, HStack, Section, Spacer, Text } from '@expo/ui/swift-ui'; +import { buttonStyle, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; +import { FormLoadingRow } from '@mobile/components/form/loading-view.ios'; +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { FOOTNOTE, SECONDARY } from '@mobile/components/form/styles.ios'; +import { useMyMachines } from '@mobile/runtime/use-my-machines'; +import { useTranslations } from 'use-intl'; + +/** Online machines; the view model lives in `use-my-machines`. */ +export function MyMachinesSection({ userId }: { userId: string }): React.ReactNode { + const t = useTranslations('mobile.connect.cloud'); + const { onlineHosts, hostsError, refresh, saveAndOpen } = useMyMachines(userId); + + return ( + // A titled Section can't also carry an action, so the header is drawn by hand — + // footnote + secondary is what SwiftUI gives a plain `title` on iOS. +
+ {t('machines')} + +
+ ); +} diff --git a/apps/mobile/src/components/connect/my-machines-section.tsx b/apps/mobile/src/components/connect/my-machines-section.tsx index df8ee5b19..2ec610245 100644 --- a/apps/mobile/src/components/connect/my-machines-section.tsx +++ b/apps/mobile/src/components/connect/my-machines-section.tsx @@ -1,74 +1,30 @@ -import { Button, HStack, ProgressView, Section, Spacer, Text } from '@expo/ui/swift-ui'; -import { buttonStyle, foregroundStyle } from '@expo/ui/swift-ui/modifiers'; +import { Text, TextButton } from '@expo/ui/jetpack-compose'; import { NavigationRow } from '@mobile/components/form/navigation-row'; -import { FOOTNOTE, SECONDARY } from '@mobile/components/form/styles'; -import { ensureDeviceRegistered } from '@mobile/runtime/cloud/devices'; -import type { OnlineHost } from '@mobile/runtime/cloud/hosts'; -import { fetchOnlineHosts } from '@mobile/runtime/cloud/hosts'; -import { useOpenHost } from '@mobile/runtime/use-open-host'; -import { useHostRegistryStore } from '@mobile/stores/host-store'; -import { noop } from 'foxact/noop'; -import { useCallback, useEffect, useState } from 'react'; +import { FormHint, FormLoadingRow } from '@mobile/components/form/rows.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { useMyMachines } from '@mobile/runtime/use-my-machines'; import { useTranslations } from 'use-intl'; -/** Online machines (daemons connected to the relay) — tapping one saves it as a tunnel host and opens it. */ +/** Android online machines; the view model lives in `use-my-machines`. */ export function MyMachinesSection({ userId }: { userId: string }): React.ReactNode { const t = useTranslations('mobile.connect.cloud'); - const openHost = useOpenHost(); - const addTunnelHost = useHostRegistryStore((state) => state.addTunnelHost); - - const [onlineHosts, setOnlineHosts] = useState(null); - const [hostsError, setHostsError] = useState(false); - - const load = useCallback(() => { - fetchOnlineHosts() - .then(setOnlineHosts) - .catch(() => setHostsError(true)); - }, []); - - const refresh = () => { - setHostsError(false); - setOnlineHosts(null); - load(); - }; - - useEffect(() => { - // Best-effort: registration only lists the phone under the account's - // devices; discovering and connecting to hosts does not depend on it. - ensureDeviceRegistered(userId).catch(noop); - load(); - }, [userId, load]); - - const saveAndOpen = (host: OnlineHost) => { - const profile = addTunnelHost({ - name: host.name ?? host.hostId.slice(0, 8), - tunnelHostId: host.hostId, - }); - openHost(profile.id); - }; + const { onlineHosts, hostsError, refresh, saveAndOpen } = useMyMachines(userId); return ( - // A titled Section can't also carry an action, so the header is drawn by hand — - // footnote + secondary is what SwiftUI gives a plain `title` on iOS. -
- {t('machines')} - -
+ ); } diff --git a/apps/mobile/src/components/connect/saved-hosts-section.ios.tsx b/apps/mobile/src/components/connect/saved-hosts-section.ios.tsx new file mode 100644 index 000000000..727baac44 --- /dev/null +++ b/apps/mobile/src/components/connect/saved-hosts-section.ios.tsx @@ -0,0 +1,31 @@ +import { Button, Section, SwipeActions } from '@expo/ui/swift-ui'; +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { useOpenHost } from '@mobile/runtime/use-open-host'; +import { useHostRegistryStore } from '@mobile/stores/host-store'; +import { useTranslations } from 'use-intl'; + +/** Saved hosts, direct or tunnelled. Removal is the list's own swipe action rather than a + * row button, so the row itself stays a single tap target for opening the host. */ +export function SavedHostsSection(): React.ReactNode { + const t = useTranslations('mobile.connect'); + const openHost = useOpenHost(); + const hosts = useHostRegistryStore((state) => state.hosts); + const removeHost = useHostRegistryStore((state) => state.removeHost); + + return ( +
+ {hosts.map((host) => ( + + +
+ ); +} diff --git a/apps/mobile/src/components/connect/saved-hosts-section.tsx b/apps/mobile/src/components/connect/saved-hosts-section.tsx index 727baac44..c55bb9369 100644 --- a/apps/mobile/src/components/connect/saved-hosts-section.tsx +++ b/apps/mobile/src/components/connect/saved-hosts-section.tsx @@ -1,31 +1,38 @@ -import { Button, Section, SwipeActions } from '@expo/ui/swift-ui'; -import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { ListItem, Text } from '@expo/ui/jetpack-compose'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { RowActions } from '@mobile/components/form/row-actions.android'; +import { FormSection } from '@mobile/components/form/section.android'; import { useOpenHost } from '@mobile/runtime/use-open-host'; import { useHostRegistryStore } from '@mobile/stores/host-store'; import { useTranslations } from 'use-intl'; -/** Saved hosts, direct or tunnelled. Removal is the list's own swipe action rather than a - * row button, so the row itself stays a single tap target for opening the host. */ export function SavedHostsSection(): React.ReactNode { const t = useTranslations('mobile.connect'); + const colors = useAppMaterialColors(); const openHost = useOpenHost(); const hosts = useHostRegistryStore((state) => state.hosts); const removeHost = useHostRegistryStore((state) => state.removeHost); return ( -
+ {hosts.map((host) => ( - - -
+ ); } diff --git a/apps/mobile/src/components/connect/sign-in-section.ios.tsx b/apps/mobile/src/components/connect/sign-in-section.ios.tsx new file mode 100644 index 000000000..daea8c1fc --- /dev/null +++ b/apps/mobile/src/components/connect/sign-in-section.ios.tsx @@ -0,0 +1,14 @@ +import { Button, Section, Text } from '@expo/ui/swift-ui'; +import { useRouter } from 'expo-router'; +import { useTranslations } from 'use-intl'; + +/** Signed-out lead-in: the account is how machines appear on the connect screen. */ +export function SignInSection(): React.ReactNode { + const t = useTranslations('mobile.connect.cloud'); + const router = useRouter(); + return ( +
{t('hint')}}> +
+ ); +} diff --git a/apps/mobile/src/components/connect/sign-in-section.tsx b/apps/mobile/src/components/connect/sign-in-section.tsx index daea8c1fc..22654e041 100644 --- a/apps/mobile/src/components/connect/sign-in-section.tsx +++ b/apps/mobile/src/components/connect/sign-in-section.tsx @@ -1,4 +1,6 @@ -import { Button, Section, Text } from '@expo/ui/swift-ui'; +import { Button, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, padding } from '@expo/ui/jetpack-compose/modifiers'; +import { FormSection } from '@mobile/components/form/section.android'; import { useRouter } from 'expo-router'; import { useTranslations } from 'use-intl'; @@ -6,9 +8,15 @@ import { useTranslations } from 'use-intl'; export function SignInSection(): React.ReactNode { const t = useTranslations('mobile.connect.cloud'); const router = useRouter(); + return ( -
{t('hint')}}> -
+ + + ); } diff --git a/apps/mobile/src/components/conversation/activity-row.ios.tsx b/apps/mobile/src/components/conversation/activity-row.ios.tsx new file mode 100644 index 000000000..eb841844c --- /dev/null +++ b/apps/mobile/src/components/conversation/activity-row.ios.tsx @@ -0,0 +1,70 @@ +import { + Button, + DisclosureGroup, + Host, + HStack, + Image, + ProgressView, + Spacer, + Text, +} from '@expo/ui/swift-ui'; +import { + buttonStyle, + disabled, + font, + foregroundStyle, + frame, + lineLimit, + textSelection, +} from '@expo/ui/swift-ui/modifiers'; +import { useState } from 'react'; +import { useTranslations } from 'use-intl'; +import type { ReasoningRowProps, ToolRowProps } from './activity-row.types'; + +const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); +const BODY = font({ textStyle: 'subheadline' }); + +export function ToolRow({ title, status, onPress }: ToolRowProps): React.ReactNode { + const t = useTranslations('mobile.chat'); + return ( + + + + ); +} + +export function ReasoningRow({ text, streaming }: ReasoningRowProps): React.ReactNode { + const [expanded, setExpanded] = useState(false); + const t = useTranslations('mobile.conversation'); + return ( + + + + + {t('reasoning')} + {streaming ? : null} + + + {text} + + + ); +} diff --git a/apps/mobile/src/components/conversation/activity-row.tsx b/apps/mobile/src/components/conversation/activity-row.tsx new file mode 100644 index 000000000..ea154e914 --- /dev/null +++ b/apps/mobile/src/components/conversation/activity-row.tsx @@ -0,0 +1,93 @@ +import { CircularProgressIndicator, Column, Icon, Row, Text } from '@expo/ui/jetpack-compose'; +import { + clickable, + defaultMinSize, + fillMaxWidth, + size, + weight, +} from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import { useState } from 'react'; +import { useTranslations } from 'use-intl'; +import chevronRight from '../../../assets/icons/chevron-right.xml'; +import expandMore from '../../../assets/icons/expand-more.xml'; +import type { ReasoningRowProps, ToolRowProps } from './activity-row.types'; + +export function ToolRow({ title, status, onPress }: ToolRowProps): React.ReactNode { + const colors = useAppMaterialColors(); + const t = useTranslations('mobile.chat'); + return ( + + + + {title} + + {status === 'failed' ? ( + + {t('failed')} + + ) : null} + {status === 'in_progress' ? ( + + ) : onPress ? ( + + ) : null} + + + ); +} + +export function ReasoningRow({ text, streaming }: ReasoningRowProps): React.ReactNode { + const [expanded, setExpanded] = useState(false); + const colors = useAppMaterialColors(); + const t = useTranslations('mobile.conversation'); + return ( + + + setExpanded((current) => !current)), + ]} + > + + {t('reasoning')} + + {streaming ? : null} + + + {expanded ? ( + + {text} + + ) : null} + + + ); +} diff --git a/apps/mobile/src/components/conversation/activity-row.types.ts b/apps/mobile/src/components/conversation/activity-row.types.ts new file mode 100644 index 000000000..0c04ec25f --- /dev/null +++ b/apps/mobile/src/components/conversation/activity-row.types.ts @@ -0,0 +1,12 @@ +import type { ToolCall } from '@linkcode/schema'; + +export interface ToolRowProps { + title: string; + status: ToolCall['status']; + onPress?: () => void; +} + +export interface ReasoningRowProps { + text: string; + streaming: boolean; +} diff --git a/apps/mobile/src/components/conversation/composer-backdrop.ios.tsx b/apps/mobile/src/components/conversation/composer-backdrop.ios.tsx new file mode 100644 index 000000000..ae3ba750c --- /dev/null +++ b/apps/mobile/src/components/conversation/composer-backdrop.ios.tsx @@ -0,0 +1,26 @@ +import { Host, Spacer, VStack } from '@expo/ui/swift-ui'; +import { frame, glassEffect } from '@expo/ui/swift-ui/modifiers'; +import { USES_IOS_26_NAVIGATION } from '@mobile/components/shell/ios-26-navigation'; +import { View } from 'react-native'; + +const GLASS = [ + frame({ maxWidth: Number.POSITIVE_INFINITY, maxHeight: Number.POSITIVE_INFINITY }), + glassEffect({ + glass: { variant: 'regular' }, + shape: 'roundedRectangle', + cornerRadius: 24, + }), +]; + +export function ComposerBackdrop(): React.ReactNode { + if (!USES_IOS_26_NAVIGATION) return null; + return ( + + + + + + + + ); +} diff --git a/apps/mobile/src/components/conversation/composer-backdrop.ts b/apps/mobile/src/components/conversation/composer-backdrop.ts new file mode 100644 index 000000000..8c53975f0 --- /dev/null +++ b/apps/mobile/src/components/conversation/composer-backdrop.ts @@ -0,0 +1,3 @@ +export function ComposerBackdrop(): React.ReactNode { + return null; +} diff --git a/apps/mobile/src/components/conversation/composer.tsx b/apps/mobile/src/components/conversation/composer.tsx index f1fed929f..0087de1c2 100644 --- a/apps/mobile/src/components/conversation/composer.tsx +++ b/apps/mobile/src/components/conversation/composer.tsx @@ -1,30 +1,17 @@ -import { Host, Spacer, VStack } from '@expo/ui/swift-ui'; -import { frame, glassEffect } from '@expo/ui/swift-ui/modifiers'; import { SendButton } from '@mobile/components/conversation/send-button'; import { USES_IOS_26_NAVIGATION } from '@mobile/components/shell/ios-26-navigation'; -import { useThemeColor } from 'heroui-native'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; import { Text, TextInput, View } from 'react-native'; import { useTranslations } from 'use-intl'; +import { ComposerBackdrop } from './composer-backdrop'; /** Cap the growing input so a long draft scrolls internally instead of eating the screen. */ const MAX_INPUT_HEIGHT = 140; -/** Matches the card's `rounded-3xl`, so the glass shape and the RN clip agree. */ -const CARD_RADIUS = 24; - -const GLASS = [ - frame({ maxWidth: Number.POSITIVE_INFINITY, maxHeight: Number.POSITIVE_INFINITY }), - glassEffect({ - glass: { variant: 'regular' }, - shape: 'roundedRectangle', - cornerRadius: CARD_RADIUS, - }), -]; - /** The message composer card, shaped like the web composer: the editor on top and a footer row * below it — optional tool slots left and trailing, then the circular send/stop action. Chat * screens pass no tools; the new-thread draft fills the slots with its start-option chips. - * Send and stop are wired by the screen; this owns nothing but the draft text. */ + * Draft state and send/stop behavior belong to the screen's runtime hook. */ export function Composer({ onSend, onStop, @@ -52,7 +39,7 @@ export function Composer({ trailing?: React.ReactNode; }): React.ReactNode { const t = useTranslations('mobile.conversation'); - const muted = useThemeColor('muted'); + const palette = useNativePalette(); const trimmed = text.trim(); const canSend = !disabled && !sendBlocked && trimmed.length > 0; @@ -64,32 +51,33 @@ export function Composer({ }; return ( - - {error ? {error} : null} + + {error ? ( + + {error} + + ) : null} {/* On iOS 26 the card's material is real Liquid Glass, drawn by a SwiftUI backdrop behind the RN input (the input itself must stay RN: multiline auto-grow + keyboard riding). Pre-26 and Android keep the filled card. */} - {USES_IOS_26_NAVIGATION ? ( - - - - - - - - ) : null} + {sf ? : null} diff --git a/apps/mobile/src/components/conversation/prompt-dock/__tests__/use-question-response.test.ts b/apps/mobile/src/components/conversation/prompt-dock/__tests__/use-question-response.test.ts new file mode 100644 index 000000000..374f40db2 --- /dev/null +++ b/apps/mobile/src/components/conversation/prompt-dock/__tests__/use-question-response.test.ts @@ -0,0 +1,85 @@ +// @vitest-environment jsdom +import type { Question } from '@linkcode/schema'; +import { act, renderHook } from '@testing-library/react'; +import { expect, it, vi } from 'vitest'; +import { useQuestionResponse } from '../use-question-response'; + +const questions: Question[] = [ + { + questionId: 'scope', + prompt: 'Scope?', + multiSelect: false, + options: [ + { optionId: 'small', label: 'Small' }, + { optionId: 'large', label: 'Large' }, + ], + }, + { + questionId: 'checks', + prompt: 'Checks?', + multiSelect: true, + options: [ + { optionId: 'unit', label: 'Unit' }, + { optionId: 'native', label: 'Native' }, + ], + }, +]; + +it('preserves earlier answers when navigating back and submits the entire batch', () => { + const respond = vi.fn(); + const { result } = renderHook(() => useQuestionResponse(questions, false, respond)); + act(() => result.current.selectOption('small')); + expect(result.current.current).toBe(2); + act(() => result.current.selectOption('unit')); + act(() => result.current.selectOption('native')); + act(() => result.current.previous(result.current.draft)); + expect(result.current.draft.selected).toEqual(['small']); + act(() => result.current.selectOption('large')); + expect(result.current.draft.selected).toEqual(['unit', 'native']); + act(() => result.current.selectOption('unit')); + act(() => result.current.advance(result.current.draft)); + expect(respond).toHaveBeenCalledExactlyOnceWith({ + outcome: 'answered', + answers: [ + { questionId: 'scope', selectedOptionIds: ['large'] }, + { questionId: 'checks', selectedOptionIds: ['native'] }, + ], + }); +}); + +it('preserves native text across back navigation and gives custom text precedence over choices', () => { + const respond = vi.fn(); + const { result } = renderHook(() => useQuestionResponse(questions, false, respond)); + act(() => result.current.selectOption('small')); + act(() => result.current.previous({ selected: ['unit'], customText: ' Smoke tests ' })); + act(() => result.current.selectOption('small')); + expect(result.current.draft.customText).toBe(' Smoke tests '); + act(() => result.current.advance(result.current.draft)); + expect(respond.mock.calls[0][0].answers[1]).toEqual({ + questionId: 'checks', + selectedOptionIds: [], + customText: 'Smoke tests', + }); +}); + +it('locks edits and navigation while responding and allows retry afterward', () => { + const respond = vi.fn(); + const { result, rerender } = renderHook( + ({ responding }) => useQuestionResponse(questions, responding, respond), + { initialProps: { responding: false } }, + ); + act(() => result.current.selectOption('small')); + rerender({ responding: true }); + act(() => { + result.current.selectOption('unit'); + result.current.setDraft({ selected: [], customText: 'late edit' }); + result.current.previous(result.current.draft); + result.current.advance(result.current.draft); + }); + expect(result.current.current).toBe(2); + expect(result.current.draft).toEqual({ selected: [], customText: '' }); + expect(respond).not.toHaveBeenCalled(); + rerender({ responding: false }); + act(() => result.current.advance(result.current.draft)); + expect(respond).toHaveBeenCalledOnce(); +}); diff --git a/apps/mobile/src/components/conversation/prompt-dock/permission-prompt.ios.tsx b/apps/mobile/src/components/conversation/prompt-dock/permission-prompt.ios.tsx new file mode 100644 index 000000000..f9207e679 --- /dev/null +++ b/apps/mobile/src/components/conversation/prompt-dock/permission-prompt.ios.tsx @@ -0,0 +1,86 @@ +import { Button, Host, HStack, Image, Spacer, Text, VStack } from '@expo/ui/swift-ui'; +import { + buttonStyle, + contentShape, + disabled, + font, + foregroundStyle, + lineLimit, + onTapGesture, + shapes, +} from '@expo/ui/swift-ui/modifiers'; +import type { PermissionPromptProps } from '@mobile/components/conversation/prompt-dock/permission-prompt.shared'; +import { + DANGER_KINDS, + detailRows, +} from '@mobile/components/conversation/prompt-dock/permission-prompt.shared'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { View } from 'react-native'; +import { useTranslations } from 'use-intl'; + +const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); +const TERTIARY = foregroundStyle({ type: 'hierarchical', style: 'tertiary' }); +const MONO_FOOTNOTE = font({ textStyle: 'footnote', design: 'monospaced' }); +const WHOLE_ROW = contentShape(shapes.rectangle()); + +/** + * Desktop `PermissionPrompt` grammar on SwiftUI: title + skip, mono detail rows, one tappable + * row per option (deny options draw destructive red). The RN shell provides the card chrome so + * it themes with the rest of the conversation surface. + */ +export function PermissionPrompt({ + toolCall, + options, + responding, + onRespond, +}: PermissionPromptProps): React.ReactNode { + const t = useTranslations('mobile.chat'); + const palette = useNativePalette(); + + return ( + + + + + + {t('allowTitle', { title: toolCall.title ?? '' })} + + + { + if (!responding) onRespond({ outcome: 'cancelled' }); + }), + ]} + /> + + {detailRows(toolCall).map((row) => ( + + {row.value} + + ))} + + {options.map((option) => ( + + ); + })} + + { + if (text.trim() && draft.selected.length > 0) { + onDraftChange({ selected: [], customText: text }); + } + }} + /> + + + {tQuestion('dismiss')} + + + + ); +} diff --git a/apps/mobile/src/components/conversation/prompt-dock/question-page.types.ts b/apps/mobile/src/components/conversation/prompt-dock/question-page.types.ts new file mode 100644 index 000000000..0b04b501b --- /dev/null +++ b/apps/mobile/src/components/conversation/prompt-dock/question-page.types.ts @@ -0,0 +1,20 @@ +import type { Question } from '@linkcode/schema'; + +export interface QuestionDraft { + selected: string[]; + customText: string; +} + +export interface QuestionPageProps { + question: Question; + draft: QuestionDraft; + current: number; + total: number; + isLast: boolean; + responding: boolean; + onDraftChange: (draft: QuestionDraft) => void; + onAdvance: (draft: QuestionDraft) => void; + onPrevious: (draft: QuestionDraft) => void; + onSelectOption: (optionId: string) => void; + onCancel: () => void; +} diff --git a/apps/mobile/src/components/conversation/prompt-dock/question-prompt.ios.tsx b/apps/mobile/src/components/conversation/prompt-dock/question-prompt.ios.tsx new file mode 100644 index 000000000..952f1a1ef --- /dev/null +++ b/apps/mobile/src/components/conversation/prompt-dock/question-prompt.ios.tsx @@ -0,0 +1,39 @@ +import type { Question, QuestionOutcome } from '@linkcode/schema'; +import { QuestionPage } from './question-page.ios'; +import { useQuestionResponse } from './use-question-response'; + +/** + * One agent question batch pages within its card (desktop `question-prompt.tsx`): tappable + * option rows draw their own selection state, `multiSelect` toggles, single-select + * auto-advances, an optional free-text answer, and the whole batch resolves as one + * `QuestionOutcome`. The page rendering is platform-split; this orchestrator is shared. + */ +export function QuestionPrompt({ + questions, + responding, + onRespond, +}: { + questions: Question[]; + responding: boolean; + onRespond: (outcome: QuestionOutcome) => void; +}): React.ReactNode { + const { question, draft, current, total, isLast, setDraft, advance, previous, selectOption } = + useQuestionResponse(questions, responding, onRespond); + + return ( + onRespond({ outcome: 'cancelled' })} + /> + ); +} diff --git a/apps/mobile/src/components/conversation/prompt-dock/question-prompt.tsx b/apps/mobile/src/components/conversation/prompt-dock/question-prompt.tsx index 0fda9748c..7ad2ddb42 100644 --- a/apps/mobile/src/components/conversation/prompt-dock/question-prompt.tsx +++ b/apps/mobile/src/components/conversation/prompt-dock/question-prompt.tsx @@ -1,42 +1,19 @@ +import { Column, FilledTonalButton, ModalBottomSheet, Row, Text } from '@expo/ui/jetpack-compose'; import { - Button, - Host, - HStack, - Image, - Spacer, - Text, - TextField, - useNativeState, - VStack, -} from '@expo/ui/swift-ui'; -import { - buttonStyle, - contentShape, - disabled, - font, - foregroundStyle, - onTapGesture, - shapes, -} from '@expo/ui/swift-ui/modifiers'; -import type { Question, QuestionAnswer, QuestionOutcome } from '@linkcode/schema'; + fillMaxWidth, + paddingAll, + verticalScroll, + weight, +} from '@expo/ui/jetpack-compose/modifiers'; +import type { Question, QuestionOutcome } from '@linkcode/schema'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; import { useState } from 'react'; -import { View } from 'react-native'; +import { Alert } from 'react-native'; import { useTranslations } from 'use-intl'; +import { QuestionPage } from './question-page'; +import { useQuestionResponse } from './use-question-response'; -const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); -const TERTIARY = foregroundStyle({ type: 'hierarchical', style: 'tertiary' }); -const WHOLE_ROW = contentShape(shapes.rectangle()); - -interface Draft { - selected: string[]; - customText: string; -} - -/** - * One agent question batch pages within its card (desktop `question-prompt.tsx`): tappable - * option rows draw their own checkmark, `multiSelect` toggles, single-select auto-advances, - * an optional free-text answer, and the whole batch resolves as one `QuestionOutcome`. - */ export function QuestionPrompt({ questions, responding, @@ -46,173 +23,74 @@ export function QuestionPrompt({ responding: boolean; onRespond: (outcome: QuestionOutcome) => void; }): React.ReactNode { - const [index, setIndex] = useState(0); - const [drafts, setDrafts] = useState>({}); - - const question = questions[Math.min(index, questions.length - 1)]; - const draft = drafts[question.questionId] ?? { selected: [], customText: '' }; - const isLast = index >= questions.length - 1; - - const setDraft = (next: Draft): void => { - setDrafts((current) => ({ ...current, [question.questionId]: next })); - }; - - const buildAnswers = (finalDraft: Draft): QuestionAnswer[] => - questions.map((entry) => { - const value = - entry.questionId === question.questionId - ? finalDraft - : (drafts[entry.questionId] ?? { selected: [], customText: '' }); - const text = value.customText.trim(); - return { - questionId: entry.questionId, - selectedOptionIds: value.selected, - ...(text.length > 0 && { customText: text }), - }; - }); - - const advanceOrSubmit = (finalDraft: Draft): void => { - if (isLast) { - onRespond({ outcome: 'answered', answers: buildAnswers(finalDraft) }); - } else { - setDraft(finalDraft); - setIndex((current) => current + 1); - } - }; - - return ( - onRespond({ outcome: 'cancelled' })} - /> - ); -} - -function QuestionPage({ - question, - draft, - current, - total, - isLast, - responding, - onDraftChange, - onAdvance, - onCancel, -}: { - question: Question; - draft: Draft; - current: number; - total: number; - isLast: boolean; - responding: boolean; - onDraftChange: (draft: Draft) => void; - onAdvance: (draft: Draft) => void; - onCancel: () => void; -}): React.ReactNode { + const [open, setOpen] = useState(false); + const colors = useAppMaterialColors(); const t = useTranslations('mobile.chat'); - // Each page owns a distinct native state. Remounting after a structured selection guarantees - // that an asynchronous native write from the previous question/answer mode cannot leak here. - const customText = useNativeState(draft.customText); - - const toggleOption = (optionId: string): void => { - if (responding) return; - const selected = question.multiSelect - ? draft.selected.includes(optionId) - ? draft.selected.filter((id) => id !== optionId) - : [...draft.selected, optionId] - : [optionId]; - const next = { selected, customText: '' }; - onDraftChange(next); - if (!question.multiSelect) onAdvance(next); - }; - - const advance = (): void => { - const text = customText.get().trim(); - onAdvance(text ? { selected: [], customText: text } : { ...draft, customText: '' }); - }; - + const tq = useTranslations('workbench.question'); + const { question, draft, current, total, isLast, setDraft, advance, previous, selectOption } = + useQuestionResponse(questions, responding, onRespond); return ( - - - - - {question.header ? ( - - {question.header} - - ) : null} - + <> + + + + + {tq('callTitle')} + + {question.prompt} - - {total > 1 ? ( - - {t('questionProgress', { current, total })} - - ) : null} - { - if (!responding) onCancel(); - }), - ]} - /> - - - {question.options.map((option) => { - const selected = draft.selected.includes(option.optionId); - return ( - toggleOption(option.optionId))]} - > - - - {option.label} - {option.description ? ( - - {option.description} - - ) : null} - - - - ); - })} - - { - if (text.trim() && draft.selected.length > 0) { - onDraftChange({ selected: [], customText: text }); - } - }} - /> - + + ); +} diff --git a/apps/mobile/src/components/form/action-button.tsx b/apps/mobile/src/components/form/action-button.tsx new file mode 100644 index 000000000..6f1064027 --- /dev/null +++ b/apps/mobile/src/components/form/action-button.tsx @@ -0,0 +1,33 @@ +import { Button, FilledTonalButton, Text, TextButton } from '@expo/ui/jetpack-compose'; +import { defaultMinSize, fillMaxWidth } from '@expo/ui/jetpack-compose/modifiers'; +import type { ActionButtonProps } from './action-button.types'; +import { useAppMaterialColors } from './compose-theme.android'; +import { ThemedHost } from './themed-host.android'; + +export function ActionButton({ + label, + onPress, + disabled = false, + variant = 'primary', + fullWidth = false, +}: ActionButtonProps): React.ReactNode { + const colors = useAppMaterialColors(); + const Control = + variant === 'text' ? TextButton : variant === 'primary' ? Button : FilledTonalButton; + return ( + + + {label} + + + ); +} diff --git a/apps/mobile/src/components/form/action-button.types.ts b/apps/mobile/src/components/form/action-button.types.ts new file mode 100644 index 000000000..381eeeabe --- /dev/null +++ b/apps/mobile/src/components/form/action-button.types.ts @@ -0,0 +1,7 @@ +export interface ActionButtonProps { + label: string; + onPress: () => void; + disabled?: boolean; + variant?: 'primary' | 'secondary' | 'text' | 'destructive'; + fullWidth?: boolean; +} diff --git a/apps/mobile/src/components/form/compose-theme.android.ts b/apps/mobile/src/components/form/compose-theme.android.ts new file mode 100644 index 000000000..ec56d6994 --- /dev/null +++ b/apps/mobile/src/components/form/compose-theme.android.ts @@ -0,0 +1,10 @@ +import type { MaterialColors } from '@expo/ui/jetpack-compose'; +import { useMaterialColors } from '@expo/ui/jetpack-compose'; +import { useResolvedColorScheme } from '@mobile/components/theme/use-color-scheme'; + +/** `useMaterialColors` pinned to the APP theme: the bare hook follows the system scheme, which + * diverges the moment the in-app appearance preference overrides it. Android components must use + * this (and `ThemedHost`), never the bare forms. */ +export function useAppMaterialColors(): MaterialColors { + return useMaterialColors({ colorScheme: useResolvedColorScheme() }); +} diff --git a/apps/mobile/src/components/form/icon-button.ios.tsx b/apps/mobile/src/components/form/icon-button.ios.tsx new file mode 100644 index 000000000..4dd9f710f --- /dev/null +++ b/apps/mobile/src/components/form/icon-button.ios.tsx @@ -0,0 +1,44 @@ +import { Button, Host } from '@expo/ui/swift-ui'; +import { + accessibilityLabel, + buttonStyle, + disabled, + frame, + labelStyle, +} from '@expo/ui/swift-ui/modifiers'; +import type { SFSymbol } from 'sf-symbols-typescript'; +import type { NativeIconButtonProps } from './icon-button.types'; + +const ICONS = { + send: 'arrow.up', + stop: 'stop.fill', + close: 'xmark', + previous: 'chevron.left', + next: 'chevron.right', + shield: 'shield', +} as const satisfies Record; + +export function NativeIconButton({ + icon, + label, + onPress, + disabled: isDisabled, + filled, +}: NativeIconButtonProps): React.ReactNode { + return ( + + + + {failure ? ( + + {failure} + + ) : null} + ); } diff --git a/apps/mobile/src/components/host/host-connection-state.types.ts b/apps/mobile/src/components/host/host-connection-state.types.ts new file mode 100644 index 000000000..33e29aa99 --- /dev/null +++ b/apps/mobile/src/components/host/host-connection-state.types.ts @@ -0,0 +1,7 @@ +export interface HostConnectionStateProps { + status: 'connecting' | 'error'; + url: string; + /** The underlying failure, when the controller reported one. */ + failure?: string; + onRetry: () => void; +} diff --git a/apps/mobile/src/components/host/host-switcher-button.ios.ts b/apps/mobile/src/components/host/host-switcher-button.ios.ts new file mode 100644 index 000000000..8202d567d --- /dev/null +++ b/apps/mobile/src/components/host/host-switcher-button.ios.ts @@ -0,0 +1,5 @@ +/** iOS renders the host switcher as native bar items (`use-host-menu-items`); this twin exists + * only so the shared header hook can reference one component name on both platforms. */ +export function HostSwitcherButton(): React.ReactNode { + return null; +} diff --git a/apps/mobile/src/components/host/host-switcher-button.tsx b/apps/mobile/src/components/host/host-switcher-button.tsx new file mode 100644 index 000000000..9117d1bd5 --- /dev/null +++ b/apps/mobile/src/components/host/host-switcher-button.tsx @@ -0,0 +1,67 @@ +import { SheetPicker } from '@mobile/components/form/sheet-picker.android'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { useHostRegistryStore, useSelectedHost } from '@mobile/stores/host-store'; +import { useRouter } from 'expo-router'; +import { ChevronsUpDownIcon } from 'lucide-react-native'; +import { useState } from 'react'; +import { Pressable, Text } from 'react-native'; +import { useTranslations } from 'use-intl'; + +/** Android host switcher: `unstable_headerLeftItems` is iOS-only, so the header carries a plain + * pressable that opens the host list as a bottom sheet. Switching writes the registry and stops — + * the host is not in the path, so whichever tab you are standing in stays put. */ +export function HostSwitcherButton(): React.ReactNode { + const t = useTranslations('mobile.connect'); + const tSettings = useTranslations('mobile.settings'); + const router = useRouter(); + const hosts = useHostRegistryStore((state) => state.hosts); + const setLastActiveHostId = useHostRegistryStore((state) => state.setLastActiveHostId); + const selected = useSelectedHost(); + const [open, setOpen] = useState(false); + const palette = useNativePalette(); + + if (!selected) return null; + + return ( + <> + setOpen(true)} + className="min-h-12 min-w-12 max-w-52 flex-row items-center gap-1 pr-3" + > + + {selected.name} + + + + setOpen(false)} + sections={[ + { + id: 'hosts', + selection: selected.id, + options: hosts.map((host) => ({ + id: host.id, + label: host.name, + hint: 'url' in host ? host.url : t('viaTunnel'), + })), + onSelect: setLastActiveHostId, + }, + ]} + actions={[ + { + id: 'manage', + label: tSettings('manageHosts'), + onPress: () => router.push('/connect'), + }, + ]} + /> + + ); +} diff --git a/apps/mobile/src/components/host/new-thread/draft-tools.ios.tsx b/apps/mobile/src/components/host/new-thread/draft-tools.ios.tsx new file mode 100644 index 000000000..1c9ac4dd2 --- /dev/null +++ b/apps/mobile/src/components/host/new-thread/draft-tools.ios.tsx @@ -0,0 +1,127 @@ +import { Host, Menu, Picker, Text as UIText } from '@expo/ui/swift-ui'; +import { tag } from '@expo/ui/swift-ui/modifiers'; +import { AgentKindSchema } from '@linkcode/schema'; +import { + AGENT_LABELS, + AgentIcon, + groupModelsByProvider, + modelChoiceKey, +} from '@linkcode/ui/native'; +import { OptionChip } from '@mobile/components/conversation/option-chip.ios'; +import type { + AgentSelectorChipProps, + ApprovalChipProps, +} from '@mobile/components/host/new-thread/draft-tools.types'; +import { clearable, DEFAULT_TAG } from '@mobile/components/host/new-thread/draft-tools.types'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { View } from 'react-native'; +import { useTranslations } from 'use-intl'; + +/** The draft's approval-policy chip: a ghost shield opening the policy menu. */ +export function ApprovalChip({ + policies, + policyId, + policyValue, + onPolicyIdChange, +}: ApprovalChipProps): React.ReactNode { + const t = useTranslations('mobile.sessions'); + if (policies.length === 0) return null; + + return ( + + + + {t('defaultOption')} + {policies.map((policy) => ( + + {policy.name} + + ))} + + + + ); +} + +/** The draft's combined selector: the harness brand mark beside one menu with Agent / Model / + * Effort submenus, mirroring the desktop `ModelSelectorMenu`. The brand mark is an RN view and + * cannot enter the SwiftUI tree, so it sits flush beside the menu trigger — ghost styling makes + * the pair read as one control. */ +export function AgentSelectorChip({ + kind, + onKindChange, + models, + modelKey, + onModelKeyChange, + selectorValue, + effortOptions, + effort, + onEffortChange, +}: AgentSelectorChipProps): React.ReactNode { + const t = useTranslations('mobile.sessions'); + const palette = useNativePalette(); + // The account label joins a row only when the list spans several accounts — the same threshold + // as the web's provider grouping; a single-account list repeating its account is noise. + const spansAccounts = models !== null && groupModelsByProvider(models) !== null; + + return ( + + {/* Footnote's 13pt metric, so the mark scales with the chip text beside it. */} + + + {/* Keyed by menu shape: children added to an already-created Menu never reach the + native UIMenu, so late-loading models/efforts must remount the chip. */} + + + + {AgentKindSchema.options.map((option) => ( + + {AGENT_LABELS[option]} + + ))} + + + {/* A labeled submenu, not an inline group: a bare Picker between sibling menus drops + out of the native UIMenu entirely. */} + {models !== null && models.length > 0 ? ( + + + {t('defaultOption')} + {models.map((model) => ( + + {spansAccounts && model.description + ? `${model.label} — ${model.description}` + : model.label} + + ))} + + + ) : null} + {effortOptions !== undefined && effortOptions.length > 0 ? ( + + + {t('defaultOption')} + {effortOptions.map((option) => ( + + {option.label} + + ))} + + + ) : null} + + + + ); +} diff --git a/apps/mobile/src/components/host/new-thread/draft-tools.tsx b/apps/mobile/src/components/host/new-thread/draft-tools.tsx index 42e74c645..7f3af6a2c 100644 --- a/apps/mobile/src/components/host/new-thread/draft-tools.tsx +++ b/apps/mobile/src/components/host/new-thread/draft-tools.tsx @@ -1,64 +1,65 @@ -import { Host, Menu, Picker, Text as UIText } from '@expo/ui/swift-ui'; -import { tag } from '@expo/ui/swift-ui/modifiers'; -import type { AgentKind, ApprovalPolicy } from '@linkcode/schema'; import { AgentKindSchema } from '@linkcode/schema'; -import type { EffortOption, ModelOption } from '@linkcode/ui/native'; -import { - AGENT_LABELS, - AgentIcon, - groupModelsByProvider, - modelChoiceKey, -} from '@linkcode/ui/native'; -import { OptionChip } from '@mobile/components/conversation/option-chip'; -import { useThemeColor } from 'heroui-native'; +import { AGENT_LABELS, AgentIcon } from '@linkcode/ui/native'; +import { ToolChip } from '@mobile/components/conversation/tool-chip.android'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { SelectorSheet } from '@mobile/components/form/selector-sheet.android'; +import type { + SelectorChipAxis, + SelectorModelAxis, +} from '@mobile/components/form/selector-sheet.types'; +import { modelAxisGroups } from '@mobile/components/form/selector-sheet.types'; +import { SheetPicker } from '@mobile/components/form/sheet-picker.android'; +import type { + AgentSelectorChipProps, + ApprovalChipProps, +} from '@mobile/components/host/new-thread/draft-tools.types'; +import { clearable, DEFAULT_TAG } from '@mobile/components/host/new-thread/draft-tools.types'; +import { useState } from 'react'; import { View } from 'react-native'; import { useTranslations } from 'use-intl'; -/** Tag for the leading "Default" entry in each menu — picking it clears the explicit pick, so - * the agent's own startup resolution applies again. */ -const DEFAULT_TAG = '__default__'; - -function clearable(onChange: (value: string | null) => void): (value: string) => void { - return (value) => { - onChange(value === DEFAULT_TAG ? null : value); - }; -} - -/** The draft's approval-policy chip: a ghost shield opening the policy menu. */ +/** The draft's approval-policy chip on Android: a ghost shield opening the policy sheet. */ export function ApprovalChip({ policies, policyId, policyValue, onPolicyIdChange, -}: { - policies: ApprovalPolicy[]; - policyId: string | null; - policyValue: string; - onPolicyIdChange: (policyId: string | null) => void; -}): React.ReactNode { +}: ApprovalChipProps): React.ReactNode { const t = useTranslations('mobile.sessions'); + const [open, setOpen] = useState(false); if (policies.length === 0) return null; return ( - - - - {t('defaultOption')} - {policies.map((policy) => ( - - {policy.name} - - ))} - - - + <> + setOpen(true)} + /> + setOpen(false)} + sections={[ + { + id: 'policy', + title: t('approvalLabel'), + selection: policyId ?? DEFAULT_TAG, + options: [ + { id: DEFAULT_TAG, label: t('defaultOption') }, + ...policies.map((policy) => ({ id: policy.policyId, label: policy.name })), + ], + onSelect: clearable(onPolicyIdChange), + }, + ]} + /> + ); } -/** The draft's combined selector: the harness brand mark beside one menu with Agent / Model / - * Effort submenus, mirroring the desktop `ModelSelectorMenu`. The brand mark is an RN view and - * cannot enter the SwiftUI tree, so it sits flush beside the menu trigger — ghost styling makes - * the pair read as one control. */ +/** The draft's combined selector on Android: the harness brand mark beside one chip opening the + * structured `SelectorSheet` — the same Agent / Model / Effort axes the iOS UIMenu nests as + * submenus. A "Default" entry leads the model and effort axes; picking it clears the pick. */ export function AgentSelectorChip({ kind, onKindChange, @@ -69,84 +70,65 @@ export function AgentSelectorChip({ effortOptions, effort, onEffortChange, -}: { - kind: AgentKind; - onKindChange: (kind: AgentKind) => void; - /** Account-backed options; null while loading (model submenu hidden). */ - models: ModelOption[] | null; - /** `modelChoiceKey` of the explicit pick, or null for default. */ - modelKey: string | null; - onModelKeyChange: (key: string | null) => void; - /** Text on the chip — resolved model (and effort), or the harness name. */ - selectorValue: string; - effortOptions: EffortOption[] | undefined; - effort: string | null; - onEffortChange: (effort: string | null) => void; -}): React.ReactNode { +}: AgentSelectorChipProps): React.ReactNode { const t = useTranslations('mobile.sessions'); - const muted = useThemeColor('muted'); - // The account label joins a row only when the list spans several accounts — the same threshold - // as the web's provider grouping; a single-account list repeating its account is noise. - const spansAccounts = models !== null && groupModelsByProvider(models) !== null; + const colors = useAppMaterialColors(); + const [open, setOpen] = useState(false); + + const harnessAxis: SelectorChipAxis = { + title: t('kindLabel'), + selection: kind, + options: AgentKindSchema.options.map((option) => ({ + id: option, + label: AGENT_LABELS[option], + })), + onSelect(id) { + const parsed = AgentKindSchema.safeParse(id); + if (parsed.success) onKindChange(parsed.data); + }, + }; + let modelAxis: SelectorModelAxis | undefined; + if (models !== null && models.length > 0) { + modelAxis = { + title: t('modelLabel'), + selection: modelKey ?? DEFAULT_TAG, + groups: [ + { label: null, options: [{ id: DEFAULT_TAG, label: t('defaultOption') }] }, + ...modelAxisGroups(models), + ], + onSelect: clearable(onModelKeyChange), + }; + } + let effortAxis: SelectorChipAxis | undefined; + if (effortOptions !== undefined && effortOptions.length > 0) { + effortAxis = { + title: t('effortLabel'), + selection: effort ?? DEFAULT_TAG, + options: [ + { id: DEFAULT_TAG, label: t('defaultOption') }, + ...effortOptions.map((option) => ({ id: option.id, label: option.label })), + ], + onSelect: clearable(onEffortChange), + }; + } return ( {/* Footnote's 13pt metric, so the mark scales with the chip text beside it. */} - - - {/* Keyed by menu shape: children added to an already-created Menu never reach the - native UIMenu, so late-loading models/efforts must remount the chip. */} - - - - {AgentKindSchema.options.map((option) => ( - - {AGENT_LABELS[option]} - - ))} - - - {/* A labeled submenu, not an inline group: a bare Picker between sibling menus drops - out of the native UIMenu entirely. */} - {models !== null && models.length > 0 ? ( - - - {t('defaultOption')} - {models.map((model) => ( - - {spansAccounts && model.description - ? `${model.label} — ${model.description}` - : model.label} - - ))} - - - ) : null} - {effortOptions !== undefined && effortOptions.length > 0 ? ( - - - {t('defaultOption')} - {effortOptions.map((option) => ( - - {option.label} - - ))} - - - ) : null} - - + + setOpen(true)} + /> + setOpen(false)} + harness={harnessAxis} + model={modelAxis} + effort={effortAxis} + /> ); } diff --git a/apps/mobile/src/components/host/new-thread/draft-tools.types.ts b/apps/mobile/src/components/host/new-thread/draft-tools.types.ts new file mode 100644 index 000000000..8f1d10b71 --- /dev/null +++ b/apps/mobile/src/components/host/new-thread/draft-tools.types.ts @@ -0,0 +1,34 @@ +import type { AgentKind, ApprovalPolicy } from '@linkcode/schema'; +import type { EffortOption, ModelOption } from '@linkcode/ui/native'; + +/** Tag for the leading "Default" entry in each menu — picking it clears the explicit pick, so + * the agent's own startup resolution applies again. */ +export const DEFAULT_TAG = '__default__'; + +export function clearable(onChange: (value: string | null) => void): (value: string) => void { + return (value) => { + onChange(value === DEFAULT_TAG ? null : value); + }; +} + +export interface ApprovalChipProps { + policies: ApprovalPolicy[]; + policyId: string | null; + policyValue: string; + onPolicyIdChange: (policyId: string | null) => void; +} + +export interface AgentSelectorChipProps { + kind: AgentKind; + onKindChange: (kind: AgentKind) => void; + /** Account-backed options; null while loading (model submenu hidden). */ + models: ModelOption[] | null; + /** `modelChoiceKey` of the explicit pick, or null for default. */ + modelKey: string | null; + onModelKeyChange: (key: string | null) => void; + /** Text on the chip — resolved model (and effort), or the harness name. */ + selectorValue: string; + effortOptions: EffortOption[] | undefined; + effort: string | null; + onEffortChange: (effort: string | null) => void; +} diff --git a/apps/mobile/src/components/host/new-thread/new-thread-screen.tsx b/apps/mobile/src/components/host/new-thread/new-thread-screen.tsx new file mode 100644 index 000000000..6baf28702 --- /dev/null +++ b/apps/mobile/src/components/host/new-thread/new-thread-screen.tsx @@ -0,0 +1,37 @@ +import { Composer } from '@mobile/components/conversation/composer'; +import { AgentSelectorChip, ApprovalChip } from '@mobile/components/host/new-thread/draft-tools'; +import { ProjectRow } from '@mobile/components/host/new-thread/project-row'; +import { useNewThreadDraft } from '@mobile/runtime/use-new-thread-draft'; +import { useRouter } from 'expo-router'; +import { noop } from 'foxact/noop'; +import { View } from 'react-native'; +import { KeyboardStickyView } from 'react-native-keyboard-controller'; + +export function NewThreadScreen(): React.ReactNode { + const router = useRouter(); + const { text, setText, start, creating, sendBlocked, error, project, approval, selector } = + useNewThreadDraft((sessionId) => router.replace(`/session/${sessionId}`)); + + return ( + <> + + + + { + void start(text); + }} + onStop={noop} + isRunning={false} + disabled={creating} + sendBlocked={sendBlocked} + error={error} + tools={} + trailing={} + /> + + + ); +} diff --git a/apps/mobile/src/components/host/new-thread/project-row.ios.tsx b/apps/mobile/src/components/host/new-thread/project-row.ios.tsx new file mode 100644 index 000000000..871eb3882 --- /dev/null +++ b/apps/mobile/src/components/host/new-thread/project-row.ios.tsx @@ -0,0 +1,88 @@ +import { Host, HStack, Image, Menu, Picker, Text as UIText } from '@expo/ui/swift-ui'; +import { + accessibilityLabel, + font, + foregroundStyle, + padding, + tag, + tint, +} from '@expo/ui/swift-ui/modifiers'; +import { repositoryLabel } from '@linkcode/ui/native'; +import type { ProjectRowProps } from '@mobile/components/host/new-thread/project-row.types'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { Color } from 'expo-router'; +import { FolderIcon } from 'lucide-react-native'; +import { TextInput, View } from 'react-native'; +import { useTranslations } from 'use-intl'; + +const ICON = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); +const VALUE = foregroundStyle({ type: 'hierarchical', style: 'primary' }); +const BODY = font({ textStyle: 'body' }); +/** SwiftUI draws a Menu label with the accent tint, and hierarchical styles derive from it — + * re-tinting to the label color turns the value primary and the icons secondary. */ +const LABEL_TINT = tint(Color.ios.label); + +/** The workspace picker as its own frame segment above the composer — outside the card, like the + * web composer's context bar: a bare row (icon + value + the up/down affordance) opening a native + * `UIMenu`. Falls back to a free path field while the host has no workspaces. */ +export function ProjectRow({ + workspaces, + workspaceLabel, + cwd, + onCwdChange, + customPath, + onCustomPathChange, +}: ProjectRowProps): React.ReactNode { + const t = useTranslations('mobile.sessions'); + const palette = useNativePalette(); + + if (workspaces.length === 0) { + return ( + + + + + ); + } + + return ( + + + + + {workspaceLabel} + + + } + > + + {workspaces.map((workspace) => ( + + {workspace.name ?? repositoryLabel(workspace.cwd)} + + ))} + + + + + ); +} diff --git a/apps/mobile/src/components/host/new-thread/project-row.tsx b/apps/mobile/src/components/host/new-thread/project-row.tsx index 45c6ad16f..7fd23d9f1 100644 --- a/apps/mobile/src/components/host/new-thread/project-row.tsx +++ b/apps/mobile/src/components/host/new-thread/project-row.tsx @@ -1,30 +1,14 @@ -import { Host, HStack, Image, Menu, Picker, Text as UIText } from '@expo/ui/swift-ui'; -import { - accessibilityLabel, - font, - foregroundStyle, - padding, - tag, - tint, -} from '@expo/ui/swift-ui/modifiers'; -import type { WorkspaceRecord } from '@linkcode/schema'; import { repositoryLabel } from '@linkcode/ui/native'; -import { Color } from 'expo-router'; -import { useThemeColor } from 'heroui-native'; -import { FolderIcon } from 'lucide-react-native'; -import { TextInput, View } from 'react-native'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { SheetPicker } from '@mobile/components/form/sheet-picker.android'; +import type { ProjectRowProps } from '@mobile/components/host/new-thread/project-row.types'; +import { ChevronsUpDownIcon, FolderIcon } from 'lucide-react-native'; +import { useState } from 'react'; +import { Pressable, Text, TextInput, View } from 'react-native'; import { useTranslations } from 'use-intl'; -const ICON = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); -const VALUE = foregroundStyle({ type: 'hierarchical', style: 'primary' }); -const BODY = font({ textStyle: 'body' }); -/** SwiftUI draws a Menu label with the accent tint, and hierarchical styles derive from it — - * re-tinting to the label color turns the value primary and the icons secondary. */ -const LABEL_TINT = tint(Color.ios.label); - -/** The workspace picker as its own frame segment above the composer — outside the card, like the - * web composer's context bar: a bare row (icon + value + the up/down affordance) opening a native - * `UIMenu`. Falls back to a free path field while the host has no workspaces. */ +/** Android workspace picker above the composer: a bare row opening the workspace list as a + * bottom sheet. Falls back to a free path field while the host has no workspaces. */ export function ProjectRow({ workspaces, workspaceLabel, @@ -32,27 +16,21 @@ export function ProjectRow({ onCwdChange, customPath, onCustomPathChange, -}: { - /** Already in recency order; the head is the default pick. */ - workspaces: WorkspaceRecord[]; - workspaceLabel: string; - cwd: string | null; - onCwdChange: (cwd: string) => void; - customPath: string; - onCustomPathChange: (path: string) => void; -}): React.ReactNode { +}: ProjectRowProps): React.ReactNode { const t = useTranslations('mobile.sessions'); - const muted = useThemeColor('muted'); + const colors = useAppMaterialColors(); + const [open, setOpen] = useState(false); if (workspaces.length === 0) { return ( - + - - - - {workspaceLabel} - - - } - > - - {workspaces.map((workspace) => ( - - {workspace.name ?? repositoryLabel(workspace.cwd)} - - ))} - - - + setOpen(true)} + className="flex-row items-center gap-2 py-1" + > + + + {workspaceLabel} + + + + setOpen(false)} + sections={[ + { + id: 'workspace', + title: t('projectLabel'), + selection: cwd, + options: workspaces.map((workspace) => ({ + id: workspace.cwd, + label: workspace.name ?? repositoryLabel(workspace.cwd), + hint: workspace.cwd, + })), + onSelect: onCwdChange, + }, + ]} + /> ); } diff --git a/apps/mobile/src/components/host/new-thread/project-row.types.ts b/apps/mobile/src/components/host/new-thread/project-row.types.ts new file mode 100644 index 000000000..14bdf6ad7 --- /dev/null +++ b/apps/mobile/src/components/host/new-thread/project-row.types.ts @@ -0,0 +1,11 @@ +import type { WorkspaceRecord } from '@linkcode/schema'; + +export interface ProjectRowProps { + /** Already in recency order; the head is the default pick. */ + workspaces: WorkspaceRecord[]; + workspaceLabel: string; + cwd: string | null; + onCwdChange: (cwd: string) => void; + customPath: string; + onCustomPathChange: (path: string) => void; +} diff --git a/apps/mobile/src/components/host/thread-list/thread-list.ios.tsx b/apps/mobile/src/components/host/thread-list/thread-list.ios.tsx new file mode 100644 index 000000000..807417b6a --- /dev/null +++ b/apps/mobile/src/components/host/thread-list/thread-list.ios.tsx @@ -0,0 +1,41 @@ +import { List, Section } from '@expo/ui/swift-ui'; +import { listStyle, refreshable } from '@expo/ui/swift-ui/modifiers'; +import type { ThreadListProps } from './thread-list.types'; +import { ThreadRow } from './thread-row'; +import { useThreadListState } from './use-thread-list-state'; + +/** The thread inbox body: one collapsible section per group. Grouping is decided by the + * caller — this only renders it. + * + * `sidebar` is not cosmetic — SwiftUI only honours a `Section`'s expanded state under that list + * style, so it is what makes the groups collapsible at all. */ +export function ThreadList({ + groups, + labelFor, + onOpenThread, + onRefresh, +}: ThreadListProps): React.ReactNode { + const { collapsed, setGroupExpanded, now } = useThreadListState(); + + return ( + + {groups.map((group) => ( +
setGroupExpanded(group.key, expanded)} + > + {group.sessions.map((session) => ( + onOpenThread(session.sessionId)} + /> + ))} +
+ ))} +
+ ); +} diff --git a/apps/mobile/src/components/host/thread-list/thread-list.tsx b/apps/mobile/src/components/host/thread-list/thread-list.tsx index d438a7ebc..f3f5ea29a 100644 --- a/apps/mobile/src/components/host/thread-list/thread-list.tsx +++ b/apps/mobile/src/components/host/thread-list/thread-list.tsx @@ -1,65 +1,84 @@ -import { List, Section } from '@expo/ui/swift-ui'; -import { listStyle, refreshable } from '@expo/ui/swift-ui/modifiers'; -import type { SessionInfo } from '@linkcode/schema'; -import type { ThreadGroup } from '@linkcode/ui/native'; -import { useEffect, useState } from 'react'; +import { Icon, LazyColumn, PullToRefreshBox, Row, Text } from '@expo/ui/jetpack-compose'; +import { + clickable, + defaultMinSize, + fillMaxWidth, + padding, + weight, +} from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import { useState } from 'react'; +import expandLessGlyph from '../../../../assets/icons/expand-less.xml'; +import expandMoreGlyph from '../../../../assets/icons/expand-more.xml'; +import type { ThreadListProps } from './thread-list.types'; import { ThreadRow } from './thread-row'; +import { useThreadListState } from './use-thread-list-state'; -const MINUTE = 60000; - -/** The thread inbox body: one collapsible section per group. Grouping is decided by the - * caller — this only renders it. Collapsed keys live here because the state is presentational: - * nothing outside the list cares which groups are open. - * - * `sidebar` is not cosmetic — SwiftUI only honours a `Section`'s expanded state under that list - * style, so it is what makes the groups collapsible at all. */ +// Each visible thread must be a direct LazyColumn child; grouping rows in a Column defeats laziness. export function ThreadList({ groups, labelFor, onOpenThread, onRefresh, -}: { - groups: ThreadGroup[]; - labelFor: (group: ThreadGroup) => string; - onOpenThread: (sessionId: SessionInfo['sessionId']) => void; - /** Drives SwiftUI's own pull-to-refresh, which holds its spinner until this resolves. */ - onRefresh: () => Promise; -}): React.ReactNode { - const [collapsed, setCollapsed] = useState>(() => new Set()); - const [now, setNow] = useState(Date.now); - - useEffect(() => { - const timer = setInterval(() => setNow(Date.now()), MINUTE); - return () => clearInterval(timer); - }, []); +}: ThreadListProps): React.ReactNode { + const { collapsed, setGroupExpanded, now } = useThreadListState(); + const colors = useAppMaterialColors(); + const [refreshing, setRefreshing] = useState(false); - const setGroupExpanded = (key: string, expanded: boolean) => - setCollapsed((current) => { - const next = new Set(current); - if (expanded) next.delete(key); - else next.add(key); - return next; - }); + const refresh = () => { + setRefreshing(true); + void onRefresh().finally(() => setRefreshing(false)); + }; return ( - - {groups.map((group) => ( -
setGroupExpanded(group.key, expanded)} - > - {group.sessions.map((session) => ( - onOpenThread(session.sessionId)} - /> - ))} -
- ))} -
+ + {/* expo-ui's indicator slot drops Compose's align(TopCenter); topCenter restores it, and the + * fillMaxWidth child is unaffected. */} + + + {groups.flatMap((group) => { + const expanded = !collapsed.has(group.key); + return [ + setGroupExpanded(group.key, !expanded)), + fillMaxWidth(), + defaultMinSize({ minHeight: 48 }), + padding(16, 14, 16, 4), + ]} + > + + {labelFor(group)} + + + , + ...(expanded + ? group.sessions.map((session) => ( + onOpenThread(session.sessionId)} + /> + )) + : []), + ]; + })} + + + ); } diff --git a/apps/mobile/src/components/host/thread-list/thread-list.types.ts b/apps/mobile/src/components/host/thread-list/thread-list.types.ts new file mode 100644 index 000000000..eb759c928 --- /dev/null +++ b/apps/mobile/src/components/host/thread-list/thread-list.types.ts @@ -0,0 +1,10 @@ +import type { SessionInfo } from '@linkcode/schema'; +import type { ThreadGroup } from '@linkcode/ui/native'; + +export interface ThreadListProps { + groups: ThreadGroup[]; + labelFor: (group: ThreadGroup) => string; + onOpenThread: (sessionId: SessionInfo['sessionId']) => void; + /** Pull-to-refresh driver; the platform view holds its spinner until this resolves. */ + onRefresh: () => Promise; +} diff --git a/apps/mobile/src/components/host/thread-list/thread-row.ios.tsx b/apps/mobile/src/components/host/thread-list/thread-row.ios.tsx new file mode 100644 index 000000000..28d831b5c --- /dev/null +++ b/apps/mobile/src/components/host/thread-list/thread-row.ios.tsx @@ -0,0 +1,71 @@ +import { HStack, Image, RNHostView, Spacer, Text, VStack } from '@expo/ui/swift-ui'; +import { + accessibilityAddTraits, + accessibilityElement, + contentShape, + foregroundStyle, + lineLimit, + onTapGesture, + shapes, +} from '@expo/ui/swift-ui/modifiers'; +import type { SessionStatus } from '@linkcode/schema'; +import { AgentIcon } from '@linkcode/ui/native'; +import { FOOTNOTE, SECONDARY, TERTIARY } from '@mobile/components/form/styles.ios'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { formatRelativeShort } from '@mobile/utils/relative-time'; +import { threadTitle } from '@mobile/utils/thread-title'; +import { View } from 'react-native'; +import type { ThreadRowProps } from './thread-row.types'; + +/** SwiftUI's semantic colours standing in for the `bg-*` tokens the RN dot used. */ +const STATUS_COLOR = { + starting: 'orange', + idle: 'gray', + running: 'green', + 'awaiting-input': 'orange', + stopped: 'secondary', +} as const satisfies Record; + +const WHOLE_ROW = contentShape(shapes.rectangle()); +const GLYPH_SIZE = 18; + +/** One thread row: the harness brand mark (the web sidebar's ghost glyph), title, how long ago it + * moved, then a status dot and the chevron `NavigationLink` would have drawn. + * + * The brand mark is the RN `AgentIcon`, carried into the SwiftUI row through `RNHostView` — + * `matchContents` so the row takes the mark's Yoga-computed 18pt box, `pointerEvents="none"` so + * the hosted surface never swallows a tap meant for the row's own gesture. */ +export function ThreadRow({ session, now, onPress }: ThreadRowProps): React.ReactNode { + const palette = useNativePalette(); + + return ( + + + + + + + + {threadTitle(session)} + + {formatRelativeShort(session.updatedAt, now)} + + + + + + + ); +} diff --git a/apps/mobile/src/components/host/thread-list/thread-row.tsx b/apps/mobile/src/components/host/thread-list/thread-row.tsx index 521701e6a..21cce93ef 100644 --- a/apps/mobile/src/components/host/thread-list/thread-row.tsx +++ b/apps/mobile/src/components/host/thread-list/thread-row.tsx @@ -1,59 +1,68 @@ -import { HStack, Image, Spacer, Text, VStack } from '@expo/ui/swift-ui'; -import { - contentShape, - foregroundStyle, - lineLimit, - onTapGesture, - shapes, -} from '@expo/ui/swift-ui/modifiers'; -import type { SessionInfo, SessionStatus } from '@linkcode/schema'; -import { AGENT_LABELS, repositoryLabel } from '@linkcode/ui/native'; -import { FOOTNOTE, SECONDARY, TERTIARY } from '@mobile/components/form/styles'; +import { Box, Icon, ListItem, Text } from '@expo/ui/jetpack-compose'; +import { background, clickable, clip, Shapes, size } from '@expo/ui/jetpack-compose/modifiers'; +import type { AgentKind, SessionStatus } from '@linkcode/schema'; +import { AGENT_INITIALS } from '@linkcode/ui/native'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; import { formatRelativeShort } from '@mobile/utils/relative-time'; +import { threadTitle } from '@mobile/utils/thread-title'; +import agentClaudeCodeGlyph from '../../../../assets/icons/agent-claude-code.xml'; +import agentCodexGlyph from '../../../../assets/icons/agent-codex.xml'; +import agentOpencodeGlyph from '../../../../assets/icons/agent-opencode.xml'; +import type { ThreadRowProps } from './thread-row.types'; -/** SwiftUI's semantic colours standing in for the `bg-*` tokens the RN dot used. */ -const STATUS_COLOR = { - starting: 'orange', - idle: 'gray', - running: 'green', - 'awaiting-input': 'orange', - stopped: 'secondary', -} as const satisfies Record; +/** Brand marks vendored as vector drawables from `@proj-airi/lobe-icons` (the web glyph set); + * kinds without a lobe glyph fall back to initials, matching the web sidebar. */ +const AGENT_GLYPHS: Partial> = { + 'claude-code': agentClaudeCodeGlyph, + codex: agentCodexGlyph, + opencode: agentOpencodeGlyph, +}; -const WHOLE_ROW = contentShape(shapes.rectangle()); +const GLYPH_SIZE = 18; -/** One thread row: title (desktop-matching fallback), which agent is driving it and how long ago it - * moved, then a status dot and the chevron `NavigationLink` would have drawn. - * - * The subtitle names the agent rather than the project because the list is already grouped by - * project — repeating it there would spend the line on something the section header already says. - * The agent is text, not a glyph: the brand marks are RN SVG components and cannot cross into - * SwiftUI. */ -export function ThreadRow({ - session, - now, - onPress, -}: { - session: SessionInfo; - now: number; - onPress: () => void; -}): React.ReactNode { - const title = session.title ?? `${AGENT_LABELS[session.kind]} in ${repositoryLabel(session.cwd)}`; - const subtitle = `${AGENT_LABELS[session.kind]} · ${formatRelativeShort(session.updatedAt, now)}`; +/** Android thread row: MD3 ListItem with the harness brand mark leading (the web sidebar's ghost + * glyph), the status dot drawn as a clipped Box, and no disclosure chevron, per MD3. The dot + * speaks Material roles — MD3 has no success/warning — running reads as the active accent, + * waiting states as tertiary. */ +export function ThreadRow({ session, now, onPress }: ThreadRowProps): React.ReactNode { + const colors = useAppMaterialColors(); + const statusColor = { + starting: colors.tertiary, + idle: colors.outline, + running: colors.primary, + 'awaiting-input': colors.tertiary, + stopped: colors.outline, + } satisfies Record; + const glyph = AGENT_GLYPHS[session.kind]; return ( - - - {title} - {subtitle} - - - - - + + + {glyph === undefined ? ( + + + {AGENT_INITIALS[session.kind]} + + + ) : ( + + )} + + + + {threadTitle(session)} + + + + + {formatRelativeShort(session.updatedAt, now)} + + + + + + ); } diff --git a/apps/mobile/src/components/host/thread-list/thread-row.types.ts b/apps/mobile/src/components/host/thread-list/thread-row.types.ts new file mode 100644 index 000000000..09ce6fd2a --- /dev/null +++ b/apps/mobile/src/components/host/thread-list/thread-row.types.ts @@ -0,0 +1,8 @@ +import type { SessionInfo } from '@linkcode/schema'; + +export interface ThreadRowProps { + session: SessionInfo; + /** Clock for the relative timestamp, ticked by the list so rows re-render together. */ + now: number; + onPress: () => void; +} diff --git a/apps/mobile/src/components/host/thread-list/use-thread-list-state.ts b/apps/mobile/src/components/host/thread-list/use-thread-list-state.ts new file mode 100644 index 000000000..f9115f17c --- /dev/null +++ b/apps/mobile/src/components/host/thread-list/use-thread-list-state.ts @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react'; + +const MINUTE = 60000; + +/** Presentational state both platform lists share: which groups are collapsed (nothing outside + * the list cares) and a once-a-minute clock for the relative timestamps. */ +export function useThreadListState(): { + collapsed: ReadonlySet; + setGroupExpanded: (key: string, expanded: boolean) => void; + now: number; +} { + const [collapsed, setCollapsed] = useState>(() => new Set()); + const [now, setNow] = useState(Date.now); + + useEffect(() => { + const timer = setInterval(() => setNow(Date.now()), MINUTE); + return () => clearInterval(timer); + }, []); + + const setGroupExpanded = (key: string, expanded: boolean) => + setCollapsed((current) => { + const next = new Set(current); + if (expanded) next.delete(key); + else next.add(key); + return next; + }); + + return { collapsed, setGroupExpanded, now }; +} diff --git a/apps/mobile/src/components/host/threads-screen.ios.tsx b/apps/mobile/src/components/host/threads-screen.ios.tsx new file mode 100644 index 000000000..dbd7016f4 --- /dev/null +++ b/apps/mobile/src/components/host/threads-screen.ios.tsx @@ -0,0 +1,80 @@ +import { Form, Host, Section, Button as UIButton, Text as UIText } from '@expo/ui/swift-ui'; +import { LoadingView } from '@mobile/components/form/loading-view.ios'; +import { SECONDARY } from '@mobile/components/form/styles.ios'; +import { ThreadList } from '@mobile/components/host/thread-list/thread-list'; +import { useThreadInbox } from '@mobile/runtime/use-thread-inbox'; +import { Stack, useRouter } from 'expo-router'; +import { noop } from 'foxact/noop'; +import { useCallback } from 'react'; +import { useTranslations } from 'use-intl'; + +/** Taken from the search bar itself: RN's own replacement for the event it declares carries no text. */ +type SearchBarChangeEvent = Parameters< + NonNullable['onChangeText']> +>[0]; + +/** Threads inbox body: grouped sessions under collapsible headers, with the native search bar + * stacked below the navigation bar. The open/close props are the Android toggle; iOS search is + * always available in the header and writes back into the route-owned query. */ +export function ThreadsScreen({ + query = '', + onQueryChange = noop, +}: { + searchOpen?: boolean; + onCloseSearch?: () => void; + query?: string; + onQueryChange?: (query: string) => void; +}): React.ReactNode { + const t = useTranslations('mobile.sessions'); + const router = useRouter(); + const { loading, groups, hasQuery, groupLabel, onRefresh } = useThreadInbox(query); + + // Stable so the search bar's options object survives a keystroke without re-registering. + const onSearchChange = useCallback( + (event: SearchBarChangeEvent) => onQueryChange(event.nativeEvent.text), + [onQueryChange], + ); + + return ( + <> + {/* `stacked` keeps the field below the inline title instead of moving into the iOS 26 toolbar. */} + + {/* The list needs the viewport as its proposed size, otherwise SwiftUI collapses it. */} + + {loading ? ( + + ) : groups.length === 0 ? ( + // A query that matched nothing is not an empty inbox: saying "no threads yet" there + // reads as though the existing threads were lost, and offering to start one is no + // remedy for a bad search. +
+ {hasQuery ? ( +
+ {t('searchEmpty')} +
+ ) : ( +
{t('emptyHint')}}> + {t('emptyTitle')} + router.push('/new-thread')} /> +
+ )} +
+ ) : ( + router.push(`/session/${sessionId}`)} + onRefresh={onRefresh} + /> + )} +
+ + ); +} diff --git a/apps/mobile/src/components/host/threads-screen.tsx b/apps/mobile/src/components/host/threads-screen.tsx new file mode 100644 index 000000000..e285a072a --- /dev/null +++ b/apps/mobile/src/components/host/threads-screen.tsx @@ -0,0 +1,75 @@ +import { Button, Text } from '@expo/ui/jetpack-compose'; +import { padding } from '@expo/ui/jetpack-compose/modifiers'; +import { FormList } from '@mobile/components/form/list.android'; +import { LoadingView } from '@mobile/components/form/loading-view.android'; +import { FormHint } from '@mobile/components/form/rows.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { ThreadList } from '@mobile/components/host/thread-list/thread-list'; +import { useThreadInbox } from '@mobile/runtime/use-thread-inbox'; +import { useRouter } from 'expo-router'; +import { noop } from 'foxact/noop'; +import { useEffect } from 'react'; +import { BackHandler } from 'react-native'; +import { useTranslations } from 'use-intl'; + +/** Android threads inbox body. The search field lives in the route's header (the M3 search view + * swaps the top app bar's content via `useSearchHeaderOptions`); this screen only filters by the + * route-owned query and collapses the search on hardware back. */ +export function ThreadsScreen({ + searchOpen = false, + onCloseSearch = noop, + query = '', +}: { + searchOpen?: boolean; + onCloseSearch?: () => void; + query?: string; + onQueryChange?: (query: string) => void; +}): React.ReactNode { + const t = useTranslations('mobile.sessions'); + const router = useRouter(); + const { loading, groups, hasQuery, groupLabel, onRefresh } = useThreadInbox(query); + + // The hardware back gesture collapses the search first, as every Android search view does. + useEffect(() => { + if (!searchOpen) return; + const subscription = BackHandler.addEventListener('hardwareBackPress', () => { + onCloseSearch(); + return true; + }); + return () => subscription.remove(); + }, [searchOpen, onCloseSearch]); + + return ( + <> + {loading ? ( + + ) : groups.length === 0 ? ( + // A query that matched nothing is not an empty inbox: saying "no threads yet" there + // reads as though the existing threads were lost, and offering to start one is no + // remedy for a bad search. + + {hasQuery ? ( + {t('searchEmpty')} + ) : ( + + {t('emptyTitle')} + + + )} + + ) : ( + router.push(`/session/${sessionId}`)} + onRefresh={onRefresh} + /> + )} + + ); +} diff --git a/apps/mobile/src/components/host/use-host-header-options.tsx b/apps/mobile/src/components/host/use-host-header-options.tsx new file mode 100644 index 000000000..34bcaf254 --- /dev/null +++ b/apps/mobile/src/components/host/use-host-header-options.tsx @@ -0,0 +1,16 @@ +import { HostSwitcherButton } from '@mobile/components/host/host-switcher-button'; +import { useHostMenuItems } from '@mobile/components/host/use-host-menu-items'; +import type { NativeStackNavigationOptions } from 'expo-router'; +import { Platform } from 'react-native'; + +/** The header's host switcher as stack-screen options: native bar items on iOS, a headerLeft + * pressable opening a bottom sheet on Android — `unstable_headerLeftItems` is silently ignored + * there. Spread into each tab's `Stack.Screen` options. */ +export function useHostHeaderOptions(): Partial { + const hostMenuItems = useHostMenuItems(); + + if (Platform.OS === 'ios') { + return { unstable_headerLeftItems: () => hostMenuItems }; + } + return { headerLeft: () => }; +} diff --git a/apps/mobile/src/components/settings/settings-screen.ios.tsx b/apps/mobile/src/components/settings/settings-screen.ios.tsx new file mode 100644 index 000000000..2d7c572bd --- /dev/null +++ b/apps/mobile/src/components/settings/settings-screen.ios.tsx @@ -0,0 +1,151 @@ +import { Form, Host, Link, Picker, Section, Text, Toggle, VStack } from '@expo/ui/swift-ui'; +import { disabled, font, foregroundStyle, pickerStyle, tag } from '@expo/ui/swift-ui/modifiers'; +import { AgentKindSchema, WIRE_PROTOCOL_VERSION } from '@linkcode/schema'; +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { + PRIVACY_POLICY_URL, + SUPPORT_URL, + TERMS_OF_SERVICE_URL, + THEME_LABEL_KEY, + THEME_PREFERENCES, +} from '@mobile/components/settings/settings-screen.shared'; +import { LARGE_TITLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; +import { useCloudAccount } from '@mobile/runtime/cloud/account'; +import { setMobileProductAnalyticsEnabled } from '@mobile/runtime/product-analytics'; +import { useNotificationSettings } from '@mobile/runtime/use-notification-settings'; +import { useAnalyticsPreferenceStore } from '@mobile/stores/analytics-store'; +import { useSettingsStore } from '@mobile/stores/settings-store'; +import { Stack, useRouter } from 'expo-router'; +import { View } from 'react-native'; +import { useTranslations } from 'use-intl'; + +const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); + +/** App settings: account + host management entries plus the About/contract summary. Nothing here is + * host-scoped, and the screen is deliberately ungated — this is where "Manage hosts" lives, so it + * has to survive the selected host being unreachable. */ +export function SettingsScreen(): React.ReactNode { + const t = useTranslations('mobile.settings'); + const tAbout = useTranslations('mobile.about'); + const router = useRouter(); + const account = useCloudAccount(); + const productAnalyticsEnabled = useAnalyticsPreferenceStore((state) => state.enabled); + const themePreference = useSettingsStore((state) => state.themePreference); + const { + enabled: notificationsEnabled, + canUpdate: notificationsToggleEnabled, + update: updateNotifications, + } = useNotificationSettings(); + const setThemePreference = useSettingsStore((state) => state.setThemePreference); + const keepHostsConnected = useSettingsStore((state) => state.keepHostsConnected); + const setKeepHostsConnected = useSettingsStore((state) => state.setKeepHostsConnected); + + // The flex container is load-bearing: a SwiftUI host left as the screen's direct child is + // proposed the whole window and paints straight over the large title. + return ( + + + {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} + +
+
+ {account.status === 'signed-in' ? ( + router.push('/account')} + /> + ) : account.status === 'signed-out' ? ( + router.push('/sign-in')} /> + ) : null} + router.push('/connect')} /> + router.push('/terminal-appearance')} + /> +
+ +
{t('keepHostsConnectedHint')}}> + +
+ +
+ + {THEME_PREFERENCES.map((preference) => ( + + {t(THEME_LABEL_KEY[preference])} + + ))} + +
+ +
{t('analyticsHint')}}> + +
+ +
+ {account.status === 'signed-in' + ? t('notificationsHint') + : t('notificationsRequiresCloud')} + + } + > + +
+ + {/* Native links open the URL themselves — no Linking.openURL fallback to get wrong. */} +
+ + + +
+ +
+ + {tAbout('title')} + + {tAbout('contract', { version: WIRE_PROTOCOL_VERSION })} + + +
+ +
{tAbout('note')} + } + > + {AgentKindSchema.options.map((kind) => ( + {kind} + ))} +
+
+
+
+ ); +} diff --git a/apps/mobile/src/components/settings/settings-screen.shared.ts b/apps/mobile/src/components/settings/settings-screen.shared.ts new file mode 100644 index 000000000..75abcc828 --- /dev/null +++ b/apps/mobile/src/components/settings/settings-screen.shared.ts @@ -0,0 +1,13 @@ +import type { ThemePreference } from '@mobile/stores/settings-store'; + +export const THEME_PREFERENCES: readonly ThemePreference[] = ['system', 'light', 'dark']; + +export const THEME_LABEL_KEY = { + system: 'appearanceSystem', + light: 'appearanceLight', + dark: 'appearanceDark', +} as const; + +export const PRIVACY_POLICY_URL = 'https://linkcode.ai/privacy'; +export const TERMS_OF_SERVICE_URL = 'https://linkcode.ai/terms'; +export const SUPPORT_URL = 'https://linkcode.ai/support'; diff --git a/apps/mobile/src/components/settings/settings-screen.tsx b/apps/mobile/src/components/settings/settings-screen.tsx index e3d0b7816..69b66a7bc 100644 --- a/apps/mobile/src/components/settings/settings-screen.tsx +++ b/apps/mobile/src/components/settings/settings-screen.tsx @@ -1,39 +1,41 @@ -import { Form, Host, Link, Picker, Section, Text, Toggle, VStack } from '@expo/ui/swift-ui'; -import { disabled, font, foregroundStyle, pickerStyle, tag } from '@expo/ui/swift-ui/modifiers'; +import { + Column, + SegmentedButton, + SingleChoiceSegmentedButtonRow, + Text, +} from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, padding } from '@expo/ui/jetpack-compose/modifiers'; import { AgentKindSchema, WIRE_PROTOCOL_VERSION } from '@linkcode/schema'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { FormList } from '@mobile/components/form/list.android'; import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { ToggleRow } from '@mobile/components/form/rows.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { + PRIVACY_POLICY_URL, + SUPPORT_URL, + TERMS_OF_SERVICE_URL, + THEME_LABEL_KEY, + THEME_PREFERENCES, +} from '@mobile/components/settings/settings-screen.shared'; import { LARGE_TITLE_HEADER_OPTIONS } from '@mobile/components/shell/use-stack-screen-options'; import { useCloudAccount } from '@mobile/runtime/cloud/account'; -import { - disableDeviceNotifications, - enableDeviceNotifications, -} from '@mobile/runtime/notifications'; import { setMobileProductAnalyticsEnabled } from '@mobile/runtime/product-analytics'; +import { useNotificationSettings } from '@mobile/runtime/use-notification-settings'; import { useAnalyticsPreferenceStore } from '@mobile/stores/analytics-store'; -import type { ThemePreference } from '@mobile/stores/settings-store'; import { useSettingsStore } from '@mobile/stores/settings-store'; import { Stack, useRouter } from 'expo-router'; -import { useRef, useState } from 'react'; -import { Alert, Linking, View } from 'react-native'; +import { noop } from 'foxact/noop'; +import { Linking, View } from 'react-native'; import { useTranslations } from 'use-intl'; -const THEME_PREFERENCES: readonly ThemePreference[] = ['system', 'light', 'dark']; - -const THEME_LABEL_KEY = { - system: 'appearanceSystem', - light: 'appearanceLight', - dark: 'appearanceDark', -} as const; - -const PRIVACY_POLICY_URL = 'https://linkcode.ai/privacy'; -const TERMS_OF_SERVICE_URL = 'https://linkcode.ai/terms'; -const SUPPORT_URL = 'https://linkcode.ai/support'; - -const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); +/** Compose has no `Link` row, so legal rows open the URL through RN `Linking`. */ +function openUrl(url: string): void { + Linking.openURL(url).catch(noop); +} -/** App settings: account + host management entries plus the About/contract summary. Nothing here is - * host-scoped, and the screen is deliberately ungated — this is where "Manage hosts" lives, so it - * has to survive the selected host being unreachable. */ +/** Android settings, mirroring the SwiftUI screen in `settings-screen.ios.tsx`: same sections, + * MD3 dress. */ export function SettingsScreen(): React.ReactNode { const t = useTranslations('mobile.settings'); const tAbout = useTranslations('mobile.about'); @@ -41,42 +43,15 @@ export function SettingsScreen(): React.ReactNode { const account = useCloudAccount(); const productAnalyticsEnabled = useAnalyticsPreferenceStore((state) => state.enabled); const themePreference = useSettingsStore((state) => state.themePreference); - const notificationsEnabled = useSettingsStore((state) => state.notificationsEnabled); + const { + enabled: notificationsEnabled, + canUpdate: notificationsToggleEnabled, + update: updateNotifications, + } = useNotificationSettings(); const setThemePreference = useSettingsStore((state) => state.setThemePreference); const keepHostsConnected = useSettingsStore((state) => state.keepHostsConnected); const setKeepHostsConnected = useSettingsStore((state) => state.setKeepHostsConnected); - const [notificationUpdatePending, setNotificationUpdatePending] = useState(false); - const notificationUpdatePendingRef = useRef(false); - const updateNotifications = async (enabled: boolean) => { - if (account.status !== 'signed-in' || notificationUpdatePendingRef.current) return; - notificationUpdatePendingRef.current = true; - setNotificationUpdatePending(true); - try { - if (!enabled) { - await disableDeviceNotifications(); - return; - } - if (await enableDeviceNotifications(account.user.id)) return; - Alert.alert(t('notificationsDeniedTitle'), t('notificationsDenied'), [ - { text: t('cancel'), style: 'cancel' }, - { - text: t('openSettings'), - onPress() { - void Linking.openSettings(); - }, - }, - ]); - } catch { - Alert.alert(t('notificationsErrorTitle'), t('notificationsError')); - } finally { - notificationUpdatePendingRef.current = false; - setNotificationUpdatePending(false); - } - }; - - // The flex container is load-bearing: a SwiftUI host left as the screen's direct child is - // proposed the whole window and paints straight over the large title. return ( - {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} - -
-
- {account.status === 'signed-in' ? ( - router.push('/account')} - /> - ) : account.status === 'signed-out' ? ( - router.push('/sign-in')} /> - ) : null} - router.push('/connect')} /> + + + {account.status === 'signed-in' ? ( router.push('/terminal-appearance')} + title={account.user.name || account.user.email} + subtitle={account.user.email} + onPress={() => router.push('/account')} /> -
+ ) : account.status === 'signed-out' ? ( + router.push('/sign-in')} /> + ) : null} + router.push('/connect')} /> + router.push('/terminal-appearance')} + /> + -
{t('keepHostsConnectedHint')}}> - -
+ + + -
- - {THEME_PREFERENCES.map((preference) => ( - - {t(THEME_LABEL_KEY[preference])} - - ))} - -
+ + + {THEME_PREFERENCES.map((preference) => ( + setThemePreference(preference)} + > + + {t(THEME_LABEL_KEY[preference])} + + + ))} + + -
{t('analyticsHint')}}> - -
+ + + -
- {account.status === 'signed-in' - ? t('notificationsHint') - : t('notificationsRequiresCloud')} - - } - > - -
+ + + - {/* Native links open the URL themselves — no Linking.openURL fallback to get wrong. */} -
- - - -
+ + openUrl(PRIVACY_POLICY_URL)} /> + openUrl(TERMS_OF_SERVICE_URL)} + /> + openUrl(SUPPORT_URL)} /> + -
- - {tAbout('title')} - - {tAbout('contract', { version: WIRE_PROTOCOL_VERSION })} - - -
+ + + -
{tAbout('note')} - } - > - {AgentKindSchema.options.map((kind) => ( - {kind} - ))} -
- -
+ + {AgentKindSchema.options.map((kind) => ( + + {kind} + + ))} + +
); } + +function AboutBlock({ title, contract }: { title: string; contract: string }): React.ReactNode { + const colors = useAppMaterialColors(); + + return ( + + {title} + + {contract} + + + ); +} diff --git a/apps/mobile/src/components/settings/terminal-appearance-screen.ios.tsx b/apps/mobile/src/components/settings/terminal-appearance-screen.ios.tsx new file mode 100644 index 000000000..9143c3914 --- /dev/null +++ b/apps/mobile/src/components/settings/terminal-appearance-screen.ios.tsx @@ -0,0 +1,113 @@ +import { + Circle, + Form, + Host, + HStack, + Picker, + Section, + Text, + VStack, + ZStack, +} from '@expo/ui/swift-ui'; +import { + font, + foregroundStyle, + frame, + pickerStyle, + strokeBorder, + tag, +} from '@expo/ui/swift-ui/modifiers'; +import { + resolveTerminalTheme, + TERMINAL_COLOR_SCHEMES, + TERMINAL_FONT_SIZES, + useTerminalPrefsStore, +} from '@mobile/stores/terminal-prefs-store'; +import { useTranslations } from 'use-intl'; + +const SWATCH = 24; + +const HIERARCHICAL_SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); + +/** Renders a theme's own colours so the row previews what it selects. `auto` has no theme + * of its own — it defers to ghostty's defaults — so it shows a neutral placeholder. */ +function ThemeSwatch({ theme }: { theme?: { background?: string; foreground?: string } }) { + return ( + + + {theme?.foreground ? ( + + a + + ) : null} + + ); +} + +/** Client-side terminal appearance: font size and color scheme. */ +export function TerminalAppearanceScreen(): React.ReactNode { + const t = useTranslations('mobile.terminalAppearance'); + const fontSize = useTerminalPrefsStore((state) => state.fontSize); + const colorScheme = useTerminalPrefsStore((state) => state.colorScheme); + const setFontSize = useTerminalPrefsStore((state) => state.setFontSize); + const setColorScheme = useTerminalPrefsStore((state) => state.setColorScheme); + + return ( + +
+
+ + {TERMINAL_FONT_SIZES.map((size) => ( + + {String(size)} + + ))} + +
+ + {/* An inline picker draws the selection checkmarks itself, so the rows only + have to carry the swatch and the label. */} +
+ + {TERMINAL_COLOR_SCHEMES.map((scheme) => { + const theme = resolveTerminalTheme(scheme); + return ( + + + {scheme === 'auto' ? ( + + {t('colorSchemeAuto')} + + {t('colorSchemeAutoHint')} + + + ) : ( + {scheme} + )} + + ); + })} + +
+
+
+ ); +} diff --git a/apps/mobile/src/components/settings/terminal-appearance-screen.tsx b/apps/mobile/src/components/settings/terminal-appearance-screen.tsx new file mode 100644 index 000000000..c51b77dd4 --- /dev/null +++ b/apps/mobile/src/components/settings/terminal-appearance-screen.tsx @@ -0,0 +1,123 @@ +import { + Box, + Column, + RadioButton, + Row, + SegmentedButton, + SingleChoiceSegmentedButtonRow, + Text, +} from '@expo/ui/jetpack-compose'; +import { + background, + border, + clip, + fillMaxWidth, + padding, + Shapes, + selectable, + selectableGroup, + size, +} from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { FormList } from '@mobile/components/form/list.android'; +import { FormSection } from '@mobile/components/form/section.android'; +import { + resolveTerminalTheme, + TERMINAL_COLOR_SCHEMES, + TERMINAL_FONT_SIZES, + useTerminalPrefsStore, +} from '@mobile/stores/terminal-prefs-store'; +import { useTranslations } from 'use-intl'; + +const SWATCH = 24; + +/** Renders a theme's own colours so the row previews what it selects. `auto` has no theme + * of its own — it defers to ghostty's defaults — so it shows a neutral placeholder. */ +function ThemeSwatch({ theme }: { theme?: { background?: string; foreground?: string } }) { + const colors = useAppMaterialColors(); + + return ( + + {theme?.foreground ? ( + + a + + ) : null} + + ); +} + +/** Android terminal appearance, mirroring `terminal-appearance-screen.ios.tsx`: the segmented + * font-size picker maps to MD3 segmented buttons; the inline color-scheme picker becomes a + * radio group, which draws the selection state the checkmarks carried on iOS. */ +export function TerminalAppearanceScreen(): React.ReactNode { + const t = useTranslations('mobile.terminalAppearance'); + const colors = useAppMaterialColors(); + const fontSize = useTerminalPrefsStore((state) => state.fontSize); + const colorScheme = useTerminalPrefsStore((state) => state.colorScheme); + const setFontSize = useTerminalPrefsStore((state) => state.setFontSize); + const setColorScheme = useTerminalPrefsStore((state) => state.setColorScheme); + + return ( + + + + {TERMINAL_FONT_SIZES.map((candidate) => ( + setFontSize(candidate)} + > + + {String(candidate)} + + + ))} + + + + + + {TERMINAL_COLOR_SCHEMES.map((scheme) => { + const theme = resolveTerminalTheme(scheme); + const selected = colorScheme === scheme; + return ( + setColorScheme(scheme), 'radioButton'), + fillMaxWidth(), + padding(16, 8, 16, 8), + ]} + > + + + {scheme === 'auto' ? ( + + {t('colorSchemeAuto')} + + {t('colorSchemeAutoHint')} + + + ) : ( + {scheme} + )} + + ); + })} + + + + ); +} diff --git a/apps/mobile/src/components/shell/app-loading-screen.tsx b/apps/mobile/src/components/shell/app-loading-screen.tsx new file mode 100644 index 000000000..f0cb66647 --- /dev/null +++ b/apps/mobile/src/components/shell/app-loading-screen.tsx @@ -0,0 +1,16 @@ +import { useNativePalette } from '@mobile/components/theme/native-palette'; +import { ActivityIndicator, View } from 'react-native'; +import { BrandMark } from './brand-mark'; + +export function AppLoadingScreen(): React.ReactNode { + const palette = useNativePalette(); + return ( + + + + + ); +} diff --git a/apps/mobile/src/components/shell/app-providers.tsx b/apps/mobile/src/components/shell/app-providers.tsx index 0e0c96cf2..8f1e408e7 100644 --- a/apps/mobile/src/components/shell/app-providers.tsx +++ b/apps/mobile/src/components/shell/app-providers.tsx @@ -1,3 +1,5 @@ +import { useResolvedColorScheme } from '@mobile/components/theme/use-color-scheme'; +import { DarkTheme, DefaultTheme, ThemeProvider } from 'expo-router'; import { HeroUINativeProvider } from 'heroui-native'; import { KeyboardProvider } from 'react-native-keyboard-controller'; import { IntlProvider } from 'use-intl'; @@ -5,7 +7,12 @@ import { IntlProvider } from 'use-intl'; /** Children-optional wrappers so the root layout can compose providers whose own * props declare `children` as required (mirrors workbench's AppI18nProvider pattern). */ export function AppThemeProvider({ children }: React.PropsWithChildren): React.ReactNode { - return {children}; + const scheme = useResolvedColorScheme(); + return ( + + {children} + + ); } /** Feeds keyboard frames to `KeyboardStickyView` so the composer tracks the keyboard. */ diff --git a/apps/mobile/src/components/shell/header-icon-button.tsx b/apps/mobile/src/components/shell/header-icon-button.tsx deleted file mode 100644 index abab1ba4a..000000000 --- a/apps/mobile/src/components/shell/header-icon-button.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useThemeColor } from 'heroui-native'; -import type { LucideIcon } from 'lucide-react-native'; -import { Pressable } from 'react-native'; - -/** Icon-only tap target for navigation headers. */ -export function HeaderIconButton({ - icon: Icon, - label, - onPress, -}: { - icon: LucideIcon; - /** Accessibility label — icon buttons carry no visible text. */ - label: string; - onPress: () => void; -}): React.ReactNode { - const foreground = useThemeColor('foreground'); - return ( - ({ opacity: pressed ? 0.4 : 1 })} - > - - - ); -} diff --git a/apps/mobile/src/components/shell/header-menu-button.ios.ts b/apps/mobile/src/components/shell/header-menu-button.ios.ts new file mode 100644 index 000000000..9f1e57aa1 --- /dev/null +++ b/apps/mobile/src/components/shell/header-menu-button.ios.ts @@ -0,0 +1,7 @@ +import type { HeaderMenuButtonProps } from '@mobile/components/shell/header-menu-button.types'; + +/** iOS renders header menus as native `UIMenu` bar items; this twin exists so shared files can + * reference one component name on both platforms. */ +export function HeaderMenuButton(_props: HeaderMenuButtonProps): React.ReactNode { + return null; +} diff --git a/apps/mobile/src/components/shell/header-menu-button.tsx b/apps/mobile/src/components/shell/header-menu-button.tsx new file mode 100644 index 000000000..602b09d20 --- /dev/null +++ b/apps/mobile/src/components/shell/header-menu-button.tsx @@ -0,0 +1,40 @@ +import { DropdownMenu, DropdownMenuItem, Icon, IconButton, Text } from '@expo/ui/jetpack-compose'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import type { HeaderMenuButtonProps } from '@mobile/components/shell/header-menu-button.types'; +import { useState } from 'react'; +import moreVertGlyph from '../../../assets/icons/more-vert.xml'; + +/** Android header overflow: an MD3 `DropdownMenu` anchored to its trigger — the Compose twin of + * the iOS `UIMenu` bar item. The menu's trigger is fully controlled and the anchor is the + * non-Items child, so the IconButton opens it explicitly. */ +export function HeaderMenuButton({ label, actions }: HeaderMenuButtonProps): React.ReactNode { + const [expanded, setExpanded] = useState(false); + const colors = useAppMaterialColors(); + + return ( + + setExpanded(false)}> + setExpanded(true)}> + + + + {actions.map((action) => ( + { + setExpanded(false); + action.onPress(); + }} + > + + {action.label} + + + ))} + + + + ); +} diff --git a/apps/mobile/src/components/shell/header-menu-button.types.ts b/apps/mobile/src/components/shell/header-menu-button.types.ts new file mode 100644 index 000000000..3da4a4f40 --- /dev/null +++ b/apps/mobile/src/components/shell/header-menu-button.types.ts @@ -0,0 +1,12 @@ +export interface HeaderMenuAction { + id: string; + label: string; + destructive?: boolean; + onPress: () => void; +} + +export interface HeaderMenuButtonProps { + /** Accessibility name of the trigger — icon buttons carry no visible text. */ + label: string; + actions: HeaderMenuAction[]; +} diff --git a/apps/mobile/src/components/shell/primary-action-fab.ios.ts b/apps/mobile/src/components/shell/primary-action-fab.ios.ts new file mode 100644 index 000000000..884a73a1e --- /dev/null +++ b/apps/mobile/src/components/shell/primary-action-fab.ios.ts @@ -0,0 +1,7 @@ +import type { PrimaryAction } from '@mobile/components/shell/primary-action'; + +/** iOS carries the primary action in the header bar items (pre-26) or the tab-bar slot (26+); + * this twin exists so the shared tab routes can reference one component name on both platforms. */ +export function PrimaryActionFab(_props: { action: PrimaryAction | null }): React.ReactNode { + return null; +} diff --git a/apps/mobile/src/components/shell/primary-action-fab.tsx b/apps/mobile/src/components/shell/primary-action-fab.tsx new file mode 100644 index 000000000..39624d134 --- /dev/null +++ b/apps/mobile/src/components/shell/primary-action-fab.tsx @@ -0,0 +1,23 @@ +import { FloatingActionButton, Icon } from '@expo/ui/jetpack-compose'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import type { PrimaryAction } from '@mobile/components/shell/primary-action'; +import { View } from 'react-native'; +import addGlyph from '../../../assets/icons/add.xml'; + +/** The tab's primary action as an MD3 floating action button at the screen's bottom-right — the + * Android counterpart of the iOS header bar item / iOS-26 tab-bar slot. */ +export function PrimaryActionFab({ action }: { action: PrimaryAction | null }): React.ReactNode { + if (!action) return null; + + return ( + + + + + + + + + + ); +} diff --git a/apps/mobile/src/components/shell/root-navigator.tsx b/apps/mobile/src/components/shell/root-navigator.tsx index 2f5a79f98..5471b167b 100644 --- a/apps/mobile/src/components/shell/root-navigator.tsx +++ b/apps/mobile/src/components/shell/root-navigator.tsx @@ -1,3 +1,4 @@ +import { AppLoadingScreen } from '@mobile/components/shell/app-loading-screen'; import { HostConnectionScope } from '@mobile/components/shell/host-connection-scope'; import { useStackScreenOptions, @@ -11,7 +12,7 @@ import { Stack } from 'expo-router'; export function RootNavigator(): React.ReactNode { const configurationReady = useMobileConfiguration(); const screenOptions = useStackScreenOptions(); - if (!configurationReady) return null; + if (!configurationReady) return ; return ( @@ -19,11 +20,15 @@ export function RootNavigator(): React.ReactNode { name="add-host" options={{ ...VISIBLE_HEADER_OPTIONS, - presentation: 'formSheet', - sheetAllowedDetents: [1], - sheetGrabberVisible: false, - headerBackVisible: false, title: '', + // iOS-only: Android's formSheet drops the native header (no title, no dismissal), + // so the deep-linked route pushes with the standard toolbar there instead. + ...(process.env.EXPO_OS === 'ios' && { + presentation: 'formSheet' as const, + sheetAllowedDetents: [1], + sheetGrabberVisible: false, + headerBackVisible: false, + }), }} /> diff --git a/apps/mobile/src/components/shell/theme-controller.tsx b/apps/mobile/src/components/shell/theme-controller.tsx index e829177bd..337091fec 100644 --- a/apps/mobile/src/components/shell/theme-controller.tsx +++ b/apps/mobile/src/components/shell/theme-controller.tsx @@ -1,6 +1,7 @@ import { useSettingsStore } from '@mobile/stores/settings-store'; import { StatusBar } from 'expo-status-bar'; import { useEffect } from 'react'; +import { Appearance } from 'react-native'; import { Uniwind, useUniwind } from 'uniwind'; /** @@ -13,6 +14,7 @@ export function ThemeController(): React.ReactNode { useEffect(() => { Uniwind.setTheme(preference); + Appearance.setColorScheme(preference === 'system' ? 'unspecified' : preference); }, [preference]); return ; diff --git a/apps/mobile/src/components/shell/use-stack-screen-options.ts b/apps/mobile/src/components/shell/use-stack-screen-options.ts index 28d7ad2ed..58f6d4aa8 100644 --- a/apps/mobile/src/components/shell/use-stack-screen-options.ts +++ b/apps/mobile/src/components/shell/use-stack-screen-options.ts @@ -1,5 +1,5 @@ +import { useNativePalette } from '@mobile/components/theme/native-palette'; import type { Stack } from 'expo-router'; -import { useThemeColor } from 'heroui-native'; import { NavigationBarBackdrop } from '../../../modules/linkcode-navigation-bar-backdrop'; import { USES_IOS_26_NAVIGATION } from './ios-26-navigation'; @@ -29,23 +29,25 @@ export const LARGE_TITLE_HEADER_OPTIONS: StackScreenOptions = { headerShown: true, }; -/** Theme-synced native-stack chrome; every Stack in the app spreads these defaults. - * Screens opt into a header per route via `Stack.Screen` options. */ +/** Native-color stack chrome; every Stack in the app spreads these defaults. Screens opt into a + * header per route via `Stack.Screen` options. Colors come from the platform-split native + * palette: UIKit system colors on iOS, Material You roles on Android. */ export function useStackScreenOptions(): StackScreenOptions { - const [background, foreground, accent] = useThemeColor(['background', 'foreground', 'accent']); + const palette = useNativePalette(); return { headerShown: false, headerLargeTitleEnabled: false, - headerTintColor: accent, - headerTitleStyle: { color: foreground }, - headerLargeTitleStyle: { color: foreground }, + headerTintColor: palette.tint, + headerTitleStyle: { color: palette.text }, + headerLargeTitleStyle: { color: palette.text }, + // Android has no header blur; painting the header the content color keeps the screen + // reading as one surface (the shadow is already off). ...(process.env.EXPO_OS !== 'ios' && { - headerBlurEffect: 'systemChromeMaterial' as const, - headerStyle: { backgroundColor: 'transparent' }, + headerStyle: { backgroundColor: palette.background }, }), headerShadowVisible: false, headerBackButtonDisplayMode: 'minimal', - contentStyle: { backgroundColor: background }, + contentStyle: { backgroundColor: palette.background }, }; } diff --git a/apps/mobile/src/components/shell/use-trailing-actions.tsx b/apps/mobile/src/components/shell/use-trailing-actions.tsx index 58af300c5..b50aa4c87 100644 --- a/apps/mobile/src/components/shell/use-trailing-actions.tsx +++ b/apps/mobile/src/components/shell/use-trailing-actions.tsx @@ -1,10 +1,11 @@ -import { HeaderIconButton } from '@mobile/components/shell/header-icon-button'; +import { HeaderMenuButton } from '@mobile/components/shell/header-menu-button'; import { USES_IOS_26_NAVIGATION } from '@mobile/components/shell/ios-26-navigation'; import type { PrimaryAction } from '@mobile/components/shell/primary-action'; +import { useNativePalette } from '@mobile/components/theme/native-palette'; import type { NativeStackHeaderItem, NativeStackNavigationOptions } from 'expo-router'; import { useRouter } from 'expo-router'; -import { EllipsisIcon } from 'lucide-react-native'; -import { Platform, View } from 'react-native'; +import { SearchIcon } from 'lucide-react-native'; +import { Platform, Pressable, View } from 'react-native'; import { useTranslations } from 'use-intl'; type TrailingHeaderOptions = Pick< @@ -12,12 +13,20 @@ type TrailingHeaderOptions = Pick< 'headerRight' | 'unstable_headerRightItems' >; -/** Trailing navigation-bar chrome for the tab screens: the screen's primary action — except on - * iOS 26, whose tab-bar slot already carries it — then the overflow menu that leads to Settings. - * Native bar items are iOS-only, so Android keeps RN header buttons. */ -export function useTrailingActions(primary: PrimaryAction | null): TrailingHeaderOptions { +/** Trailing navigation-bar chrome for the tab screens: on iOS the screen's primary action — + * except on iOS 26, whose tab-bar slot already carries it — then the overflow menu that leads to + * Settings. Native bar items are iOS-only; Android renders the overflow as a Compose + * `DropdownMenu` (right-most) and carries the primary action as a floating action button + * (`PrimaryActionFab`). `onSearchPress` adds an Android search toggle before the overflow — iOS + * search lives in the native search bar instead. */ +export function useTrailingActions( + primary: PrimaryAction | null, + { onSearchPress }: { onSearchPress?: () => void } = {}, +): TrailingHeaderOptions { const t = useTranslations('mobile.settings'); + const tSessions = useTranslations('mobile.sessions'); const router = useRouter(); + const palette = useNativePalette(); if (Platform.OS === 'ios') { const items: NativeStackHeaderItem[] = []; @@ -49,14 +58,22 @@ export function useTrailingActions(primary: PrimaryAction | null): TrailingHeade return { headerRight: () => ( - - {primary ? ( - + + {onSearchPress ? ( + ({ opacity: pressed ? 0.4 : 1 })} + > + + ) : null} - router.push('/settings')} + actions={[{ id: 'settings', label: t('title'), onPress: () => router.push('/settings') }]} /> ), diff --git a/apps/mobile/src/components/terminal/new-terminal-sheet.ios.tsx b/apps/mobile/src/components/terminal/new-terminal-sheet.ios.tsx new file mode 100644 index 000000000..0a08ea8e8 --- /dev/null +++ b/apps/mobile/src/components/terminal/new-terminal-sheet.ios.tsx @@ -0,0 +1,82 @@ +import { + BottomSheet, + Button, + Form, + Host, + HStack, + Section, + Text, + TextField, + useNativeState, +} from '@expo/ui/swift-ui'; +import { + autocorrectionDisabled, + disabled, + foregroundStyle, + onSubmit, + submitLabel, + textInputAutocapitalization, +} from '@expo/ui/swift-ui/modifiers'; +import { useTranslations } from 'use-intl'; +import type { NewTerminalSheetProps } from './new-terminal-sheet.types'; + +export function NewTerminalSheet({ + isPresented, + onIsPresentedChange, + creating, + error, + onCreate, +}: NewTerminalSheetProps): React.ReactNode { + const t = useTranslations('mobile.terminals'); + const cwd = useNativeState(''); + + const create = () => { + if (creating) return; + void onCreate(cwd.get().trim()).then((created) => { + if (created) cwd.set(''); + }); + }; + + return ( + + +
+ {error ? ( +
+ {t('createError', { error })} +
+ ) : null} + +
+ + {t('cwdLabel')} + + +
+ +
+
+
+
+
+ ); +} diff --git a/apps/mobile/src/components/terminal/new-terminal-sheet.tsx b/apps/mobile/src/components/terminal/new-terminal-sheet.tsx index e3b9c1158..dc151fe12 100644 --- a/apps/mobile/src/components/terminal/new-terminal-sheet.tsx +++ b/apps/mobile/src/components/terminal/new-terminal-sheet.tsx @@ -1,40 +1,32 @@ import { - BottomSheet, Button, - Form, - Host, - HStack, - Section, + Column, + ModalBottomSheet, + OutlinedTextField, Text, - TextField, useNativeState, -} from '@expo/ui/swift-ui'; -import { - autocorrectionDisabled, - disabled, - foregroundStyle, - onSubmit, - submitLabel, - textInputAutocapitalization, -} from '@expo/ui/swift-ui/modifiers'; +} from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, imePadding, padding, testID } from '@expo/ui/jetpack-compose/modifiers'; +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; import { useTranslations } from 'use-intl'; +import type { NewTerminalSheetProps } from './new-terminal-sheet.types'; +/** Android new-terminal sheet. Compose's ModalBottomSheet has no `isPresented` — mounting shows + * it — so this renders nothing while closed and maps dismissal back through the prop. */ export function NewTerminalSheet({ isPresented, onIsPresentedChange, creating, error, onCreate, -}: { - isPresented: boolean; - onIsPresentedChange: (isPresented: boolean) => void; - creating: boolean; - error: string | null; - onCreate: (cwd: string) => Promise; -}): React.ReactNode { +}: NewTerminalSheetProps): React.ReactNode { const t = useTranslations('mobile.terminals'); + const colors = useAppMaterialColors(); const cwd = useNativeState(''); + if (!isPresented) return null; + const create = () => { if (creating) return; void onCreate(cwd.get().trim()).then((created) => { @@ -43,45 +35,43 @@ export function NewTerminalSheet({ }; return ( - - -
+ + onIsPresentedChange(false)}> + + + {t('newTerminal')} + {error ? ( -
- {t('createError', { error })} -
+ + {t('createError', { error })} + ) : null} - -
- + + {t('cwdLabel')} - - -
- -
-
- -
-
+ + + {t('cwdPlaceholder')} + + + + + + ); } diff --git a/apps/mobile/src/components/terminal/new-terminal-sheet.types.ts b/apps/mobile/src/components/terminal/new-terminal-sheet.types.ts new file mode 100644 index 000000000..d9ee6e422 --- /dev/null +++ b/apps/mobile/src/components/terminal/new-terminal-sheet.types.ts @@ -0,0 +1,7 @@ +export interface NewTerminalSheetProps { + isPresented: boolean; + onIsPresentedChange: (isPresented: boolean) => void; + creating: boolean; + error: string | null; + onCreate: (cwd: string) => Promise; +} diff --git a/apps/mobile/src/components/terminal/terminals-screen.ios.tsx b/apps/mobile/src/components/terminal/terminals-screen.ios.tsx new file mode 100644 index 000000000..c3bf6be7e --- /dev/null +++ b/apps/mobile/src/components/terminal/terminals-screen.ios.tsx @@ -0,0 +1,102 @@ +import { Button, ContentUnavailableView, Form, Host, Section, Text } from '@expo/ui/swift-ui'; +import { foregroundStyle, refreshable } from '@expo/ui/swift-ui/modifiers'; +import { repositoryLabel } from '@linkcode/ui/native'; +import { LoadingView } from '@mobile/components/form/loading-view.ios'; +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { NewTerminalSheet } from '@mobile/components/terminal/new-terminal-sheet'; +import { useTerminalInbox } from '@mobile/runtime/use-terminal-inbox'; +import { Platform } from 'react-native'; +import { useTranslations } from 'use-intl'; + +const SECONDARY = foregroundStyle({ type: 'hierarchical', style: 'secondary' }); +const SUPPORTS_CONTENT_UNAVAILABLE_VIEW = + Platform.OS === 'ios' && Number.parseInt(Platform.Version, 10) >= 17; + +/** Host terminal inbox body: attach to a running PTY or start a new one on the host. */ +export function TerminalsScreen({ + sheetOpen, + onSheetOpenChange, +}: { + sheetOpen: boolean; + onSheetOpenChange: (open: boolean) => void; +}): React.ReactNode { + const t = useTranslations('mobile.terminals'); + const { + terminals, + loading, + loadError, + createError, + creating, + onRefresh, + retry, + openTerminal, + onCreate, + } = useTerminalInbox(() => onSheetOpenChange(false)); + + return ( + <> + {/* Form needs the viewport as its proposed size, otherwise it collapses to its content. */} + + {loading ? ( + + ) : loadError && terminals.length === 0 ? ( +
+
+ + {t('loadError', { error: loadError })} + +
+
+ ) : terminals.length === 0 ? ( + SUPPORTS_CONTENT_UNAVAILABLE_VIEW ? ( + + ) : ( +
+
+ {t('emptyHint')} +
+
+ ) + ) : ( +
+ {loadError ? ( +
+ + {t('loadError', { error: loadError })} + +
+ ) : null} +
+ {terminals.map((terminal) => ( + openTerminal(terminal.terminalId)} + /> + ))} +
+
+ )} +
+ { + onSheetOpenChange(open); + }} + creating={creating} + error={createError} + onCreate={onCreate} + /> + + ); +} diff --git a/apps/mobile/src/components/terminal/terminals-screen.tsx b/apps/mobile/src/components/terminal/terminals-screen.tsx new file mode 100644 index 000000000..a6d6bc827 --- /dev/null +++ b/apps/mobile/src/components/terminal/terminals-screen.tsx @@ -0,0 +1,93 @@ +import { Button, LazyColumn, PullToRefreshBox, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, padding } from '@expo/ui/jetpack-compose/modifiers'; +import { repositoryLabel } from '@linkcode/ui/native'; +import { FormList } from '@mobile/components/form/list.android'; +import { LoadingView } from '@mobile/components/form/loading-view.android'; +import { NavigationRow } from '@mobile/components/form/navigation-row'; +import { FormHint } from '@mobile/components/form/rows.android'; +import { ThemedHost } from '@mobile/components/form/themed-host.android'; +import { NewTerminalSheet } from '@mobile/components/terminal/new-terminal-sheet'; +import { useTerminalInbox } from '@mobile/runtime/use-terminal-inbox'; +import { useState } from 'react'; +import { useTranslations } from 'use-intl'; + +/** Android terminal inbox body, mirroring `terminals-screen.ios.tsx` with MD3 list rows and a + * controlled PullToRefreshBox in place of SwiftUI's awaiting `refreshable`. */ +export function TerminalsScreen({ + sheetOpen, + onSheetOpenChange, +}: { + sheetOpen: boolean; + onSheetOpenChange: (open: boolean) => void; +}): React.ReactNode { + const t = useTranslations('mobile.terminals'); + const { + terminals, + loading, + loadError, + createError, + creating, + onRefresh, + retry, + openTerminal, + onCreate, + } = useTerminalInbox(() => onSheetOpenChange(false)); + const [refreshing, setRefreshing] = useState(false); + + const refresh = () => { + setRefreshing(true); + void onRefresh().finally(() => setRefreshing(false)); + }; + + return ( + <> + {loading ? ( + + ) : loadError && terminals.length === 0 ? ( + + {t('loadError', { error: loadError })} + + + ) : ( + + {/* topCenter: see thread-list.tsx — restores the indicator alignment expo-ui drops. */} + + + {loadError ? ( + {t('loadError', { error: loadError })} + ) : null} + {terminals.length === 0 ? ( + {t('emptyHint')} + ) : ( + terminals.map((terminal) => ( + openTerminal(terminal.terminalId)} + /> + )) + )} + + + + )} + + + ); +} diff --git a/apps/mobile/src/components/theme/native-palette.ios.ts b/apps/mobile/src/components/theme/native-palette.ios.ts new file mode 100644 index 000000000..857577af8 --- /dev/null +++ b/apps/mobile/src/components/theme/native-palette.ios.ts @@ -0,0 +1,24 @@ +import type { NativePalette } from '@mobile/components/theme/native-palette.types'; +import { Color } from 'expo-router'; + +/** iOS resolves the palette from UIKit semantic colors — `PlatformColor` values that adapt to + * light/dark natively, so no hook state is needed. White-on-blue matches UIKit's own filled + * controls, which never invert their glyph. */ +const IOS_PALETTE: NativePalette = { + background: Color.ios.systemBackground, + groupedBackground: Color.ios.systemGroupedBackground, + surface: Color.ios.secondarySystemBackground, + text: Color.ios.label, + textSecondary: Color.ios.secondaryLabel, + tint: Color.ios.systemBlue, + onTint: '#FFFFFF', + outline: Color.ios.separator, + danger: Color.ios.systemRed, + success: Color.ios.systemGreen, + warning: Color.ios.systemOrange, +}; + +// eslint-disable-next-line @eslint-react/no-unnecessary-use-prefix -- the Android twin resolves its palette through hooks; the shared contract keeps the hook name +export function useNativePalette(): NativePalette { + return IOS_PALETTE; +} diff --git a/apps/mobile/src/components/theme/native-palette.ts b/apps/mobile/src/components/theme/native-palette.ts new file mode 100644 index 000000000..6ee54d4df --- /dev/null +++ b/apps/mobile/src/components/theme/native-palette.ts @@ -0,0 +1,22 @@ +import { useAppMaterialColors } from '@mobile/components/form/compose-theme.android'; +import type { NativePalette } from '@mobile/components/theme/native-palette.types'; + +/** Android resolves the palette from Material You dynamic roles (the same scheme the Compose + * surfaces render), pinned to the app theme. */ +export function useNativePalette(): NativePalette { + const colors = useAppMaterialColors(); + + return { + background: colors.surface, + groupedBackground: colors.surface, + surface: colors.surfaceContainerHigh, + text: colors.onSurface, + textSecondary: colors.onSurfaceVariant, + tint: colors.primary, + onTint: colors.onPrimary, + outline: colors.outlineVariant, + danger: colors.error, + success: colors.primary, + warning: colors.tertiary, + }; +} diff --git a/apps/mobile/src/components/theme/native-palette.types.ts b/apps/mobile/src/components/theme/native-palette.types.ts new file mode 100644 index 000000000..d9595b3e1 --- /dev/null +++ b/apps/mobile/src/components/theme/native-palette.types.ts @@ -0,0 +1,25 @@ +import type { ColorValue } from 'react-native'; + +/** Semantic palette bridging RN-drawn surfaces to each platform's NATIVE colors: adaptive + * `PlatformColor` system colors on iOS, Material You dynamic roles on Android. Components take + * roles from here instead of the app's own brand tokens. */ +export interface NativePalette { + /** Screens and navigation chrome. */ + background: ColorValue; + /** Grouped list/form screens, and the load states that stand in for them. */ + groupedBackground: ColorValue; + /** Raised cards: composer, bubbles, prompt cards. */ + surface: ColorValue; + text: ColorValue; + textSecondary: ColorValue; + /** Interactive accent (send, header tint). */ + tint: ColorValue; + /** Content drawn on top of `tint`. */ + onTint: ColorValue; + /** Hairline borders and idle indicators. */ + outline: ColorValue; + danger: ColorValue; + /** Status accents; Android maps to primary/tertiary — MD3 has no success/warning roles. */ + success: ColorValue; + warning: ColorValue; +} diff --git a/apps/mobile/src/components/theme/use-color-scheme.ts b/apps/mobile/src/components/theme/use-color-scheme.ts new file mode 100644 index 000000000..eee45843e --- /dev/null +++ b/apps/mobile/src/components/theme/use-color-scheme.ts @@ -0,0 +1,9 @@ +import { useSettingsStore } from '@mobile/stores/settings-store'; +import { useColorScheme } from 'react-native'; + +export function useResolvedColorScheme(): 'light' | 'dark' { + const preference = useSettingsStore((state) => state.themePreference); + const system = useColorScheme(); + if (preference === 'system') return system === 'dark' ? 'dark' : 'light'; + return preference; +} diff --git a/apps/mobile/src/runtime/create-host-transport.ts b/apps/mobile/src/runtime/create-host-transport.ts index d213c4c44..dcbe064b0 100644 --- a/apps/mobile/src/runtime/create-host-transport.ts +++ b/apps/mobile/src/runtime/create-host-transport.ts @@ -3,10 +3,14 @@ import { SocketIoTransport, TunnelTransport, WsTransport } from '@linkcode/trans import type { HostProfile } from '@mobile/stores/host-store'; import { CLOUD_URL, fetchTunnelJwt } from './cloud/client'; +type MockModule = typeof import('@linkcode/client-core/mock'); + /** * Transport by host kind: tunnel entries dial the cloud relay; direct entries pick by * URL scheme — http(s) is the daemon's default Socket.IO listener (same as - * webview/desktop), ws(s) a raw WebSocket listener. + * webview/desktop), ws(s) a raw WebSocket listener, and `mock:` the in-process mock host + * (webview's `dev:mock` showcase). Dev builds only: the `__DEV__` guard keeps Metro from + * bundling the mock into release, where the throw surfaces as a plain connection failure. */ export function createHostTransport(host: HostProfile): Transport { if ('tunnelHostId' in host) { @@ -18,6 +22,14 @@ export function createHostTransport(host: HostProfile): Transport { }); } const { protocol } = new URL(host.url); + if (protocol === 'mock:') { + if (__DEV__) { + // eslint-disable-next-line @typescript-eslint/no-require-imports -- static import would bundle the mock into release builds + const mock = require('@linkcode/client-core/mock') as MockModule; + return mock.createDevMockTransport(); + } + throw new Error('mock:// hosts are only available in development builds'); + } return protocol === 'ws:' || protocol === 'wss:' ? new WsTransport({ url: host.url }) : new SocketIoTransport({ url: host.url }); diff --git a/apps/mobile/src/runtime/use-add-host.ts b/apps/mobile/src/runtime/use-add-host.ts new file mode 100644 index 000000000..2435188d7 --- /dev/null +++ b/apps/mobile/src/runtime/use-add-host.ts @@ -0,0 +1,39 @@ +import { HostUrlSchema, useHostRegistryStore } from '@mobile/stores/host-store'; +import { useRouter } from 'expo-router'; +import { useState } from 'react'; +import { useTranslations } from 'use-intl'; + +/** Validation + submit flow of the add-host form. Field text stays in each platform view's own + * `useNativeState` (the shared `@expo/ui` State export is the web polyfill, typed without + * `.get()`), so submit takes the values instead of owning them. */ +export function useAddHost(): { + urlInvalid: boolean; + urlValid: boolean; + onUrlChange: (text: string) => void; + submit: (rawName: string, rawUrl: string) => void; +} { + const t = useTranslations('mobile.connect'); + const router = useRouter(); + const addHost = useHostRegistryStore((state) => state.addHost); + const setLastActiveHostId = useHostRegistryStore((state) => state.setLastActiveHostId); + const [urlInvalid, setUrlInvalid] = useState(false); + const [urlValid, setUrlValid] = useState(false); + + const onUrlChange = (text: string) => { + setUrlInvalid(false); + setUrlValid(HostUrlSchema.safeParse(text.trim()).success); + }; + + const submit = (rawName: string, rawUrl: string) => { + const trimmedUrl = rawUrl.trim(); + if (!HostUrlSchema.safeParse(trimmedUrl).success) { + setUrlInvalid(true); + return; + } + const profile = addHost({ name: rawName.trim() || t('namePlaceholder'), url: trimmedUrl }); + setLastActiveHostId(profile.id); + router.dismissTo('/threads'); + }; + + return { urlInvalid, urlValid, onUrlChange, submit }; +} diff --git a/apps/mobile/src/runtime/use-delete-account.ts b/apps/mobile/src/runtime/use-delete-account.ts new file mode 100644 index 000000000..b3edbbf91 --- /dev/null +++ b/apps/mobile/src/runtime/use-delete-account.ts @@ -0,0 +1,80 @@ +import { deleteAccount, runAccountDeletionTeardown } from '@mobile/runtime/cloud/deletion'; +import { useState } from 'react'; +import { Alert } from 'react-native'; +import { useTranslations } from 'use-intl'; + +export interface DeleteAccountState { + busy: boolean; + confirmDelete: () => void; +} + +/** Shared deletion flow for both platform account screens. Confirmation is an RN `Alert` + * because that already is the native alert on iOS and Android. */ +export function useDeleteAccount(): DeleteAccountState { + const t = useTranslations('mobile.account'); + const [busy, setBusy] = useState(false); + + const failureMessage = (code: string | undefined): string => { + if (code === 'ACCOUNT_DELETION_EMERGENCY_AUDIT_HOLD') return t('deleteEmergencyHold'); + if (code === 'ACCOUNT_DELETION_SOLE_ORGANIZATION_OWNER') return t('deleteSoleOwner'); + return t('deleteFailed'); + }; + + const run = async () => { + setBusy(true); + try { + const outcome = await deleteAccount(); + if (outcome.kind === 'reauthentication-failed') { + Alert.alert(t('deleteReauthenticationFailed')); + return; + } + if (outcome.kind === 'apple-device-required') { + Alert.alert(t('deleteAppleDeviceRequired')); + return; + } + if (outcome.kind === 'account-mismatch') { + Alert.alert(t('deleteAccountMismatch')); + return; + } + if (outcome.kind === 'unknown') { + Alert.alert(t('deleteUnknown')); + return; + } + if (outcome.kind === 'failed') { + Alert.alert(failureMessage(outcome.code)); + return; + } + + // Both remaining outcomes (`pending` and `completed`) mean the server + // already accepted the deletion — local teardown runs regardless. + await runAccountDeletionTeardown(); + if (outcome.kind === 'pending') { + Alert.alert(t('deletePending')); + return; + } + // A failed revocation still leaves deletion successful and needs manual Apple follow-up. + if (outcome.authorizationRevocation === 'failed') { + Alert.alert(t('deleteRevocationFailedTitle'), t('deleteRevocationFailedMessage')); + } else { + Alert.alert(t('deleteCompleted')); + } + } finally { + setBusy(false); + } + }; + + const confirmDelete = () => { + Alert.alert(t('deleteTitle'), t('deleteMessage'), [ + { text: t('deleteCancel'), style: 'cancel' }, + { + text: t('deleteConfirm'), + style: 'destructive', + onPress() { + void run(); + }, + }, + ]); + }; + + return { busy, confirmDelete }; +} diff --git a/apps/mobile/src/runtime/use-devices-section.ts b/apps/mobile/src/runtime/use-devices-section.ts new file mode 100644 index 000000000..54df8c2f8 --- /dev/null +++ b/apps/mobile/src/runtime/use-devices-section.ts @@ -0,0 +1,91 @@ +import { signOutOfCloud } from '@mobile/runtime/cloud/account'; +import type { CloudDevice } from '@mobile/runtime/cloud/devices'; +import { fetchDevices, getEnrolledDeviceId, revokeDevice } from '@mobile/runtime/cloud/devices'; +import { formatRelativeShort } from '@mobile/utils/relative-time'; +import { noop } from 'foxact/noop'; +import { useCallback, useEffect, useState } from 'react'; +import { Alert } from 'react-native'; +import { useTranslations } from 'use-intl'; + +export interface DevicesSectionState { + devices: CloudDevice[] | null; + devicesError: boolean; + enrolledId: string | null; + busyId: string | null; + refresh: () => void; + confirmRevoke: (device: CloudDevice) => void; + describeDevice: (device: CloudDevice) => string; +} + +/** View-model of the account's device registry, shared by both platform views. Revoking cuts + * access to new tunnel tokens; revoking this phone also signs it out (the cloud kills its + * sessions). Confirmation stays an RN `Alert` — that already is the native alert on both. */ +export function useDevicesSection(): DevicesSectionState { + const t = useTranslations('mobile.account'); + + const [devices, setDevices] = useState(null); + const [devicesError, setDevicesError] = useState(false); + const [enrolledId, setEnrolledId] = useState(null); + const [busyId, setBusyId] = useState(null); + + const load = useCallback(() => { + fetchDevices() + .then(setDevices) + .catch(() => setDevicesError(true)); + }, []); + + useEffect(() => { + getEnrolledDeviceId().then(setEnrolledId).catch(noop); + load(); + }, [load]); + + const refresh = () => { + setDevicesError(false); + setDevices(null); + load(); + }; + + const revoke = async (device: CloudDevice) => { + setBusyId(device.id); + try { + await revokeDevice(device.id); + if (device.id === enrolledId) { + // The device revoke already removed its push token and killed this phone's sessions. + await signOutOfCloud({ revokePushToken: false }).catch(noop); + return; + } + refresh(); + } catch { + Alert.alert(t('revokeError')); + } finally { + setBusyId(null); + } + }; + + const confirmRevoke = (device: CloudDevice) => { + Alert.alert( + t('revokeTitle', { name: device.name }), + device.id === enrolledId ? t('revokeThisDeviceMessage') : t('revokeMessage'), + [ + { text: t('revokeCancel'), style: 'cancel' }, + { + text: t('revoke'), + style: 'destructive', + onPress() { + void revoke(device); + }, + }, + ], + ); + }; + + const describeDevice = (device: CloudDevice): string => { + const kind = t(`deviceKind.${device.kind}`); + const platform = device.platform ? `${kind} · ${device.platform}` : kind; + return device.lastSeenAt + ? `${platform} · ${t('lastSeen', { time: formatRelativeShort(new Date(device.lastSeenAt).getTime()) })}` + : platform; + }; + + return { devices, devicesError, enrolledId, busyId, refresh, confirmRevoke, describeDevice }; +} diff --git a/apps/mobile/src/runtime/use-discovered-hosts.ts b/apps/mobile/src/runtime/use-discovered-hosts.ts new file mode 100644 index 000000000..c163f8d0f --- /dev/null +++ b/apps/mobile/src/runtime/use-discovered-hosts.ts @@ -0,0 +1,27 @@ +import { canonicalDirectHostUrl } from '@mobile/runtime/daemon-discovery'; +import { useDaemonDiscovery } from '@mobile/runtime/use-daemon-discovery'; +import { useOpenHost } from '@mobile/runtime/use-open-host'; +import { useHostRegistryStore } from '@mobile/stores/host-store'; + +/** Discovered LAN daemons minus the ones already saved, plus the save-and-open action — + * shared by both platform views of the discovered-hosts section. */ +export function useDiscoveredHosts() { + const { hosts, status } = useDaemonDiscovery(); + const openHost = useOpenHost(); + const savedHosts = useHostRegistryStore((state) => state.hosts); + const addHost = useHostRegistryStore((state) => state.addHost); + + const savedUrls = new Set(); + for (let i = 0, len = savedHosts.length; i < len; i++) { + const host = savedHosts[i]; + if ('url' in host) savedUrls.add(canonicalDirectHostUrl(host.url)); + } + const discoveredHosts = hosts.filter((host) => !savedUrls.has(canonicalDirectHostUrl(host.url))); + + const saveAndOpen = (host: (typeof discoveredHosts)[number]) => { + const profile = addHost({ name: host.name, url: host.url }); + openHost(profile.id); + }; + + return { discoveredHosts, status, saveAndOpen }; +} diff --git a/apps/mobile/src/runtime/use-my-machines.ts b/apps/mobile/src/runtime/use-my-machines.ts new file mode 100644 index 000000000..48091de72 --- /dev/null +++ b/apps/mobile/src/runtime/use-my-machines.ts @@ -0,0 +1,53 @@ +import { ensureDeviceRegistered } from '@mobile/runtime/cloud/devices'; +import type { OnlineHost } from '@mobile/runtime/cloud/hosts'; +import { fetchOnlineHosts } from '@mobile/runtime/cloud/hosts'; +import { useOpenHost } from '@mobile/runtime/use-open-host'; +import { useHostRegistryStore } from '@mobile/stores/host-store'; +import { noop } from 'foxact/noop'; +import { useCallback, useEffect, useState } from 'react'; + +export interface MyMachinesState { + onlineHosts: OnlineHost[] | null; + hostsError: boolean; + refresh: () => void; + saveAndOpen: (host: OnlineHost) => void; +} + +/** Online machines (daemons connected to the relay), shared by both platform views. Tapping one + * saves it as a tunnel host and opens it. */ +export function useMyMachines(userId: string): MyMachinesState { + const openHost = useOpenHost(); + const addTunnelHost = useHostRegistryStore((state) => state.addTunnelHost); + + const [onlineHosts, setOnlineHosts] = useState(null); + const [hostsError, setHostsError] = useState(false); + + const load = useCallback(() => { + fetchOnlineHosts() + .then(setOnlineHosts) + .catch(() => setHostsError(true)); + }, []); + + const refresh = () => { + setHostsError(false); + setOnlineHosts(null); + load(); + }; + + useEffect(() => { + // Best-effort: registration only lists the phone under the account's + // devices; discovering and connecting to hosts does not depend on it. + ensureDeviceRegistered(userId).catch(noop); + load(); + }, [userId, load]); + + const saveAndOpen = (host: OnlineHost) => { + const profile = addTunnelHost({ + name: host.name ?? host.hostId.slice(0, 8), + tunnelHostId: host.hostId, + }); + openHost(profile.id); + }; + + return { onlineHosts, hostsError, refresh, saveAndOpen }; +} diff --git a/apps/mobile/src/runtime/use-notification-settings.ts b/apps/mobile/src/runtime/use-notification-settings.ts new file mode 100644 index 000000000..31d780252 --- /dev/null +++ b/apps/mobile/src/runtime/use-notification-settings.ts @@ -0,0 +1,46 @@ +import { useCloudAccount } from '@mobile/runtime/cloud/account'; +import { + disableDeviceNotifications, + enableDeviceNotifications, +} from '@mobile/runtime/notifications'; +import { useSettingsStore } from '@mobile/stores/settings-store'; +import { useRef, useState } from 'react'; +import { Alert, Linking } from 'react-native'; +import { useTranslations } from 'use-intl'; + +export function useNotificationSettings() { + const t = useTranslations('mobile.settings'); + const account = useCloudAccount(); + const enabled = useSettingsStore((state) => state.notificationsEnabled); + const [pending, setPending] = useState(false); + const pendingRef = useRef(false); + + const update = async (enabled: boolean) => { + if (account.status !== 'signed-in' || pendingRef.current) return; + pendingRef.current = true; + setPending(true); + try { + if (!enabled) { + await disableDeviceNotifications(); + return; + } + if (await enableDeviceNotifications(account.user.id)) return; + Alert.alert(t('notificationsDeniedTitle'), t('notificationsDenied'), [ + { text: t('cancel'), style: 'cancel' }, + { + text: t('openSettings'), + onPress() { + void Linking.openSettings(); + }, + }, + ]); + } catch { + Alert.alert(t('notificationsErrorTitle'), t('notificationsError')); + } finally { + pendingRef.current = false; + setPending(false); + } + }; + + return { enabled, canUpdate: account.status === 'signed-in' && !pending, update }; +} diff --git a/apps/mobile/src/runtime/use-terminal-inbox.ts b/apps/mobile/src/runtime/use-terminal-inbox.ts new file mode 100644 index 000000000..47a454422 --- /dev/null +++ b/apps/mobile/src/runtime/use-terminal-inbox.ts @@ -0,0 +1,110 @@ +import { useLinkCodeClient } from '@linkcode/client-core'; +import type { TerminalMetadata } from '@linkcode/schema'; +import { useFocusEffect, useRouter } from 'expo-router'; +import { extractErrorMessage } from 'foxts/extract-error-message'; +import { useCallback, useState } from 'react'; + +const INITIAL_TERMINAL_SIZE = { cols: 80, rows: 24 }; + +export interface TerminalInboxState { + terminals: TerminalMetadata[]; + loading: boolean; + loadError: string | null; + createError: string | null; + creating: boolean; + /** Pull-to-refresh driver; the platform view holds its spinner until this resolves. */ + onRefresh: () => Promise; + /** Full-screen retry after a load error: re-enters the loading state around a refresh. */ + retry: () => void; + openTerminal: (terminalId: string, takeControl?: boolean) => void; + onCreate: (cwd: string) => Promise; +} + +/** The terminal inbox view model: attach to a running PTY or start a new one on the host. + * `onCreated` closes whatever surface collected the cwd (the new-terminal sheet). */ +export function useTerminalInbox(onCreated: () => void): TerminalInboxState { + const router = useRouter(); + const client = useLinkCodeClient(); + const [terminals, setTerminals] = useState([]); + const [loading, setLoading] = useState(true); + const [loadError, setLoadError] = useState(null); + const [createError, setCreateError] = useState(null); + const [creating, setCreating] = useState(false); + + const load = useCallback( + async () => (await client.listTerminals()).sort((a, b) => b.createdAt - a.createdAt), + [client], + ); + + useFocusEffect( + useCallback(() => { + let active = true; + setLoadError(null); + void load() + .then((nextTerminals) => { + if (active) setTerminals(nextTerminals); + }) + .catch((error_: unknown) => { + if (active) setLoadError(extractErrorMessage(error_, false) ?? 'Unknown error'); + }) + .finally(() => { + if (active) setLoading(false); + }); + return () => { + active = false; + }; + }, [load]), + ); + + const onRefresh = async () => { + setLoadError(null); + try { + setTerminals(await load()); + } catch (error_) { + setLoadError(extractErrorMessage(error_, false) ?? 'Unknown error'); + } + }; + + const retry = () => { + setLoading(true); + void onRefresh().finally(() => setLoading(false)); + }; + + const openTerminal = (terminalId: string, takeControl = false) => { + const query = takeControl ? '?takeover=1' : ''; + router.push(`/terminal/${encodeURIComponent(terminalId)}${query}`); + }; + + const onCreate = async (cwd: string): Promise => { + if (creating) return false; + setCreating(true); + setCreateError(null); + try { + const terminalId = await client.openTerminal({ + ...INITIAL_TERMINAL_SIZE, + cwd: cwd || undefined, + }); + client.detachTerminal(terminalId); + onCreated(); + openTerminal(terminalId, true); + return true; + } catch (error_) { + setCreateError(extractErrorMessage(error_, false) ?? 'Unknown error'); + return false; + } finally { + setCreating(false); + } + }; + + return { + terminals, + loading, + loadError, + createError, + creating, + onRefresh, + retry, + openTerminal, + onCreate, + }; +} diff --git a/apps/mobile/src/runtime/use-thread-inbox.ts b/apps/mobile/src/runtime/use-thread-inbox.ts new file mode 100644 index 000000000..5e436234a --- /dev/null +++ b/apps/mobile/src/runtime/use-thread-inbox.ts @@ -0,0 +1,50 @@ +import { useSessions } from '@linkcode/client-core'; +import type { ThreadGroup } from '@linkcode/ui/native'; +import { + groupThreadsByWorkspace, + repositoryLabel, + withoutAutomationSessions, +} from '@linkcode/ui/native'; +import { useWorkspaces } from '@mobile/runtime/use-workspaces'; +import { threadTitle } from '@mobile/utils/thread-title'; +import { useTranslations } from 'use-intl'; + +/** The threads inbox view model: sessions grouped by workspace, filtered by the search query. + * The query is owned by the route — on Android it feeds the toolbar search field, on iOS the + * native search bar. Empty workspace groups are hidden — the new-thread page is where they + * surface. */ +export function useThreadInbox(query: string): { + loading: boolean; + groups: ThreadGroup[]; + hasQuery: boolean; + groupLabel: (group: ThreadGroup) => string; + onRefresh: () => Promise; +} { + const t = useTranslations('mobile.sessions'); + const { sessions, refresh, loading } = useSessions(); + const { workspaces, refresh: refreshWorkspaces } = useWorkspaces(); + + const needle = query.trim().toLowerCase(); + const groups = groupThreadsByWorkspace(withoutAutomationSessions(sessions), workspaces).reduce< + ThreadGroup[] + >((kept, group) => { + const matched = + needle === '' + ? group.sessions + : group.sessions.filter((session) => threadTitle(session).toLowerCase().includes(needle)); + if (matched.length > 0) kept.push({ ...group, sessions: matched }); + return kept; + }, []); + + const groupLabel = (group: ThreadGroup): string => { + if (group.isChat) return t('chats'); + if (!group.workspace) return t('otherThreads'); + return group.workspace.name ?? repositoryLabel(group.workspace.cwd); + }; + + const onRefresh = async () => { + await Promise.all([refresh(), refreshWorkspaces()]); + }; + + return { loading, groups, hasQuery: needle !== '', groupLabel, onRefresh }; +} diff --git a/apps/mobile/src/stores/host-store.ts b/apps/mobile/src/stores/host-store.ts index 637f7ca8c..1d06cb2d7 100644 --- a/apps/mobile/src/stores/host-store.ts +++ b/apps/mobile/src/stores/host-store.ts @@ -5,9 +5,12 @@ import { z } from 'zod'; import { create } from 'zustand'; import { createJSONStorage } from 'zustand/middleware'; -const HOST_URL_PROTOCOLS = new Set(['http:', 'https:', 'ws:', 'wss:']); +// `mock:` stays accepted in release schemas so a dev-added mock host never poisons the persisted +// registry blob; its transport is what refuses to dial outside dev builds. +const HOST_URL_PROTOCOLS = new Set(['http:', 'https:', 'ws:', 'wss:', 'mock:']); -/** http(s) dials the daemon's Socket.IO listener; ws(s) dials a raw WebSocket listener. */ +/** http(s) dials the daemon's Socket.IO listener; ws(s) dials a raw WebSocket listener; `mock:` + * runs the in-process mock host (dev builds only). */ export const HostUrlSchema = z.string().refine((value) => { try { return HOST_URL_PROTOCOLS.has(new URL(value).protocol); diff --git a/apps/mobile/src/utils/thread-title.ts b/apps/mobile/src/utils/thread-title.ts new file mode 100644 index 000000000..6b34e7192 --- /dev/null +++ b/apps/mobile/src/utils/thread-title.ts @@ -0,0 +1,7 @@ +import type { SessionInfo } from '@linkcode/schema'; +import { AGENT_LABELS, repositoryLabel } from '@linkcode/ui/native'; + +/** The title a thread is listed and searched under — the same fallback the row renders. */ +export function threadTitle(session: SessionInfo): string { + return session.title ?? `${AGENT_LABELS[session.kind]} in ${repositoryLabel(session.cwd)}`; +} diff --git a/devenv.nix b/devenv.nix index 89349230f..e08d7fad8 100644 --- a/devenv.nix +++ b/devenv.nix @@ -13,7 +13,7 @@ # Mobile UI e2e drives the iOS simulator, which only exists on macOS; Linux CI would pull the # JVM closure for nothing. Pinned here rather than installed per-machine so the driver version # is part of the toolchain like every other tool. - ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ pkgs.maestro ]; + ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ pkgs.maestro pkgs.cocoapods ]; languages.rust = { enable = true; @@ -86,7 +86,7 @@ scripts.mobile.exec = '' export PATH="/usr/bin:$PATH" SENTRY_DISABLE_AUTO_UPLOAD=true unset DEVELOPER_DIR SDKROOT CC CXX LD NIX_CFLAGS_COMPILE NIX_LDFLAGS MACOSX_DEPLOYMENT_TARGET - pnpm run --filter @linkcode/mobile ios + pnpm run --filter @linkcode/mobile ios "$@" ''; scripts.app.exec = "pnpm run --filter @linkcode/daemon build:rust && pnpm --filter @linkcode/daemon --filter @linkcode/desktop --parallel dev"; } diff --git a/packages/client/core/package.json b/packages/client/core/package.json index f9c6fd31e..8b963b845 100644 --- a/packages/client/core/package.json +++ b/packages/client/core/package.json @@ -6,7 +6,8 @@ "main": "./src/index.ts", "types": "./src/index.ts", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./mock": "./src/mock/dev-mock-transport.ts" }, "scripts": { "lint": "eslint --format=sukka ." diff --git a/packages/client/workbench/src/mock/data/commands.ts b/packages/client/core/src/mock/data/commands.ts similarity index 100% rename from packages/client/workbench/src/mock/data/commands.ts rename to packages/client/core/src/mock/data/commands.ts diff --git a/packages/client/workbench/src/mock/data/files.ts b/packages/client/core/src/mock/data/files.ts similarity index 100% rename from packages/client/workbench/src/mock/data/files.ts rename to packages/client/core/src/mock/data/files.ts diff --git a/packages/client/workbench/src/mock/data/git.ts b/packages/client/core/src/mock/data/git.ts similarity index 100% rename from packages/client/workbench/src/mock/data/git.ts rename to packages/client/core/src/mock/data/git.ts diff --git a/packages/client/workbench/src/mock/data/history.ts b/packages/client/core/src/mock/data/history.ts similarity index 100% rename from packages/client/workbench/src/mock/data/history.ts rename to packages/client/core/src/mock/data/history.ts diff --git a/packages/client/workbench/src/mock/data/long-thread.ts b/packages/client/core/src/mock/data/long-thread.ts similarity index 100% rename from packages/client/workbench/src/mock/data/long-thread.ts rename to packages/client/core/src/mock/data/long-thread.ts diff --git a/packages/client/workbench/src/mock/data/models.ts b/packages/client/core/src/mock/data/models.ts similarity index 100% rename from packages/client/workbench/src/mock/data/models.ts rename to packages/client/core/src/mock/data/models.ts diff --git a/packages/client/workbench/src/mock/data/plugins.ts b/packages/client/core/src/mock/data/plugins.ts similarity index 100% rename from packages/client/workbench/src/mock/data/plugins.ts rename to packages/client/core/src/mock/data/plugins.ts diff --git a/packages/client/workbench/src/mock/data/prompt.ts b/packages/client/core/src/mock/data/prompt.ts similarity index 100% rename from packages/client/workbench/src/mock/data/prompt.ts rename to packages/client/core/src/mock/data/prompt.ts diff --git a/packages/client/workbench/src/mock/data/scripts.ts b/packages/client/core/src/mock/data/scripts.ts similarity index 100% rename from packages/client/workbench/src/mock/data/scripts.ts rename to packages/client/core/src/mock/data/scripts.ts diff --git a/packages/client/workbench/src/mock/data/sessions.ts b/packages/client/core/src/mock/data/sessions.ts similarity index 100% rename from packages/client/workbench/src/mock/data/sessions.ts rename to packages/client/core/src/mock/data/sessions.ts diff --git a/packages/client/workbench/src/mock/data/showcase.ts b/packages/client/core/src/mock/data/showcase.ts similarity index 98% rename from packages/client/workbench/src/mock/data/showcase.ts rename to packages/client/core/src/mock/data/showcase.ts index 84d811647..763552d17 100644 --- a/packages/client/workbench/src/mock/data/showcase.ts +++ b/packages/client/core/src/mock/data/showcase.ts @@ -126,7 +126,7 @@ export const SHOWCASE_PLAN: Plan = { export const SHOWCASE_PERMISSION_DIFF: Extract = { type: 'diff', - path: 'packages/client/workbench/src/mock/dev-mock-transport.ts', + path: 'packages/client/core/src/mock/dev-mock-transport.ts', oldText: "const coverage = 'thin';\n", newText: "const coverage = 'rich';\n", }; @@ -271,7 +271,7 @@ export const SHOWCASE_STREAM_START_DELAY_MS = 1000; export const SHOWCASE_STREAM_CHUNK_LATENCY_MS = 220; export const SHOWCASE_TERMINAL_START_OUTPUT = - '$ pnpm vitest run packages/client/workbench/src/mock\n' + + '$ pnpm vitest run packages/client/core/src/mock\n' + '\u{1B}[32mPASS\u{1B}[0m dev mock transport (4)\n' + '\u{1B}[33mWARN\u{1B}[0m retry fixture exercised\n' + '\u{1B}[31mFAIL\u{1B}[0m expected negative-path fixture\n'; @@ -552,7 +552,7 @@ export function createShowcaseToolBursts(terminalId = SHOWCASE_TERMINAL_ID): Sho kind: 'execute', status: 'completed', content: [{ type: 'terminal', terminalId }], - rawInput: { command: 'pnpm vitest run packages/client/workbench/src/mock' }, + rawInput: { command: 'pnpm vitest run packages/client/core/src/mock' }, }, { toolCallId: 'mock-tool-execute-lint', @@ -781,11 +781,11 @@ export function createShowcaseToolBursts(terminalId = SHOWCASE_TERMINAL_ID): Sho title: 'Move activity fixture', kind: 'move', status: 'completed', - locations: [{ path: 'packages/client/workbench/src/mock/data/showcase.ts' }], + locations: [{ path: 'packages/client/core/src/mock/data/showcase.ts' }], content: [], rawInput: { - path: 'packages/client/workbench/src/mock/data/activity.ts', - move_path: 'packages/client/workbench/src/mock/data/showcase.ts', + path: 'packages/client/core/src/mock/data/activity.ts', + move_path: 'packages/client/core/src/mock/data/showcase.ts', }, }, { diff --git a/packages/client/workbench/src/mock/dev-mock-host.ts b/packages/client/core/src/mock/dev-mock-host.ts similarity index 99% rename from packages/client/workbench/src/mock/dev-mock-host.ts rename to packages/client/core/src/mock/dev-mock-host.ts index dd9155e0c..d7e48564c 100644 --- a/packages/client/workbench/src/mock/dev-mock-host.ts +++ b/packages/client/core/src/mock/dev-mock-host.ts @@ -561,7 +561,7 @@ export class DevMockHost { ? { status: 'conflict', files: [ - { path: 'packages/client/workbench/src/mock.ts', additions: 12, deletions: 4 }, + { path: 'packages/client/core/src/mock.ts', additions: 12, deletions: 4 }, { path: 'packages/presentation/ui/src/shell.tsx', additions: 3, deletions: 1 }, ], } diff --git a/packages/client/workbench/src/mock/dev-mock-transport.ts b/packages/client/core/src/mock/dev-mock-transport.ts similarity index 100% rename from packages/client/workbench/src/mock/dev-mock-transport.ts rename to packages/client/core/src/mock/dev-mock-transport.ts diff --git a/packages/client/workbench/tests/integration/dev-mock-transport.test.ts b/packages/client/core/tests/integration/dev-mock-transport.test.ts similarity index 99% rename from packages/client/workbench/tests/integration/dev-mock-transport.test.ts rename to packages/client/core/tests/integration/dev-mock-transport.test.ts index 3febd842e..2540b2b97 100644 --- a/packages/client/workbench/tests/integration/dev-mock-transport.test.ts +++ b/packages/client/core/tests/integration/dev-mock-transport.test.ts @@ -1,4 +1,3 @@ -import { LinkCodeClient } from '@linkcode/client-core'; import type { Accounts, AgentEvent, @@ -10,6 +9,7 @@ import type { import { nullthrow } from 'foxts/guard'; import { wait } from 'foxts/wait'; import { describe, expect, it } from 'vitest'; +import { LinkCodeClient } from '../../src'; import { createDevMockTransport } from '../../src/mock/dev-mock-transport'; const rBlobUrl = /^blob:/; @@ -636,7 +636,7 @@ describe('dev mock transport', () => { true, ); expect(tools.some((tool) => tool.status === 'failed')).toBe(true); - expect(terminalOutput).toContain('pnpm vitest run packages/client/workbench/src/mock'); + expect(terminalOutput).toContain('pnpm vitest run packages/client/core/src/mock'); expect(terminalOutput).toContain('mock terminal stream finished'); const streamChunks = events.filter( diff --git a/packages/client/workbench/src/runtime/daemon-transport.ts b/packages/client/workbench/src/runtime/daemon-transport.ts index 50d7d2a59..e8046e62f 100644 --- a/packages/client/workbench/src/runtime/daemon-transport.ts +++ b/packages/client/workbench/src/runtime/daemon-transport.ts @@ -1,6 +1,6 @@ +import { createDevMockTransport } from '@linkcode/client-core/mock'; import type { Transport } from '@linkcode/transport'; import { SocketIoTransport } from '@linkcode/transport'; -import { createDevMockTransport } from '../mock/dev-mock-transport'; /** * Construct the transport the workbench data plane rides on. `--mode mock` (dev only) swaps the diff --git a/packages/presentation/i18n/src/locales/en.ts b/packages/presentation/i18n/src/locales/en.ts index 7ca194b4b..58d591fff 100644 --- a/packages/presentation/i18n/src/locales/en.ts +++ b/packages/presentation/i18n/src/locales/en.ts @@ -1407,6 +1407,8 @@ export const en = { }, chat: { allowTitle: 'Allow "{title}"?', + answer: 'Answer', + close: 'Close', skip: 'Skip', next: 'Next', submitAnswers: 'Submit answers', @@ -1419,7 +1421,6 @@ export const en = { cancel: 'Cancel', }, connection: { - connecting: 'Connecting to the host…', unavailableTitle: 'Host unavailable', error: 'Unable to reach the host at {url}.', retry: 'Retry', diff --git a/packages/presentation/i18n/src/locales/zh-cn.ts b/packages/presentation/i18n/src/locales/zh-cn.ts index badd487a0..60ec1ca2d 100644 --- a/packages/presentation/i18n/src/locales/zh-cn.ts +++ b/packages/presentation/i18n/src/locales/zh-cn.ts @@ -1362,6 +1362,8 @@ export const zhCN = { compactedTokens: '{pre} → {post} tokens', }, chat: { + answer: '回答', + close: '关闭', allowTitle: '允许「{title}」?', skip: '跳过', next: '下一步', @@ -1375,7 +1377,6 @@ export const zhCN = { cancel: '取消', }, connection: { - connecting: '正在连接 host…', unavailableTitle: '无法连接 host', error: '无法连接 host({url})。', retry: '重试', diff --git a/packages/presentation/ui/src/chat/question-answer.ts b/packages/presentation/ui/src/chat/question-answer.ts new file mode 100644 index 000000000..89af12806 --- /dev/null +++ b/packages/presentation/ui/src/chat/question-answer.ts @@ -0,0 +1,8 @@ +import type { Question, QuestionAnswer } from '@linkcode/schema'; + +export function answerText(question: Question, answer: QuestionAnswer): string | undefined { + const labelByOption = new Map(question.options.map((option) => [option.optionId, option.label])); + const parts = answer.selectedOptionIds.map((optionId) => labelByOption.get(optionId) ?? optionId); + if (answer.customText) parts.push(answer.customText); + return parts.length > 0 ? parts.join(', ') : undefined; +} diff --git a/packages/presentation/ui/src/chat/question-call-item.tsx b/packages/presentation/ui/src/chat/question-call-item.tsx index 01b4f28af..db94c2aa4 100644 --- a/packages/presentation/ui/src/chat/question-call-item.tsx +++ b/packages/presentation/ui/src/chat/question-call-item.tsx @@ -1,7 +1,8 @@ -import type { Question, QuestionAnswer, ToolCall } from '@linkcode/schema'; +import type { ToolCall } from '@linkcode/schema'; import { MessageCircleQuestionMarkIcon } from 'lucide-react'; import { useTranslations } from 'use-intl'; import type { QuestionConversationItem } from './conversation-prompts'; +import { answerText } from './question-answer'; import { Tool, ToolContent, ToolHeader } from './tool'; /** Timeline record of an agent question ask. The dock's QuestionPrompt is the actionable @@ -66,11 +67,3 @@ export function QuestionCallItem({ ); } - -/** Chosen option labels plus any custom text; undefined when the question was skipped. */ -function answerText(question: Question, answer: QuestionAnswer): string | undefined { - const labelByOption = new Map(question.options.map((option) => [option.optionId, option.label])); - const parts = answer.selectedOptionIds.map((optionId) => labelByOption.get(optionId) ?? optionId); - if (answer.customText) parts.push(answer.customText); - return parts.length > 0 ? parts.join(', ') : undefined; -} diff --git a/packages/presentation/ui/src/native/agent-icon.tsx b/packages/presentation/ui/src/native/agent-icon.tsx index 9c5886b5a..462ecf018 100644 --- a/packages/presentation/ui/src/native/agent-icon.tsx +++ b/packages/presentation/ui/src/native/agent-icon.tsx @@ -1,5 +1,6 @@ import type { AgentKind } from '@linkcode/schema'; import { useThemeColor } from 'heroui-native'; +import type { ColorValue } from 'react-native'; import { Text, View } from 'react-native'; import Svg, { Path } from 'react-native-svg'; import { AGENT_INITIALS } from '../agent-meta'; @@ -24,7 +25,7 @@ export interface AgentIconProps { /** Outer box size in dp (the glyph scales inside it). */ size?: number; /** Glyph tint override; defaults to accent-foreground (solid) / foreground (ghost). */ - color?: string; + color?: ColorValue; } /** Agent brand chip — the native counterpart of chat/agent-icon.tsx. */ diff --git a/packages/presentation/ui/src/native/index.ts b/packages/presentation/ui/src/native/index.ts index 59233841b..af27e2628 100644 --- a/packages/presentation/ui/src/native/index.ts +++ b/packages/presentation/ui/src/native/index.ts @@ -5,7 +5,12 @@ export type { ModelOption } from '../agent-models'; export { groupModelsByProvider, modelChoiceKey, resolveModel } from '../agent-models'; export { stripAnsi } from '../ansi'; export type { CurrentPlan, PromptConversationItem } from '../chat/conversation-prompts'; -export { selectCurrentPlan, selectPendingPromptItems } from '../chat/conversation-prompts'; +export { + conversationFlowItems, + selectCurrentPlan, + selectPendingPromptItems, +} from '../chat/conversation-prompts'; +export { answerText } from '../chat/question-answer'; export { diffLines, patchLines } from '../diff-utils'; export { repositoryLabel } from '../repository-label'; export * from '../thread-groups'; diff --git a/packages/presentation/ui/src/native/markdown/code-block.tsx b/packages/presentation/ui/src/native/markdown/code-block.tsx index 134175415..48c707972 100644 --- a/packages/presentation/ui/src/native/markdown/code-block.tsx +++ b/packages/presentation/ui/src/native/markdown/code-block.tsx @@ -3,8 +3,9 @@ import { noop } from 'foxact/noop'; import { useEffect } from 'foxact/use-abortable-effect'; import { useState } from 'react'; import { ScrollView, Text, useColorScheme, View } from 'react-native'; +import { MONO_FONT_FAMILY } from './mono-font'; -const CODE_FONT = { fontFamily: 'Menlo', fontSize: 12, lineHeight: 18 } as const; +const CODE_FONT = { fontFamily: MONO_FONT_FAMILY, fontSize: 12, lineHeight: 18 } as const; interface HighlightedCode { code: string; diff --git a/packages/presentation/ui/src/native/markdown/markdown.tsx b/packages/presentation/ui/src/native/markdown/markdown.tsx index 6bbae22c4..cf59a26df 100644 --- a/packages/presentation/ui/src/native/markdown/markdown.tsx +++ b/packages/presentation/ui/src/native/markdown/markdown.tsx @@ -3,9 +3,10 @@ import type { ListItem, PhrasingContent, RootContent } from 'mdast'; import { Linking, Text, View } from 'react-native'; import { CodeBlock } from './code-block'; +import { MONO_FONT_FAMILY } from './mono-font'; import { parseMarkdown } from './parse'; -const MONO = { fontFamily: 'Menlo', fontSize: 13 } as const; +const MONO = { fontFamily: MONO_FONT_FAMILY, fontSize: 13 } as const; function renderInline(nodes: PhrasingContent[]): React.ReactNode { return nodes.map((node, key) => { diff --git a/packages/presentation/ui/src/native/markdown/mono-font.ts b/packages/presentation/ui/src/native/markdown/mono-font.ts new file mode 100644 index 000000000..12185bbca --- /dev/null +++ b/packages/presentation/ui/src/native/markdown/mono-font.ts @@ -0,0 +1,5 @@ +import { Platform } from 'react-native'; + +/** Menlo is an Apple font; without the select Android silently falls back to proportional sans + * and code loses column alignment. */ +export const MONO_FONT_FAMILY = Platform.select({ ios: 'Menlo', default: 'monospace' }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9fcb0df18..5a593f200 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -442,13 +442,13 @@ importers: dependencies: '@better-auth/expo': specifier: 1.7.0-rc.2 - version: 1.7.0-rc.2(5705381685e3aee0fa9e57d39f706f20) + version: 1.7.0-rc.2(3e673fc1d89bb5bb2e0be4405d007f02) '@expo/ui': - specifier: 57.0.7 - version: 57.0.7(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: 57.0.11 + version: 57.0.11(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) '@gorhom/bottom-sheet': specifier: ^5.2.14 - version: 5.2.14(6e44dcb7dcfecf271936e923703300f2) + version: 5.2.14(b3053a3c63c272f26bedf4b267721ee5) '@linkcode/client-core': specifier: workspace:* version: link:../../packages/client/core @@ -472,97 +472,100 @@ importers: version: link:../../packages/presentation/ui '@react-native-community/netinfo': specifier: 12.0.1 - version: 12.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 12.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) '@sentry/react-native': specifier: ~7.11.0 - version: 7.11.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + version: 7.11.0(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) better-auth: specifier: 1.7.0-rc.2 version: 1.7.0-rc.2(@opentelemetry/api@1.9.1)(better-sqlite3@12.11.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(kysely@0.29.4))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(happy-dom@20.10.6)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(tsx@4.23.1)(yaml@2.9.0))) expo: - specifier: ~57.0.4 - version: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + specifier: ~57.0.14 + version: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) expo-apple-authentication: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-audio: - specifier: ~57.0.0 - version: 57.0.0(expo-asset@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: ~57.0.3 + version: 57.0.4(expo-asset@57.0.16(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-background-task: - specifier: ~57.0.2 - version: 57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + specifier: ~57.0.11 + version: 57.0.16(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) expo-camera: - specifier: ~57.0.1 - version: 57.0.1(@types/emscripten@1.41.5)(expo@57.0.4)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: ~57.0.3 + version: 57.0.4(@types/emscripten@1.41.5)(expo@57.0.20)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-clipboard: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-constants: - specifier: ~57.0.3 - version: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + specifier: ~57.0.12 + version: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) expo-crypto: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.4) + version: 57.0.1(expo@57.0.20) expo-dev-client: - specifier: ~57.0.5 - version: 57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + specifier: ~57.0.13 + version: 57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) expo-device: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.20) expo-document-picker: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.20) expo-file-system: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + specifier: ~57.0.4 + version: 57.0.6(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) expo-font: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: ~57.0.1 + version: 57.0.3(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-haptics: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4) + specifier: ~57.0.1 + version: 57.0.2(expo@57.0.20) expo-image-picker: - specifier: ~57.0.2 - version: 57.0.2(expo@57.0.4) + specifier: ~57.0.11 + version: 57.0.16(expo@57.0.20) expo-libghostty: specifier: ^0.8.1 - version: 0.8.1(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 0.8.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-linking: - specifier: ~57.0.2 - version: 57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + specifier: ~57.0.6 + version: 57.0.9(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) expo-local-authentication: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4) + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.20) expo-network: specifier: ~57.0.1 - version: 57.0.1(expo@57.0.4)(react@19.2.3) + version: 57.0.1(expo@57.0.20)(react@19.2.3) expo-notifications: - specifier: ~57.0.3 - version: 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + specifier: ~57.0.12 + version: 57.0.17(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) expo-router: - specifier: ~57.0.4 - version: 57.0.4(3bc51b20989ed2c3020a920bb0eeb449) + specifier: ~57.0.14 + version: 57.0.19(c021d0cd4acfce308650ff4c6746808d) expo-secure-store: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4) + specifier: ~57.0.1 + version: 57.0.3(expo@57.0.20) expo-sharing: - specifier: ~57.0.3 - version: 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + specifier: ~57.0.13 + version: 57.0.18(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) expo-sqlite: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: ~57.0.1 + version: 57.0.2(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-status-bar: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-task-manager: + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-symbols: specifier: ~57.0.2 - version: 57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + version: 57.0.2(expo-font@57.0.3)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-task-manager: + specifier: ~57.0.11 + version: 57.0.16(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) expo-updates: - specifier: ~57.0.6 - version: 57.0.6(expo-dev-client@57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + specifier: ~57.0.15 + version: 57.0.21(expo-dev-client@57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) expo-web-browser: - specifier: ~57.0.0 - version: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) foxact: specifier: ^0.3.10 version: 0.3.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -571,13 +574,13 @@ importers: version: 5.9.1 heroui-native: specifier: 1.0.6 - version: 1.0.6(165eb86db294c7d42ce40e8c9ab66801) + version: 1.0.6(5dab09f39b7c1f66cb0e1b4de78ecc64) lucide-react-native: specifier: ^1.25.0 - version: 1.27.0(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 1.27.0(react-native-svg@15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) posthog-react-native: specifier: ^4.60.0 - version: 4.60.0(expo-application@57.0.2(expo@57.0.4))(expo-device@57.0.0(expo@57.0.4))(expo-file-system@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo-localization@57.0.1(expo@57.0.4)(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)) + version: 4.60.0(expo-application@57.0.2(expo@57.0.20))(expo-device@57.0.1(expo@57.0.20))(expo-file-system@57.0.6(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo-localization@57.0.1(expo@57.0.20)(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)) react: specifier: 19.2.3 version: 19.2.3 @@ -585,35 +588,35 @@ importers: specifier: 19.2.3 version: 19.2.3(react@19.2.3) react-native: - specifier: 0.86.0 - version: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + specifier: 0.86.2 + version: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) react-native-gesture-handler: specifier: ~2.32.0 - version: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-keyboard-controller: specifier: 1.21.9 - version: 1.21.9(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 1.21.9(react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-reanimated: - specifier: 4.5.0 - version: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: 4.5.1 + version: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-safe-area-context: specifier: ~5.7.0 - version: 5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-screens: - specifier: 4.25.2 - version: 4.25.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + specifier: 4.26.2 + version: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-svg: specifier: 15.15.4 - version: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-web: specifier: ^0.21.2 version: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-native-webview: specifier: 13.16.1 - version: 13.16.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + version: 13.16.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-worklets: - specifier: 0.10.0 - version: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + specifier: 0.10.1 + version: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -622,7 +625,7 @@ importers: version: 3.3.0(tailwind-merge@3.6.0)(tailwindcss@4.3.3) uniwind: specifier: 1.10.0 - version: 1.10.0(@expo/metro-config@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(supports-color@8.1.1))(metro-cache@0.84.4(supports-color@8.1.1))(metro-transform-worker@0.84.4(supports-color@8.1.1))(metro@0.84.4(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(tailwindcss@4.3.3) + version: 1.10.0(@expo/metro-config@57.0.12(@typescript/typescript6@6.0.2)(expo@57.0.20)(supports-color@8.1.1))(metro-cache@0.84.5(supports-color@8.1.1))(metro-transform-worker@0.84.5(supports-color@8.1.1))(metro@0.84.5(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(tailwindcss@4.3.3) use-intl: specifier: 'catalog:' version: 4.13.4(react@19.2.3) @@ -635,7 +638,7 @@ importers: devDependencies: '@bacons/apple-targets': specifier: 4.0.7 - version: 4.0.7(expo@57.0.4)(supports-color@8.1.1) + version: 4.0.7(expo@57.0.20)(supports-color@8.1.1) '@types/react': specifier: 'catalog:' version: 19.2.17 @@ -1265,10 +1268,10 @@ importers: version: 9.3.0 react-native: specifier: '>=0.80' - version: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + version: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) react-native-safe-area-context: specifier: '>=5.0.0' - version: 5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + version: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) rehype-slug: specifier: ^6.0.0 version: 6.0.0 @@ -1326,7 +1329,7 @@ importers: version: 19.2.3(@types/react@19.2.17) heroui-native: specifier: ^1.0.6 - version: 1.0.6(63282ea4a5d8a79f137ba1bf529bfa14) + version: 1.0.6(759903ba85becadd460e21d1d0660b91) react: specifier: 'catalog:' version: 19.3.0-canary-96fcba90-20260728 @@ -1335,7 +1338,7 @@ importers: version: 19.3.0-canary-96fcba90-20260728(react@19.3.0-canary-96fcba90-20260728) react-native-svg: specifier: ^15.15.4 - version: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + version: 15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) typescript: specifier: 'catalog:' version: '@typescript/typescript6@6.0.2' @@ -1398,9 +1401,6 @@ importers: packages: - '@adobe/css-tools@4.5.0': - resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} @@ -2874,8 +2874,8 @@ packages: '@expo-google-fonts/material-symbols@0.4.38': resolution: {integrity: sha512-IJkBtN1o8u9BW5fvSii1MyHPQ7Q0HxbWcVBvOrOzgMLpVtZw7R2w94wBTVR7kZwv3w1JNTESMmLA5Sqn1+Z36A==} - '@expo/cli@57.0.6': - resolution: {integrity: sha512-14wEb2e8lctlxGARbinUEr+9EmrTR2jIgcaPBBkZXDFMRJmdoY0ic18JuFJPi+7CuQ4XWiGK6obN4VK2PXosLA==} + '@expo/cli@57.0.22': + resolution: {integrity: sha512-MdToR0ZZ1yjfkqbP1jcA7viS9duMPGq/C+qQ/21dtq+en583Ghp+O11BP/wujHa9ZGRtXimzuKPZDpPresVE0g==} hasBin: true peerDependencies: expo: '*' @@ -2890,20 +2890,20 @@ packages: '@expo/code-signing-certificates@0.0.6': resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} - '@expo/config-plugins@57.0.3': - resolution: {integrity: sha512-J3P2T7FoOE9P3TuLcJXwt8jISKRxo7UntTzbJ/Qc5F7QXenNntk/4t1XndVkLucYjpnHArPd9xzDXuxxKEHVbQ==} + '@expo/config-plugins@57.0.9': + resolution: {integrity: sha512-hHgfL1avkCdEvDSw7IwlKwRYYNgcxzbNNMIk6W6lTkJpY0MajinAfeJUS0J+wPCsjUfGbVqOJM+XhPaO5ulUxg==} - '@expo/config-types@57.0.1': - resolution: {integrity: sha512-fo7d/Ym28uwGzdTV2leEvpsb9t+7i8YHjy471m31+cmDt4BRd/l7e94JHyrXAq4SWOBVus16S0JLqm89SRCCdw==} + '@expo/config-types@57.0.2': + resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} - '@expo/config@57.0.3': - resolution: {integrity: sha512-IshqjjpGtT7wj86pxgsfMD1/abNMfu1wZ07ubyYO29l+PWa0eAh2TcpyLcyBaqo01IonF6646xWwrCPcdlUl3Q==} + '@expo/config@57.0.9': + resolution: {integrity: sha512-dmzlKraIFxa7wLwV6K7WzI8jp6QZpW6Mc5mGjLimJUFjzh4uQdYaT3m3plEutM5yxBoBEwqzks7l+I/ljCbxAQ==} '@expo/devcert@1.2.1': resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} - '@expo/devtools@57.0.0': - resolution: {integrity: sha512-i8ITQmf/wB0JNQ2gASFOKvqo1zRWCl/VfPlFRXdz6UkHen4s31NmMy0zmumS+pMpwn0+gA6oU4FF4zRDRG488Q==} + '@expo/devtools@57.0.1': + resolution: {integrity: sha512-GyUf+wFNkbttaX0jR7MZa9bm77U0IrLg6d2AjpxdyoXw/w4abHoXG0oFufwLMgP9zLTd5+Ct4X/ffNUTnlzZgg==} peerDependencies: react: '*' react-native: '*' @@ -2913,59 +2913,59 @@ packages: react-native: optional: true - '@expo/dom-webview@57.0.0': - resolution: {integrity: sha512-zBZw52KnaHf9zGVp1eJk8kNfc+5ArGf+KvTL3SeMsr03K6tPJtLbgycVLjrAiLMfhzqGVjPD2G+rbNwpbLUxcg==} + '@expo/dom-webview@57.0.1': + resolution: {integrity: sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==} peerDependencies: expo: '*' react: '*' react-native: '*' - '@expo/env@2.4.1': - resolution: {integrity: sha512-3c9Mg9x0HmGPEsVrGAGyEDJsNUOZ55cZvZ47/HLmXh7MHV9Zv7My73wThklKrObaBBoMfE4YqpKjYKDRzojpjQ==} + '@expo/env@2.4.3': + resolution: {integrity: sha512-M1NXeZCA1mkMkYOyIe7PlyRX0/jqFtMoJgyblnlq/vpCRfmueFT7RnGSQG8uEFDF5WHOFGijAQ3fogPh3/n5Ng==} engines: {node: '>=20.12.0'} - '@expo/expo-modules-macros-plugin@0.3.0': - resolution: {integrity: sha512-2tRq8kiIZTVZcI5uggh86HefQ7s++Zk5WkFFomNp4aUqyN5ownHHvj1jPEP9jWXaXjPDmWuf5SUZTGD5G6AKkg==} + '@expo/expo-modules-macros-plugin@0.6.1': + resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==} - '@expo/fingerprint@0.20.3': - resolution: {integrity: sha512-WDV2hS87C61TsX55w4A0cPnbnN8bqdVHp2MpZsJTvB4Tv4TITgSxWYhRj/xv0pwcZvKHn05MBg6AfHPNj9o5Sw==} + '@expo/fingerprint@0.20.12': + resolution: {integrity: sha512-FIR5fkZYeFaLSowmjgyB6RPKl8AXeE8HuCBHHvyxK8UhOjpPfCAmzT4E7v2yub4qqDafKnfcOFCYxvpUEu+01w==} hasBin: true - '@expo/image-utils@0.11.1': - resolution: {integrity: sha512-0JueH4vdgAZQmhlSYFvTQzt4b4NO5cnByDuApw7bMUIjhwLRnT46Ki3ritMrzJMQaO2lLK2flInZbsZbOuy8nw==} + '@expo/image-utils@0.11.5': + resolution: {integrity: sha512-KPQBTpmpAfy/Vu9y4wPW808/qtZxjYmyJg8cm2QCPAupp+qEWA3b5zmk0ulOwQ9OgeHxuCPgUqWgkwHFo7UsrQ==} - '@expo/inline-modules@0.1.2': - resolution: {integrity: sha512-wc5wH4ND647Ne/6A/ESuelcqOQUEvK8MYjjvbp5wcl59dECk7juh0cFOJjAWkpc4pLTcvqXBSRcUDpGWm5XNWg==} + '@expo/inline-modules@0.1.7': + resolution: {integrity: sha512-Bz/khd1gIJqDkje7t5ejD5e9jFbm4xEJzWSwRKadRo6gruepbw1xJ3Eb+e58OS8fY1ZNQYjzZbspnuph67sN0g==} - '@expo/json-file@11.0.0': - resolution: {integrity: sha512-pHJCETqFL5x5BzNV6cEPwjwuECgGmnl0bNmfHIJ6LM1tlh2eVXi5HEdit3zby/JO/B8Otk5cgcqtJXgvvUat3A==} + '@expo/json-file@11.0.1': + resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==} - '@expo/local-build-cache-provider@57.0.2': - resolution: {integrity: sha512-/8/FoYrEBJZPG4wR8kO6REI4VRCPAStD+7wS/Ds6XXddxhmyMP3pvUJz/c3icyAoqpbqeZZkR6E1ptT5Gi5iXg==} + '@expo/local-build-cache-provider@57.0.8': + resolution: {integrity: sha512-SEdE0pAQrr90bRh3MNR0ZuwoIBw390cYdFgbn7Vk0Mtm9EHaBfP7kYj+2hXnXZJgOEm3/JMtfoD3rV7rWA9FGg==} - '@expo/log-box@57.0.0': - resolution: {integrity: sha512-tt9x76IEE8hp2FWTLPfEArrTyD7GCoUe3TpohESy+SPZ/OqkUnSXz40xnUuE0XbE132z8c5CRCfXQLM9DTEknQ==} + '@expo/log-box@57.0.4': + resolution: {integrity: sha512-IxwS9s1L2muj8mj8AQSuiy7u8OFJdc02NRFo2me/Tj6DiaeG5SREqmpBE4rQpR2cadqSg5jl8Qab8Cjie616dg==} peerDependencies: - '@expo/dom-webview': ^57.0.0 + '@expo/dom-webview': ^57.0.1 expo: '*' react: '*' react-native: '*' - '@expo/metro-config@57.0.3': - resolution: {integrity: sha512-k7qcjTe1xDrUW15Ue86WAsJSL0ubiSluBXVNRFoe9snAWhMzBGzdZfl9XiDf4TNUV43T0L1ch+n4GmDqamvfEg==} + '@expo/metro-config@57.0.12': + resolution: {integrity: sha512-S62Lrq35HZqBFD55423pmWb8PjaiR/W02zQC1uECBmw1vTN8WZaFz4TJ0i21EeJzwfebMb9MLxL8JZ102Z6VbA==} peerDependencies: expo: '*' peerDependenciesMeta: expo: optional: true - '@expo/metro-file-map@57.0.0': - resolution: {integrity: sha512-/sxwKQmwpYFYjFT6VYFpgldTwSv53OCx4/UCpLCv9iqQLVQ30hFEJsxGA9Jif8n9pacigK4P5/0ZJ2zMHm6arA==} + '@expo/metro-file-map@57.0.2': + resolution: {integrity: sha512-tb50nSIWwKpRufSkGuivOK0FbUxv1Uwqptb0SzFTk0bkmYmcy3gIwnCbOHTfmrQDVndRhPEiu2SYgh9Z0PCvGg==} - '@expo/metro-runtime@57.0.3': - resolution: {integrity: sha512-FMXIpvPIlAvJlaQmlmP79gNt7KS5DyruKSOkOW1DTkXnJOiXQnzvebqPLOEYH71fbS1emJ3Dq45lDz7+sryVbA==} + '@expo/metro-runtime@57.0.15': + resolution: {integrity: sha512-9RaFkqIzRud6ol9fc5ZKHgNDYeDjQVBdAQC+rjSQ6RiKV0pY+SjDqGmKY5a4M9TaV0qLMCNNrp5R88BqC2xbuQ==} peerDependencies: - '@expo/log-box': ^57.0.0 + '@expo/log-box': ^57.0.4 expo: '*' react: '*' react-dom: '*' @@ -2974,42 +2974,42 @@ packages: react-dom: optional: true - '@expo/metro@56.0.0': - resolution: {integrity: sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==} + '@expo/metro@56.0.2': + resolution: {integrity: sha512-Ld5AeYMCCDa8bLeWhfuLbZFFjlV3f6ORqyPz2glGh6RltIngMuLf9BTC2yvHFjkKuGxL5SynijmA8xmNNWn5iA==} - '@expo/osascript@2.7.0': - resolution: {integrity: sha512-wKIXL8UtbuX4KwavPasIW3CUcgTbYfjzLcgUhjyKUAYDEqMaf6gmU1bqz3ffBPTokmX+G8/vFG1ZuI9etQWukA==} + '@expo/osascript@2.7.1': + resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==} engines: {node: '>=12'} - '@expo/package-manager@1.13.0': - resolution: {integrity: sha512-s3W3eZafJDEyVL7W/jxj2Nz3eONKxSCU604S5xj8ijrVaRz83x0DnZznLf/UXQEI1w+FyibH68nHeQyk767b1A==} + '@expo/package-manager@1.13.1': + resolution: {integrity: sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==} '@expo/plist@0.0.18': resolution: {integrity: sha512-+48gRqUiz65R21CZ/IXa7RNBXgAI/uPSdvJqoN9x1hfL44DNbUoWHgHiEXTx7XelcATpDwNTz6sHLfy0iNqf+w==} - '@expo/plist@0.8.0': - resolution: {integrity: sha512-24JlUJI4PwHN4PLydlzFEzCdiqybfaV5t04QBkOg8em3AjvHKbMgBGlKreiuOoc0rNa3DZ21ZqL+xLGMBLQNKQ==} + '@expo/plist@0.8.1': + resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} - '@expo/prebuild-config@57.0.5': - resolution: {integrity: sha512-KnKx6Iu4jppbNVtt9yTrN10OVoPZlOYYyLZuarY70u6fgUO9lYKDa06Hxv+PmIwcnygfj2CSBBl5ewBqS2bxtw==} + '@expo/prebuild-config@57.0.15': + resolution: {integrity: sha512-xTbWHroj0PDmlbqvmU+zF9ZZxveJkiuyiPoeRJYRGruFHebRAWnoTdw5S7d/UCzDBI8ropGGu9g2eb2nMxtvAw==} - '@expo/require-utils@57.0.1': - resolution: {integrity: sha512-uXen5/4x7j60I5slShgZr5QEtJDBK8homFiNLDnDrNrxZhrRHXASo0H6JArs3/1PDzw1wahzhGWg2WKuYyZd0A==} + '@expo/require-utils@57.0.5': + resolution: {integrity: sha512-kTAXj9lDFEIPMsbAOGCGbjBbMF0oi7CqkYM79KOX0DDD9wSwXmlKL1z2h8OwsrBf7mbOo2DjlRvZu4BEjrIxGw==} peerDependencies: - typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: typescript: optional: true - '@expo/router-server@57.0.2': - resolution: {integrity: sha512-hu9qhnq2PKuwMXIoRMAa2+HUeqSut9HwiQf3a62tfGTIxPN3rKUZ10TozEEB+Pd/7WIa238sXMS5v1YhTuC9gA==} + '@expo/router-server@57.0.9': + resolution: {integrity: sha512-/PxRQozFesIyCJZOAtrQE8XcmcojNiL5ctPMQnbE4ojC2EJPu0zc7c0Y4PuXsoRxrZxm8Usw76/lCVrXcfTZ2w==} peerDependencies: - '@expo/metro-runtime': ^57.0.3 + '@expo/metro-runtime': ^57.0.15 expo: '*' - expo-constants: ^57.0.3 - expo-font: ^57.0.0 + expo-constants: ^57.0.17 + expo-font: ^57.0.3 expo-router: '*' - expo-server: ^57.0.0 + expo-server: ^57.0.3 react: '*' react-dom: '*' react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 @@ -3023,8 +3023,8 @@ packages: react-server-dom-webpack: optional: true - '@expo/schema-utils@57.0.1': - resolution: {integrity: sha512-IVdaqtP3+iHFRE/y22mKijQtZanLcB18q1zi2kfN6RINTCryyzM7qHGsWHc5LBJbOuj1G4avCOECs97hOJm0GQ==} + '@expo/schema-utils@57.0.2': + resolution: {integrity: sha512-fMu/jyN0l1Wzv7XkeWR4IYCx1M8ryui3FdBNGrWwbRgJ7EhxXxK8E2jxP2W3pbgUwUY0V3hG8+GyfCZwny+Lxw==} '@expo/sdk-runtime-versions@1.0.0': resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} @@ -3036,8 +3036,25 @@ packages: '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} - '@expo/ui@57.0.7': - resolution: {integrity: sha512-WqRVabl8VpHf3+YLHVjUy7PMIuXXI6DG88Vgmavro7Nd8Ks13h9sEJH/RLSCaJE2daVnqzEKY1v797BibuY9aw==} + '@expo/ui@57.0.11': + resolution: {integrity: sha512-m1BSq15sg4sCmoFwOsMxBpETgyFhGlCbVPUyjdJ6Y00Ve0Fb4sueaFLRyeYRbZ8DC1z3Hgp5LNCYzBBBdMhxMg==} + peerDependencies: + '@babel/core': '*' + expo: '*' + react: '*' + react-dom: '*' + react-native: '*' + react-native-worklets: '*' + peerDependenciesMeta: + '@babel/core': + optional: true + react-dom: + optional: true + react-native-worklets: + optional: true + + '@expo/ui@57.0.16': + resolution: {integrity: sha512-3MK74dEPE4mwlv0VbyQKepmHigOs2bcOvay2SFqc1/QSywVTOjbkNPYUbq2H+LyOLP2sHMazrPBwfXGiYn3q7A==} peerDependencies: '@babel/core': '*' expo: '*' @@ -4264,66 +4281,88 @@ packages: react: '>=16' react-native: '>=0.57' - '@react-native/assets-registry@0.86.0': - resolution: {integrity: sha512-nIaXbm2jX1OTYp0qbviJ3O6KZivoE8z3BnhUQ2LsqfZSWRoOK/n1qsiAr6oALiNKWnXY3j2KPwtYORnZzp8xew==} + '@react-native/assets-registry@0.86.2': + resolution: {integrity: sha512-vcX/mBjWAVnWofu7KecotquI2unZ/tITwA7OGdq/mdY/zmGXIEvYhfEYyOQij/LRqi9WAL+iizInTBWnxDhK/Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-plugin-codegen@0.86.2': + resolution: {integrity: sha512-NNDZqOlNbH5SzgPks1jFDYH3234Rpa5e/nhZymxhIiBH3NcE3uD+rGj/HWXhH7nHF2ToGK6XbUpqy7nmJPeh+g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-plugin-codegen@0.86.3': + resolution: {integrity: sha512-O6Xza4JBGPIU8J7YbKTyBoYL4thpy8jMW/oaLDWdAyOwYHKIjK47pAL5HUEbOe2bWz2PEKjbYRF2ApkJv1ottQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/babel-plugin-codegen@0.86.0': - resolution: {integrity: sha512-qdsABWNW7uTll90l4Vh03gjeyu3WVDi2CyiiyvYGMRDcoYbjbQi6df3BMAm9lQI2yslZ1T14LlDDAsgTwNxplA==} + '@react-native/babel-preset@0.86.2': + resolution: {integrity: sha512-4XKEJ6jKW9lXMB1O5o47gBoGgolde1fbX13gLW/erlcn+1ky+MHHo5UjuM3RWGdPJHIvIzekDDumSUHhB9x5iQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' - '@react-native/babel-preset@0.86.0': - resolution: {integrity: sha512-bYQcWiPySNvF4dns9Ls9gMmwgq66ohvM9Fwc/Kn8r85t66UNHxch3p1QwPiSorDelFauZwJbgo9+ReibTgvpbA==} + '@react-native/codegen@0.86.2': + resolution: {integrity: sha512-xKkudsahUJ1n//55g4fXk5BStVqqmZlz8HQveL45ZxcfDnwvhuYe2GymksQANFsSN+slvrarjrfq8kIxJzbceA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/codegen@0.86.0': - resolution: {integrity: sha512-uTs9DBo3+/lUqinsGZK0FKJRBVClrwMXoZToaDxE1Q2SL2e55vs2GwyZfIKzPl5uJnbu4PfFMIp0/mLXLWUMuA==} + '@react-native/codegen@0.86.3': + resolution: {integrity: sha512-Ux4jHi0fh+bdtVEcL0gaPLbY56V+SvFUDl/8sRAE1jdb4k+o7fT/4Nc29yz4X+qfjstkSqObQTMBGhdzxH9JvA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/community-cli-plugin@0.86.0': - resolution: {integrity: sha512-Jv8p1ebEPfTzs8gmrjsdT2XMXFfeAg45Pman+XPLFGaSeGAZkutRFRyX9Cs9aGTSOyIA9YPJ6vDNb1ayTf1FKQ==} + '@react-native/community-cli-plugin@0.86.2': + resolution: {integrity: sha512-YHXNKoM6Y/HjREySZ5arET2xgiHgg67r1MdwJB//MPJAJ0Xc5g0u6UHxY9VzsHO3Y07dre6s0BinYwjt1SEWvQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@react-native-community/cli': '*' - '@react-native/metro-config': 0.86.0 + '@react-native/metro-config': 0.86.2 peerDependenciesMeta: '@react-native-community/cli': optional: true '@react-native/metro-config': optional: true - '@react-native/debugger-frontend@0.86.0': - resolution: {integrity: sha512-7Mb3nDfyJeys+ELF75Ageu7VKERlnIMoO+aNPoXqTXvz+b41L6l2CqMyLpDHxkBSlenij6gEepPNgaIyWHbJZw==} + '@react-native/debugger-frontend@0.86.2': + resolution: {integrity: sha512-KGS1aV5F6cIqpnoIUhLBXyVzy1oAj8jBFGau6vX4Vy0HXRJN7p+68RU7x6NuyraHvQcR14ccMGT5TkFuNjQ4gA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/debugger-shell@0.86.0': - resolution: {integrity: sha512-Y0zEkZzLz8ou6o/VLml1A31X/rMgc6DRjwxwzPMa94qRTMY070WeBCNTITQo4kKTBAUgbxh07oXPQqp0Tpja8w==} + '@react-native/debugger-frontend@0.86.3': + resolution: {integrity: sha512-TQmeofQ0PcuylhhlleOeuzHYZfbrgm3gayXzowqUEzgRisTm1D40/J3ggqs7XkQi5HP5ZA3n8dHmKL9vIzPcsw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/dev-middleware@0.86.0': - resolution: {integrity: sha512-20pTO6yTybmvXvro520H6C7jydIQnLKOl5qFtVEcHSdFrY63r3OGei+Rx9bILgSRmH6jgnfEcijcMx7pwWuQtw==} + '@react-native/debugger-shell@0.86.2': + resolution: {integrity: sha512-/TaVJ2+gGajZPJGrFaObUQmHmlaxAlfmOPZicl6pNKDUjzSgFMpcLkdTOExvb+USYTVdGX1XwxXyvjQdUO2bvg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/gradle-plugin@0.86.0': - resolution: {integrity: sha512-a1RcfaEDqWExCGfCwadIxt4l8FvKYgFqeMf2uzeKyAOnb+vTGNIeCvifFL2MqvgaeYxlER437HbMIajGcuJ1pQ==} + '@react-native/debugger-shell@0.86.3': + resolution: {integrity: sha512-O4ds+J7xZfxkbih9T+cAGegBdvKSPKYJm/lDgC9CpEjFMkmzWTpVLU3Qsv9sqZuo58z+sGhIcJfPsCFFWHpqbQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/js-polyfills@0.86.0': - resolution: {integrity: sha512-zYy/Cjd1VTnZ2iCNaG9bDF9C3l2ntESiPRscjIlI5FKugu6aeTwsDSv1aI8Bc4Kp3vEdoVg+UQhLAhE4svREaQ==} + '@react-native/dev-middleware@0.86.2': + resolution: {integrity: sha512-B7L0vKvg+IcEElT7Vpqh1xj5yJAqWUegjbP+bQRaorJMAYnv11GkliTnZV2AdTDfZQJWgOEx8i8LGkHkUg7bnA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/metro-babel-transformer@0.86.0': - resolution: {integrity: sha512-SjKej3E5qIahqo/G+rSOrmJUQM44RyKtWtO+VfmKAAMoJWkBFomM22hTLKCIS5cdbIAJ9COAmU+KAi2wVSO0wQ==} + '@react-native/dev-middleware@0.86.3': + resolution: {integrity: sha512-LiEPTqTg/63bYUnrPyHLfjTDCNhA/+CUqI1+DsA9tYyewtSbULd5awsva6SgE10I+2iMhgKXS3ymkhU/kSCrGA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/gradle-plugin@0.86.2': + resolution: {integrity: sha512-2F6x14NcHMpVmfTTFKfMkpV5dZedZrLiv6PE+c3vgnesV2bjleUBydr4U+NI8VkI7OwW71L0A5qQ76I9LCrfoQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/js-polyfills@0.86.2': + resolution: {integrity: sha512-bIwNcGBaQ74shB5z1mRkxOpjikimuwsnOCEkZSzL67Z1FTyK1ObpENfyd2QvcvVW9Cjl+tHuw9ynpBnb2jPoJQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/metro-babel-transformer@0.86.2': + resolution: {integrity: sha512-mX1wgLErdb2hDgXJr9zM9SWLe+ZteZTFTwRWGOQ53yEJwc9DVSnxdTlAtVdMeOj2ntycKh0R8jYdat2Am43cwQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/metro-config@0.86.0': - resolution: {integrity: sha512-7v+xbTeEci9ZcQ/Z1OqI4RXcqN69wSMDYL5BAMvOReZ7U04+aDQ0/SQhClYPn6x2/RxM4WzMKSAuNyLKqvYVtw==} + '@react-native/metro-config@0.86.2': + resolution: {integrity: sha512-hJno256j+MS0b3JD1aD3ouTGZVacKNVBuXL2atMQQ8BZ060vl1ptnZ83y569aDW+/rgFSOcqn6ydKeSz4uUKQQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} '@react-native/normalize-colors@0.74.89': @@ -4332,16 +4371,19 @@ packages: '@react-native/normalize-colors@0.79.7': resolution: {integrity: sha512-RrvewhdanEWhlyrHNWGXGZCc6MY0JGpNgRzA8y6OomDz0JmlnlIsbBHbNpPnIrt9Jh2KaV10KTscD1Ry8xU9gQ==} - '@react-native/normalize-colors@0.86.0': - resolution: {integrity: sha512-kG0wfCGghUKlfxkJyyHCDVutWVYWK7/DG58ojA/4v9EfulgF+osuSQmlbNb3rcKX58qutm7JcldSeVLgGFha9g==} + '@react-native/normalize-colors@0.86.2': + resolution: {integrity: sha512-EzPFc9Y6lzYOWeso2almwXI7f8+qReHxWvT+algsOczb2UhWXIWXDoSvkdwoSfiwwmGt/ijJgKJoeHlzPkLwRg==} + + '@react-native/normalize-colors@0.86.3': + resolution: {integrity: sha512-Cv3CDkprb67GrzuaS9BGbBJC/6G4lIw3nyKOHRKTqTTum4bn37y5+R0Z04L8mcbQN85eEohNrRwb7IOM4j6uvg==} - '@react-native/virtualized-lists@0.86.0': - resolution: {integrity: sha512-4/ZLXdf/OSpPDVO0AsQ1SJdRIzt5t9BNQ46QwGgxvX7/cirYR5k8KXctNGGgW8lQo2gZChEfY2zFCZg9nM/jiw==} + '@react-native/virtualized-lists@0.86.2': + resolution: {integrity: sha512-uO0J72gh3EvE+1/GHRk18QRyBDTRHRB0AraAfojsRjbT7VMuJwKrZYaKGshavoaEud6aw00ZB9/8mTMIKjjcAw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@types/react': ^19.2.0 react: '*' - react-native: 0.86.0 + react-native: 0.86.2 peerDependenciesMeta: '@types/react': optional: true @@ -5191,10 +5233,6 @@ packages: resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.9.1': - resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.3.2': resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} engines: {node: '>=18'} @@ -5874,8 +5912,8 @@ packages: resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} engines: {node: '>= 20'} - agent-cli-detector@0.1.2: - resolution: {integrity: sha512-qdZ/9JFORtTKJNhT/IczMeEfEUbUU0K5umYeiIQHX+AjHs+Y9SXVzSgaYlpZeyNMrvuh2HpZiOTpvS57iPfBkQ==} + agent-cli-detector@0.1.7: + resolution: {integrity: sha512-d8OWDVdZMgjhLUT9ZPgSv/BdFFF9pVuscC0JdUSz3bjwE15gcp6u/o0/JooM2yyAWC49KThFhXlgTXRa9B7yng==} engines: {node: '>=18.18'} hasBin: true @@ -5966,10 +6004,6 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -6039,12 +6073,12 @@ packages: babel-plugin-transform-flow-enums@0.0.2: resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} - babel-preset-expo@57.0.2: - resolution: {integrity: sha512-PgsWlNSlNhnSkU4UCAcvl252CaB7VU1eQuSvJoRSiqfrZDGmeqwQmMUs+PcS5VUppFwmmhVlDPwOc7kR3Qr08Q==} + babel-preset-expo@57.0.10: + resolution: {integrity: sha512-08bt6bqMZFoQuWO9gkt0EXotbqKHimELFGa1LlKJvY89iKsi4S0FJSFBFJ4pn9NZvofet48HIjYDQ0SZJ7bNrg==} peerDependencies: '@babel/runtime': ^7.20.0 expo: '*' - expo-widgets: ^57.0.3 + expo-widgets: ^57.0.16 react-refresh: '>=0.14.0 <1.0.0' peerDependenciesMeta: '@babel/runtime': @@ -6578,9 +6612,6 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} - css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -6889,9 +6920,6 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -7381,28 +7409,28 @@ packages: peerDependencies: expo: '*' - expo-asset@57.0.3: - resolution: {integrity: sha512-6E08JDuYrktY5pKNn2WiFBPBfj6bvFb++ccuBTijK8BUk/afSoy6n35h583LmC2YHgHTepWHHWNfr61nYcMd8Q==} + expo-asset@57.0.16: + resolution: {integrity: sha512-IBRfQdW3iFT+GOBERMZLZM1MUNyrjMgMskuD0elVZ1ae44858UFlTJkHO3f8vi+u5zuv7O7KofsiN8NMG/uWzw==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-audio@57.0.0: - resolution: {integrity: sha512-4eV3rIFksVygrnm/dxdrP3Umjsn9/1Zr6cgBzoFtm43nw65g4s3WB5nkszG3JB4a2zpQ9N/IRS5EdRqXqLdbSg==} + expo-audio@57.0.4: + resolution: {integrity: sha512-TLP8rt1UvUDzgxGnyQ0TR9hV6tNP/UJQdDu7mSK+dgEydMFoptq55D3hcUoB1gF39f3/3AUuWfOtpGM+4N4X1A==} peerDependencies: expo: '*' expo-asset: '*' react: '*' react-native: '*' - expo-background-task@57.0.2: - resolution: {integrity: sha512-DA5VIVmbY/f2wlrSNW+EjsagCaAmyuvy1OXwouvTPFLU4rX7lFAPRw1+kb0fXSeq8fO9Z4s1lN4x/vTParoLWw==} + expo-background-task@57.0.16: + resolution: {integrity: sha512-RWn9BrGHvEKh9YVgOWkjFEVlcam2vLeDdSagOJxOcukR1x1Xya3xD4KSBvcQDxpnfBVMIUruymLN6F0woYNarw==} peerDependencies: expo: '*' - expo-camera@57.0.1: - resolution: {integrity: sha512-OtQOOFQ8djlB/UXlETa+Agu9m88P6d1go0VIO9b/a54AFUuowUqEH9vpzkq5iJ4jVhvJSblm/II7kLtwNnxW7Q==} + expo-camera@57.0.4: + resolution: {integrity: sha512-MqbbQ63O+cA8JbK3lfFHiD0f2jv8jB+EG/ILK6f5xnOPV9IR37yaG5+lGtRZNoOeYBzGypyOi17USItleMyuGw==} peerDependencies: expo: '*' react: '*' @@ -7412,15 +7440,15 @@ packages: react-native-web: optional: true - expo-clipboard@57.0.0: - resolution: {integrity: sha512-eIt47f6kvR33HOnIEWvs9l1AUAY4GeLY27szwamSuwRl7nntNythSFFbmnrEtiSUaz//y8XVqCG9WLpdMkOpjA==} + expo-clipboard@57.0.1: + resolution: {integrity: sha512-HWICri4+1ao7S6QEfcorxVumXDiDnx1guGGewjZgGJWLGxFYs0RgH8ujBs+lkTzBkMmlwADaWSlaesR+nDJt5Q==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-constants@57.0.3: - resolution: {integrity: sha512-BghbZlzwFnA22BG0CWv6W29zx8w19FozYPfSeZ3HjMitoy4aAmF1FnFbbjYSmZz6HHXXTWOfqwobOEIaglfzxg==} + expo-constants@57.0.17: + resolution: {integrity: sha512-cPWYBKN1SEbg2lXg2f8VkePJqGZrJPLvVdQDCTfbFu9sQHO1M31Y1zILReUuGnmt/VKeUz40ze579vR00xGu/A==} peerDependencies: expo: '*' react-native: '*' @@ -7430,13 +7458,13 @@ packages: peerDependencies: expo: '*' - expo-dev-client@57.0.5: - resolution: {integrity: sha512-OOQJHb4Cf403AhZAbcZg0OOVJKLc1HArVck92LHd9PO2HuTadj/6YAlWIyXXYfeYYdjoVgLd/cAXnaEAwp6HKg==} + expo-dev-client@57.0.18: + resolution: {integrity: sha512-cT+6iO4rRoguIxeONAPHF2wbAlc002tZya2M3xHlc6EWKptjsAL2Vr+kagYdLH/4vtLX7XKhWvOzaBf+rQpRFg==} peerDependencies: expo: '*' - expo-dev-launcher@57.0.5: - resolution: {integrity: sha512-f76DIU/mF3YdDZOgwLL3A5gwb3NVIySLE5ot+gy1T0pFo6VPW3Ew7h8oyG9fQdYum58v/3LoB1uWAIhhkHhsqA==} + expo-dev-launcher@57.0.19: + resolution: {integrity: sha512-IF/EJ3tcHqiyfBb5VtpAU3upBXRVTFVuVoWqEzrkZCesuMi35td4yQFvPtqXbMceaEErgFyiyxhW+L6AXVYapA==} peerDependencies: expo: '*' react-native: '*' @@ -7446,65 +7474,65 @@ packages: peerDependencies: expo: '*' - expo-dev-menu@57.0.5: - resolution: {integrity: sha512-ITQdZBawOe7Z9aB9erLNKIM/0X1iL6VvTrC5By6ECQqTptiRLJmpeqCt1vCJ72Wz+8YeHk+n2GY5V+1yzXf5OA==} + expo-dev-menu@57.0.18: + resolution: {integrity: sha512-H7m9ENtVYz4FpJktQj+z/njIN1t7kOEaKCv8Qgv2SdAlyFTW8s2EnufxPoSlg2zUTanaauJ6wrR6XESHwW7kpw==} peerDependencies: expo: '*' react-native: '*' - expo-device@57.0.0: - resolution: {integrity: sha512-XiTtUhyS64xO8AtEY14FI1vJpUT5b+cyxhjAsHIsoAx6y4rLF719pVY2sNx1MJYb/l7qa9kxWAcMDfk8Q8Gcpg==} + expo-device@57.0.1: + resolution: {integrity: sha512-jyEMDUticH+dhcL3GHa2aiifOvGXJsmb3oVT2R2q4i8bN7Bddy61+NkpMmuS2VAZrvoLQwf0TJJ/1vi1ukvutA==} peerDependencies: expo: '*' - expo-document-picker@57.0.0: - resolution: {integrity: sha512-foT1pA3tD7KvcqXwjQBh4qEDjHT9U9MhZ6H2aF38VolCiHRhXDQLtKQqCeXjbHxJhSf8HC/YPV3IRxDxFTL2sA==} + expo-document-picker@57.0.1: + resolution: {integrity: sha512-qBwM5oxDZ3I9kwFD3pUE1oK/WNv9artoEKO6UpqhQgNRr0XA1ALRVWYjkF4+ge9lUNDRehjTm/jenINkzqg84g==} peerDependencies: expo: '*' - expo-eas-client@57.0.0: - resolution: {integrity: sha512-HYroDzqYFpALIXW5tpQD2WHqij3iUal+fXzVJhIYV3OdVcbobxt+p6209OJDARlU431CANZwp4vPmS7yRCWTCw==} + expo-eas-client@57.0.3: + resolution: {integrity: sha512-cyC2AZg85DfAt4Ge7jIRmxmL62dUXob4EnrLZatrl+xJ6AuObqjMYf3hx5JYFfvro4ajkFo25qNREPTdiHCYQQ==} - expo-file-system@57.0.0: - resolution: {integrity: sha512-G+eytNuNGMiS7dbdj1j0nAYMowXnNr1vpO+jss6nQvBlRxshcGBFMtk8xfc67ynz0MUVkzod7WwKO7Vf1iOaJw==} + expo-file-system@57.0.6: + resolution: {integrity: sha512-pm8PMYEW6BnVOCBJ7df9FcDmQtE1tqImuYphlfYe1ipQRLYtdCayRczJcbKIsnB3mqEyp4gC2gWmMbsAsAOjVg==} peerDependencies: expo: '*' react-native: '*' - expo-font@57.0.0: - resolution: {integrity: sha512-zF+J7WrNFjqyAADwdvDgkFEoIQv9DqcjJ57HVstNEH7/7Tx1ThPEhKraodEQOwSjMYWirP+4BYsVbdb+/Zr4QQ==} + expo-font@57.0.3: + resolution: {integrity: sha512-kiVUnc2A8vAvO2FfDJTsQa5BwmY+PAkof/1wRb5MOkcX1jtiaSTwz9gCUAyscMBFLundZDmZrLy1P7LZVC+NvA==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-glass-effect@57.0.0: - resolution: {integrity: sha512-QadzKSKpjXOlkIEnkX7SqHwCgUJZLjhWUvg1AauaUgKVnz35Ror41juM4y+KNr9pFxnukSDUz8L+UvdZVCNu6Q==} + expo-glass-effect@57.0.1: + resolution: {integrity: sha512-m/n8maxqNcHk6ZDhuqXBfD5Kt1Iz3M8xykVgdB0iSCIXvF70IqWXmQhX8Psswhrp8eZ+3r0mAD0Jh/2gFA3QaA==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-haptics@57.0.0: - resolution: {integrity: sha512-va+uB/DoMlT9f0OJ8v24iNqs+YH6Q6wip+LLjVvalbJzGK8LvZQ+gIiauogFxAAFLQIJzrQVJGf+0BmV3q13bQ==} + expo-haptics@57.0.2: + resolution: {integrity: sha512-vPths6zTxxaGaemC7D1GKbw1iOnOAAL5oAcCFVSgVNWnuLCBefL1LkhRBaJsepiMXRh2y3vwPiXkPaf6d16blA==} peerDependencies: expo: '*' - expo-image-loader@57.0.0: - resolution: {integrity: sha512-EhwnoPC4T/EMdB7Nsg7qITzhO/qB0hUnmVghmtAKQwwDVaLWWYCGE4NLkes3zk9Ub451SmS/swgPp4PCPzOlnw==} + expo-image-loader@57.0.1: + resolution: {integrity: sha512-uhrZKLT/cTl2mXyR28kPpVkS5O+PK9N1QA/07IFM4f5T4g0lTW1JHT3NEWwEEsGFldPmVX4j7LwUVVZxE+woug==} peerDependencies: expo: '*' - expo-image-picker@57.0.2: - resolution: {integrity: sha512-XW+C5weIthkOLCJZzExeRJf9pcWfaXNHSDm76gmZVhDUVIHdi9IS3eihAIF0WA0fBo9ogIfQs/iep/c6CzIMKg==} + expo-image-picker@57.0.16: + resolution: {integrity: sha512-9CrY/hoBHI2GsOo/4AWz6cInRHhGhhCmbza3JYr4j3P/NWpthLlIm0IXSWpxadSYJx0a2FAti0f7XWMg95aPEg==} peerDependencies: expo: '*' - expo-json-utils@57.0.0: - resolution: {integrity: sha512-GJMjJlS3ZRTXWkKJPXY9OjscEEyPxbvNURBJ9Gkd3KxS4Hzof+EdK8pF+3Ty4ec435ciYm9ll+IWznuVHYMiow==} + expo-json-utils@57.0.1: + resolution: {integrity: sha512-cgTe1NqzQdYs/WN+3nIY5IZg8s0pb0xaTUbhYvxQDn137GbwRfHoGM2se3m3Vsl4Qu+B9G4RPEK5WJDEU2Do7g==} - expo-keep-awake@57.0.0: - resolution: {integrity: sha512-WqEoyDNSmUeAI9Gu7UaWKDjOhfaV+jGcms7N0hh/EAr7sqJrI2s0HpLSC3P9cWfXFUZCL1zZjd22m/NbsJYCKg==} + expo-keep-awake@57.0.1: + resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} peerDependencies: expo: '*' react: '*' @@ -7516,14 +7544,14 @@ packages: react: '*' react-native: '*' - expo-linking@57.0.2: - resolution: {integrity: sha512-A7912NxC4ibDiv7fN2BuEWCrTCmmWdaD+bQa4iNddPDOa3Svs1YDoWdbhJt3mniSaAh9AnUKS2MdFbO4zk8z+Q==} + expo-linking@57.0.9: + resolution: {integrity: sha512-TQJe3NsnpZXApBg1n6DKaZ4TNhYm+iYWq7W9rTp+G44IBMZSUcI8ba+ffYC/LMY1qw+95AuBPIF1zTx1NQoTXw==} peerDependencies: react: '*' react-native: '*' - expo-local-authentication@57.0.0: - resolution: {integrity: sha512-/ZzS7OD5OaAEhSF/+zPWZqzT8xWy1rMICfDqP5ojJ6Flh4wgAOedG37p8UZlTQYJRR5u1NQ9AoAePV8H/ky14g==} + expo-local-authentication@57.0.2: + resolution: {integrity: sha512-8K4zcrQ5wZkRS1rwEWY8qHbeGVMNh35e9D1VTVKlMHz1O47itW+pW6iBVuqwGFLozN4fRBeEDQH8hu9Gt58YuQ==} peerDependencies: expo: '*' @@ -7533,17 +7561,17 @@ packages: expo: '*' react: '*' - expo-manifests@57.0.0: - resolution: {integrity: sha512-JDKDuF1gd3wPywhu+VzH+qFNDlu91Unqpcre6R9b14MEMq5L3mG6XL9LOySChiWlLJAoObeNPdVV7Od6P++49g==} + expo-manifests@57.0.1: + resolution: {integrity: sha512-qB/mDG2dYdl+EvUeQuqP8KFYCFgFCQjJYdWIHo8SFBgDzMYmdF286DFY2M1M9Okr99wkb5M4tgA3aCcwv3aEQA==} peerDependencies: expo: '*' - expo-modules-autolinking@57.0.5: - resolution: {integrity: sha512-Vv2PVeEUN0/VW19ItkVA8LxAuH8SV8cjui0MKhJxCKrNyCAGGZU6sRsBJciYXR4uVxuXWKiepGW6E3k/c+Ytdg==} + expo-modules-autolinking@57.0.12: + resolution: {integrity: sha512-Q8KAlq37nLKsQ+HsS9NpQVpd5jCgqtu694TDUNHBUBpV9ViD82mRBh8Uug/h68RG9xnLS+kuL4nYaCuFRghHjg==} hasBin: true - expo-modules-core@57.0.3: - resolution: {integrity: sha512-fuD3DjPQvdaldtCJm2erV2/trPMrDJvEwPdz0RuxAQnduiNwZ3yxBoi81ZEKC5GBSJauMo53YXSwogsFagjwFQ==} + expo-modules-core@57.0.16: + resolution: {integrity: sha512-vPbxDOdvG6Ucpws5ucdbQa+nNmdxKONS1fTg92sy0p4TnHX8hBiExtbrtUA/5+1t3FRCUuiy29/1nJEGdt3E4A==} peerDependencies: react: '*' react-native: '*' @@ -7552,8 +7580,8 @@ packages: react-native-worklets: optional: true - expo-modules-jsi@57.0.1: - resolution: {integrity: sha512-dECN3pOFv+KrQcGrOhJKEBc1/ob7SBjTTYGRB1bc84zmQ65La0FSrAPxpvnEQf8g9giLB5y9RLqwGxHspjXigQ==} + expo-modules-jsi@57.0.8: + resolution: {integrity: sha512-MFwvKP0FXLkEET27KsKvOh/L7Dpu2d00UgzAlmKozJgtI23WTKSyKPxu+EOSs9peSgkHzKI0vQOhWjOseGBMkQ==} peerDependencies: react-native: '*' @@ -7563,29 +7591,29 @@ packages: expo: '*' react: '*' - expo-notifications@57.0.3: - resolution: {integrity: sha512-BTfrlnWF4EQZ0Oufsj00OesdxxlXCaC3OexnJTWKdQJ2a26w/bIAM3kXIxHsizmBWaHt5EJc5LvuDzVeUt7JJA==} + expo-notifications@57.0.17: + resolution: {integrity: sha512-P/9ZqBOlFBVCZK3T5bhqORtWJhtVBcXQXe/MY1YWuKaxWgKvoGeEvD9ItWuo87jg2nD+eloVmFYqwdhhVCxPlA==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-router@57.0.4: - resolution: {integrity: sha512-JVUAxamOQV7oG/uo/itLuR8gLiN3+C00bg/JPU+p/g/8Mx3PceZ4Mga/34cLmMc+RETpi5EjC/+AN0mJJzOW3w==} + expo-router@57.0.19: + resolution: {integrity: sha512-JovzZ066+qhykyJLC4hSEJpM8ToVWnJqbxmTN7USzARM0HgRR/g05a75f/EIsY4S//hbPGm6wGl/ObcCVtAX+A==} peerDependencies: - '@expo/log-box': ^57.0.0 - '@expo/metro-runtime': ^57.0.3 + '@expo/log-box': ^57.0.4 + '@expo/metro-runtime': ^57.0.15 '@testing-library/react-native': '>= 13.2.0' expo: '*' - expo-constants: ^57.0.3 - expo-linking: ^57.0.2 + expo-constants: ^57.0.17 + expo-linking: ^57.0.9 react: '*' react-dom: '*' react-native: '*' react-native-gesture-handler: '*' react-native-reanimated: '*' react-native-safe-area-context: '>= 5.4.0' - react-native-screens: ^4.25.2 + react-native-screens: ^4.26.0 react-native-web: '*' react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 peerDependenciesMeta: @@ -7602,31 +7630,31 @@ packages: react-server-dom-webpack: optional: true - expo-secure-store@57.0.0: - resolution: {integrity: sha512-vkP16rhW7b4bljW5BC4kKXBpNxQ0O1E9SpI5NIfh2biZnszLTpI/gUF4oBsvOY2nvkh7oXS2ERuUoA8cuS8FWQ==} + expo-secure-store@57.0.3: + resolution: {integrity: sha512-w7XkSQeUiYXPoKXo1jSrQqql7pyCSyIzOp2k0apsZZBE+RkoLTQIMjcbqc181y6KgupfNnYxkaKe+4MEg94+SA==} peerDependencies: expo: '*' - expo-server@57.0.0: - resolution: {integrity: sha512-18byjwFbHVFrGzI4IH1o2MZiiYwvO/y3d+JL3sq50Lg3Id1titwIRMh1fjbHbpM+wP6x14KJY0Ers1UYsjGq8Q==} + expo-server@57.0.3: + resolution: {integrity: sha512-aK+LdKzauHSGmsOStZtyxdzv0zWssCkxTw3m4QuOhfDSJsZaMRTd9O41d8ixU/QfELTbaJ0oRNcF7JFV/7O9YQ==} engines: {node: '>=20.16.0'} - expo-sharing@57.0.3: - resolution: {integrity: sha512-0bCVd27Hb0eokJ0UfafpfGwzePXjaxfgayQQSPXN2tsWDTYKtU2soIMF2+KkgEh7MBLq4PwdQmntPVcapAMnKA==} + expo-sharing@57.0.18: + resolution: {integrity: sha512-Q09Gab1yn+OLR7sqcHBNyjM/4MMQa290hDKCV2pf2THnTLkLLuUmC0AKgMgxygqU0uuzhOgVqfnE70dePCLq9A==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-sqlite@57.0.0: - resolution: {integrity: sha512-BYTaLjO9+CMaEYIp5fdrO0RGTkaumZrNuDSQgFkeZe1MfKeDjiS1myg66jxJviWaqpycLq7QnRT11+bdO23JHQ==} + expo-sqlite@57.0.2: + resolution: {integrity: sha512-5KVbT7BQFZlIcQBXKWvwBERK3ODF6PSqct27GQKukZzAjsx2B97R+mwtYqrTUtUbw71VJrQrdr9Yl2OWYC7UpA==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-status-bar@57.0.0: - resolution: {integrity: sha512-wq1fDVAjfrzCj67hOcvEkGpgRrb6xVI7oSA3bfsQ08SFk8il7vGixJq+xyEbtdcrI9CDiNKQrT3ZIjgnyGbbBA==} + expo-status-bar@57.0.1: + resolution: {integrity: sha512-Xwaq1gAoVRWx5dPG5VhT5RSbnI9OilhZnO5qoPBnUaBAa5VzRzfdS8q0/bsPt0jR2DKLtGuP0bQ6efMJ4RIMDg==} peerDependencies: expo: '*' react: '*' @@ -7635,27 +7663,27 @@ packages: expo-structured-headers@57.0.0: resolution: {integrity: sha512-//t9UNPbJSEysc2x4VKJG/u7Osvv5DYJWsET5bqt/B+qcD1by/JXvSQzX3Q/YAgA96xFPontrz6OAPLbO4JKEA==} - expo-symbols@57.0.0: - resolution: {integrity: sha512-pLjyiSqOV8NEqs2LqVZmF0cS1j90XoeT2oHbFciVjW7DiV7cP34SGLUgeDsOWY54QkWFR5p2Gz9LZWM8eBsSkA==} + expo-symbols@57.0.2: + resolution: {integrity: sha512-qZ0iqOflm5lZGwRsQ5Y8sDksw3GAUKwHSX1bJBoocXf7gu14vafIXXYWte+JT9VfXAUGyKodJhLHP/GoOrcNWg==} peerDependencies: expo: '*' expo-font: '*' react: '*' react-native: '*' - expo-task-manager@57.0.2: - resolution: {integrity: sha512-UMhs+Vc8TQoJOJRRRNapKNEFcBjmUIP0kFpk9xHBRZc//I9qkfxTG6Pxgakg7j7Be/6v03HO68wlIL8KwbPzHA==} + expo-task-manager@57.0.16: + resolution: {integrity: sha512-/GaGM1t+rA4PXtoMavL4HJwl9cUjbh3/u7LHGT2R0yzBiBGJ/nU0EQMx52qngrmZXebOwg4v39xcdBDC6190dQ==} peerDependencies: expo: '*' react-native: '*' - expo-updates-interface@57.0.0: - resolution: {integrity: sha512-AQASxPDpUjHG55R4WXZ5mLu0rE4F2T/JfHOsXLfZPS1A268ynHHFv+MnZ99/Gb0xWvg3ZjNTgXPEWoRCShSJJg==} + expo-updates-interface@57.0.1: + resolution: {integrity: sha512-+LUWwJ0gf/TEKMVdQAw/Gjih4dvrk+URgy24X9qEGKuuMDZqjBRm9T4yQyBVALGL5TTdPUaB6ILxx3lshm3pwQ==} peerDependencies: expo: '*' - expo-updates@57.0.6: - resolution: {integrity: sha512-w2bROszU/n4ZwHjUC+sRBwVWCkn1yoe5KrEOMS+q7r0OEj/8a+jUNIZbG6fJAQqjW74x8/gqulaxJRwZgGGlQg==} + expo-updates@57.0.21: + resolution: {integrity: sha512-JwTmguNvBvISEz9ALwrT8yDLnYuSP4/6kUz0kXkQuC8izebtTGkRPR37Cv39KPheDDA1F+QPhNyvw4lXM/BPvA==} hasBin: true peerDependencies: expo: '*' @@ -7666,14 +7694,14 @@ packages: expo-dev-client: optional: true - expo-web-browser@57.0.0: - resolution: {integrity: sha512-J+kQAqbH423Ge6YXrQTVKkmH+cS2SSjnc7ocoRDjhmfZ2+XSBXX3Ogds5/aEKjNKUe7ObtPMkVq9uWbXJv4BGw==} + expo-web-browser@57.0.2: + resolution: {integrity: sha512-3vl5kvd7PB48ub6PpNIJUuPxO8xVa6D8RnIgNba6SXRwqFprOfeEZgwTgtm41kz0AAtvMOztUVNEUkwrHKjqMQ==} peerDependencies: expo: '*' react-native: '*' - expo@57.0.4: - resolution: {integrity: sha512-5wW0SR6OnGUh+UPb3JWlFTAL22IgBcX36RqofjLAH2AuFqxAIpYIdwr9BYR9Wl3xyIe0t9lCoMQIZlAIZUNQvg==} + expo@57.0.20: + resolution: {integrity: sha512-H57GWLLpNPrltsrjvx6ROsMecoFQza7h9OzujUjBsaY+aDL+bj3GsZaH3T5v5B96R7txuidUwjUh5q5hi3qttg==} hasBin: true peerDependencies: '@expo/dom-webview': '*' @@ -8095,8 +8123,8 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - hermes-compiler@250829098.0.14: - resolution: {integrity: sha512-5meXwsZxgiqFaJjNzwjzI9IyUkuGGBisu+z9BvQWmGVpjH6nz11hgqkyxe4dl8UAdyIV4lTbz91+Dlnjz0VxqA==} + hermes-compiler@250829098.0.16: + resolution: {integrity: sha512-xsgzk+mUyvt9t1nUbF8USBlYxajTUtPJhVZ86q85s/SEoMKCF+52YZcudb0ENSnV3T3lV9mgB3s6R7+pH90zgw==} hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} @@ -8230,11 +8258,6 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - image-size@1.2.1: - resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} - engines: {node: '>=16.x'} - hasBin: true - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -8250,10 +8273,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -8939,61 +8958,61 @@ packages: mermaid@11.16.0: resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==} - metro-babel-transformer@0.84.4: - resolution: {integrity: sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==} + metro-babel-transformer@0.84.5: + resolution: {integrity: sha512-2WbHILKMiJUzfdjmGOQOqU1bWi9//gqiclc/tkk/AIsrrVw3efhZ1uhkOwMTxUEPOzqoo091H0olLmVZH5FHGQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache-key@0.84.4: - resolution: {integrity: sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==} + metro-cache-key@0.84.5: + resolution: {integrity: sha512-3dPB2TnvGjjf0/9O7AXVQURKXuQNauTZE7WpTGTlR017Gh/B5y0m/2wcqxfveUguHSpu89KhVxCAlr2k/H7uhQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache@0.84.4: - resolution: {integrity: sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==} + metro-cache@0.84.5: + resolution: {integrity: sha512-WHS0n2OxQqtwEjSeQFPePNrMvEFhmQcUQM9cRJMHByWoi/GMWFBEWOf7hVkAM/0KRutAXNbDlSu/cZB6CyxgQQ==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-config@0.84.4: - resolution: {integrity: sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==} + metro-config@0.84.5: + resolution: {integrity: sha512-zie+uN6oohscowi2S7ByU+wUw6CrT4ZxW9uAbONOObSxx86RGmnIAmjXHLkfmcdYoY7jzOPEbqcI6oeVmqyBQA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-core@0.84.4: - resolution: {integrity: sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==} + metro-core@0.84.5: + resolution: {integrity: sha512-xwm605hCi5Y6eJTTb8ZWo6pkUcoBEIyiQOfkZh5GwtDwUrP9SNhTQZhzJHrBCwwxlf3Ptl/pxWJgQ1rsNYMnrA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-file-map@0.84.4: - resolution: {integrity: sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==} + metro-file-map@0.84.5: + resolution: {integrity: sha512-mlm/JL8toSbSc2akpKIGmzvrVRSCgZ5vkbycI34oMLoOnLGuLyC8WTyVJ6P0hZG/usDaGwZSl/s9BCRriqjGJA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-minify-terser@0.84.4: - resolution: {integrity: sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==} + metro-minify-terser@0.84.5: + resolution: {integrity: sha512-BJoFwCEDsYnagPqarayInv2+diCDNDdLlaof/p6s9w4gh+gc9HXYM+pDvsKGKKUumpZswNF3Z/ftTMqKl/5IBg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-resolver@0.84.4: - resolution: {integrity: sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==} + metro-resolver@0.84.5: + resolution: {integrity: sha512-VSSnepg1k6LyCwtb6eirWdAWlpKwBG8Rdtsr1mU38rMelFyWgh3/QuMSiZIZAIjwg/fsa8GhW5/FO54CAUPCEA==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-runtime@0.84.4: - resolution: {integrity: sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==} + metro-runtime@0.84.5: + resolution: {integrity: sha512-U1m2+d1Pr+JO2/iVXBB2OfXXityz7tqwIorxfrT15IEgaHvpJBq/OHiqnOWPKJbUl3JcxjcdviZZOKk85oK4Qg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-source-map@0.84.4: - resolution: {integrity: sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==} + metro-source-map@0.84.5: + resolution: {integrity: sha512-2BtV5L9uPc49F13Gn5wiP6bX/EncqzqTIk2VL/0F/96Vo0YEOjluT/qktQjFODfqGFsucwnh5mPEAl/2jVEfeg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-symbolicate@0.84.4: - resolution: {integrity: sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==} + metro-symbolicate@0.84.5: + resolution: {integrity: sha512-rQ40zYDAkaWBN9yvjUuAD0ZpzBMZSoKyGYXnb5JrfbKjun7fTvfoLHL3KXFYenBTYZkQtlp4cKSCv/1utxFyOw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true - metro-transform-plugins@0.84.4: - resolution: {integrity: sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==} + metro-transform-plugins@0.84.5: + resolution: {integrity: sha512-+InaSVGaOyt0DyRo4Y/zIdPI6CZwnbNho5LAL23tgmuGwv7fyfkF7kKfPjZcfxXBcoYdTLLFnCfCH/dHSiCqNg==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-transform-worker@0.84.4: - resolution: {integrity: sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==} + metro-transform-worker@0.84.5: + resolution: {integrity: sha512-ui1Z8x4s5RL36gMmKLaMMO7O9NNDHNdthEZSCDQHAau3JcAsTaFOK6I+2q4I/kW5u8hSEjJk9L45TXSVJw6g1A==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro@0.84.4: - resolution: {integrity: sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==} + metro@0.84.5: + resolution: {integrity: sha512-r1liLkyFZMVSEMNjU1CJU5pRzs3NdkxHqXS60O25c0rCIqAR+cGk7rPydw/g0WAIKVXojIBIF45yYBPagJGcgw==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true @@ -9157,10 +9176,6 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -9269,8 +9284,8 @@ packages: multipasta@0.2.8: resolution: {integrity: sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q==} - multitars@1.0.0: - resolution: {integrity: sha512-H/J4fMLedtudftaYMOg7ajzLYgT3/rwbWVJbqr/iUgB8DQztn38ys5HOqI1CzSxx8QhXXwOOnnBvd4v3jG5+Mg==} + multitars@1.0.2: + resolution: {integrity: sha512-6GwVw5eLi9sThdtlS4PKwC7yRLaf45pYhIEzKBHdKxi+YOXGKFX8acIniH+Uh/+k9mS2lQOupTccjoe5r0/1IQ==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -9387,8 +9402,8 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} - ob1@0.84.4: - resolution: {integrity: sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==} + ob1@0.84.5: + resolution: {integrity: sha512-aH9RkoZc7w/90HBamFxTw8ZLFr05wXS+iOnvmrgo53Ep8Pyrm5FieQSaPIVROkfFVQISeD/zo92fes26TOwe+A==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} object-assign@4.1.1: @@ -9839,9 +9854,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - queue@6.0.2: - resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} - quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -9938,8 +9950,8 @@ packages: react-native: '*' react-native-reanimated: '>=3.0.0' - react-native-reanimated@4.5.0: - resolution: {integrity: sha512-+iPfvK34PKKYP/p/4TaBliFkbfvjGDIvXuiiaxvISP5ip7sWegvlacwU/uAV6zNDSSmX0tDyER7PurPMKGDipA==} + react-native-reanimated@4.5.1: + resolution: {integrity: sha512-RnMvtDuR+68ig864gAvZCOdZehqhC5rFmMo0kn+ARfgVSTvFeF6IFLBVgMPUu0KwihaapEyW24WRi6nEyy1kSA==} peerDependencies: react: '*' react-native: 0.83 - 0.86 @@ -9951,11 +9963,11 @@ packages: react: '*' react-native: '*' - react-native-screens@4.25.2: - resolution: {integrity: sha512-1Nj1fusFd+rIMKU/qC9yGKVG+3ofh11d3OdBQKL1iVvQfKvcB8vhvTGQf2TkfxW3bamxN+hCZIXmNuU0mRkyDg==} + react-native-screens@4.26.2: + resolution: {integrity: sha512-2XnWsZToKj76trGtEZzx5ELD/qOICFEprEeUntImmitQFVUkea27fiWdUSITArI356Y1qynpXZINW+Unbhky/A==} peerDependencies: react: '*' - react-native: '>=0.82.0' + react-native: '*' react-native-svg@15.15.4: resolution: {integrity: sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A==} @@ -9975,20 +9987,20 @@ packages: react: '*' react-native: '*' - react-native-worklets@0.10.0: - resolution: {integrity: sha512-JhE6IxDf6iabC0qu3+TAKA4v9RlluXmoIngPQX7/QUByf75lfrsHZ6/dQhyjEWnp1EEQiwzz8Cpew140ZcewDw==} + react-native-worklets@0.10.1: + resolution: {integrity: sha512-62mRM19bDpfpdI8HLkEErcdOsrAPDtE9lA/sw+5lLRpzBHNhxaoj9QyY2KjXqUmirelxkX4zuPGTC3VdA0feJA==} peerDependencies: '@babel/core': '*' '@react-native/metro-config': '*' react: '*' react-native: 0.83 - 0.86 - react-native@0.86.0: - resolution: {integrity: sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w==} + react-native@0.86.2: + resolution: {integrity: sha512-zbJXGZpwfZGA79Z9ob6Atvfx4nAQL8yJBa35s58E4Oo+khPykfQP2sTeumkKbjwajFYfVayg8pj7Il9nIfTk7A==} engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true peerDependencies: - '@react-native/jest-preset': 0.86.0 + '@react-native/jest-preset': 0.86.2 '@types/react': ^19.1.1 react: ^19.2.3 peerDependenciesMeta: @@ -10071,10 +10083,6 @@ packages: real-require@1.0.0: resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -10286,6 +10294,11 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sandbox-cli-detector@0.2.0: + resolution: {integrity: sha512-4lyHX0ZU0AZKwjgZ1InxZAa3PNpyEb8rOQ+Zss1ReYmhNzW0Q+h1zE5nvniXN0HaAWZaZE1zgVNEirb0R7LmNg==} + engines: {node: '>=18.18'} + hasBin: true + sanitize-filename@1.6.4: resolution: {integrity: sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==} @@ -10599,10 +10612,6 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -10987,8 +10996,8 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - unimodules-app-loader@57.0.0: - resolution: {integrity: sha512-1hHiDvLMu4dwTnFzEjFKa/HS6TvTrO4qdbFWyPENCElab5BqeATMPLP1NtNPE4p2pwiJveVHuq6qmjDvdfj7VA==} + unimodules-app-loader@57.0.1: + resolution: {integrity: sha512-wey5ChoJkCTq0j0JWdIMu2QB81vVrdhmrNAP14ZZ6WDslnZ7ff7Ezv8rMdEnVHaCKz3xK4mIVXbVU51xHgdyCA==} unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -11505,8 +11514,6 @@ packages: snapshots: - '@adobe/css-tools@4.5.0': {} - '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.6.0 @@ -12342,12 +12349,12 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@bacons/apple-targets@4.0.7(expo@57.0.4)(supports-color@8.1.1)': + '@bacons/apple-targets@4.0.7(expo@57.0.20)(supports-color@8.1.1)': dependencies: '@bacons/xcode': 1.0.0-alpha.32(supports-color@8.1.1) '@react-native/normalize-colors': 0.79.7 debug: 4.4.3(supports-color@8.1.1) - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) glob: 10.5.0 transitivePeerDependencies: - supports-color @@ -12425,7 +12432,7 @@ snapshots: optionalDependencies: electron: 43.2.0(supports-color@8.1.1) - '@better-auth/expo@1.7.0-rc.2(5705381685e3aee0fa9e57d39f706f20)': + '@better-auth/expo@1.7.0-rc.2(3e673fc1d89bb5bb2e0be4405d007f02)': dependencies: '@better-auth/core': 1.7.0-rc.2(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.7)(kysely@0.29.4)(nanostores@1.4.2) '@better-fetch/fetch': 1.3.1 @@ -12433,10 +12440,10 @@ snapshots: better-call: 1.3.7(zod@4.4.3) zod: 4.4.3 optionalDependencies: - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) - expo-linking: 57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) - expo-network: 57.0.1(expo@57.0.4)(react@19.2.3) - expo-web-browser: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + expo-linking: 57.0.9(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + expo-network: 57.0.1(expo@57.0.20)(react@19.2.3) + expo-web-browser: 57.0.2(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) '@better-auth/kysely-adapter@1.7.0-rc.2(@better-auth/core@1.7.0-rc.2(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.7)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4)': dependencies: @@ -13101,33 +13108,33 @@ snapshots: '@expo-google-fonts/material-symbols@0.4.38': {} - '@expo/cli@57.0.6(@expo/dom-webview@57.0.0)(@expo/metro-runtime@57.0.3)(@typescript/typescript6@6.0.2)(expo-constants@57.0.3)(expo-font@57.0.0)(expo-router@57.0.4)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)': + '@expo/cli@57.0.22(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(@typescript/typescript6@6.0.2)(expo-constants@57.0.17)(expo-font@57.0.3)(expo-router@57.0.19)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)': dependencies: '@expo/code-signing-certificates': 0.0.6 - '@expo/config': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/config-plugins': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-plugins': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@expo/devcert': 1.2.1(supports-color@8.1.1) - '@expo/env': 2.4.1(supports-color@8.1.1) - '@expo/image-utils': 0.11.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/inline-modules': 0.1.2(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/json-file': 11.0.0 - '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@expo/metro': 56.0.0(supports-color@8.1.1) - '@expo/metro-config': 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(supports-color@8.1.1) - '@expo/metro-file-map': 57.0.0(supports-color@8.1.1) - '@expo/osascript': 2.7.0 - '@expo/package-manager': 1.13.0 - '@expo/plist': 0.8.0 - '@expo/prebuild-config': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/require-utils': 57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/router-server': 57.0.2(@expo/metro-runtime@57.0.3)(expo-constants@57.0.3)(expo-font@57.0.0)(expo-router@57.0.4)(expo-server@57.0.0)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@8.1.1) - '@expo/schema-utils': 57.0.1 + '@expo/env': 2.4.3(supports-color@8.1.1) + '@expo/image-utils': 0.11.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/inline-modules': 0.1.7(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/json-file': 11.0.1 + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/metro': 56.0.2(supports-color@8.1.1) + '@expo/metro-config': 57.0.12(@typescript/typescript6@6.0.2)(expo@57.0.20)(supports-color@8.1.1) + '@expo/metro-file-map': 57.0.2(supports-color@8.1.1) + '@expo/osascript': 2.7.1 + '@expo/package-manager': 1.13.1 + '@expo/plist': 0.8.1 + '@expo/prebuild-config': 57.0.15(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/require-utils': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/router-server': 57.0.9(@expo/metro-runtime@57.0.15)(expo-constants@57.0.17)(expo-font@57.0.3)(expo-router@57.0.19)(expo-server@57.0.3)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@8.1.1) + '@expo/schema-utils': 57.0.2 '@expo/spawn-async': 1.8.0 '@expo/ws-tunnel': 2.0.0(ws@8.21.1) '@expo/xcpretty': 4.4.4 - '@react-native/dev-middleware': 0.86.0(supports-color@8.1.1) + '@react-native/dev-middleware': 0.86.3(supports-color@8.1.1) accepts: 1.3.8 - agent-cli-detector: 0.1.2 + agent-cli-detector: 0.1.7 arg: 5.0.2 bplist-creator: 0.1.0 bplist-parser: 0.3.2 @@ -13137,13 +13144,13 @@ snapshots: connect: 3.7.0(supports-color@8.1.1) debug: 4.4.3(supports-color@8.1.1) dnssd-advertise: 1.1.6 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-server: 57.0.0 + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-server: 57.0.3 fetch-nodeshim: 0.4.10 getenv: 2.0.0 glob: 13.0.6 lan-network: 0.2.1 - multitars: 1.0.0 + multitars: 1.0.2 node-forge: 1.4.0 npm-package-arg: 11.0.3 ora: 3.4.0 @@ -13152,6 +13159,7 @@ snapshots: progress: 2.0.3 prompts: 2.4.2 resolve-from: 5.0.0 + sandbox-cli-detector: 0.2.0 semver: 7.8.5 send: 0.19.2(supports-color@8.1.1) slugify: 1.6.9 @@ -13163,8 +13171,8 @@ snapshots: ws: 8.21.1 zod: 3.25.76 optionalDependencies: - expo-router: 57.0.4(3bc51b20989ed2c3020a920bb0eeb449) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + expo-router: 57.0.19(c021d0cd4acfce308650ff4c6746808d) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - '@expo/dom-webview' - '@expo/metro-runtime' @@ -13182,12 +13190,12 @@ snapshots: dependencies: node-forge: 1.4.0 - '@expo/config-plugins@57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/config-plugins@57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@expo/config-types': 57.0.1 - '@expo/json-file': 11.0.0 - '@expo/plist': 0.8.0 - '@expo/require-utils': 57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/plist': 0.8.1 + '@expo/require-utils': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 debug: 4.4.3(supports-color@8.1.1) @@ -13201,14 +13209,14 @@ snapshots: - supports-color - typescript - '@expo/config-types@57.0.1': {} + '@expo/config-types@57.0.2': {} - '@expo/config@57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/config@57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@expo/config-plugins': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/config-types': 57.0.1 - '@expo/json-file': 11.0.0 - '@expo/require-utils': 57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-plugins': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/require-utils': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) deepmerge: 4.3.1 getenv: 2.0.0 glob: 13.0.6 @@ -13226,20 +13234,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@57.0.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@expo/devtools@57.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: chalk: 4.1.2 optionalDependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - '@expo/dom-webview@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@expo/dom-webview@57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - '@expo/env@2.4.1(supports-color@8.1.1)': + '@expo/env@2.4.3(supports-color@8.1.1)': dependencies: chalk: 4.1.2 debug: 4.4.3(supports-color@8.1.1) @@ -13247,11 +13255,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/expo-modules-macros-plugin@0.3.0': {} + '@expo/expo-modules-macros-plugin@0.6.1': {} - '@expo/fingerprint@0.20.3(supports-color@8.1.1)': + '@expo/fingerprint@0.20.12(supports-color@8.1.1)': dependencies: - '@expo/env': 2.4.1(supports-color@8.1.1) + '@expo/env': 2.4.3(supports-color@8.1.1) '@expo/spawn-async': 1.8.0 arg: 5.0.2 chalk: 4.1.2 @@ -13265,9 +13273,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/image-utils@0.11.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/image-utils@0.11.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@expo/require-utils': 57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/require-utils': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@expo/spawn-async': 1.8.0 chalk: 4.1.2 getenv: 2.0.0 @@ -13278,45 +13286,45 @@ snapshots: - supports-color - typescript - '@expo/inline-modules@0.1.2(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/inline-modules@0.1.7(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@expo/config-plugins': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-plugins': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - typescript - '@expo/json-file@11.0.0': + '@expo/json-file@11.0.1': dependencies: '@babel/code-frame': 7.29.7 json5: 2.2.3 - '@expo/local-build-cache-provider@57.0.2(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/local-build-cache-provider@57.0.8(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@expo/config': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) chalk: 4.1.2 transitivePeerDependencies: - supports-color - typescript - '@expo/log-box@57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@expo/log-box@57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: - '@expo/dom-webview': 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/dom-webview': 57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) anser: 1.4.10 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) stacktrace-parser: 0.1.11 - '@expo/metro-config@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(supports-color@8.1.1)': + '@expo/metro-config@57.0.12(@typescript/typescript6@6.0.2)(expo@57.0.20)(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/generator': 7.29.7 - '@expo/config': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/env': 2.4.1(supports-color@8.1.1) - '@expo/json-file': 11.0.0 - '@expo/metro': 56.0.0(supports-color@8.1.1) - '@expo/require-utils': 57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/env': 2.4.3(supports-color@8.1.1) + '@expo/json-file': 11.0.1 + '@expo/metro': 56.0.2(supports-color@8.1.1) + '@expo/require-utils': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@expo/spawn-async': 1.8.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/remapping': 2.3.5 @@ -13333,14 +13341,14 @@ snapshots: postcss: 8.5.24 resolve-from: 5.0.0 optionalDependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) transitivePeerDependencies: - bufferutil - supports-color - typescript - utf-8-validate - '@expo/metro-file-map@57.0.0(supports-color@8.1.1)': + '@expo/metro-file-map@57.0.2(supports-color@8.1.1)': dependencies: debug: 4.4.3(supports-color@8.1.1) fb-watchman: 2.0.2 @@ -13351,47 +13359,47 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/metro-runtime@57.0.3(@expo/log-box@57.0.0)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@expo/metro-runtime@57.0.15(@expo/log-box@57.0.4)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: - '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) anser: 1.4.10 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) pretty-format: 29.7.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) stacktrace-parser: 0.1.11 whatwg-fetch: 3.6.20 optionalDependencies: react-dom: 19.2.3(react@19.2.3) - '@expo/metro@56.0.0(supports-color@8.1.1)': - dependencies: - metro: 0.84.4(supports-color@8.1.1) - metro-babel-transformer: 0.84.4(supports-color@8.1.1) - metro-cache: 0.84.4(supports-color@8.1.1) - metro-cache-key: 0.84.4 - metro-config: 0.84.4(supports-color@8.1.1) - metro-core: 0.84.4 - metro-file-map: 0.84.4(supports-color@8.1.1) - metro-minify-terser: 0.84.4 - metro-resolver: 0.84.4 - metro-runtime: 0.84.4 - metro-source-map: 0.84.4(supports-color@8.1.1) - metro-symbolicate: 0.84.4(supports-color@8.1.1) - metro-transform-plugins: 0.84.4(supports-color@8.1.1) - metro-transform-worker: 0.84.4(supports-color@8.1.1) + '@expo/metro@56.0.2(supports-color@8.1.1)': + dependencies: + metro: 0.84.5(supports-color@8.1.1) + metro-babel-transformer: 0.84.5(supports-color@8.1.1) + metro-cache: 0.84.5(supports-color@8.1.1) + metro-cache-key: 0.84.5 + metro-config: 0.84.5(supports-color@8.1.1) + metro-core: 0.84.5 + metro-file-map: 0.84.5(supports-color@8.1.1) + metro-minify-terser: 0.84.5 + metro-resolver: 0.84.5 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@8.1.1) + metro-symbolicate: 0.84.5(supports-color@8.1.1) + metro-transform-plugins: 0.84.5(supports-color@8.1.1) + metro-transform-worker: 0.84.5(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@expo/osascript@2.7.0': + '@expo/osascript@2.7.1': dependencies: '@expo/spawn-async': 1.8.0 - '@expo/package-manager@1.13.0': + '@expo/package-manager@1.13.1': dependencies: - '@expo/json-file': 11.0.0 + '@expo/json-file': 11.0.1 '@expo/spawn-async': 1.8.0 chalk: 4.1.2 npm-package-arg: 11.0.3 @@ -13404,29 +13412,29 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 14.0.0 - '@expo/plist@0.8.0': + '@expo/plist@0.8.1': dependencies: '@xmldom/xmldom': 0.8.13 base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/prebuild-config@57.0.15(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@expo/config': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/config-plugins': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/config-types': 57.0.1 - '@expo/image-utils': 0.11.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/json-file': 11.0.0 - '@react-native/normalize-colors': 0.86.0 + '@expo/config': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-plugins': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-types': 57.0.2 + '@expo/image-utils': 0.11.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/json-file': 11.0.1 + '@react-native/normalize-colors': 0.86.3 debug: 4.4.3(supports-color@8.1.1) - expo-modules-autolinking: 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + expo-modules-autolinking: 57.0.12(@typescript/typescript6@6.0.2)(supports-color@8.1.1) resolve-from: 5.0.0 semver: 7.8.5 transitivePeerDependencies: - supports-color - typescript - '@expo/require-utils@57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': + '@expo/require-utils@57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@8.1.1) @@ -13436,22 +13444,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/router-server@57.0.2(@expo/metro-runtime@57.0.3)(expo-constants@57.0.3)(expo-font@57.0.0)(expo-router@57.0.4)(expo-server@57.0.0)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@8.1.1)': + '@expo/router-server@57.0.9(@expo/metro-runtime@57.0.15)(expo-constants@57.0.17)(expo-font@57.0.3)(expo-router@57.0.19)(expo-server@57.0.3)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@8.1.1)': dependencies: debug: 4.4.3(supports-color@8.1.1) - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) - expo-font: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-server: 57.0.0 + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + expo-font: 57.0.3(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-server: 57.0.3 react: 19.2.3 optionalDependencies: - '@expo/metro-runtime': 57.0.3(@expo/log-box@57.0.0)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-router: 57.0.4(3bc51b20989ed2c3020a920bb0eeb449) + '@expo/metro-runtime': 57.0.15(@expo/log-box@57.0.4)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-router: 57.0.19(c021d0cd4acfce308650ff4c6746808d) react-dom: 19.2.3(react@19.2.3) transitivePeerDependencies: - supports-color - '@expo/schema-utils@57.0.1': {} + '@expo/schema-utils@57.0.2': {} '@expo/sdk-runtime-versions@1.0.0': {} @@ -13461,17 +13469,32 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/ui@57.0.7(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@expo/ui@57.0.11(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) sf-symbols-typescript: 2.2.0 vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: '@babel/core': 7.29.7(supports-color@8.1.1) react-dom: 19.2.3(react@19.2.3) - react-native-worklets: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + '@expo/ui@57.0.16(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + dependencies: + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + sf-symbols-typescript: 2.2.0 + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + optionalDependencies: + '@babel/core': 7.29.7(supports-color@8.1.1) + react-dom: 19.2.3(react@19.2.3) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -13540,40 +13563,40 @@ snapshots: - supports-color - utf-8-validate - '@gorhom/bottom-sheet@5.2.14(6e44dcb7dcfecf271936e923703300f2)': + '@gorhom/bottom-sheet@5.2.14(50aa6fff1dfa2319f41b6b41f4bd94c3)': dependencies: - '@gorhom/portal': 1.0.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@gorhom/portal': 1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) invariant: 2.2.4 - react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react: 19.3.0-canary-96fcba90-20260728 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) optionalDependencies: '@types/react': 19.2.17 + optional: true - '@gorhom/bottom-sheet@5.2.14(da77d43574db60c81967e07362a6f6fb)': + '@gorhom/bottom-sheet@5.2.14(b3053a3c63c272f26bedf4b267721ee5)': dependencies: - '@gorhom/portal': 1.0.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + '@gorhom/portal': 1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) invariant: 2.2.4 - react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) - react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react: 19.2.3 + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) optionalDependencies: '@types/react': 19.2.17 - optional: true - '@gorhom/portal@1.0.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@gorhom/portal@1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: nanoid: 3.3.16 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - '@gorhom/portal@1.0.14(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)': + '@gorhom/portal@1.0.14(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)': dependencies: nanoid: 3.3.16 react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) optional: true '@hono/node-server@2.0.12(hono@4.12.32)': @@ -14568,27 +14591,35 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 - '@react-native-community/netinfo@12.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@react-native-community/netinfo@12.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - '@react-native/assets-registry@0.86.0': {} + '@react-native/assets-registry@0.86.2': {} + + '@react-native/babel-plugin-codegen@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': + dependencies: + '@babel/traverse': 7.29.7(supports-color@8.1.1) + '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1)) + transitivePeerDependencies: + - '@babel/core' + - supports-color - '@react-native/babel-plugin-codegen@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': + '@react-native/babel-plugin-codegen@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/traverse': 7.29.7(supports-color@8.1.1) - '@react-native/codegen': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1)) + '@react-native/codegen': 0.86.3(@babel/core@7.29.7(supports-color@8.1.1)) transitivePeerDependencies: - '@babel/core' - supports-color - '@react-native/babel-preset@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': + '@react-native/babel-preset@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) @@ -14619,14 +14650,14 @@ snapshots: '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) - '@react-native/babel-plugin-codegen': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/babel-plugin-codegen': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-syntax-hermes-parser: 0.36.0 babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7(supports-color@8.1.1)) react-refresh: 0.14.2 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))': + '@react-native/codegen@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))': dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/parser': 7.29.7 @@ -14636,25 +14667,37 @@ snapshots: tinyglobby: 0.2.17 yargs: 17.7.3 - '@react-native/community-cli-plugin@0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1)': + '@react-native/codegen@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))': dependencies: - '@react-native/dev-middleware': 0.86.0(supports-color@8.1.1) + '@babel/core': 7.29.7(supports-color@8.1.1) + '@babel/parser': 7.29.7 + hermes-parser: 0.36.0 + invariant: 2.2.4 + nullthrows: 1.1.1 + tinyglobby: 0.2.17 + yargs: 17.7.3 + + '@react-native/community-cli-plugin@0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1)': + dependencies: + '@react-native/dev-middleware': 0.86.2(supports-color@8.1.1) debug: 4.4.3(supports-color@8.1.1) invariant: 2.2.4 - metro: 0.84.4(supports-color@8.1.1) - metro-config: 0.84.4(supports-color@8.1.1) - metro-core: 0.84.4 + metro: 0.84.5(supports-color@8.1.1) + metro-config: 0.84.5(supports-color@8.1.1) + metro-core: 0.84.5 semver: 7.8.5 optionalDependencies: - '@react-native/metro-config': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/debugger-frontend@0.86.0': {} + '@react-native/debugger-frontend@0.86.2': {} - '@react-native/debugger-shell@0.86.0(supports-color@8.1.1)': + '@react-native/debugger-frontend@0.86.3': {} + + '@react-native/debugger-shell@0.86.2(supports-color@8.1.1)': dependencies: cross-spawn: 7.0.6 debug: 4.4.3(supports-color@8.1.1) @@ -14662,11 +14705,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/dev-middleware@0.86.0(supports-color@8.1.1)': + '@react-native/debugger-shell@0.86.3(supports-color@8.1.1)': + dependencies: + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color + + '@react-native/dev-middleware@0.86.2(supports-color@8.1.1)': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.86.0 - '@react-native/debugger-shell': 0.86.0(supports-color@8.1.1) + '@react-native/debugger-frontend': 0.86.2 + '@react-native/debugger-shell': 0.86.2(supports-color@8.1.1) chrome-launcher: 0.15.2(supports-color@8.1.1) chromium-edge-launcher: 0.3.0(supports-color@8.1.1) connect: 3.7.0(supports-color@8.1.1) @@ -14681,25 +14732,44 @@ snapshots: - supports-color - utf-8-validate - '@react-native/gradle-plugin@0.86.0': {} + '@react-native/dev-middleware@0.86.3(supports-color@8.1.1)': + dependencies: + '@isaacs/ttlcache': 1.4.1 + '@react-native/debugger-frontend': 0.86.3 + '@react-native/debugger-shell': 0.86.3(supports-color@8.1.1) + chrome-launcher: 0.15.2(supports-color@8.1.1) + chromium-edge-launcher: 0.3.0(supports-color@8.1.1) + connect: 3.7.0(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) + invariant: 2.2.4 + nullthrows: 1.1.1 + open: 7.4.2 + serve-static: 1.16.3(supports-color@8.1.1) + ws: 7.5.11 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - '@react-native/js-polyfills@0.86.0': {} + '@react-native/gradle-plugin@0.86.2': {} - '@react-native/metro-babel-transformer@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': + '@react-native/js-polyfills@0.86.2': {} + + '@react-native/metro-babel-transformer@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) - '@react-native/babel-preset': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/babel-preset': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) hermes-parser: 0.36.0 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': + '@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@react-native/js-polyfills': 0.86.0 - '@react-native/metro-babel-transformer': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) - metro-config: 0.84.4(supports-color@8.1.1) - metro-runtime: 0.84.4 + '@react-native/js-polyfills': 0.86.2 + '@react-native/metro-babel-transformer': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + metro-config: 0.84.5(supports-color@8.1.1) + metro-runtime: 0.84.5 transitivePeerDependencies: - '@babel/core' - bufferutil @@ -14710,23 +14780,25 @@ snapshots: '@react-native/normalize-colors@0.79.7': {} - '@react-native/normalize-colors@0.86.0': {} + '@react-native/normalize-colors@0.86.2': {} + + '@react-native/normalize-colors@0.86.3': {} - '@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': + '@react-native/virtualized-lists@0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) optionalDependencies: '@types/react': 19.2.17 - '@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)': + '@react-native/virtualized-lists@0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) optionalDependencies: '@types/react': 19.2.17 @@ -15023,7 +15095,7 @@ snapshots: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/react-native@7.11.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)': + '@sentry/react-native@7.11.0(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)': dependencies: '@sentry/babel-plugin-component-annotate': 4.8.0 '@sentry/browser': 10.37.0 @@ -15032,9 +15104,9 @@ snapshots: '@sentry/react': 10.37.0(react@19.2.3) '@sentry/types': 10.37.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) optionalDependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) transitivePeerDependencies: - encoding - supports-color @@ -15465,15 +15537,6 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.1': - dependencies: - '@adobe/css-tools': 4.5.0 - aria-query: 5.3.2 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - picocolors: 1.1.1 - redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.3.0-canary-96fcba90-20260728(react@19.3.0-canary-96fcba90-20260728))(react@19.3.0-canary-96fcba90-20260728)': dependencies: '@babel/runtime': 7.29.7 @@ -16088,7 +16151,7 @@ snapshots: agent-base@9.0.0: {} - agent-cli-detector@0.1.2: {} + agent-cli-detector@0.1.7: {} ajv-formats@3.0.1(ajv@8.20.0): optionalDependencies: @@ -16208,8 +16271,6 @@ snapshots: dependencies: dequal: 2.0.3 - aria-query@5.3.2: {} - asap@2.0.6: {} asn1js@3.0.10: @@ -16280,7 +16341,7 @@ snapshots: transitivePeerDependencies: - '@babel/core' - babel-preset-expo@57.0.2(@babel/core@7.29.7(supports-color@8.1.1))(@babel/runtime@7.29.7)(expo@57.0.4)(react-refresh@0.14.2)(supports-color@8.1.1): + babel-preset-expo@57.0.10(@babel/core@7.29.7(supports-color@8.1.1))(@babel/runtime@7.29.7)(expo@57.0.20)(react-refresh@0.14.2)(supports-color@8.1.1): dependencies: '@babel/generator': 7.29.7 '@babel/helper-module-imports': 7.29.7(supports-color@8.1.1) @@ -16318,7 +16379,7 @@ snapshots: '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) - '@react-native/babel-plugin-codegen': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/babel-plugin-codegen': 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 babel-plugin-syntax-hermes-parser: 0.36.1 @@ -16327,7 +16388,7 @@ snapshots: react-refresh: 0.14.2 optionalDependencies: '@babel/runtime': 7.29.7 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) transitivePeerDependencies: - '@babel/core' - supports-color @@ -16867,8 +16928,6 @@ snapshots: css-what@6.2.2: {} - css.escape@1.5.1: {} - csstype@3.2.3: {} culori@4.0.2: {} @@ -17181,8 +17240,6 @@ snapshots: dom-accessibility-api@0.5.16: {} - dom-accessibility-api@0.6.3: {} - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -17757,184 +17814,184 @@ snapshots: expect-type@1.3.0: {} - expo-apple-authentication@57.0.1(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-apple-authentication@57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-application@57.0.2(expo@57.0.4): + expo-application@57.0.2(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-asset@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + expo-asset@57.0.16(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: - '@expo/image-utils': 0.11.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + '@expo/image-utils': 0.11.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - typescript - expo-audio@57.0.0(expo-asset@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-audio@57.0.4(expo-asset@57.0.16(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-asset: 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-asset: 57.0.16(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-background-task@57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-background-task@57.0.16(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-task-manager: 57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-task-manager: 57.0.16(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) transitivePeerDependencies: - react-native - expo-camera@57.0.1(@types/emscripten@1.41.5)(expo@57.0.4)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-camera@57.0.4(@types/emscripten@1.41.5)(expo@57.0.20)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: barcode-detector: 3.2.1(@types/emscripten@1.41.5) - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) optionalDependencies: react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - '@types/emscripten' - expo-clipboard@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-clipboard@57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-constants@57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1): + expo-constants@57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@expo/env': 2.4.1(supports-color@8.1.1) - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + '@expo/env': 2.4.3(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - expo-crypto@57.0.1(expo@57.0.4): + expo-crypto@57.0.1(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-dev-client@57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-dev-client@57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-dev-launcher: 57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) - expo-dev-menu: 57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) - expo-dev-menu-interface: 57.0.0(expo@57.0.4) - expo-manifests: 57.0.0(expo@57.0.4) - expo-updates-interface: 57.0.0(expo@57.0.4) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-dev-launcher: 57.0.19(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-dev-menu: 57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-dev-menu-interface: 57.0.0(expo@57.0.20) + expo-manifests: 57.0.1(expo@57.0.20) + expo-updates-interface: 57.0.1(expo@57.0.20) transitivePeerDependencies: - react-native - expo-dev-launcher@57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-dev-launcher@57.0.19(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - '@expo/schema-utils': 57.0.1 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-dev-menu: 57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) - expo-manifests: 57.0.0(expo@57.0.4) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + '@expo/schema-utils': 57.0.2 + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-dev-menu: 57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-manifests: 57.0.1(expo@57.0.20) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-dev-menu-interface@57.0.0(expo@57.0.4): + expo-dev-menu-interface@57.0.0(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-dev-menu@57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-dev-menu@57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-dev-menu-interface: 57.0.0(expo@57.0.4) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-dev-menu-interface: 57.0.0(expo@57.0.20) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-device@57.0.0(expo@57.0.4): + expo-device@57.0.1(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) ua-parser-js: 0.7.41 - expo-document-picker@57.0.0(expo@57.0.4): + expo-document-picker@57.0.1(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-eas-client@57.0.0: {} + expo-eas-client@57.0.3: {} - expo-file-system@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-file-system@57.0.6(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-font@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-font@57.0.3(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) fontfaceobserver: 2.3.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-glass-effect@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-glass-effect@57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-haptics@57.0.0(expo@57.0.4): + expo-haptics@57.0.2(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-image-loader@57.0.0(expo@57.0.4): + expo-image-loader@57.0.1(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-image-picker@57.0.2(expo@57.0.4): + expo-image-picker@57.0.16(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-image-loader: 57.0.0(expo@57.0.4) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-image-loader: 57.0.1(expo@57.0.20) - expo-json-utils@57.0.0: {} + expo-json-utils@57.0.1: {} - expo-keep-awake@57.0.0(expo@57.0.4)(react@19.2.3): + expo-keep-awake@57.0.1(expo@57.0.20)(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - expo-libghostty@0.8.1(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-libghostty@0.8.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-linking@57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + expo-linking@57.0.9(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - expo - supports-color - expo-local-authentication@57.0.0(expo@57.0.4): + expo-local-authentication@57.0.2(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) invariant: 2.2.4 - expo-localization@57.0.1(expo@57.0.4)(react@19.2.3): + expo-localization@57.0.1(expo@57.0.20)(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 rtl-detect: 1.1.2 optional: true - expo-manifests@57.0.0(expo@57.0.4): + expo-manifests@57.0.1(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-json-utils: 57.0.0 + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-json-utils: 57.0.1 - expo-modules-autolinking@57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1): + expo-modules-autolinking@57.0.12(@typescript/typescript6@6.0.2)(supports-color@8.1.1): dependencies: - '@expo/require-utils': 57.0.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/require-utils': 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@expo/spawn-async': 1.8.0 chalk: 4.1.2 commander: 7.2.0 @@ -17942,60 +17999,58 @@ snapshots: - supports-color - typescript - expo-modules-core@57.0.3(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-modules-core@57.0.16(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - '@expo/expo-modules-macros-plugin': 0.3.0 - expo-modules-jsi: 57.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + '@expo/expo-modules-macros-plugin': 0.6.1 + expo-modules-jsi: 57.0.8(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) optionalDependencies: - react-native-worklets: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) - expo-modules-jsi@57.0.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-modules-jsi@57.0.8(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-network@57.0.1(expo@57.0.4)(react@19.2.3): + expo-network@57.0.1(expo@57.0.20)(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - expo-notifications@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + expo-notifications@57.0.17(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: - '@expo/image-utils': 0.11.1(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/image-utils': 0.11.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) abort-controller: 3.0.0 badgin: 1.2.3 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-application: 57.0.2(expo@57.0.4) - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-application: 57.0.2(expo@57.0.20) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - typescript - expo-router@57.0.4(3bc51b20989ed2c3020a920bb0eeb449): + expo-router@57.0.19(c021d0cd4acfce308650ff4c6746808d): dependencies: - '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@expo/metro-runtime': 57.0.3(@expo/log-box@57.0.0)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@expo/schema-utils': 57.0.1 - '@expo/ui': 57.0.7(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/metro-runtime': 57.0.15(@expo/log-box@57.0.4)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/schema-utils': 57.0.2 + '@expo/ui': 57.0.16(@babel/core@7.29.7(supports-color@8.1.1))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.3) '@radix-ui/react-tabs': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@testing-library/jest-dom': 6.9.1 - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) client-only: 0.0.1 color: 4.2.3 debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) - expo-glass-effect: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-linking: 57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) - expo-server: 57.0.0 - expo-symbols: 57.0.0(expo-font@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + expo-glass-effect: 57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-linking: 57.0.9(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + expo-server: 57.0.3 + expo-symbols: 57.0.2(expo-font@57.0.3)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) fast-deep-equal: 3.1.3 invariant: 2.2.4 nanoid: 3.3.16 @@ -18003,10 +18058,10 @@ snapshots: react: 19.2.3 react-fast-compare: 3.2.2 react-is: 19.2.7 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-drawer-layout: 4.2.5(5192597165fff2dd7abc0f37e93a3df3) - react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-screens: 4.25.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-drawer-layout: 4.2.5(1a764842d23a66d6618895018fb163bf) + react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) server-only: 0.0.1 sf-symbols-typescript: 2.2.0 shallowequal: 1.1.0 @@ -18014,132 +18069,131 @@ snapshots: vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: react-dom: 19.2.3(react@19.2.3) - react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - '@babel/core' - - '@testing-library/dom' - '@types/react' - '@types/react-dom' - expo-font - react-native-worklets - supports-color - expo-secure-store@57.0.0(expo@57.0.4): + expo-secure-store@57.0.3(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-server@57.0.0: {} + expo-server@57.0.3: {} - expo-sharing@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + expo-sharing@57.0.18(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: - '@expo/config-plugins': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/config-types': 57.0.1 - '@expo/plist': 0.8.0 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + '@expo/config-plugins': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-types': 57.0.2 + '@expo/plist': 0.8.1 + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - typescript - expo-sqlite@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-sqlite@57.0.2(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: await-lock: 2.2.2 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo-status-bar@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-status-bar@57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) expo-structured-headers@57.0.0: {} - expo-symbols@57.0.0(expo-font@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + expo-symbols@57.0.2(expo-font@57.0.3)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: '@expo-google-fonts/material-symbols': 0.4.38 - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-font: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-font: 57.0.3(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) sf-symbols-typescript: 2.2.0 - expo-task-manager@57.0.2(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-task-manager@57.0.16(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - unimodules-app-loader: 57.0.0 + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + unimodules-app-loader: 57.0.1 - expo-updates-interface@57.0.0(expo@57.0.4): + expo-updates-interface@57.0.1(expo@57.0.20): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) - expo-updates@57.0.6(expo-dev-client@57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + expo-updates@57.0.21(expo-dev-client@57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: '@expo/code-signing-certificates': 0.0.6 - '@expo/plist': 0.8.0 + '@expo/plist': 0.8.1 '@expo/spawn-async': 1.8.0 arg: 4.1.3 chalk: 4.1.2 debug: 4.4.3(supports-color@8.1.1) - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - expo-eas-client: 57.0.0 - expo-manifests: 57.0.0(expo@57.0.4) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + expo-eas-client: 57.0.3 + expo-manifests: 57.0.1(expo@57.0.20) expo-structured-headers: 57.0.0 - expo-updates-interface: 57.0.0(expo@57.0.4) + expo-updates-interface: 57.0.1(expo@57.0.20) getenv: 2.0.0 glob: 13.0.6 ignore: 5.3.2 nullthrows: 1.1.1 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) resolve-from: 5.0.0 optionalDependencies: - expo-dev-client: 57.0.5(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-dev-client: 57.0.18(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) transitivePeerDependencies: - supports-color - expo-web-browser@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): + expo-web-browser@57.0.2(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.4(ffe90122b92e78d11051c8a0633e15fd) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + expo: 57.0.20(a8e7ccc650ea22a90fc5a67384b934a6) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - expo@57.0.4(ffe90122b92e78d11051c8a0633e15fd): + expo@57.0.20(a8e7ccc650ea22a90fc5a67384b934a6): dependencies: '@babel/runtime': 7.29.7 - '@expo/cli': 57.0.6(@expo/dom-webview@57.0.0)(@expo/metro-runtime@57.0.3)(@typescript/typescript6@6.0.2)(expo-constants@57.0.3)(expo-font@57.0.0)(expo-router@57.0.4)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) - '@expo/config': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/config-plugins': 57.0.3(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/devtools': 57.0.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@expo/fingerprint': 0.20.3(supports-color@8.1.1) - '@expo/local-build-cache-provider': 57.0.2(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - '@expo/log-box': 57.0.0(@expo/dom-webview@57.0.0)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@expo/metro': 56.0.0(supports-color@8.1.1) - '@expo/metro-config': 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(supports-color@8.1.1) + '@expo/cli': 57.0.22(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(@typescript/typescript6@6.0.2)(expo-constants@57.0.17)(expo-font@57.0.3)(expo-router@57.0.19)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + '@expo/config': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/config-plugins': 57.0.9(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/devtools': 57.0.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/fingerprint': 0.20.12(supports-color@8.1.1) + '@expo/local-build-cache-provider': 57.0.8(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/metro': 56.0.2(supports-color@8.1.1) + '@expo/metro-config': 57.0.12(@typescript/typescript6@6.0.2)(expo@57.0.20)(supports-color@8.1.1) '@ungap/structured-clone': 1.3.1 - babel-preset-expo: 57.0.2(@babel/core@7.29.7(supports-color@8.1.1))(@babel/runtime@7.29.7)(expo@57.0.4)(react-refresh@0.14.2)(supports-color@8.1.1) - expo-asset: 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) - expo-constants: 57.0.3(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) - expo-file-system: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) - expo-font: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-keep-awake: 57.0.0(expo@57.0.4)(react@19.2.3) - expo-modules-autolinking: 57.0.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) - expo-modules-core: 57.0.3(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + babel-preset-expo: 57.0.10(@babel/core@7.29.7(supports-color@8.1.1))(@babel/runtime@7.29.7)(expo@57.0.20)(react-refresh@0.14.2)(supports-color@8.1.1) + expo-asset: 57.0.16(@typescript/typescript6@6.0.2)(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + expo-constants: 57.0.17(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) + expo-file-system: 57.0.6(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-font: 57.0.3(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-keep-awake: 57.0.1(expo@57.0.20)(react@19.2.3) + expo-modules-autolinking: 57.0.12(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + expo-modules-core: 57.0.16(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) pretty-format: 29.7.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) react-refresh: 0.14.2 whatwg-url-minimum: 0.1.2 optionalDependencies: - '@expo/dom-webview': 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - '@expo/metro-runtime': 57.0.3(@expo/log-box@57.0.0)(expo@57.0.4)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/dom-webview': 57.0.1(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@expo/metro-runtime': 57.0.15(@expo/log-box@57.0.4)(expo@57.0.20)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-dom: 19.2.3(react@19.2.3) react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-native-webview: 13.16.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-webview: 13.16.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -18718,7 +18772,7 @@ snapshots: property-information: 7.2.0 space-separated-tokens: 2.0.2 - hermes-compiler@250829098.0.14: {} + hermes-compiler@250829098.0.16: {} hermes-estree@0.25.1: {} @@ -18744,35 +18798,35 @@ snapshots: dependencies: hermes-estree: 0.36.1 - heroui-native@1.0.6(165eb86db294c7d42ce40e8c9ab66801): + heroui-native@1.0.6(5dab09f39b7c1f66cb0e1b4de78ecc64): dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-worklets: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-svg: 15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) tailwind-merge: 3.6.0 tailwind-variants: 3.3.0(tailwind-merge@3.6.0)(tailwindcss@4.3.3) optionalDependencies: - '@gorhom/bottom-sheet': 5.2.14(6e44dcb7dcfecf271936e923703300f2) - react-native-screens: 4.25.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@gorhom/bottom-sheet': 5.2.14(b3053a3c63c272f26bedf4b267721ee5) + react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - heroui-native@1.0.6(63282ea4a5d8a79f137ba1bf529bfa14): + heroui-native@1.0.6(759903ba85becadd460e21d1d0660b91): dependencies: react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) - react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) - react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) - react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) - react-native-worklets: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react-native-svg: 15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) tailwind-merge: 3.6.0 tailwind-variants: 3.3.0(tailwind-merge@3.6.0)(tailwindcss@4.3.3) optionalDependencies: - '@gorhom/bottom-sheet': 5.2.14(da77d43574db60c81967e07362a6f6fb) - react-native-screens: 4.25.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + '@gorhom/bottom-sheet': 5.2.14(50aa6fff1dfa2319f41b6b41f4bd94c3) + react-native-screens: 4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) highlight.js@10.7.3: {} @@ -18879,10 +18933,6 @@ snapshots: ignore@7.0.5: {} - image-size@1.2.1: - dependencies: - queue: 6.0.2 - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -18899,8 +18949,6 @@ snapshots: imurmurhash@0.1.4: {} - indent-string@4.0.0: {} - inflight@1.0.6: dependencies: once: 1.4.0 @@ -19356,11 +19404,11 @@ snapshots: lru_map@0.4.1: {} - lucide-react-native@1.27.0(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + lucide-react-native@1.27.0(react-native-svg@15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-svg: 15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) lucide-react@1.27.0(react@19.3.0-canary-96fcba90-20260728): dependencies: @@ -19630,51 +19678,51 @@ snapshots: ts-dedent: 2.3.0 uuid: 11.1.1 - metro-babel-transformer@0.84.4(supports-color@8.1.1): + metro-babel-transformer@0.84.5(supports-color@8.1.1): dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) flow-enums-runtime: 0.0.6 hermes-parser: 0.35.0 - metro-cache-key: 0.84.4 + metro-cache-key: 0.84.5 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-cache-key@0.84.4: + metro-cache-key@0.84.5: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.84.4(supports-color@8.1.1): + metro-cache@0.84.5(supports-color@8.1.1): dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 https-proxy-agent: 7.0.6(supports-color@8.1.1) - metro-core: 0.84.4 + metro-core: 0.84.5 transitivePeerDependencies: - supports-color - metro-config@0.84.4(supports-color@8.1.1): + metro-config@0.84.5(supports-color@8.1.1): dependencies: connect: 3.7.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.84.4(supports-color@8.1.1) - metro-cache: 0.84.4(supports-color@8.1.1) - metro-core: 0.84.4 - metro-runtime: 0.84.4 + metro: 0.84.5(supports-color@8.1.1) + metro-cache: 0.84.5(supports-color@8.1.1) + metro-core: 0.84.5 + metro-runtime: 0.84.5 yaml: 2.9.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro-core@0.84.4: + metro-core@0.84.5: dependencies: flow-enums-runtime: 0.0.6 lodash.throttle: 4.1.1 - metro-resolver: 0.84.4 + metro-resolver: 0.84.5 - metro-file-map@0.84.4(supports-color@8.1.1): + metro-file-map@0.84.5(supports-color@8.1.1): dependencies: debug: 4.4.3(supports-color@8.1.1) fb-watchman: 2.0.2 @@ -19688,46 +19736,46 @@ snapshots: transitivePeerDependencies: - supports-color - metro-minify-terser@0.84.4: + metro-minify-terser@0.84.5: dependencies: flow-enums-runtime: 0.0.6 terser: 5.48.0 - metro-resolver@0.84.4: + metro-resolver@0.84.5: dependencies: flow-enums-runtime: 0.0.6 - metro-runtime@0.84.4: + metro-runtime@0.84.5: dependencies: '@babel/runtime': 7.29.7 flow-enums-runtime: 0.0.6 - metro-source-map@0.84.4(supports-color@8.1.1): + metro-source-map@0.84.5(supports-color@8.1.1): dependencies: '@babel/traverse': 7.29.7(supports-color@8.1.1) '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.84.4(supports-color@8.1.1) + metro-symbolicate: 0.84.5(supports-color@8.1.1) nullthrows: 1.1.1 - ob1: 0.84.4 + ob1: 0.84.5 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-symbolicate@0.84.4(supports-color@8.1.1): + metro-symbolicate@0.84.5(supports-color@8.1.1): dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.84.4(supports-color@8.1.1) + metro-source-map: 0.84.5(supports-color@8.1.1) nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-transform-plugins@0.84.4(supports-color@8.1.1): + metro-transform-plugins@0.84.5(supports-color@8.1.1): dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/generator': 7.29.7 @@ -19738,27 +19786,27 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.84.4(supports-color@8.1.1): + metro-transform-worker@0.84.5(supports-color@8.1.1): dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 - metro: 0.84.4(supports-color@8.1.1) - metro-babel-transformer: 0.84.4(supports-color@8.1.1) - metro-cache: 0.84.4(supports-color@8.1.1) - metro-cache-key: 0.84.4 - metro-minify-terser: 0.84.4 - metro-source-map: 0.84.4(supports-color@8.1.1) - metro-transform-plugins: 0.84.4(supports-color@8.1.1) + metro: 0.84.5(supports-color@8.1.1) + metro-babel-transformer: 0.84.5(supports-color@8.1.1) + metro-cache: 0.84.5(supports-color@8.1.1) + metro-cache-key: 0.84.5 + metro-minify-terser: 0.84.5 + metro-source-map: 0.84.5(supports-color@8.1.1) + metro-transform-plugins: 0.84.5(supports-color@8.1.1) nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.84.4(supports-color@8.1.1): + metro@0.84.5(supports-color@8.1.1): dependencies: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@8.1.1) @@ -19775,23 +19823,22 @@ snapshots: flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 hermes-parser: 0.35.0 - image-size: 1.2.1 invariant: 2.2.4 jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.84.4(supports-color@8.1.1) - metro-cache: 0.84.4(supports-color@8.1.1) - metro-cache-key: 0.84.4 - metro-config: 0.84.4(supports-color@8.1.1) - metro-core: 0.84.4 - metro-file-map: 0.84.4(supports-color@8.1.1) - metro-resolver: 0.84.4 - metro-runtime: 0.84.4 - metro-source-map: 0.84.4(supports-color@8.1.1) - metro-symbolicate: 0.84.4(supports-color@8.1.1) - metro-transform-plugins: 0.84.4(supports-color@8.1.1) - metro-transform-worker: 0.84.4(supports-color@8.1.1) + metro-babel-transformer: 0.84.5(supports-color@8.1.1) + metro-cache: 0.84.5(supports-color@8.1.1) + metro-cache-key: 0.84.5 + metro-config: 0.84.5(supports-color@8.1.1) + metro-core: 0.84.5 + metro-file-map: 0.84.5(supports-color@8.1.1) + metro-resolver: 0.84.5 + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@8.1.1) + metro-symbolicate: 0.84.5(supports-color@8.1.1) + metro-transform-plugins: 0.84.5(supports-color@8.1.1) + metro-transform-worker: 0.84.5(supports-color@8.1.1) mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 @@ -20056,8 +20103,6 @@ snapshots: mimic-response@3.1.0: {} - min-indent@1.0.1: {} - minimatch@10.2.5: dependencies: brace-expansion: 5.0.9 @@ -20168,7 +20213,7 @@ snapshots: multipasta@0.2.8: {} - multitars@1.0.0: {} + multitars@1.0.2: {} mz@2.7.0: dependencies: @@ -20269,7 +20314,7 @@ snapshots: nullthrows@1.1.1: {} - ob1@0.84.4: + ob1@0.84.5: dependencies: flow-enums-runtime: 0.0.6 @@ -20533,17 +20578,17 @@ snapshots: transitivePeerDependencies: - preact-render-to-string - posthog-react-native@4.60.0(expo-application@57.0.2(expo@57.0.4))(expo-device@57.0.0(expo@57.0.4))(expo-file-system@57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo-localization@57.0.1(expo@57.0.4)(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)): + posthog-react-native@4.60.0(expo-application@57.0.2(expo@57.0.20))(expo-device@57.0.1(expo@57.0.20))(expo-file-system@57.0.6(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)))(expo-localization@57.0.1(expo@57.0.20)(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-svg@15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)): dependencies: '@posthog/core': 1.46.1 '@posthog/types': 1.399.0 optionalDependencies: - expo-application: 57.0.2(expo@57.0.4) - expo-device: 57.0.0(expo@57.0.4) - expo-file-system: 57.0.0(expo@57.0.4)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) - expo-localization: 57.0.1(expo@57.0.4)(react@19.2.3) - react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + expo-application: 57.0.2(expo@57.0.20) + expo-device: 57.0.1(expo@57.0.20) + expo-file-system: 57.0.6(expo@57.0.20)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1)) + expo-localization: 57.0.1(expo@57.0.20)(react@19.2.3) + react-native-safe-area-context: 5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-svg: 15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) postject@1.0.0-alpha.6: dependencies: @@ -20690,10 +20735,6 @@ snapshots: queue-microtask@1.2.3: {} - queue@6.0.2: - dependencies: - inherits: 2.0.4 - quick-format-unescaped@4.0.4: {} quick-lru@5.1.1: {} @@ -20766,105 +20807,105 @@ snapshots: react-is@19.2.7: {} - react-native-drawer-layout@4.2.5(5192597165fff2dd7abc0f37e93a3df3): + react-native-drawer-layout@4.2.5(1a764842d23a66d6618895018fb163bf): dependencies: color: 4.2.3 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-gesture-handler: 2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-gesture-handler: 2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) use-latest-callback: 0.2.6(react@19.2.3) - react-native-gesture-handler@2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: '@egjs/hammerjs': 2.0.17 '@types/react-test-renderer': 19.1.0 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-gesture-handler@2.32.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): + react-native-gesture-handler@2.32.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): dependencies: '@egjs/hammerjs': 2.0.17 '@types/react-test-renderer': 19.1.0 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) - react-native-is-edge-to-edge@1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-is-edge-to-edge@1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-is-edge-to-edge@1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): + react-native-is-edge-to-edge@1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): dependencies: react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) - react-native-keyboard-controller@1.21.9(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-keyboard-controller@1.21.9(react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-is-edge-to-edge: 1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-reanimated: 4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-is-edge-to-edge: 1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - react-native-worklets: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) semver: 7.8.5 - react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): + react-native-reanimated@4.5.1(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): dependencies: react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) - react-native-is-edge-to-edge: 1.3.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) - react-native-worklets: 0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + react-native-worklets: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) semver: 7.8.5 - react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): + react-native-safe-area-context@5.7.0(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): dependencies: react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) - react-native-screens@4.25.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-screens@4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 react-freeze: 1.0.4(react@19.2.3) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) warn-once: 0.1.1 - react-native-screens@4.25.2(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): + react-native-screens@4.26.2(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): dependencies: react: 19.3.0-canary-96fcba90-20260728 react-freeze: 1.0.4(react@19.3.0-canary-96fcba90-20260728) - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) warn-once: 0.1.1 optional: true - react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-svg@15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: css-select: 5.2.2 css-tree: 1.1.3 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) warn-once: 0.1.1 - react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): + react-native-svg@15.15.4(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728): dependencies: css-select: 5.2.2 css-tree: 1.1.3 react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) warn-once: 0.1.1 react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): @@ -20882,14 +20923,14 @@ snapshots: transitivePeerDependencies: - encoding - react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + react-native-webview@13.16.1(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: escape-string-regexp: 4.0.0 invariant: 2.2.4 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) - react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) @@ -20902,15 +20943,15 @@ snapshots: '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) '@babel/types': 7.29.7 - '@react-native/metro-config': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) convert-source-map: 2.0.0 react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) semver: 7.8.5 transitivePeerDependencies: - supports-color - react-native-worklets@0.10.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1): + react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1): dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) @@ -20923,23 +20964,23 @@ snapshots: '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1)) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) '@babel/types': 7.29.7 - '@react-native/metro-config': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/metro-config': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) convert-source-map: 2.0.0 react: 19.3.0-canary-96fcba90-20260728 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1) semver: 7.8.5 transitivePeerDependencies: - supports-color - react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1): + react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1): dependencies: - '@react-native/assets-registry': 0.86.0 - '@react-native/codegen': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1)) - '@react-native/community-cli-plugin': 0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1) - '@react-native/gradle-plugin': 0.86.0 - '@react-native/js-polyfills': 0.86.0 - '@react-native/normalize-colors': 0.86.0 - '@react-native/virtualized-lists': 0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) + '@react-native/assets-registry': 0.86.2 + '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1)) + '@react-native/community-cli-plugin': 0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/gradle-plugin': 0.86.2 + '@react-native/js-polyfills': 0.86.2 + '@react-native/normalize-colors': 0.86.2 + '@react-native/virtualized-lists': 0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -20947,11 +20988,11 @@ snapshots: base64-js: 1.5.1 commander: 12.1.0 flow-enums-runtime: 0.0.6 - hermes-compiler: 250829098.0.14 + hermes-compiler: 250829098.0.16 invariant: 2.2.4 memoize-one: 5.2.1 - metro-runtime: 0.84.4 - metro-source-map: 0.84.4(supports-color@8.1.1) + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@8.1.1) nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 @@ -20976,15 +21017,15 @@ snapshots: - supports-color - utf-8-validate - react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1): + react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1): dependencies: - '@react-native/assets-registry': 0.86.0 - '@react-native/codegen': 0.86.0(@babel/core@7.29.7(supports-color@8.1.1)) - '@react-native/community-cli-plugin': 0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1) - '@react-native/gradle-plugin': 0.86.0 - '@react-native/js-polyfills': 0.86.0 - '@react-native/normalize-colors': 0.86.0 - '@react-native/virtualized-lists': 0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) + '@react-native/assets-registry': 0.86.2 + '@react-native/codegen': 0.86.2(@babel/core@7.29.7(supports-color@8.1.1)) + '@react-native/community-cli-plugin': 0.86.2(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/gradle-plugin': 0.86.2 + '@react-native/js-polyfills': 0.86.2 + '@react-native/normalize-colors': 0.86.2 + '@react-native/virtualized-lists': 0.86.2(@types/react@19.2.17)(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.3.0-canary-96fcba90-20260728)(supports-color@8.1.1))(react@19.3.0-canary-96fcba90-20260728) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -20992,11 +21033,11 @@ snapshots: base64-js: 1.5.1 commander: 12.1.0 flow-enums-runtime: 0.0.6 - hermes-compiler: 250829098.0.14 + hermes-compiler: 250829098.0.16 invariant: 2.2.4 memoize-one: 5.2.1 - metro-runtime: 0.84.4 - metro-source-map: 0.84.4(supports-color@8.1.1) + metro-runtime: 0.84.5 + metro-source-map: 0.84.5(supports-color@8.1.1) nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 @@ -21090,11 +21131,6 @@ snapshots: real-require@1.0.0: {} - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - redis-errors@1.2.0: {} redis-parser@3.0.0: @@ -21382,6 +21418,8 @@ snapshots: safer-buffer@2.1.2: {} + sandbox-cli-detector@0.2.0: {} + sanitize-filename@1.6.4: dependencies: truncate-utf8-bytes: 1.0.2 @@ -21751,10 +21789,6 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - strip-json-comments@2.0.1: optional: true @@ -22141,7 +22175,7 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 - unimodules-app-loader@57.0.0: {} + unimodules-app-loader@57.0.1: {} unist-util-is@6.0.1: dependencies: @@ -22170,20 +22204,20 @@ snapshots: universalify@2.0.1: {} - uniwind@1.10.0(@expo/metro-config@57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(supports-color@8.1.1))(metro-cache@0.84.4(supports-color@8.1.1))(metro-transform-worker@0.84.4(supports-color@8.1.1))(metro@0.84.4(supports-color@8.1.1))(react-native@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(tailwindcss@4.3.3): + uniwind@1.10.0(@expo/metro-config@57.0.12(@typescript/typescript6@6.0.2)(expo@57.0.20)(supports-color@8.1.1))(metro-cache@0.84.5(supports-color@8.1.1))(metro-transform-worker@0.84.5(supports-color@8.1.1))(metro@0.84.5(supports-color@8.1.1))(react-native@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(tailwindcss@4.3.3): dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 culori: 4.0.2 lightningcss: 1.30.1 - metro: 0.84.4(supports-color@8.1.1) - metro-cache: 0.84.4(supports-color@8.1.1) + metro: 0.84.5(supports-color@8.1.1) + metro-cache: 0.84.5(supports-color@8.1.1) react: 19.2.3 - react-native: 0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) + react-native: 0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.2(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.17)(react@19.2.3)(supports-color@8.1.1) tailwindcss: 4.3.3 optionalDependencies: - '@expo/metro-config': 57.0.3(@typescript/typescript6@6.0.2)(expo@57.0.4)(supports-color@8.1.1) - metro-transform-worker: 0.84.4(supports-color@8.1.1) + '@expo/metro-config': 57.0.12(@typescript/typescript6@6.0.2)(expo@57.0.20)(supports-color@8.1.1) + metro-transform-worker: 0.84.5(supports-color@8.1.1) unpipe@1.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7946137a7..99fa53eb9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -136,24 +136,24 @@ minimumReleaseAgeExclude: - shiki@4.3.0 - "@expo/metro-runtime@56.0.16" - "@expo/ui@56.0.19" - - expo-constants@56.0.19 - - expo-router@56.2.12 + - expo-constants@56.0.19 || 57.0.12 + - expo-router@56.2.12 || 57.0.14 - foxts - uniwind@1.10.0 - - "@expo/cli@56.1.17" + - '@expo/cli@56.1.17 || 57.0.16' - "@expo/config-plugins@56.0.10" - "@expo/config-types@56.0.7" - - "@expo/config@56.0.10" - - "@expo/fingerprint@0.19.5" - - "@expo/log-box@56.0.14" + - '@expo/config@56.0.10 || 57.0.8' + - '@expo/fingerprint@0.19.5 || 0.20.8' + - '@expo/log-box@56.0.14 || 57.0.3' - "@expo/metro-config@56.0.15" - "@expo/prebuild-config@56.0.17" - "@expo/router-server@56.0.15" - babel-preset-expo@56.0.16 - - expo-asset@56.0.18 + - expo-asset@56.0.18 || 57.0.12 - expo-modules-autolinking@56.0.17 - expo-modules-core@56.0.18 - - expo@56.0.13 + - expo@56.0.13 || 57.0.14 - uuid@11.1.1 - esbuild@0.28.1 - ws@8.21.0 @@ -193,6 +193,16 @@ minimumReleaseAgeExclude: - restty@0.2.3 || 0.2.5 - text-shaper@0.1.26 || 0.1.27 - scheduler@0.28.0-canary-96fcba90-20260728 + - '@expo/inline-modules@0.1.6' + - expo-background-task@57.0.11 + - expo-dev-client@57.0.13 + - expo-dev-launcher@57.0.13 + - expo-dev-menu@57.0.13 + - expo-image-picker@57.0.11 + - expo-notifications@57.0.12 + - expo-sharing@57.0.13 + - expo-task-manager@57.0.11 + - expo-updates@57.0.15 overrides: # GHSA-67mh-4wv8-2f99: advisory metadata claims 0.24.3, but esbuild 0.24.x