Skip to content
Open
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
4 changes: 2 additions & 2 deletions ios/OffgridMobile/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSLocalNetworkUsageDescription</key>
<string>Off Grid scans your local network to automatically discover LLM servers such as Ollama and LM Studio.</string>
<key>NSBonjourServices</key>
<array>
<string>_http._tcp</string>
Expand All @@ -43,6 +41,8 @@
<string>This app needs access to your camera to take photos and attach them to conversations.</string>
<key>NSFaceIDUsageDescription</key>
<string>This app may use Face ID to protect access to your stored credentials.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Off Grid scans your local network to automatically discover LLM servers such as Ollama and LM Studio.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to your microphone for voice-to-text transcription using Whisper.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
Expand Down
64 changes: 32 additions & 32 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ PODS:
- hermes-engine (0.14.0):
- hermes-engine/Pre-built (= 0.14.0)
- hermes-engine/Pre-built (0.14.0)
- llama-rn (0.11.4):
- llama-rn (0.12.0-rc.5):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -40,15 +40,15 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- lottie-ios (4.6.0)
- lottie-react-native (7.3.6):
- lottie-ios (4.5.0)
- lottie-react-native (7.3.5):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- lottie-ios (= 4.6.0)
- lottie-ios (= 4.5.0)
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
Expand Down Expand Up @@ -2094,7 +2094,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-safe-area-context (5.7.0):
- react-native-safe-area-context (5.6.2):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2112,8 +2112,8 @@ PODS:
- React-graphics
- React-ImageManager
- React-jsi
- react-native-safe-area-context/common (= 5.7.0)
- react-native-safe-area-context/fabric (= 5.7.0)
- react-native-safe-area-context/common (= 5.6.2)
- react-native-safe-area-context/fabric (= 5.6.2)
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
Expand All @@ -2124,7 +2124,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-safe-area-context/common (5.7.0):
- react-native-safe-area-context/common (5.6.2):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -2152,7 +2152,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-safe-area-context/fabric (5.7.0):
- react-native-safe-area-context/fabric (5.6.2):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -2825,7 +2825,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- RNDeviceInfo (15.0.2):
- RNDeviceInfo (15.0.1):
- React-Core
- RNFS (2.20.0):
- React-Core
Expand Down Expand Up @@ -2913,7 +2913,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- RNReanimated (4.2.2):
- RNReanimated (4.2.1):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2940,11 +2940,11 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNReanimated/reanimated (= 4.2.2)
- RNReanimated/reanimated (= 4.2.1)
- RNWorklets
- SocketRocket
- Yoga
- RNReanimated/reanimated (4.2.2):
- RNReanimated/reanimated (4.2.1):
- boost
- DoubleConversion
- fast_float
Expand All @@ -2971,11 +2971,11 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNReanimated/reanimated/apple (= 4.2.2)
- RNReanimated/reanimated/apple (= 4.2.1)
- RNWorklets
- SocketRocket
- Yoga
- RNReanimated/reanimated/apple (4.2.2):
- RNReanimated/reanimated/apple (4.2.1):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3005,7 +3005,7 @@ PODS:
- RNWorklets
- SocketRocket
- Yoga
- RNScreens (4.24.0):
- RNScreens (4.20.0):
- boost
- DoubleConversion
- fast_float
Expand All @@ -3032,10 +3032,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNScreens/common (= 4.24.0)
- RNScreens/common (= 4.20.0)
- SocketRocket
- Yoga
- RNScreens/common (4.24.0):
- RNScreens/common (4.20.0):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3149,7 +3149,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- RNWorklets (0.7.4):
- RNWorklets (0.7.3):
- boost
- DoubleConversion
- fast_float
Expand All @@ -3176,10 +3176,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNWorklets/worklets (= 0.7.4)
- RNWorklets/worklets (= 0.7.3)
- SocketRocket
- Yoga
- RNWorklets/worklets (0.7.4):
- RNWorklets/worklets (0.7.3):
- boost
- DoubleConversion
- fast_float
Expand All @@ -3206,10 +3206,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNWorklets/worklets/apple (= 0.7.4)
- RNWorklets/worklets/apple (= 0.7.3)
- SocketRocket
- Yoga
- RNWorklets/worklets/apple (0.7.4):
- RNWorklets/worklets/apple (0.7.3):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3603,10 +3603,10 @@ SPEC CHECKSUMS:
FBLazyVector: 309703e71d3f2f1ed7dc7889d58309c9d77a95a4
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: 3f74bbb07573d284e764cee0131ae769e16c53b8
llama-rn: f673d63fdd04ce96793c60ba03be52804d94f49a
lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3
lottie-react-native: 983fd0489530e8d40f173de7f04e2f88b9317a15
hermes-engine: 8c6be38f94b3bf8b864981980e64e55f08e467ec
llama-rn: 3ae5a64b3d08ff41f9e62b214ba5004e475b9561
lottie-ios: a881093fab623c467d3bce374367755c272bdd59
lottie-react-native: 691b8363e8c591fb78a78254ff2517258891456b
op-sqlite: bafff369cecaee4fe65c89eec47deaba26f2db95
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
RCTDeprecation: a41bbdd9af30bf2e5715796b313e44ec43eefff1
Expand Down Expand Up @@ -3648,7 +3648,7 @@ SPEC CHECKSUMS:
react-native-document-picker: dc2d83366e47e89e7c51e8a41eab99c1d54e941c
react-native-document-viewer: 8c6ed07e7e27352743fa98e8dd6d288ad925b884
react-native-image-picker: 0314366753615115fa55c3cc937ac44cb7e75702
react-native-safe-area-context: befb5404eb8a16fdc07fa2bebab3568ecabcbb8a
react-native-safe-area-context: c00143b4823773bba23f2f19f85663ae89ceb460
react-native-slider: 34064ca1a6864d7b263e44dd76a2d794e8d26744
react-native-voice: 908a0eba96c8c3d643e4f98b7232c6557d0a6f9c
React-NativeModulesApple: a2c3d2cbec893956a5b3e4060322db2984fff75b
Expand Down Expand Up @@ -3685,16 +3685,16 @@ SPEC CHECKSUMS:
ReactCodegen: 3d48510bcef445f6403c0004047d4d9cbb915435
ReactCommon: ac934cb340aee91282ecd6f273a26d24d4c55cae
RNCAsyncStorage: 29f0230e1a25f36c20b05f65e2eb8958d6526e82
RNDeviceInfo: 4c852998208b60dc192ae3529e5867817719ad1e
RNDeviceInfo: 36d7f232bfe7c9b5c494cb7793230424ed32c388
RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8
RNGestureHandler: cd4be101cfa17ea6bbd438710caa02e286a84381
RNKeychain: a2c134ab796272c3d605e035ab727591000b30f3
RNReactNativeHapticFeedback: be4f1b4bf0398c30b59b76ed92ecb0a2ff3a69c6
RNReanimated: 18324d3313d6477e1d12836c20c3ee30afb5de30
RNScreens: 7f643ee0fd1407dc5085c7795460bd93da113b8f
RNReanimated: 292cd58688552a22b3fc1cefcfbc49b336dfed68
RNScreens: 714e10b6b554f7dc7ad9f78dcf36dc8e3fc73415
RNSVG: 595abfa0f9ac26d56afcaaedf4e37a00f54cab71
RNVectorIcons: 791f13226ec4a3fd13062eda9e892159f0981fae
RNWorklets: a3184955a41f2be46898a937e2821469c8c8da42
RNWorklets: 944dddd0eef13006b658e653abbb3ee8365c3809
RNZipArchive: 4304f5100eab004eeb7349adc51997b3a28deb76
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
SSZipArchive: c69881e8ac5521f0e622291387add5f60f30f3c4
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@shopify/flash-list": "^2.2.2",
"@testing-library/react-native": "^13.3.3",
"@types/react-native-vector-icons": "^6.4.18",
"llama.rn": "^0.11.2",
"llama.rn": "^0.12.0-rc.5",
"lottie-react-native": "^7.3.5",
"moti": "^0.30.0",
"patch-package": "^8.0.1",
Expand Down
32 changes: 28 additions & 4 deletions src/components/ChatMessage/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,36 @@

