Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/expo-example/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"bundleIdentifier": "com.callstack.ai.example"
},
"android": {
"forceDarkAllowed": false,
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
Expand Down
2 changes: 1 addition & 1 deletion apps/expo-example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ const styles = StyleSheet.create({
},
drawer: {
width: 280,
backgroundColor: colors.systemBackground,
backgroundColor: '#fff',
},
drawerScroll: {
flex: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,13 @@ export function RecordButtonUIBase({
)
}

const uiDisabled = disabled || isProcessing || !transcriptionModel

return (
<AdaptiveGlass isInteractive style={styles.glassButton}>
<Pressable
onPress={handlePress}
disabled={disabled || isProcessing || !transcriptionModel}
disabled={uiDisabled}
style={styles.pressable}
>
{isProcessing ? (
Expand All @@ -202,9 +204,15 @@ export function RecordButtonUIBase({
<SymbolView
name="mic.fill"
size={20}
tintColor={colors.label}
tintColor={uiDisabled ? colors.label : colors.placeholderText}
resizeMode="scaleAspectFit"
fallback={<Ionicons name="mic" size={20} color={colors.label} />}
fallback={
<Ionicons
name="mic"
size={20}
color={uiDisabled ? '#666' : colors.label}
/>
}
/>
)}
</Pressable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export const createAppleLanguageSetupAdapter = (
tools: ToolSet = {}
): SetupAdapter<LanguageModelV3> => {
const apple = createAppleProvider({
// @ts-expect-error
availableTools: tools,
})
const model = apple.languageModel()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { LanguageModelV3 } from '@ai-sdk/provider'
import { mlc } from '@react-native-ai/mlc'
import RNBlobUtil from 'react-native-blob-util'
import { File, Paths } from 'expo-file-system'

import type { Availability, SetupAdapter } from '../../config/providers.common'

Expand All @@ -18,10 +18,8 @@ export const createMLCLanguageSetupAdapter = (
icon: 'cpu',
},
builtIn: false,
async isAvailable(): Promise<Availability> {
return (await RNBlobUtil.fs.exists(
RNBlobUtil.fs.dirs.SDCardDir + `/${modelId}/tensor-cache.json`
))
isAvailable(): Availability {
return new File(Paths.document, model.modelId, 'tensor-cache.json').exists
? 'yes'
: 'availableForDownload'
},
Expand Down
2 changes: 1 addition & 1 deletion apps/expo-example/src/config/providers.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface SetupAdapter<TModel> {
// Whether the model is built-in (true) or downloadable (false)
builtIn: boolean
// Check if model is ready, unavailable, or downloadable
isAvailable: () => Availability | Promise<Availability>
isAvailable: () => Availability
// Download the model with progress callback
download: (onProgress: (percentage: number) => void) => Promise<void>
// Remove the downloaded model from storage
Expand Down
14 changes: 8 additions & 6 deletions apps/expo-example/src/screens/ChatScreen/ChatInputBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,20 @@ export function ChatInputBar({ onSend, isGenerating }: ChatInputBarProps) {
<ContextMenu activationMethod="singlePress">
<ContextMenu.Items>
<Button
// @ts-expect-error - conditional icons per platform
systemImage={Platform.select({
ios: 'camera',
android: 'rounded.Contrast',
android: 'rounded.ArrowForward',
})}
onPress={() => console.log('Take Photo')}
>
Take Photo
</Button>
<Button
// @ts-expect-error - conditional icons per platform
systemImage={Platform.select({
ios: 'photo.on.rectangle',
android: 'rounded.Build',
android: 'rounded.ArrowBack',
})}
onPress={() => console.log('Photo Library')}
>
Expand All @@ -51,13 +53,13 @@ export function ChatInputBar({ onSend, isGenerating }: ChatInputBarProps) {
</ContextMenu.Items>
<ContextMenu.Trigger>
<Button
// @ts-expect-error - conditional icons per platform
systemImage={Platform.select({
ios: 'plus',
android: 'rounded.Add',
})}
{...(Platform.select({
ios: { variant: 'borderless', color: '#000' },
}) ?? {})}
variant="borderless"
color={colors.tertiaryLabel as any}
/>
</ContextMenu.Trigger>
</ContextMenu>
Expand All @@ -69,7 +71,7 @@ export function ChatInputBar({ onSend, isGenerating }: ChatInputBarProps) {
value={input}
onChangeText={setInput}
placeholder="Message"
placeholderTextColor={colors.placeholderText as any}
placeholderTextColor={colors.secondaryLabel as any}
multiline
style={styles.textInput}
editable={!isGenerating}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function ModelPickerSheet({ ref }: ModelPickerSheetProps) {
}

const { modelId: selectedModelId } = chatSettings
console.log({ selectedModelId })

return (
<TrueSheet ref={ref} scrollable style={{ backgroundColor: '#fff' }}>
<View style={styles.sheetContainer}>
Expand Down
27 changes: 25 additions & 2 deletions apps/expo-example/src/screens/ChatScreen/SettingsSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import Ionicons from '@expo/vector-icons/Ionicons'
import { TrueSheet } from '@lodev09/react-native-true-sheet'
import { SymbolView } from 'expo-symbols'
import React, { RefObject } from 'react'
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
import {
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
} from 'react-native'

import { Host, Slider } from '../../components/expo-ui'
import { useChatStore } from '../../store/chatStore'
Expand All @@ -18,7 +25,7 @@ export function SettingsSheet({ ref }: SettingsSheetProps) {
const { temperature, maxSteps, enabledToolIds } = chatSettings

return (
<TrueSheet ref={ref} scrollable>
<TrueSheet ref={ref} scrollable style={styles.sheetContainerSlidingWrapper}>
<View style={styles.sheetContainer}>
<ScrollView nestedScrollEnabled>
<View style={styles.sheetHeader}>
Expand Down Expand Up @@ -93,6 +100,11 @@ export function SettingsSheet({ ref }: SettingsSheetProps) {
temperature: Math.round(value * 10) / 10,
})
}
style={
Platform.OS === 'android'
? styles.androidSlider
: undefined
}
/>
</Host>
</View>
Expand All @@ -110,6 +122,11 @@ export function SettingsSheet({ ref }: SettingsSheetProps) {
onValueChange={(value) =>
updateChatSettings({ maxSteps: Math.round(value) })
}
style={
Platform.OS === 'android'
? styles.androidSlider
: undefined
}
/>
</Host>
</View>
Expand All @@ -126,6 +143,9 @@ export function SettingsSheet({ ref }: SettingsSheetProps) {
}

const styles = StyleSheet.create({
sheetContainerSlidingWrapper: {
backgroundColor: '#fff',
},
sheetContainer: {
paddingHorizontal: 20,
paddingBottom: 40,
Expand Down Expand Up @@ -239,4 +259,7 @@ const styles = StyleSheet.create({
color: colors.secondaryLabel as any,
lineHeight: 18,
},
androidSlider: {
minHeight: 20,
},
})
5 changes: 4 additions & 1 deletion apps/expo-example/src/store/chatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ export function useChatStore() {
}

const updateChatSettings = (updates: Partial<ChatSettings>) => {
setPendingSettings((prev) => ({ ...prev, ...updates }))
if (!currentChatId) {
setPendingSettings((prev) => ({ ...prev, ...updates }))
return
}
setChats((prev) => {
const arr = Array.isArray(prev) ? prev : []
return arr.map((chat) =>
Expand Down
4 changes: 2 additions & 2 deletions apps/expo-example/src/store/providerStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ const adaptersAtom = atom((get) => {
return [...languageAdapters, ...customModels]
})

const availabilityAtom = atomWithRefresh(async (get) => {
const availabilityAtom = atomWithRefresh((get) => {
const adapters = get(adaptersAtom)
const map = new Map<string, Availability>()
for (const adapter of adapters) {
map.set(adapter.modelId, await adapter.isAvailable())
map.set(adapter.modelId, adapter.isAvailable())
}
return map
})
Expand Down
4 changes: 2 additions & 2 deletions apps/expo-example/src/theme/colors.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const colors = {
// Text
label: PlatformColor('@android:color/primary_text_light'),
secondaryLabel: PlatformColor('@android:color/secondary_text_light'),
tertiaryLabel: PlatformColor('@android:color/tertiary_text_light'),
tertiaryLabel: PlatformColor('@android:color/secondary_text_light'),
placeholderText: PlatformColor('@android:color/background_light'),

// Backgrounds
Expand All @@ -13,7 +13,7 @@ export const colors = {
tertiarySystemBackground: PlatformColor('@android:color/background_light'),

// Fills
tertiarySystemFill: PlatformColor('@android:color/darker_gray'),
tertiarySystemFill: PlatformColor('@android:color/lighter_gray'),

// Accent colors
systemBlue: PlatformColor('@android:color/holo_blue_light'),
Expand Down
1 change: 1 addition & 0 deletions apps/expo-example/src/utils/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { File } from 'expo-file-system'
export function isModelDownloaded(modelId: string): boolean {
let path = getModelPath(modelId)

// expo-file-system requires that this URI starts with the file:// protocol
if (!path.startsWith('file://')) {
path = `file://${path}`
}
Expand Down
3 changes: 1 addition & 2 deletions apps/expo-example/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"paths": {
"@react-native-ai/apple": ["../../packages/apple-llm/src"],
"@react-native-ai/mlc": ["../../packages/mlc/src"]
},
"types": ["nativewind/types"]
}
},
"include": ["src/**/*", "nativewind-env.d.ts"]
}