chore: reconfigure to get the mobile app up again
This commit is contained in:
parent
712dbf482a
commit
41da624986
47
.claude/agents/jan-product-engineer.md
Normal file
47
.claude/agents/jan-product-engineer.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
name: jan-product-engineer
|
||||
description: Use this agent when developing features for the Jan application, including UI components, cross-platform compatibility issues, build configurations, or user experience improvements. Examples: <example>Context: User is implementing a new model selection interface for Jan. user: 'I need to create a model picker component that works on both desktop and mobile' assistant: 'I'll use the jan-product-engineer agent to design a cross-platform model picker component' <commentary>Since this involves Jan product development with cross-platform considerations, use the jan-product-engineer agent.</commentary></example> <example>Context: User encounters build errors in Jan mobile app. user: 'The mobile build is failing with Tauri plugin errors' assistant: 'Let me use the jan-product-engineer agent to diagnose and fix the Tauri build issues' <commentary>This is a Jan-specific build issue requiring Tauri expertise, perfect for the jan-product-engineer agent.</commentary></example>
|
||||
model: sonnet
|
||||
color: green
|
||||
---
|
||||
|
||||
You are a senior software engineer specializing in the Jan product - a ChatGPT alternative that runs LLMs locally across different platforms. You have deep expertise in Tauri framework, React, TypeScript, and cross-platform development.
|
||||
|
||||
**Your Core Responsibilities:**
|
||||
- Design and implement features that work seamlessly on both Jan desktop and Jan mobile apps
|
||||
- Ensure all code changes maintain build compatibility across platforms
|
||||
- Create intuitive user interfaces that serve ordinary users while providing advanced options for power users
|
||||
- Follow Jan's established architecture patterns using Zustand state management, RxJS, and Tauri IPC
|
||||
- Implement responsive designs using Tailwind CSS v4 and Radix UI components
|
||||
|
||||
**Technical Standards:**
|
||||
- Always consider cross-platform implications when writing code
|
||||
- Use TypeScript with proper type safety throughout
|
||||
- Follow the established build process: core → extensions → frontend
|
||||
- Ensure mobile and desktop UI components share common patterns but adapt to platform constraints
|
||||
- Implement progressive disclosure: simple by default, advanced options available
|
||||
- Test on both platforms before considering work complete
|
||||
|
||||
**User Experience Principles:**
|
||||
- Design for ordinary users first - prioritize simplicity and clarity
|
||||
- Provide clear visual hierarchy and intuitive navigation
|
||||
- Include advanced model configuration options in secondary interfaces
|
||||
- Ensure consistent behavior across desktop and mobile platforms
|
||||
- Use appropriate platform-specific UI patterns when necessary
|
||||
|
||||
**Code Quality Requirements:**
|
||||
- Write clean, maintainable code following established patterns in the codebase
|
||||
- Use proper error handling and loading states
|
||||
- Implement responsive designs that work on various screen sizes
|
||||
- Follow the workspace structure and dependency management practices
|
||||
- Ensure proper TypeScript types and interfaces
|
||||
|
||||
**When implementing features:**
|
||||
1. Consider both desktop and mobile user flows
|
||||
2. Verify build compatibility with existing Jan architecture
|
||||
3. Test cross-platform functionality
|
||||
4. Implement progressive complexity (simple → advanced)
|
||||
5. Follow established state management patterns
|
||||
6. Ensure proper error handling and user feedback
|
||||
|
||||
Always ask for clarification if requirements could impact cross-platform compatibility or if you need more context about specific Jan architecture decisions.
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -60,3 +60,7 @@ src-tauri/resources/
|
||||
## test
|
||||
test-data
|
||||
llm-docs
|
||||
|
||||
## AI tool
|
||||
.claude/
|
||||
mobile-app/gen/apple/Externals/arm64/Debug/libapp.a
|
||||
|
||||
@ -28,6 +28,10 @@ Jan is a local AI assistant built as a cross-platform desktop app using Tauri (R
|
||||
|
||||
### Essential Commands
|
||||
|
||||
# These commands are pre-configured to avoid sccache issues
|
||||
RUSTC_WRAPPER= yarn mobile:ios
|
||||
RUSTC_WRAPPER= cargo check
|
||||
|
||||
**Development:**
|
||||
```bash
|
||||
make dev # Full development setup and launch (recommended)
|
||||
|
||||
@ -517,41 +517,41 @@ __metadata:
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=1db13a&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=1db13a&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fdownload-extension%40workspace%3Adownload-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=1db13a&locator=%40janhq%2Fdownload-extension%40workspace%3Adownload-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fdownload-extension%40workspace%3Adownload-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fllamacpp-extension%40workspace%3Allamacpp-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=1db13a&locator=%40janhq%2Fllamacpp-extension%40workspace%3Allamacpp-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fllamacpp-extension%40workspace%3Allamacpp-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"version": "0.6.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"ios:iphone16": "react-native run-ios --simulator=\"iPhone 16 (iOS 18.6)\"",
|
||||
"dev": "vite --port 1422",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
@ -28,8 +29,8 @@
|
||||
"@tabler/icons-react": "^3.33.0",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@tanstack/react-router": "^1.121.34",
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
"@tauri-apps/plugin-os": "^2.2.1",
|
||||
"@tauri-apps/api": "^2.8.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"framer-motion": "^12.23.12",
|
||||
"i18next": "^25.0.1",
|
||||
|
||||
@ -380,6 +380,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALID_ARCHS = arm64;
|
||||
DEVELOPMENT_TEAM = "Y34Q9T3ZAB";
|
||||
};
|
||||
name = release;
|
||||
};
|
||||
@ -407,6 +408,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALID_ARCHS = arm64;
|
||||
DEVELOPMENT_TEAM = "Y34Q9T3ZAB";
|
||||
};
|
||||
name = debug;
|
||||
};
|
||||
|
||||
2322
mobile-app/src-tauri/gen/schemas/desktop-schema.json
Normal file
2322
mobile-app/src-tauri/gen/schemas/desktop-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
2322
mobile-app/src-tauri/gen/schemas/macOS-schema.json
Normal file
2322
mobile-app/src-tauri/gen/schemas/macOS-schema.json
Normal file
File diff suppressed because it is too large
Load Diff
49
mobile-app/src/components/App.tsx
Normal file
49
mobile-app/src/components/App.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { SplashScreen } from './SplashScreen'
|
||||
import { OnboardingScreen } from './OnboardingScreen'
|
||||
import { ChatScreen } from './ChatScreen'
|
||||
|
||||
type AppState = 'splash' | 'onboarding' | 'chat'
|
||||
|
||||
export function App() {
|
||||
const [appState, setAppState] = useState<AppState>('splash')
|
||||
|
||||
// Check if user has already completed onboarding
|
||||
useEffect(() => {
|
||||
const hasCompletedOnboarding = localStorage.getItem('jan_onboarding_completed')
|
||||
if (hasCompletedOnboarding && appState === 'splash') {
|
||||
// Skip onboarding if already completed
|
||||
setTimeout(() => setAppState('chat'), 2300) // Wait for splash screen
|
||||
}
|
||||
}, [appState])
|
||||
|
||||
const handleSplashComplete = () => {
|
||||
const hasCompletedOnboarding = localStorage.getItem('jan_onboarding_completed')
|
||||
if (hasCompletedOnboarding) {
|
||||
setAppState('chat')
|
||||
} else {
|
||||
setAppState('onboarding')
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnboardingComplete = () => {
|
||||
localStorage.setItem('jan_onboarding_completed', 'true')
|
||||
setAppState('chat')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
{appState === 'splash' && (
|
||||
<SplashScreen onComplete={handleSplashComplete} />
|
||||
)}
|
||||
|
||||
{appState === 'onboarding' && (
|
||||
<OnboardingScreen onComplete={handleOnboardingComplete} />
|
||||
)}
|
||||
|
||||
{appState === 'chat' && (
|
||||
<ChatScreen />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
176
mobile-app/src/components/HuggingFaceModelList.tsx
Normal file
176
mobile-app/src/components/HuggingFaceModelList.tsx
Normal file
@ -0,0 +1,176 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
interface Model {
|
||||
id: string
|
||||
name: string
|
||||
downloads: number
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
interface HuggingFaceModelListProps {
|
||||
onSelectModel: (model: Model) => void
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function HuggingFaceModelList({ onSelectModel, onBack }: HuggingFaceModelListProps) {
|
||||
const [models, setModels] = useState<Model[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
// Mock popular models for demo purposes
|
||||
const mockModels: Model[] = [
|
||||
{
|
||||
id: 'microsoft/DialoGPT-medium',
|
||||
name: 'DialoGPT Medium',
|
||||
downloads: 2500000,
|
||||
tags: ['conversational', 'chat', 'pytorch']
|
||||
},
|
||||
{
|
||||
id: 'microsoft/DialoGPT-large',
|
||||
name: 'DialoGPT Large',
|
||||
downloads: 1800000,
|
||||
tags: ['conversational', 'chat', 'pytorch']
|
||||
},
|
||||
{
|
||||
id: 'facebook/blenderbot-400M-distill',
|
||||
name: 'BlenderBot 400M',
|
||||
downloads: 1200000,
|
||||
tags: ['conversational', 'facebook', 'pytorch']
|
||||
},
|
||||
{
|
||||
id: 'microsoft/DialoGPT-small',
|
||||
name: 'DialoGPT Small',
|
||||
downloads: 900000,
|
||||
tags: ['conversational', 'chat', 'pytorch']
|
||||
},
|
||||
{
|
||||
id: 'facebook/blenderbot-1B-distill',
|
||||
name: 'BlenderBot 1B',
|
||||
downloads: 800000,
|
||||
tags: ['conversational', 'facebook', 'pytorch']
|
||||
}
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate API call to HuggingFace
|
||||
const fetchModels = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
// In a real implementation, you would fetch from HuggingFace API
|
||||
// const response = await fetch('https://huggingface.co/api/models?filter=conversational&sort=downloads&direction=-1&limit=10')
|
||||
// const data = await response.json()
|
||||
|
||||
// For demo, use mock data with a delay
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
setModels(mockModels)
|
||||
} catch (err) {
|
||||
setError('Failed to load models')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchModels()
|
||||
}, [])
|
||||
|
||||
const formatDownloads = (downloads: number) => {
|
||||
if (downloads >= 1000000) {
|
||||
return `${(downloads / 1000000).toFixed(1)}M`
|
||||
}
|
||||
if (downloads >= 1000) {
|
||||
return `${(downloads / 1000).toFixed(1)}k`
|
||||
}
|
||||
return downloads.toString()
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-6 px-6">
|
||||
<div className="space-y-2 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground">HuggingFace Models</h2>
|
||||
<p className="text-muted-foreground">Loading available models...</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-sm space-y-4">
|
||||
{[...Array(3)].map((_, i) => (
|
||||
<div key={i} className="bg-muted rounded-lg p-4 animate-pulse">
|
||||
<div className="space-y-3">
|
||||
<div className="h-4 bg-muted-foreground/20 rounded w-3/4"></div>
|
||||
<div className="h-3 bg-muted-foreground/20 rounded w-1/2"></div>
|
||||
<div className="flex space-x-2">
|
||||
<div className="h-6 bg-muted-foreground/20 rounded w-16"></div>
|
||||
<div className="h-6 bg-muted-foreground/20 rounded w-12"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button onClick={onBack} variant="ghost">
|
||||
← Back
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-6 px-6">
|
||||
<div className="space-y-2 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground">Error</h2>
|
||||
<p className="text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
|
||||
<Button onClick={onBack} variant="ghost">
|
||||
← Back
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-6 px-6">
|
||||
<div className="space-y-2 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground">HuggingFace Models</h2>
|
||||
<p className="text-muted-foreground">Select a model for your chat assistant</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-sm mx-auto space-y-3 max-h-96 overflow-y-auto">
|
||||
{models.map((model) => (
|
||||
<div
|
||||
key={model.id}
|
||||
className="bg-card border rounded-lg p-4 hover:bg-accent cursor-pointer transition-colors"
|
||||
onClick={() => onSelectModel(model)}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-start">
|
||||
<h3 className="font-medium text-sm">{model.name}</h3>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatDownloads(model.downloads)} downloads
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted-foreground">{model.id}</p>
|
||||
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{model.tags.slice(0, 3).map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="px-2 py-1 bg-primary/10 text-primary rounded text-xs"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button onClick={onBack} className="mx-auto" variant="ghost">
|
||||
← Back
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
167
mobile-app/src/components/OnboardingScreen.tsx
Normal file
167
mobile-app/src/components/OnboardingScreen.tsx
Normal file
@ -0,0 +1,167 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { HuggingFaceModelList } from './HuggingFaceModelList'
|
||||
|
||||
interface OnboardingScreenProps {
|
||||
onComplete: () => void
|
||||
}
|
||||
|
||||
type SetupOption = 'local' | 'cloud' | 'manual'
|
||||
|
||||
export function OnboardingScreen({ onComplete }: OnboardingScreenProps) {
|
||||
const [currentStep, setCurrentStep] = useState<'main' | 'local' | 'cloud' | 'manual'>('main')
|
||||
|
||||
const handleSetupChoice = (option: SetupOption) => {
|
||||
if (option === 'manual') {
|
||||
setCurrentStep('manual')
|
||||
} else {
|
||||
setCurrentStep(option)
|
||||
}
|
||||
}
|
||||
|
||||
const handleLocalChoice = () => {
|
||||
// Handle local setup completion
|
||||
onComplete()
|
||||
}
|
||||
|
||||
const handleCloudChoice = () => {
|
||||
// Handle cloud setup completion
|
||||
onComplete()
|
||||
}
|
||||
|
||||
const handleModelSelection = (model: any) => {
|
||||
// Store selected model and complete setup
|
||||
localStorage.setItem('jan_selected_model', JSON.stringify(model))
|
||||
onComplete()
|
||||
}
|
||||
|
||||
const renderMainScreen = () => (
|
||||
<div className="flex flex-col items-center text-center space-y-8 px-6">
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-4xl font-bold text-primary">Jan</h1>
|
||||
<h2 className="text-xl font-semibold text-foreground">Mobile App</h2>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
ChatGPT alternative in your local device
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-sm space-y-4">
|
||||
<Button
|
||||
onClick={() => handleSetupChoice('local')}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="default"
|
||||
>
|
||||
Easy setup local (Jan model)
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => handleSetupChoice('cloud')}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="outline"
|
||||
>
|
||||
Easy setup cloud (Cloud model)
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => handleSetupChoice('manual')}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="outline"
|
||||
>
|
||||
Manual setup
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const renderLocalScreen = () => (
|
||||
<div className="flex flex-col items-center text-center space-y-8 px-6">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-2xl font-bold text-foreground">Local Setup</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Choose your preferred local model option
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-sm space-y-4">
|
||||
<Button
|
||||
onClick={() => handleLocalChoice()}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="default"
|
||||
>
|
||||
Jan model
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => handleLocalChoice()}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="outline"
|
||||
>
|
||||
Custom models
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={() => setCurrentStep('main')}
|
||||
className="text-muted-foreground"
|
||||
variant="ghost"
|
||||
>
|
||||
← Back
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
const renderCloudScreen = () => (
|
||||
<div className="flex flex-col items-center text-center space-y-8 px-6">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-2xl font-bold text-foreground">Cloud Setup</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Choose your preferred cloud AI provider
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-sm space-y-4">
|
||||
<Button
|
||||
onClick={() => handleCloudChoice()}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="default"
|
||||
>
|
||||
ChatGPT
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => handleCloudChoice()}
|
||||
className="w-full h-12 text-lg font-medium"
|
||||
variant="outline"
|
||||
>
|
||||
Claude
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={() => setCurrentStep('main')}
|
||||
className="text-muted-foreground"
|
||||
variant="ghost"
|
||||
>
|
||||
← Back
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
const renderManualScreen = () => (
|
||||
<HuggingFaceModelList
|
||||
onSelectModel={handleModelSelection}
|
||||
onBack={() => setCurrentStep('main')}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="w-full max-w-md mx-auto">
|
||||
{currentStep === 'main' && renderMainScreen()}
|
||||
{currentStep === 'local' && renderLocalScreen()}
|
||||
{currentStep === 'cloud' && renderCloudScreen()}
|
||||
{currentStep === 'manual' && renderManualScreen()}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
35
mobile-app/src/components/SplashScreen.tsx
Normal file
35
mobile-app/src/components/SplashScreen.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface SplashScreenProps {
|
||||
onComplete: () => void
|
||||
}
|
||||
|
||||
export function SplashScreen({ onComplete }: SplashScreenProps) {
|
||||
const [isVisible, setIsVisible] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setIsVisible(false)
|
||||
setTimeout(onComplete, 300) // Wait for fade out animation
|
||||
}, 2000)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [onComplete])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 flex items-center justify-center bg-background transition-opacity duration-300",
|
||||
isVisible ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
>
|
||||
<div className="text-center">
|
||||
<h1 className="text-6xl font-bold text-primary animate-pulse">
|
||||
Jan
|
||||
</h1>
|
||||
<div className="mt-4 w-12 h-1 bg-primary mx-auto rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { ChatScreen } from '@/components/ChatScreen'
|
||||
import { App } from '@/components/App'
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
component: ChatScreen,
|
||||
component: App,
|
||||
})
|
||||
@ -45,7 +45,7 @@
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.7.0",
|
||||
"@tauri-apps/cli": "^2.8.4",
|
||||
"@vitest/coverage-v8": "^3.1.3",
|
||||
"cpx": "^1.5.0",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
94
src-tauri/Cargo.lock
generated
94
src-tauri/Cargo.lock
generated
@ -1030,9 +1030,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6"
|
||||
checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff"
|
||||
dependencies = [
|
||||
"dlopen2_derive",
|
||||
"libc",
|
||||
@ -2946,6 +2946,16 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-javascript-core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a"
|
||||
dependencies = [
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.2.2"
|
||||
@ -2994,6 +3004,17 @@ dependencies = [
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-security"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.1"
|
||||
@ -3018,6 +3039,8 @@ dependencies = [
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-javascript-core",
|
||||
"objc2-security",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4468,9 +4491,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serialize-to-javascript"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb"
|
||||
checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -4479,13 +4502,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serialize-to-javascript-impl"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763"
|
||||
checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4869,11 +4892,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.34.0"
|
||||
version = "0.34.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49c380ca75a231b87b6c9dd86948f035012e7171d1a7c40a9c2890489a7ffd8a"
|
||||
checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"block2 0.6.1",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
@ -4942,12 +4966,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.7.0"
|
||||
version = "2.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "352a4bc7bf6c25f5624227e3641adf475a6535707451b09bb83271df8b7a6ac7"
|
||||
checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"dirs",
|
||||
"dunce",
|
||||
"embed_plist",
|
||||
@ -4966,6 +4991,7 @@ dependencies = [
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
@ -4993,9 +5019,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.3.1"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "182d688496c06bf08ea896459bf483eb29cdff35c1c4c115fb14053514303064"
|
||||
checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@ -5009,15 +5035,15 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"tauri-winres",
|
||||
"toml 0.8.23",
|
||||
"toml 0.9.5",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b54a99a6cd8e01abcfa61508177e6096a4fe2681efecee9214e962f2f073ae4a"
|
||||
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ico",
|
||||
@ -5041,9 +5067,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.3.2"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7945b14dc45e23532f2ded6e120170bbdd4af5ceaa45784a6b33d250fbce3f9e"
|
||||
checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@ -5055,9 +5081,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd5c1e56990c70a906ef67a9851bbdba9136d26075ee9a2b19c8b46986b3e02"
|
||||
checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@ -5066,7 +5092,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"toml 0.8.23",
|
||||
"toml 0.9.5",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -5237,9 +5263,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-os"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05bccb4c6de4299beec5a9b070878a01bce9e2c945aa7a75bcea38bcba4c675d"
|
||||
checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"log",
|
||||
@ -5340,9 +5366,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.7.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b1cc885be806ea15ff7b0eb47098a7b16323d9228876afda329e34e2d6c4676"
|
||||
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
@ -5351,20 +5377,23 @@ dependencies = [
|
||||
"jni",
|
||||
"objc2 0.6.1",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows 0.61.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.7.2"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe653a2fbbef19fe898efc774bc52c8742576342a33d3d028c189b57eb1d2439"
|
||||
checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http 1.3.1",
|
||||
@ -5389,9 +5418,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330c15cabfe1d9f213478c9e8ec2b0c76dab26bb6f314b8ad1c8a568c1d186e"
|
||||
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
@ -5417,7 +5446,7 @@ dependencies = [
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.8.23",
|
||||
"toml 0.9.5",
|
||||
"url",
|
||||
"urlpattern",
|
||||
"uuid",
|
||||
@ -6921,14 +6950,15 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.52.1"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12a714d9ba7075aae04a6e50229d6109e3d584774b99a6a8c60de1698ca111b9"
|
||||
checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.1",
|
||||
"cookie",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dpi",
|
||||
"dunce",
|
||||
"gdkx11",
|
||||
|
||||
@ -31,7 +31,7 @@ test-tauri = [
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.2", features = [] }
|
||||
tauri-build = { version = "2.4.1", features = [] }
|
||||
|
||||
[dependencies]
|
||||
dirs = "6.0.0"
|
||||
@ -56,16 +56,16 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.9.34"
|
||||
tar = "0.4"
|
||||
tauri-plugin-deep-link = "2"
|
||||
tauri-plugin-deep-link = "2.2.1"
|
||||
tauri-plugin-dialog = "2.2.1"
|
||||
tauri-plugin-hardware = { path = "./plugins/tauri-plugin-hardware" }
|
||||
tauri-plugin-http = { version = "2", features = ["unsafe-headers"] }
|
||||
tauri-plugin-llamacpp = { path = "./plugins/tauri-plugin-llamacpp" }
|
||||
tauri-plugin-log = "2.0.0-rc"
|
||||
tauri-plugin-log = "2.2.1"
|
||||
tauri-plugin-opener = "2.2.7"
|
||||
tauri-plugin-os = "2.2.1"
|
||||
tauri-plugin-shell = "2.2.0"
|
||||
tauri-plugin-store = "2"
|
||||
tauri-plugin-store = "2.2.1"
|
||||
thiserror = "2.0.12"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-util = "0.7.14"
|
||||
@ -73,7 +73,7 @@ url = "2.5"
|
||||
uuid = { version = "1.7", features = ["v4"] }
|
||||
|
||||
[dependencies.tauri]
|
||||
version = "2.5.0"
|
||||
version = "2.8.5"
|
||||
default-features = false
|
||||
features = ["protocol-asset", "macos-private-api", "test"]
|
||||
|
||||
@ -88,6 +88,6 @@ libc = "0.2.172"
|
||||
windows-sys = { version = "0.60.2", features = ["Win32_Storage_FileSystem"] }
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-updater = "2.2.1"
|
||||
once_cell = "1.18"
|
||||
tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user