diff --git a/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-cold-menu-light.png b/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-cold-menu-light.png new file mode 100644 index 0000000000..a472791691 Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-cold-menu-light.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-dark.png b/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-dark.png new file mode 100644 index 0000000000..4f8cd058e1 Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-dark.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-light.png b/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-light.png new file mode 100644 index 0000000000..761d950f41 Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-quick-actions-light.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-release-cold-menu-1.png b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-menu-1.png new file mode 100644 index 0000000000..46f603063b Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-menu-1.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-release-cold-menu-2.png b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-menu-2.png new file mode 100644 index 0000000000..a472791691 Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-menu-2.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-release-cold-open-1.png b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-open-1.png new file mode 100644 index 0000000000..f781528ecb Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-open-1.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-release-cold-open-2.png b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-open-2.png new file mode 100644 index 0000000000..f781528ecb Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-release-cold-open-2.png differ diff --git a/.github/pr-screenshots/2380-hardware-send/2380-release-warm-open.png b/.github/pr-screenshots/2380-hardware-send/2380-release-warm-open.png new file mode 100644 index 0000000000..bbaa09f702 Binary files /dev/null and b/.github/pr-screenshots/2380-hardware-send/2380-release-warm-open.png differ diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index ed58b5e520..bb73492dbb 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -117,7 +117,7 @@ jobs: env: CI: '1' EXPO_OFFLINE: '1' - - run: bash apps/mobile/android/gradlew -p apps/mobile/android :stream-reveal:testDebugUnitTest -PreactNativeArchitectures=x86_64 --max-workers=2 --console=plain + - run: bash apps/mobile/android/gradlew -p apps/mobile/android :stream-reveal:testDebugUnitTest :hardware-send:testDebugUnitTest -PreactNativeArchitectures=x86_64 --max-workers=2 --console=plain test-full: name: test (shard ${{ matrix.shard }}) diff --git a/apps/mobile/DESIGN.md b/apps/mobile/DESIGN.md index c43648a255..9bb668ada8 100644 --- a/apps/mobile/DESIGN.md +++ b/apps/mobile/DESIGN.md @@ -149,6 +149,7 @@ The shell is set by [ADR-0211](../../docs/adr/0211-the-mobile-app-has-two-tabs-w - Opening a conversation marks it read on the Host (`channelMarkRead`) whenever the Human is at the newest message, so the chip clears on the phone and on desktop. - Swipe for pin and mute; long-press opens a native menu. - On iOS a long-press on a Host conversation row peeks at the chat, like Messages: the row lifts into the system context-menu preview (expo-router `Link.Preview`) showing the conversation itself, and tapping the preview opens it. A plain tap opens the conversation as before. +- A long-press on the app icon lists the three chats opened most recently (Home Screen quick actions on iOS, app shortcuts on Android), newest first, and tapping one opens that chat on its Host. The list updates whenever a chat opens and is kept with the device's saved Host list. **Count chip** @@ -215,6 +216,7 @@ The shell is set by [ADR-0211](../../docs/adr/0211-the-mobile-app-has-two-tabs-w - **+** sits outside the capsule, bottom-aligned as the field grows. While dictating it becomes **×** (cancel), and while the mic is held, a red dot. With one trailing button (the mic or Send) the capsule is one row: the field and the button. When the text no longer fits that one row, or with more (attached files, or the mic next to Send), the capsule becomes a rounded rectangle with the same corner radius and stacks like the web composer: the field on top at full width, and a row below with expand on the left and the buttons on the right. - **+** opens Photos, Camera and File below the composer. Picked files show as removable tiles inside the capsule, up to 10 per message. Send appears once there is text or a file. - While the field is focused and the clipboard holds an image, a panel above the composer, styled like the @ suggestions (Liquid Glass on iOS 26, otherwise the `sheet` layer), reads "Image on the clipboard" with a paste control. On iOS it is the system Paste button (`UIPasteControl`, through `expo-clipboard`'s `ClipboardPasteButton`, image only, icon only at accessibility text sizes), so no paste-permission prompt appears. On Android it is a text `Button` "Paste image" that reads the clipboard; Android only shows its own brief "pasted from your clipboard" notice. The image attaches as a JPEG tile and the row hides until something new is copied. The @ suggestions take that slot first. A text field cannot take an image paste on its own in React Native, so this is the paste path. + - With a hardware keyboard, **⌘↩** sends on iOS and iPadOS (listed in the ⌘-hold shortcut overlay as "Send"), and on Android **Enter** sends while **Shift+Enter** adds a line. **Ctrl+Enter** also sends on Android. In the full-screen draft editor plain Enter adds a line, and only ⌘↩ or Ctrl+Enter sends. The on-screen keyboard never sends; its return key adds a line as before. The local `hardware-send` module listens on the view around the field, so it only acts while the field is focused and the draft can be sent. - Sending uploads each file to the Host first. A retry keeps the uploads that already finished. - Nothing sits under the composer, so the chat stays clean. A DM's Model and Workspace settings live in the Bot profile, in a **Bot settings** group under the profile actions: a **Model** row (the Bot's current model name, or "Loading…", "No model set" or "Unavailable") and a **Workspace** row (the folder count, "N folders"), each with a chevron. - **Model** opens the native Models and presets page inside the profile drawer: Orchestrator, Assignment default, allowed Assignment models, reasoning effort, restore global default, save as preset and apply a shared preset. The searchable chooser pushes inside that same drawer and returns only after an accepted save and canonical refresh. Editing only the Orchestrator preserves Assignment policy. Assignment edits retain other allowed models, efforts and notes; the current default cannot be removed until another is chosen. Existing preset copies and per-Bot plans retain the Host's revision checks. Global default updates use the Host's existing profile-default contract; mobile does not create a separate global Assignment authority. diff --git a/apps/mobile/modules/hardware-send/README.md b/apps/mobile/modules/hardware-send/README.md new file mode 100644 index 0000000000..0967c4fa32 --- /dev/null +++ b/apps/mobile/modules/hardware-send/README.md @@ -0,0 +1,29 @@ +# Hardware keyboard send + +This local Expo module wraps the chat composer's text field so a hardware +keyboard can send the draft. It owns no text and no layout: the field, its +draft and the send action stay in React Native, and the view only reports +`onSend`. + +- **iOS and iPadOS:** the view adds a `UIKeyCommand` for ⌘↩. UIKit collects key + commands along the responder chain, so it applies only while the field inside + is first responder. The command title appears in the ⌘-hold shortcut overlay. +- **Android:** the view intercepts `dispatchKeyEvent` before the focused + `EditText`. Enter sends when `enter` is set, Shift+Enter always adds a line, + and Ctrl+Enter or Meta+Enter always sends. Key events flagged as coming from + the on-screen keyboard, or from a virtual input device, pass through + unchanged. Like `stream-reveal`, the view keeps the size Yoga gives it and + leaves child layout to React Native. + +When `enabled` is false the view does nothing, so an empty draft or a +dictation in progress never sends. The binding lives in +`src/ui/primitives/hardware-send.*.tsx`; builds without the module fall back to +a plain `View`. **Adding this module requires a native rebuild**; an OTA update +alone cannot install it. + +After Expo prebuild, run the Android unit tests from the repository root: + +```sh +pnpm --filter @botharness/mobile exec expo prebuild --platform android --no-install +bash apps/mobile/android/gradlew -p apps/mobile/android :hardware-send:testDebugUnitTest +``` diff --git a/apps/mobile/modules/hardware-send/android/build.gradle b/apps/mobile/modules/hardware-send/android/build.gradle new file mode 100644 index 0000000000..d4f1befc07 --- /dev/null +++ b/apps/mobile/modules/hardware-send/android/build.gradle @@ -0,0 +1,17 @@ +apply plugin: 'com.android.library' +apply plugin: 'expo-module-gradle-plugin' + +group = 'expo.modules.hardwaresend' +version = '0.1.0' + +android { + namespace 'expo.modules.hardwaresend' + defaultConfig { versionCode 1; versionName '0.1.0' } + testOptions { unitTests.includeAndroidResources = true } +} + +dependencies { + testImplementation 'com.facebook.react:react-android' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.14.1' +} diff --git a/apps/mobile/modules/hardware-send/android/src/main/AndroidManifest.xml b/apps/mobile/modules/hardware-send/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..94cbbcfc39 --- /dev/null +++ b/apps/mobile/modules/hardware-send/android/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/apps/mobile/modules/hardware-send/android/src/main/java/expo/modules/hardwaresend/HardwareSendModule.kt b/apps/mobile/modules/hardware-send/android/src/main/java/expo/modules/hardwaresend/HardwareSendModule.kt new file mode 100644 index 0000000000..1458b2dadb --- /dev/null +++ b/apps/mobile/modules/hardware-send/android/src/main/java/expo/modules/hardwaresend/HardwareSendModule.kt @@ -0,0 +1,23 @@ +package expo.modules.hardwaresend + +import android.view.View +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class HardwareSendModule : Module() { + override fun definition() = ModuleDefinition { + Name("HardwareSend") + View(HardwareSendView::class) { + Events("onSend") + Prop("enabled") { view: HardwareSendView, value: Boolean -> view.sending = value } + Prop("enter") { view: HardwareSendView, value: Boolean -> view.enter = value } + GroupView { + AddChildView { parent: HardwareSendView, child: View, index: Int -> parent.addView(child, index) } + GetChildCount { parent -> parent.childCount } + GetChildViewAt { parent, index -> parent.getChildAt(index) } + RemoveChildView { parent: HardwareSendView, child: View -> parent.removeView(child) } + RemoveChildViewAt { parent, index -> parent.removeViewAt(index) } + } + } + } +} diff --git a/apps/mobile/modules/hardware-send/android/src/main/java/expo/modules/hardwaresend/HardwareSendView.kt b/apps/mobile/modules/hardware-send/android/src/main/java/expo/modules/hardwaresend/HardwareSendView.kt new file mode 100644 index 0000000000..7795755e7a --- /dev/null +++ b/apps/mobile/modules/hardware-send/android/src/main/java/expo/modules/hardwaresend/HardwareSendView.kt @@ -0,0 +1,38 @@ +package expo.modules.hardwaresend + +import android.content.Context +import android.view.KeyEvent +import expo.modules.kotlin.AppContext +import expo.modules.kotlin.viewevent.EventDispatcher +import expo.modules.kotlin.views.ExpoView + +internal fun sendsOnKey(keyCode: Int, meta: Int, enter: Boolean, soft: Boolean): Boolean { + if (soft) return false + if (keyCode != KeyEvent.KEYCODE_ENTER && keyCode != KeyEvent.KEYCODE_NUMPAD_ENTER) return false + if (meta and KeyEvent.META_SHIFT_ON != 0) return false + if (meta and (KeyEvent.META_CTRL_ON or KeyEvent.META_META_ON) != 0) return true + return enter && meta and KeyEvent.META_ALT_ON == 0 +} + +internal fun fromSoftKeyboard(event: KeyEvent): Boolean = + event.flags and KeyEvent.FLAG_SOFT_KEYBOARD != 0 || event.device?.isVirtual == true + +class HardwareSendView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { + var sending = false + var enter = false + private val onSend by EventDispatcher() + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (!sending || !sendsOnKey(event.keyCode, event.metaState, enter, fromSoftKeyboard(event))) { + return super.dispatchKeyEvent(event) + } + if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) onSend(mapOf()) + return true + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)) + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) = Unit +} diff --git a/apps/mobile/modules/hardware-send/android/src/test/java/expo/modules/hardwaresend/HardwareSendTest.kt b/apps/mobile/modules/hardware-send/android/src/test/java/expo/modules/hardwaresend/HardwareSendTest.kt new file mode 100644 index 0000000000..41d8269e9a --- /dev/null +++ b/apps/mobile/modules/hardware-send/android/src/test/java/expo/modules/hardwaresend/HardwareSendTest.kt @@ -0,0 +1,38 @@ +package expo.modules.hardwaresend + +import android.view.KeyEvent +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class HardwareSendTest { + private val enterKey = KeyEvent.KEYCODE_ENTER + + @Test fun plainEnterSendsOnlyWhereEnterSends() { + assertTrue(sendsOnKey(enterKey, 0, enter = true, soft = false)) + assertTrue(sendsOnKey(KeyEvent.KEYCODE_NUMPAD_ENTER, 0, enter = true, soft = false)) + assertFalse(sendsOnKey(enterKey, 0, enter = false, soft = false)) + } + + @Test fun shiftEnterAlwaysAddsALine() { + assertFalse(sendsOnKey(enterKey, KeyEvent.META_SHIFT_ON, enter = true, soft = false)) + assertFalse( + sendsOnKey(enterKey, KeyEvent.META_SHIFT_ON or KeyEvent.META_CTRL_ON, enter = true, soft = false), + ) + } + + @Test fun ctrlOrMetaEnterSendsEvenInTheFullEditor() { + assertTrue(sendsOnKey(enterKey, KeyEvent.META_CTRL_ON, enter = false, soft = false)) + assertTrue(sendsOnKey(enterKey, KeyEvent.META_META_ON, enter = false, soft = false)) + } + + @Test fun altEnterAndOtherKeysPassThrough() { + assertFalse(sendsOnKey(enterKey, KeyEvent.META_ALT_ON, enter = true, soft = false)) + assertFalse(sendsOnKey(KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON, enter = true, soft = false)) + } + + @Test fun theOnScreenKeyboardNeverSends() { + assertFalse(sendsOnKey(enterKey, 0, enter = true, soft = true)) + assertFalse(sendsOnKey(enterKey, KeyEvent.META_CTRL_ON, enter = true, soft = true)) + } +} diff --git a/apps/mobile/modules/hardware-send/expo-module.config.json b/apps/mobile/modules/hardware-send/expo-module.config.json new file mode 100644 index 0000000000..fba13a8ebd --- /dev/null +++ b/apps/mobile/modules/hardware-send/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["apple", "android"], + "apple": { + "modules": ["HardwareSendModule"] + }, + "android": { + "modules": ["expo.modules.hardwaresend.HardwareSendModule"] + } +} diff --git a/apps/mobile/modules/hardware-send/ios/HardwareSend.podspec b/apps/mobile/modules/hardware-send/ios/HardwareSend.podspec new file mode 100644 index 0000000000..f7af4f6dbb --- /dev/null +++ b/apps/mobile/modules/hardware-send/ios/HardwareSend.podspec @@ -0,0 +1,21 @@ +Pod::Spec.new do |s| + s.name = 'HardwareSend' + s.version = '1.0.0' + s.summary = 'Sends the chat draft from a hardware keyboard shortcut.' + s.description = 'Sends the chat draft from a hardware keyboard shortcut.' + s.license = 'MIT' + s.author = 'BotHarness' + s.homepage = 'https://github.com/BotHarness/DeepSeekBot' + s.platforms = { :ios => '16.4' } + s.swift_version = '5.9' + s.source = { git: 'https://github.com/BotHarness/DeepSeekBot.git' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + s.source_files = '**/*.{h,m,swift}' + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'SWIFT_COMPILATION_MODE' => 'wholemodule' + } +end diff --git a/apps/mobile/modules/hardware-send/ios/HardwareSendModule.swift b/apps/mobile/modules/hardware-send/ios/HardwareSendModule.swift new file mode 100644 index 0000000000..3f907c3811 --- /dev/null +++ b/apps/mobile/modules/hardware-send/ios/HardwareSendModule.swift @@ -0,0 +1,37 @@ +import ExpoModulesCore +import UIKit + +public final class HardwareSendModule: Module { + public func definition() -> ModuleDefinition { + Name("HardwareSend") + + View(HardwareSendView.self) { + Events("onSend") + Prop("enabled") { (view: HardwareSendView, enabled: Bool) in + view.enabled = enabled + } + Prop("title") { (view: HardwareSendView, title: String) in + view.title = title + } + } + } +} + +final class HardwareSendView: ExpoView { + let onSend = EventDispatcher() + var enabled = false + var title = "" + + override var keyCommands: [UIKeyCommand]? { + guard enabled else { return super.keyCommands } + let command = UIKeyCommand( + title: title, action: #selector(send), input: "\r", modifierFlags: .command) + command.wantsPriorityOverSystemBehavior = true + return [command] + (super.keyCommands ?? []) + } + + @objc private func send() { + guard enabled else { return } + onSend() + } +} diff --git a/apps/mobile/package.json b/apps/mobile/package.json index efd428112f..c003948934 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -39,6 +39,7 @@ "expo-image-picker": "~58.0.10", "expo-linking": "~58.0.13", "expo-notifications": "~58.1.5", + "expo-quick-actions": "6.0.2", "expo-router": "~58.0.24", "expo-secure-store": "~58.0.2", "expo-sharing": "~58.0.19", diff --git a/apps/mobile/src/ui/chat/composer.tsx b/apps/mobile/src/ui/chat/composer.tsx index a06c839826..dfc6931da2 100644 --- a/apps/mobile/src/ui/chat/composer.tsx +++ b/apps/mobile/src/ui/chat/composer.tsx @@ -21,6 +21,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import type { PickSource, PickedFile } from '../../host/pick-files'; import { pressTint, useDesignTheme } from '../design/theme'; import { edge, motion, radius, space } from '../design/tokens'; +import { HardwareSend } from '../primitives/hardware-send'; import { Text } from '../primitives/text'; import { ArrowUpIcon, @@ -617,14 +618,20 @@ export function Composer({ ) : null} - { + haptic('send'); + onSend(); + }} style={{ flexDirection: 'row', paddingRight: stacked || trailing.length === 0 ? 0 : button + space.x2, }} > {field} - + {stacked && expand === null && trailing.length === 0 ? null : ( { mountRef(node); readMount(node); + recentMount(node); }, - [mountRef, readMount], + [mountRef, readMount, recentMount], ); const asking = snapshot.messages.findLast( diff --git a/apps/mobile/src/ui/chat/draft-editor.tsx b/apps/mobile/src/ui/chat/draft-editor.tsx index 5182d55b78..698c6625b0 100644 --- a/apps/mobile/src/ui/chat/draft-editor.tsx +++ b/apps/mobile/src/ui/chat/draft-editor.tsx @@ -5,6 +5,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useDesignTheme } from '../design/theme'; import { space } from '../design/tokens'; +import { HardwareSend } from '../primitives/hardware-send'; import { TextInput } from '../primitives/system-text'; import { Text } from '../primitives/text'; import { ArrowUpIcon, CheckIcon } from '../shell/icons'; @@ -70,32 +71,34 @@ export function DraftEditor({ ) : null} {above} - - onSelection(event.nativeEvent.selection.end), - })} - placeholder={placeholder} - accessibilityLabel={t.editDraft} - placeholderTextColor={theme.color.subtle} - style={{ - flex: 1, - textAlignVertical: 'top', - paddingHorizontal: space.x5, - paddingTop: space.x3, - paddingBottom: Math.max(insets.bottom, space.x3), - color: theme.color.fg, - fontSize: theme.type.body.size, - lineHeight: theme.type.body.line, - }} - /> + + + onSelection(event.nativeEvent.selection.end), + })} + placeholder={placeholder} + accessibilityLabel={t.editDraft} + placeholderTextColor={theme.color.subtle} + style={{ + flex: 1, + textAlignVertical: 'top', + paddingHorizontal: space.x5, + paddingTop: space.x3, + paddingBottom: Math.max(insets.bottom, space.x3), + color: theme.color.fg, + fontSize: theme.type.body.size, + lineHeight: theme.type.body.line, + }} + /> + ); diff --git a/apps/mobile/src/ui/design/tokens.ts b/apps/mobile/src/ui/design/tokens.ts index c36ead5308..aadc523eaf 100644 --- a/apps/mobile/src/ui/design/tokens.ts +++ b/apps/mobile/src/ui/design/tokens.ts @@ -126,6 +126,8 @@ export const pullToCompose = { threshold: 40, flick: 1 } as const; export const doubleTap = { window: 300, emoji: '👍' } as const; +export const recentChats = { limit: 3 } as const; + export const markdownTable = { minColumnWidth: 96, maxColumnWidth: 280 } as const; export const drawerSpring = { duration: 280, dampingRatio: 1 } as const; diff --git a/apps/mobile/src/ui/primitives/hardware-send.android.tsx b/apps/mobile/src/ui/primitives/hardware-send.android.tsx new file mode 100644 index 0000000000..f4beecc8f8 --- /dev/null +++ b/apps/mobile/src/ui/primitives/hardware-send.android.tsx @@ -0,0 +1,29 @@ +import { requireNativeView, requireOptionalNativeModule } from 'expo'; +import type { ReactNode } from 'react'; +import { View, type StyleProp, type ViewProps, type ViewStyle } from 'react-native'; + +type NativeProps = ViewProps & { enabled: boolean; enter: boolean; onSend(): void }; + +export const nativeHardwareSend = requireOptionalNativeModule('HardwareSend') !== null; +const NativeSend = nativeHardwareSend ? requireNativeView('HardwareSend') : undefined; + +export function HardwareSend({ + enabled, + enter, + onSend, + style, + children, +}: { + enabled: boolean; + enter: boolean; + onSend(): void; + style?: StyleProp; + children: ReactNode; +}) { + if (NativeSend === undefined) return {children}; + return ( + onSend()} style={style}> + {children} + + ); +} diff --git a/apps/mobile/src/ui/primitives/hardware-send.ios.tsx b/apps/mobile/src/ui/primitives/hardware-send.ios.tsx new file mode 100644 index 0000000000..4a16ed60ec --- /dev/null +++ b/apps/mobile/src/ui/primitives/hardware-send.ios.tsx @@ -0,0 +1,30 @@ +import { requireNativeView, requireOptionalNativeModule } from 'expo'; +import type { ReactNode } from 'react'; +import { View, type StyleProp, type ViewProps, type ViewStyle } from 'react-native'; + +import { t } from '../strings'; + +type NativeProps = ViewProps & { enabled: boolean; title: string; onSend(): void }; + +export const nativeHardwareSend = requireOptionalNativeModule('HardwareSend') !== null; +const NativeSend = nativeHardwareSend ? requireNativeView('HardwareSend') : undefined; + +export function HardwareSend({ + enabled, + onSend, + style, + children, +}: { + enabled: boolean; + enter: boolean; + onSend(): void; + style?: StyleProp; + children: ReactNode; +}) { + if (NativeSend === undefined) return {children}; + return ( + onSend()} style={style}> + {children} + + ); +} diff --git a/apps/mobile/src/ui/primitives/hardware-send.tsx b/apps/mobile/src/ui/primitives/hardware-send.tsx new file mode 100644 index 0000000000..b45719a6f3 --- /dev/null +++ b/apps/mobile/src/ui/primitives/hardware-send.tsx @@ -0,0 +1,17 @@ +import type { ReactNode } from 'react'; +import { View, type StyleProp, type ViewStyle } from 'react-native'; + +export const nativeHardwareSend = false; + +export function HardwareSend({ + style, + children, +}: { + enabled: boolean; + enter: boolean; + onSend(): void; + style?: StyleProp; + children: ReactNode; +}) { + return {children}; +} diff --git a/apps/mobile/src/ui/shell/bottom-navigation.tsx b/apps/mobile/src/ui/shell/bottom-navigation.tsx index 63c1289295..3a5efbc2b2 100644 --- a/apps/mobile/src/ui/shell/bottom-navigation.tsx +++ b/apps/mobile/src/ui/shell/bottom-navigation.tsx @@ -8,6 +8,7 @@ import { hostSession, useHostSession } from '../../host/host-session'; import { useConversationFilter } from '../../host/native-conversation-filter'; import { useHostLook } from '../../host/native-host-look'; import { useActivityCount } from '../../host/native-activity-count'; +import { useQuickActionRouting } from './quick-action-routing'; import { creationTargets, type CreationAction, @@ -50,18 +51,21 @@ function NavigationLayout() { ); const bottom = Math.max(insets.bottom, space.x4); const reserved = size + bottom + space.x5; + const quickActions = useQuickActionRouting(); return ( - ( - - )} - screenOptions={{ headerShown: false, sceneStyle: { backgroundColor: theme.color.base } }} - > - - - + + ( + + )} + screenOptions={{ headerShown: false, sceneStyle: { backgroundColor: theme.color.base } }} + > + + + + ); } diff --git a/apps/mobile/src/ui/shell/quick-action-routing.ts b/apps/mobile/src/ui/shell/quick-action-routing.ts new file mode 100644 index 0000000000..cd1c356f0a --- /dev/null +++ b/apps/mobile/src/ui/shell/quick-action-routing.ts @@ -0,0 +1,58 @@ +import * as QuickActions from 'expo-quick-actions'; +import { router } from 'expo-router'; +import type { RefCallback } from 'react'; + +import { hostSession } from '../../host/host-session'; +import { useMounted } from '../mounted'; + +let handledInitial = false; + +export function quickActionHref(action: QuickActions.Action | undefined): string | undefined { + const href = action?.params?.href; + return typeof href === 'string' && href.startsWith('/') && !href.startsWith('//') + ? href + : undefined; +} + +type Routing = { + initial: QuickActions.Action | undefined; + listen: (handler: (action: QuickActions.Action) => void) => { remove(): void }; + session: { subscribe(listener: () => void): () => void; ready(): boolean }; + navigate: (href: string) => void; +}; + +export function routeQuickActions({ initial, listen, session, navigate }: Routing): () => void { + let pending = quickActionHref(initial); + const drain = () => { + const href = pending; + if (href === undefined || !session.ready()) return; + pending = undefined; + setTimeout(() => navigate(href), 0); + }; + const action = listen((next) => { + pending = quickActionHref(next); + drain(); + }); + const offSession = session.subscribe(drain); + drain(); + return () => { + action.remove(); + offSession(); + }; +} + +export function useQuickActionRouting(): RefCallback { + return useMounted(() => { + const initial = handledInitial ? undefined : QuickActions.initial; + handledInitial = true; + return routeQuickActions({ + initial, + listen: QuickActions.addListener, + session: { + subscribe: hostSession.subscribe, + ready: () => hostSession.getState().saved !== undefined, + }, + navigate: (href) => router.navigate(href), + }); + }, []); +} diff --git a/apps/mobile/src/ui/shell/recent-chats.ts b/apps/mobile/src/ui/shell/recent-chats.ts new file mode 100644 index 0000000000..562a151dff --- /dev/null +++ b/apps/mobile/src/ui/shell/recent-chats.ts @@ -0,0 +1,84 @@ +import * as QuickActions from 'expo-quick-actions'; +import * as SecureStore from 'expo-secure-store'; +import { useCallback, type RefCallback } from 'react'; + +import { recentChats } from '../design/tokens'; + +export type RecentChat = { id: string; title: string; href: string }; + +const KEY = 'deepseekbot.recent-chats.v1'; + +export function chatHref(path: string, hostId: string | undefined): string { + return hostId === undefined ? path : `${path}?hostId=${encodeURIComponent(hostId)}`; +} + +export function rememberChat( + list: readonly RecentChat[], + chat: RecentChat, + limit: number, +): RecentChat[] { + return [chat, ...list.filter((item) => item.id !== chat.id)].slice(0, limit); +} + +export function parseRecentChats(raw: string | null): RecentChat[] { + if (raw === null) return []; + try { + const value: unknown = JSON.parse(raw); + if (!Array.isArray(value)) return []; + return value.filter( + (item): item is RecentChat => + typeof item === 'object' && + item !== null && + typeof item.id === 'string' && + typeof item.title === 'string' && + typeof item.href === 'string' && + item.href.startsWith('/'), + ); + } catch { + return []; + } +} + +export function quickAction(chat: RecentChat): QuickActions.Action { + return { + id: chat.id, + title: chat.title, + icon: 'symbol:bubble.left', + params: { href: chat.href }, + }; +} + +let recent: Promise | undefined; + +function load(): Promise { + recent ??= SecureStore.getItemAsync(KEY).then(parseRecentChats, () => []); + return recent; +} + +export function recordRecentChat(chat: RecentChat): Promise { + recent = load().then(async (list) => { + const first = list[0]; + if (first?.id === chat.id && first.title === chat.title && first.href === chat.href) + return list; + const next = rememberChat(list, chat, recentChats.limit); + await Promise.all([ + SecureStore.setItemAsync(KEY, JSON.stringify(next)).catch(() => undefined), + QuickActions.setItems(next.map(quickAction)).catch(() => undefined), + ]); + return next; + }); + return recent; +} + +export function useRecentChat(chat: RecentChat | undefined): RefCallback { + const id = chat?.id; + const title = chat?.title; + const href = chat?.href; + return useCallback( + (node: unknown) => { + if (node === null || id === undefined || title === undefined || href === undefined) return; + void recordRecentChat({ id, title, href }); + }, + [id, title, href], + ); +} diff --git a/apps/mobile/test/bottom-navigation.test.tsx b/apps/mobile/test/bottom-navigation.test.tsx index 004e5f3e99..1eeae38e80 100644 --- a/apps/mobile/test/bottom-navigation.test.tsx +++ b/apps/mobile/test/bottom-navigation.test.tsx @@ -3,6 +3,7 @@ import { act, createElement, type ReactNode } from 'react'; import { createRoot } from 'react-dom/client'; import { expect, it, vi } from 'vitest'; const fixture = vi.hoisted(() => ({ scope: 'all' })); +vi.mock('../src/ui/shell/quick-action-routing', () => ({ useQuickActionRouting: () => () => {} })); vi.mock('react-native', async () => ({ ...(await import('./react-native-shim')), Keyboard: { dismiss: vi.fn() }, diff --git a/apps/mobile/test/conversation-navigation.test.tsx b/apps/mobile/test/conversation-navigation.test.tsx index 102f8aa973..a7445e57fd 100644 --- a/apps/mobile/test/conversation-navigation.test.tsx +++ b/apps/mobile/test/conversation-navigation.test.tsx @@ -21,6 +21,10 @@ const state = vi.hoisted(() => ({ display: { mode: 'streaming', loaded: true }, })); +vi.mock('../src/ui/shell/recent-chats', () => ({ + chatHref: () => '', + useRecentChat: () => () => {}, +})); vi.mock('../src/host/native-source-drafts', () => ({ useSourceDraft: () => ['', () => {}] })); vi.mock('expo-router', () => ({ useIsFocused: () => true, diff --git a/apps/mobile/test/conversation-streaming-rows.test.tsx b/apps/mobile/test/conversation-streaming-rows.test.tsx index 0e0a680ba4..3cbab9e91d 100644 --- a/apps/mobile/test/conversation-streaming-rows.test.tsx +++ b/apps/mobile/test/conversation-streaming-rows.test.tsx @@ -64,6 +64,10 @@ const stream = vi.hoisted(() => { }; }); +vi.mock('../src/ui/shell/recent-chats', () => ({ + chatHref: () => '', + useRecentChat: () => () => {}, +})); vi.mock('../src/host/native-source-drafts', () => ({ useSourceDraft: () => ['', () => {}] })); vi.mock('expo-router', () => ({ useIsFocused: () => true, diff --git a/apps/mobile/test/quick-action-routing.test.ts b/apps/mobile/test/quick-action-routing.test.ts new file mode 100644 index 0000000000..9027d3ea7e --- /dev/null +++ b/apps/mobile/test/quick-action-routing.test.ts @@ -0,0 +1,81 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +vi.mock('expo-quick-actions', () => ({ initial: undefined, addListener: () => ({ remove() {} }) })); +vi.mock('expo-router', () => ({ router: { navigate: vi.fn() } })); +vi.mock('../src/host/host-session', () => ({ + hostSession: { subscribe: () => () => {}, getState: () => ({ saved: undefined }) }, +})); + +const { quickActionHref, routeQuickActions } = await import('../src/ui/shell/quick-action-routing'); + +function session(ready: boolean) { + const listeners = new Set<() => void>(); + const state = { ready }; + return { + state, + emit: () => listeners.forEach((listener) => listener()), + session: { + subscribe: (listener: () => void) => { + listeners.add(listener); + return () => listeners.delete(listener); + }, + ready: () => state.ready, + }, + }; +} + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('home-screen shortcut routing', () => { + it('opens only in-app paths', () => { + expect(quickActionHref({ id: 'a', title: 'A', params: { href: '/dm/writer' } })).toBe( + '/dm/writer', + ); + expect( + quickActionHref({ id: 'a', title: 'A', params: { href: 'https://example.com' } }), + ).toBeUndefined(); + expect(quickActionHref({ id: 'a', title: 'A', params: { href: '//evil' } })).toBeUndefined(); + expect(quickActionHref({ id: 'a', title: 'A' })).toBeUndefined(); + expect(quickActionHref(undefined)).toBeUndefined(); + }); + + it('holds a cold-start shortcut until the saved Host has loaded', () => { + vi.useFakeTimers(); + const navigate = vi.fn(); + const host = session(false); + routeQuickActions({ + initial: { id: 'a', title: 'A', params: { href: '/dm/writer' } }, + listen: () => ({ remove() {} }), + session: host.session, + navigate, + }); + vi.runAllTimers(); + expect(navigate).not.toHaveBeenCalled(); + host.state.ready = true; + host.emit(); + host.emit(); + vi.runAllTimers(); + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith('/dm/writer'); + }); + + it('opens a warm shortcut right away', () => { + vi.useFakeTimers(); + const navigate = vi.fn(); + let handler: ((action: { id: string; title: string; params?: object }) => void) | undefined; + routeQuickActions({ + initial: undefined, + listen: (next) => { + handler = next; + return { remove() {} }; + }, + session: session(true).session, + navigate, + }); + handler?.({ id: 'b', title: 'B', params: { href: '/channel/c1' } }); + vi.runAllTimers(); + expect(navigate).toHaveBeenCalledWith('/channel/c1'); + }); +}); diff --git a/apps/mobile/test/recent-chats.test.ts b/apps/mobile/test/recent-chats.test.ts new file mode 100644 index 0000000000..6b0269937c --- /dev/null +++ b/apps/mobile/test/recent-chats.test.ts @@ -0,0 +1,58 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const stored = new Map(); +const shortcuts: unknown[][] = []; + +vi.mock('expo-secure-store', () => ({ + getItemAsync: async (key: string) => stored.get(key) ?? null, + setItemAsync: async (key: string, value: string) => { + stored.set(key, value); + }, +})); +vi.mock('expo-quick-actions', () => ({ + setItems: async (items: unknown[]) => { + shortcuts.push(items); + }, +})); + +const { chatHref, parseRecentChats, quickAction, recordRecentChat, rememberChat } = + await import('../src/ui/shell/recent-chats'); + +const chat = (id: string, title = id) => ({ id, title, href: `/dm/${id}` }); + +beforeEach(() => { + shortcuts.length = 0; +}); + +describe('recent chats on the home-screen icon', () => { + it('keeps the newest chat first, without duplicates, up to the limit', () => { + const list = [chat('a'), chat('b'), chat('c')]; + expect(rememberChat(list, chat('b'), 3).map((item) => item.id)).toEqual(['b', 'a', 'c']); + expect(rememberChat(list, chat('d'), 3).map((item) => item.id)).toEqual(['d', 'a', 'b']); + }); + + it('links to the chat on the same Host', () => { + expect(chatHref('/dm/writer', 'host 1')).toBe('/dm/writer?hostId=host%201'); + expect(chatHref('/channel/c1', undefined)).toBe('/channel/c1'); + expect(quickAction(chat('writer', 'Writer'))).toMatchObject({ + title: 'Writer', + params: { href: '/dm/writer' }, + }); + }); + + it('ignores stored entries that are damaged or point outside the app', () => { + expect(parseRecentChats('not json')).toEqual([]); + expect( + parseRecentChats(JSON.stringify([chat('a'), { id: 'x', title: 'X', href: 'https://x' }, 3])), + ).toEqual([chat('a')]); + }); + + it('updates the shortcuts when a different chat opens, and not again for the same one', async () => { + await recordRecentChat(chat('a', 'Alpha')); + await recordRecentChat(chat('b', 'Beta')); + await recordRecentChat(chat('b', 'Beta')); + expect(shortcuts).toHaveLength(2); + expect(shortcuts.at(-1)).toMatchObject([{ title: 'Beta' }, { title: 'Alpha' }]); + expect(parseRecentChats(stored.get('deepseekbot.recent-chats.v1') ?? null)).toHaveLength(2); + }); +}); diff --git a/changes/2380-hardware-send-shortcuts.md b/changes/2380-hardware-send-shortcuts.md new file mode 100644 index 0000000000..ccaf91fa85 --- /dev/null +++ b/changes/2380-hardware-send-shortcuts.md @@ -0,0 +1,11 @@ +--- +section: Added +--- + +## en + +- With a hardware keyboard, ⌘↩ sends a message on iPhone and iPad, and Enter sends on Android (Shift+Enter adds a line). A long-press on the app icon now lists the three most recently opened chats ([#2380](https://github.com/BotHarness/DeepSeekBot/pull/2380)). + +## zh + +- 接上实体键盘后,iPhone 和 iPad 上按 ⌘↩ 发送消息,Android 上按回车发送(Shift+回车换行)。长按 App 图标会列出最近打开的三个聊天,点一下直接进入([#2380](https://github.com/BotHarness/DeepSeekBot/pull/2380))。 diff --git a/packages/client/src/client/release-notes-view.tsx b/packages/client/src/client/release-notes-view.tsx index 52e90ffa85..10ea28eac2 100644 --- a/packages/client/src/client/release-notes-view.tsx +++ b/packages/client/src/client/release-notes-view.tsx @@ -8,7 +8,6 @@ import { IconDownloadOutlineRegular, MarkdownText, Tag, - Tooltip, type MarkdownLabels, type TagTone, } from '@deepseek-ai/dsh-client-ui-primitives'; @@ -17,6 +16,7 @@ import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'; import type { ReleaseNote } from '../../../core/src/release/notes.js'; import type { BotHarnessKey, BotHarnessTranslate } from './locale.js'; import { IconButton } from './ds/icon-button.js'; +import { Tooltip } from './ds/tooltip.js'; import { SettingsRow } from './ds/settings-row.js'; import { color, space, text } from './ds/tokens.stylex.js'; import { Modal } from './modal.js'; diff --git a/patches/expo-quick-actions@6.0.2.patch b/patches/expo-quick-actions@6.0.2.patch new file mode 100644 index 0000000000..ebf7140ccb --- /dev/null +++ b/patches/expo-quick-actions@6.0.2.patch @@ -0,0 +1,42 @@ +diff --git a/ios/AppDelegate.swift b/ios/AppDelegate.swift +index 37558e006b606eae26e2e5be6e44afee93590547..19676d60e95b8014a3668239f91397686130d2a7 100644 +--- a/ios/AppDelegate.swift ++++ b/ios/AppDelegate.swift +@@ -17,6 +17,9 @@ public class ExpoQuickActionsAppDelegate: ExpoAppDelegateSubscriber { + } + + public func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { ++ if !observingQuickActions && initialAction == nil { ++ initialAction = shortcutItem ++ } + NotificationCenter.default.post(name: Notification.Name(onQuickAction), object: shortcutItem) + completionHandler(true) + } +diff --git a/ios/ExpoQuickActionsModule.swift b/ios/ExpoQuickActionsModule.swift +index 252da492ce374eef886d574a2c38af242de6d6c3..d8aaca1b03330a5adf5fd4c9d972c8806084d6a6 100644 +--- a/ios/ExpoQuickActionsModule.swift ++++ b/ios/ExpoQuickActionsModule.swift +@@ -72,6 +72,7 @@ func toActionObject(item: UIApplicationShortcutItem?) -> ActionObject? { + } + + var initialAction: UIApplicationShortcutItem? ++var observingQuickActions = false + + public class ExpoQuickActionsModule: Module { + +@@ -103,6 +104,7 @@ public class ExpoQuickActionsModule: Module { + Events(onQuickAction) + + OnStartObserving { ++ observingQuickActions = true + NotificationCenter.default.addObserver( + self, + selector: #selector(self.quickActionListener), +@@ -112,6 +114,7 @@ public class ExpoQuickActionsModule: Module { + } + + OnStopObserving { ++ observingQuickActions = false + NotificationCenter.default.removeObserver( + self, + name: Notification.Name(onQuickAction), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 414f8c4e2b..88b4a6e511 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -172,6 +172,7 @@ patchedDependencies: '@deepseek-ai/dsh-llm-pi-ai@0.2.1-rc.2': c02d991e1c83a16bccce25bd47847fe9e5c97117ff3d0b6b04ef9f8edb273c9d '@deepseek-ai/dsh-tool-fs@0.2.1-rc.2': 4b2e510cb0df4904b95b99936edd4cdaec5b19438561254906b3abf0d2c3b204 dockview-core@8.4.1: 6d5f9165424be95472edbb4e667b16cf9abd150f139d3524cc1ca9a6b7863b86 + expo-quick-actions@6.0.2: 2d9cefa269045271145e1989e004232aac3c206249494311d39f49c13293dddb importers: @@ -345,7 +346,7 @@ importers: version: 8.1.0 expo: specifier: 58.0.16 - version: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + version: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-alternate-app-icons: specifier: 8.0.0 version: 8.0.0(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) @@ -360,7 +361,7 @@ importers: version: 58.0.3(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) expo-constants: specifier: ~58.0.12 - version: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + version: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) expo-crypto: specifier: ~58.0.7 version: 58.0.7(expo@58.0.16) @@ -393,10 +394,13 @@ importers: version: 58.0.10(expo@58.0.16) expo-linking: specifier: ~58.0.13 - version: 58.0.13(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2) + version: 58.0.13(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3) expo-notifications: specifier: ~58.1.5 - version: 58.1.5(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2) + version: 58.1.5(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3) + expo-quick-actions: + specifier: 6.0.2 + version: 6.0.2(patch_hash=2d9cefa269045271145e1989e004232aac3c206249494311d39f49c13293dddb)(expo@58.0.16)(supports-color@10.2.2)(typescript@5.9.3) expo-router: specifier: ~58.0.24 version: 58.0.24(809975b795ae32b2306b92910a7dbc02) @@ -405,7 +409,7 @@ importers: version: 58.0.2(expo@58.0.16) expo-sharing: specifier: ~58.0.19 - version: 58.0.19(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2) + version: 58.0.19(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3) expo-speech-recognition: specifier: ~58.0.0 version: 58.0.0(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) @@ -4895,6 +4899,9 @@ packages: '@expo/image-utils@0.12.5': resolution: {integrity: sha512-nsFYd7GfDdwZa+S+p+EkVCkh7UKKNf0kfUXDQ53HbyMmNFD3psb5JYwU/2N+uZG5wOEfKGfQ5u7pk/wn2gMlBA==} + '@expo/image-utils@0.8.17': + resolution: {integrity: sha512-1eVrX6FFkPNl9DJNiw3Gjsf6PtG7HttbapEVvcROVrA9PCMikujOJWWHDYKGptmKoVIIVajFZE8UHGHUb3mPug==} + '@expo/inline-modules@0.2.6': resolution: {integrity: sha512-S8PK3wx6ogwunoQ7pqdPbklQGtXloeWTAOzWJ9yIL7xHFxvnI/Ryj0phA3KEGCMlaKZjaCP1h/3s7n/ueErRgQ==} @@ -4982,6 +4989,14 @@ packages: '@expo/prebuild-config@58.0.14': resolution: {integrity: sha512-2wVzVDfXjRB3LzUbW5shOM/A+6eUUCfR+2bpZiWRBBZBtZP2HA5Zq1YgCio1tDqMGAHf2NQQDhvy/Rz1U6aC3A==} + '@expo/require-utils@55.0.8': + resolution: {integrity: sha512-DDS5R67iz2SJwncbpcUMHtnsq31dSfllhaEncKxGlK0/ewgc2CaXUCkDA2AumfT7ubnSXiPkU9Gn8v2xCrvj+A==} + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 + peerDependenciesMeta: + typescript: + optional: true + '@expo/require-utils@58.0.4': resolution: {integrity: sha512-DvjS5NCitXYcad2Woe1EMHJKcbhTbE77HX0BgOQGL2LFHE3q2H0n8kCWDPBOCVJ63Drap3c35nwoJwF5TQL7Tw==} peerDependencies: @@ -7311,6 +7326,9 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -7897,6 +7915,11 @@ packages: peerDependencies: ajv: ^8.0.0-beta.0 + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} @@ -9217,6 +9240,11 @@ packages: react: '*' react-native: '*' + expo-quick-actions@6.0.2: + resolution: {integrity: sha512-IVoTMCx55MjSnPqUEb5jhePpOAUdOTonifY3QXM/DBFxc6qMzBy5iJWEkfDZcFAzFzahK1ZW9tFaNv4pi2AAvQ==} + peerDependencies: + expo: '*' + expo-router@58.0.24: resolution: {integrity: sha512-k6Bb673jDAyq34W8V/XUiEmOR9jMRXIYIuBYjPDchi6tsp7MOCcD8qfjj5WMtHuKp3cGe89E3X35SJBaO3yLLw==} peerDependencies: @@ -11837,6 +11865,16 @@ packages: scheduler@0.28.0: resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + schema-utils@4.8.1: + resolution: {integrity: sha512-x39pYlJPyH7Uge5QO64B9XGP9zLDc9WNmiQZc+CE3IzZPvMu3lHOuAMGpYNoPixEL7MSWlGhYTg5UlLSL8G6iw==} + engines: {node: '>= 10.13.0'} + hasBin: true + peerDependencies: + webpack: ^5.112.0 + peerDependenciesMeta: + webpack: + optional: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -13129,9 +13167,9 @@ packages: snapshots: - 2g@1.0.1(typescript@7.0.2): + 2g@1.0.1(typescript@5.9.3): optionalDependencies: - typescript: 7.0.2 + typescript: 5.9.3 '@agentclientprotocol/sdk@1.4.0(zod@4.6.5)': dependencies: @@ -17896,28 +17934,28 @@ snapshots: '@expo-google-fonts/material-symbols@0.4.50': {} - '@expo/cli@58.1.13(@expo/dom-webview@58.0.2)(@expo/metro-runtime@58.0.14)(expo-constants@58.0.12)(expo-font@58.0.9)(expo-router@58.0.24)(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/cli@58.1.13(@expo/dom-webview@58.0.2)(@expo/metro-runtime@58.0.14)(expo-constants@58.0.12)(expo-font@58.0.9)(expo-router@58.0.24)(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - 2g: 1.0.1(typescript@7.0.2) + 2g: 1.0.1(typescript@5.9.3) '@expo/code-signing-certificates': 0.0.7 - '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@7.0.2) - '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@5.9.3) + '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@5.9.3) '@expo/devcert': 1.2.2(supports-color@10.2.2) '@expo/env': 2.5.2(supports-color@10.2.2) - '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@7.0.2) - '@expo/inline-modules': 0.2.6(supports-color@10.2.2)(typescript@7.0.2) + '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@5.9.3) + '@expo/inline-modules': 0.2.6(supports-color@10.2.2)(typescript@5.9.3) '@expo/json-file': 11.2.1 '@expo/log-box': 58.0.12(@expo/dom-webview@58.0.2)(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) '@expo/log-box-utils': 58.0.2 '@expo/metro': 58.0.0(supports-color@10.2.2) - '@expo/metro-config': 58.0.15(expo@58.0.16)(supports-color@10.2.2)(typescript@7.0.2) - '@expo/metro-file-map': 58.0.5(supports-color@10.2.2)(typescript@7.0.2) + '@expo/metro-config': 58.0.15(expo@58.0.16)(supports-color@10.2.2)(typescript@5.9.3) + '@expo/metro-file-map': 58.0.5(supports-color@10.2.2)(typescript@5.9.3) '@expo/osascript': 2.8.1 '@expo/package-manager': 1.14.2 '@expo/platforms': 58.0.1 '@expo/plist': 0.10.1 - '@expo/prebuild-config': 58.0.14(supports-color@10.2.2)(typescript@7.0.2) - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) + '@expo/prebuild-config': 58.0.14(supports-color@10.2.2)(typescript@5.9.3) + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) '@expo/router-server': 58.0.15(@expo/metro-runtime@58.0.14)(expo-constants@58.0.12)(expo-font@58.0.9)(expo-router@58.0.24)(expo-server@58.0.5)(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(supports-color@10.2.2) '@expo/schema-utils': 58.0.2 '@expo/spawn-async': 1.8.0 @@ -17936,7 +17974,7 @@ snapshots: compression: 1.8.2(supports-color@10.2.2) connect: 3.7.0(supports-color@10.2.2) dnssd-advertise: 1.1.6 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-server: 58.0.5 fetch-nodeshim: 0.4.10 getenv: 2.0.0 @@ -17982,12 +18020,12 @@ snapshots: dependencies: node-forge: 1.4.0 - '@expo/config-plugins@58.0.9(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/config-plugins@58.0.9(supports-color@10.2.2)(typescript@5.9.3)': dependencies: '@expo/config-types': 58.0.3 '@expo/json-file': 11.2.1 '@expo/plist': 0.10.1 - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) '@expo/sdk-runtime-versions': 1.0.1 chalk: 4.1.2 debug: 4.4.3(supports-color@10.2.2) @@ -18003,13 +18041,13 @@ snapshots: '@expo/config-types@58.0.3': {} - '@expo/config@58.0.6(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/config@58.0.6(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@5.9.3) '@expo/config-types': 58.0.3 '@expo/json-file': 11.2.1 '@expo/platforms': 58.0.1 - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) deepmerge: 4.3.1 getenv: 2.0.0 glob: 13.0.6 @@ -18031,13 +18069,13 @@ snapshots: dependencies: chalk: 4.1.2 optionalDependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) '@expo/dom-webview@58.0.2(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)': dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) @@ -18065,9 +18103,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/image-utils@0.12.5(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/image-utils@0.12.5(supports-color@10.2.2)(typescript@5.9.3)': + dependencies: + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 + chalk: 4.1.2 + getenv: 2.0.0 + jimp-compact: 0.16.1 + parse-png: 2.1.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/image-utils@0.8.17(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) + '@expo/require-utils': 55.0.8(supports-color@10.2.2)(typescript@5.9.3) '@expo/spawn-async': 1.8.0 chalk: 4.1.2 getenv: 2.0.0 @@ -18078,9 +18129,9 @@ snapshots: - supports-color - typescript - '@expo/inline-modules@0.2.6(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/inline-modules@0.2.6(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@5.9.3) transitivePeerDependencies: - supports-color - typescript @@ -18090,9 +18141,9 @@ snapshots: '@babel/code-frame': 7.29.7 json5: 2.2.3 - '@expo/local-build-cache-provider@58.0.6(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/local-build-cache-provider@58.0.6(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@5.9.3) chalk: 4.1.2 transitivePeerDependencies: - supports-color @@ -18109,20 +18160,20 @@ snapshots: react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) stacktrace-parser: 0.1.11 optionalDependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) - '@expo/metro-config@58.0.15(expo@58.0.16)(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/metro-config@58.0.15(expo@58.0.16)(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - 2g: 1.0.1(typescript@7.0.2) + 2g: 1.0.1(typescript@5.9.3) '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/generator': 7.29.8 - '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@5.9.3) '@expo/env': 2.5.2(supports-color@10.2.2) '@expo/json-file': 11.2.1 '@expo/metro': 58.0.0(supports-color@10.2.2) '@expo/platforms': 58.0.1 - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) '@expo/spawn-async': 1.8.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/remapping': 2.3.5 @@ -18142,16 +18193,16 @@ snapshots: postcss: 8.5.28 resolve-from: 5.0.0 optionalDependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) transitivePeerDependencies: - bufferutil - supports-color - typescript - utf-8-validate - '@expo/metro-file-map@58.0.5(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/metro-file-map@58.0.5(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - 2g: 1.0.1(typescript@7.0.2) + 2g: 1.0.1(typescript@5.9.3) debug: 4.4.3(supports-color@10.2.2) fb-watchman: 2.0.4 invariant: 2.2.4 @@ -18165,7 +18216,7 @@ snapshots: dependencies: '@expo/log-box': 58.0.12(@expo/dom-webview@58.0.2)(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) anser: 1.4.10 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) pretty-format: 29.7.0 react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) @@ -18228,37 +18279,47 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@58.0.14(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/prebuild-config@58.0.14(supports-color@10.2.2)(typescript@5.9.3)': dependencies: - '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@7.0.2) - '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@5.9.3) + '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@5.9.3) '@expo/config-types': 58.0.3 - '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@7.0.2) + '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@5.9.3) '@expo/json-file': 11.2.1 '@react-native/normalize-colors': 0.88.0 debug: 4.4.3(supports-color@10.2.2) - expo-modules-autolinking: 58.0.13(supports-color@10.2.2)(typescript@7.0.2) + expo-modules-autolinking: 58.0.13(supports-color@10.2.2)(typescript@5.9.3) resolve-from: 5.0.0 semver: 7.8.5 transitivePeerDependencies: - supports-color - typescript - '@expo/require-utils@58.0.4(supports-color@10.2.2)(typescript@7.0.2)': + '@expo/require-utils@55.0.8(supports-color@10.2.2)(typescript@5.9.3)': dependencies: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) optionalDependencies: - typescript: 7.0.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@expo/require-utils@58.0.4(supports-color@10.2.2)(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - supports-color '@expo/router-server@58.0.15(@expo/metro-runtime@58.0.14)(expo-constants@58.0.12)(expo-font@58.0.9)(expo-router@58.0.24)(expo-server@58.0.5)(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) - expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) + expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) expo-font: 58.0.9(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) expo-server: 58.0.5 react: 19.3.0 @@ -18281,7 +18342,7 @@ snapshots: '@expo/ui@58.0.21(@babel/core@7.29.7(supports-color@10.2.2))(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react-native-worklets@0.13.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)': dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) sf-symbols-typescript: 2.2.0 @@ -20341,6 +20402,8 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/json-schema@7.0.15': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -20771,6 +20834,11 @@ snapshots: dependencies: ajv: 8.20.0 + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -21027,7 +21095,7 @@ snapshots: react-refresh: 0.14.2 optionalDependencies: '@babel/runtime': 7.29.7 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) transitivePeerDependencies: - '@babel/core' - supports-color @@ -22037,19 +22105,19 @@ snapshots: expo-alternate-app-icons@8.0.0(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-application@58.1.2(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) - expo-asset@58.0.17(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2): + expo-asset@58.0.17(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3): dependencies: - '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@7.0.2) - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) - expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@5.9.3) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) + expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) transitivePeerDependencies: @@ -22059,7 +22127,7 @@ snapshots: expo-camera@58.0.11(@types/emscripten@1.41.6)(expo@58.0.16)(react-native-web@0.21.4(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: barcode-detector: 3.2.2(@types/emscripten@1.41.6) - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) optionalDependencies: @@ -22069,15 +22137,15 @@ snapshots: expo-clipboard@58.0.3(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) - expo-constants@58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2): + expo-constants@58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3): dependencies: '@expo/env': 2.5.2(supports-color@10.2.2) - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -22085,11 +22153,11 @@ snapshots: expo-crypto@58.0.7(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-dev-client@58.0.14(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-dev-launcher: 58.0.14(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)) expo-dev-menu: 58.0.11(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)) expo-dev-menu-interface: 58.0.1(expo@58.0.16) @@ -22101,72 +22169,72 @@ snapshots: expo-dev-launcher@58.0.14(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)): dependencies: '@expo/schema-utils': 58.0.2 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-dev-menu: 58.0.11(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)) expo-manifests: 58.0.1(expo@58.0.16) react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-dev-menu-interface@58.0.1(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-dev-menu@58.0.11(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-dev-menu-interface: 58.0.1(expo@58.0.16) react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-device@58.0.5(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) ua-parser-js: 0.7.41 expo-doctor@1.21.4: {} expo-document-picker@58.0.4(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-file-system@58.0.10(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-font@58.0.9(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-glass-effect@58.0.4(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-haptics@58.0.7(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-image-loader@58.0.3(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-image-manipulator@58.0.15(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-image-loader: 58.0.3(expo@58.0.16) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-image-picker@58.0.10(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-image-loader: 58.0.3(expo@58.0.16) expo-image@58.0.16(expo@58.0.16)(react-native-web@0.21.4(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) sf-symbols-typescript: 2.2.0 @@ -22177,13 +22245,13 @@ snapshots: expo-keep-awake@58.0.3(expo@58.0.16)(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 - expo-linking@58.0.13(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2): + expo-linking@58.0.13(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) - expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) + expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) invariant: 2.2.4 react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) @@ -22193,13 +22261,13 @@ snapshots: expo-manifests@58.0.1(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-json-utils: 58.0.1 - expo-modules-autolinking@58.0.13(supports-color@10.2.2)(typescript@7.0.2): + expo-modules-autolinking@58.0.13(supports-color@10.2.2)(typescript@5.9.3): dependencies: '@expo/platforms': 58.0.1 - '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@7.0.2) + '@expo/require-utils': 58.0.4(supports-color@10.2.2)(typescript@5.9.3) '@expo/spawn-async': 1.8.0 chalk: 4.1.2 commander: 7.2.0 @@ -22232,20 +22300,31 @@ snapshots: '@expo/modules-macros-win32-arm64': 0.21.0 '@expo/modules-macros-win32-x64': 0.21.0 - expo-notifications@58.1.5(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2): + expo-notifications@58.1.5(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3): dependencies: - '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@7.0.2) + '@expo/image-utils': 0.12.5(supports-color@10.2.2)(typescript@5.9.3) abort-controller: 3.0.0 badgin: 1.2.3 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-application: 58.1.2(expo@58.0.16) - expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - typescript + expo-quick-actions@6.0.2(patch_hash=2d9cefa269045271145e1989e004232aac3c206249494311d39f49c13293dddb)(expo@58.0.16)(supports-color@10.2.2)(typescript@5.9.3): + dependencies: + '@expo/image-utils': 0.8.17(supports-color@10.2.2)(typescript@5.9.3) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) + schema-utils: 4.8.1 + sf-symbols-typescript: 2.2.0 + transitivePeerDependencies: + - supports-color + - typescript + - webpack + expo-router@58.0.24(809975b795ae32b2306b92910a7dbc02): dependencies: '@expo/log-box': 58.0.12(@expo/dom-webview@58.0.2)(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) @@ -22256,10 +22335,10 @@ snapshots: '@react-native-masked-view/masked-view': 0.3.2(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) '@react-native/normalize-colors': 0.88.0 client-only: 0.0.1 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) - expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) + expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) expo-glass-effect: 58.0.4(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) - expo-linking: 58.0.13(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2) + expo-linking: 58.0.13(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3) expo-server: 58.0.5 nanoid: 3.3.19 react: 19.3.0 @@ -22286,16 +22365,16 @@ snapshots: expo-secure-store@58.0.2(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-server@58.0.5: {} - expo-sharing@58.0.19(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2): + expo-sharing@58.0.19(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3): dependencies: - '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@7.0.2) + '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@5.9.3) '@expo/config-types': 58.0.3 '@expo/plist': 0.10.1 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) transitivePeerDependencies: @@ -22304,20 +22383,20 @@ snapshots: expo-speech-recognition@58.0.0(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-status-bar@58.0.3(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) expo-symbols@58.0.4(expo-font@58.0.9)(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0): dependencies: '@expo-google-fonts/material-symbols': 0.4.50 - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-font: 58.0.9(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) react: 19.3.0 react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) @@ -22327,7 +22406,7 @@ snapshots: dependencies: '@react-native/normalize-colors': 0.88.0 debug: 4.4.3(supports-color@10.2.2) - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) optionalDependencies: react-native-web: 0.21.4(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -22336,34 +22415,34 @@ snapshots: expo-updates-interface@58.0.1(expo@58.0.16): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) expo-web-browser@58.0.6(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)): dependencies: - expo: 58.0.16(8b51b359222a2f7e63ded559e0e83dc1) + expo: 58.0.16(18978b6cf407e7e2b52d0562def5d3cd) react-native: 0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2) - expo@58.0.16(8b51b359222a2f7e63ded559e0e83dc1): + expo@58.0.16(18978b6cf407e7e2b52d0562def5d3cd): dependencies: '@babel/runtime': 7.29.7 - '@expo/cli': 58.1.13(@expo/dom-webview@58.0.2)(@expo/metro-runtime@58.0.14)(expo-constants@58.0.12)(expo-font@58.0.9)(expo-router@58.0.24)(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2) - '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@7.0.2) - '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@7.0.2) + '@expo/cli': 58.1.13(@expo/dom-webview@58.0.2)(@expo/metro-runtime@58.0.14)(expo-constants@58.0.12)(expo-font@58.0.9)(expo-router@58.0.24)(expo@58.0.16)(react-dom@19.3.0(react@19.3.0))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3) + '@expo/config': 58.0.6(supports-color@10.2.2)(typescript@5.9.3) + '@expo/config-plugins': 58.0.9(supports-color@10.2.2)(typescript@5.9.3) '@expo/devtools': 58.0.2(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) '@expo/fingerprint': 0.21.5(supports-color@10.2.2) - '@expo/local-build-cache-provider': 58.0.6(supports-color@10.2.2)(typescript@7.0.2) + '@expo/local-build-cache-provider': 58.0.6(supports-color@10.2.2)(typescript@5.9.3) '@expo/log-box': 58.0.12(@expo/dom-webview@58.0.2)(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) '@expo/log-box-utils': 58.0.2 '@expo/metro': 58.0.0(supports-color@10.2.2) - '@expo/metro-config': 58.0.15(expo@58.0.16)(supports-color@10.2.2)(typescript@7.0.2) + '@expo/metro-config': 58.0.15(expo@58.0.16)(supports-color@10.2.2)(typescript@5.9.3) '@ungap/structured-clone': 1.4.0 babel-preset-expo: 58.0.18(@babel/core@7.29.7(supports-color@10.2.2))(@babel/runtime@7.29.7)(expo@58.0.16)(react-refresh@0.14.2)(supports-color@10.2.2) - expo-asset: 58.0.17(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@7.0.2) - expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@7.0.2) + expo-asset: 58.0.17(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2)(typescript@5.9.3) + expo-constants: 58.0.12(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) expo-file-system: 58.0.10(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2)) expo-font: 58.0.9(expo@58.0.16)(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) expo-keep-awake: 58.0.3(expo@58.0.16)(react@19.3.0) - expo-modules-autolinking: 58.0.13(supports-color@10.2.2)(typescript@7.0.2) + expo-modules-autolinking: 58.0.13(supports-color@10.2.2)(typescript@5.9.3) expo-modules-cli: 0.3.1 expo-modules-core: 58.0.23(react-native-worklets@0.13.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0)(supports-color@10.2.2))(react-native@0.88.0(@babel/core@7.29.7(supports-color@10.2.2))(@react-native/metro-config@0.87.1(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2))(@types/react@19.3.0)(react@19.3.0)(supports-color@10.2.2))(react@19.3.0) pretty-format: 29.7.0 @@ -25565,6 +25644,13 @@ snapshots: scheduler@0.28.0: {} + schema-utils@4.8.1: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) + semver@6.3.1: {} semver@7.8.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8b12c91d50..17a91df78d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -388,3 +388,4 @@ patchedDependencies: '@deepseek-ai/dsh-tool-fs@0.2.1-rc.2': patches/@deepseek-ai__dsh-tool-fs@0.2.1-rc.2.patch '@deepseek-ai/dsh-llm-pi-ai@0.2.1-rc.2': patches/@deepseek-ai__dsh-llm-pi-ai@0.2.1-rc.2.patch dockview-core@8.4.1: patches/dockview-core@8.4.1.patch + expo-quick-actions@6.0.2: patches/expo-quick-actions@6.0.2.patch diff --git a/tsconfig.json b/tsconfig.json index 812a3bdc86..7d464ebd0d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -47,6 +47,7 @@ "apps/mobile/test/action-cards.test.ts", "apps/mobile/test/activity-feed.test.ts", "apps/mobile/test/open-conversation.test.ts", + "apps/mobile/test/quick-action-routing.test.ts", "apps/mobile/test/grant-request-card.test.ts", "apps/mobile/test/attention-actions.test.ts", "apps/mobile/test/reanimated-shim.ts",