/**
* Parse content that may contain thinking/reasoning sections.
* Handles two formats:
* 1. HLSL.. HLSL tags (used by llama models with thinking enabled)
* 2. <|channel|>analysis<|message|>...<|channel|>final<|message|> (used by Qwen and similar models)
* Handles three formats:
* 1. <think>...</think> tags (DeepSeek-style, used by llama models with thinking enabled)
* 2. <|channel>thought\n...<channel|> (Gemma 4)
* 3. <|channel|>analysis<|message|>...<|channel|>final<|message|> (Qwen and similar models)
*/
export function parseThinkingContent(content: string): ParsedContent {

Check failure on line 12 in src/components/ChatMessage/utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=alichherawalla_off-grid-mobile&issues=AZ1jam7Z7JvP60NAY9fQ&open=AZ1jam7Z7JvP60NAY9fQ&pullRequest=232
// First, check for channel-based thinking format
// Gemma 4 thinking format: <|channel>thought\n[thinking]<channel|>[response]
// Note asymmetric tags: <|channel> opens (with channel name 'thought'), <channel|> closes.
const gemmaOpenMatch = content.match(/<\|channel>thought\n/i);

Check warning on line 15 in src/components/ChatMessage/utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=alichherawalla_off-grid-mobile&issues=AZ1jam7Z7JvP60NAY9fR&open=AZ1jam7Z7JvP60NAY9fR&pullRequest=232
const gemmaCloseMatch = content.match(/<channel\|>/i);

Check warning on line 16 in src/components/ChatMessage/utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=alichherawalla_off-grid-mobile&issues=AZ1jam7Z7JvP60NAY9fS&open=AZ1jam7Z7JvP60NAY9fS&pullRequest=232

if (gemmaOpenMatch) {
const thinkStart = gemmaOpenMatch.index! + gemmaOpenMatch[0].length;

Check warning on line 19 in src/components/ChatMessage/utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This assertion is unnecessary since it does not change the type of the expression.

See more on https://sonarcloud.io/project/issues?id=alichherawalla_off-grid-mobile&issues=AZ1jam7Z7JvP60NAY9fT&open=AZ1jam7Z7JvP60NAY9fT&pullRequest=232
if (gemmaCloseMatch && gemmaCloseMatch.index! >= thinkStart) {

Check warning on line 20 in src/components/ChatMessage/utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This assertion is unnecessary since it does not change the type of the expression.

See more on https://sonarcloud.io/project/issues?id=alichherawalla_off-grid-mobile&issues=AZ1jam7Z7JvP60NAY9fU&open=AZ1jam7Z7JvP60NAY9fU&pullRequest=232
const thinkEnd = gemmaCloseMatch.index!;

Check warning on line 21 in src/components/ChatMessage/utils.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This assertion is unnecessary since it does not change the type of the expression.

See more on https://sonarcloud.io/project/issues?id=alichherawalla_off-grid-mobile&issues=AZ1jam7Z7JvP60NAY9fV&open=AZ1jam7Z7JvP60NAY9fV&pullRequest=232
return {
thinking: content.slice(thinkStart, thinkEnd).trim(),
response: content.slice(thinkEnd + gemmaCloseMatch[0].length).trim(),
isThinkingComplete: true,
};
}
// Still streaming — thinking not yet closed
return {
thinking: content.slice(thinkStart).trim(),
response: '',
isThinkingComplete: false,
};
}

// Check for channel-based thinking format
// Format: <|channel|>analysis<|message|>[thinking content]<|channel|>final<|message|>[response]
const channelAnalysisMatch = content.match(/<\|channel\|>analysis<\|message\|>/i);
const channelFinalMatch = content.match(/<\|channel\|>final<\|message\|>/i);
Expand Down
2 changes: 1 addition & 1 deletion src/components/ModelCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export const ModelCard: React.FC<ModelCardProps> = ({
disabled={!onPress}
testID={testID}
>
<View style={styles.cardRow}>
<View style={styles.cardRow}>
<View style={styles.cardContent}>
{compact ? (
<CompactModelCardContent
Expand Down
23 changes: 22 additions & 1 deletion src/constants/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,30 @@ export const MODEL_RECOMMENDATIONS = {
],
};

// Curated list of recommended models for mobile (updated Feb 2026)
// Curated list of recommended models for mobile (updated Apr 2026)
// All IDs use official org repos where available, ggml-org (HuggingFace official) as fallback
export const RECOMMENDED_MODELS = [
// --- NEW: Gemma 4 (Apr 2026) ---
{
id: 'ggml-org/gemma-4-E2B-it-GGUF',
name: 'Gemma 4 E2B',
params: 2,
description: 'Google\'s latest with thinking mode, MoE architecture',
minRam: 4,
type: 'text' as const,
org: 'google',
isNew: true,
},
{
id: 'ggml-org/gemma-4-E4B-it-GGUF',
name: 'Gemma 4 E4B',
params: 4,
description: 'Google\'s latest, stronger reasoning + vision',
minRam: 6,
type: 'vision' as const,
org: 'google',
isNew: true,
},
// --- Text: Ultra-light (3 GB+) ---
{
id: 'unsloth/Qwen3.5-0.8B-GGUF',
Expand Down
Loading
Loading