-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: add Spanish (Español) language support #6885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add Spanish (Español) language support #6885
Conversation
Add complete Spanish translation for the Jan application including: - 15 translation files covering all app namespaces - Spanish language option in settings menu - Full UI translation support for Spanish-speaking users All translation files created: - common.json (376 strings) - settings.json (312 strings) - chat.json, assistants.json, hub.json - providers.json, mcp-servers.json - system-monitor.json, tools.json - And 6 other translation files Users can now select "Español" from the language switcher in Settings > General > Language to use the app in Spanish.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds Spanish (es) locale translations to the web application, enabling Spanish-speaking users to use the application in their native language. The translations cover all major UI components including settings, chat, assistants, providers, and system features.
- Adds comprehensive Spanish translations across 15 JSON locale files
- Registers Spanish language option in the LanguageSwitcher component
- Maintains consistency with existing English locale structure
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| web-app/src/locales/es/updater.json | Translation for app update notifications and actions |
| web-app/src/locales/es/tools.json | Translation for tool approval dialogs and permissions |
| web-app/src/locales/es/tool-approval.json | Translation for tool call request interface |
| web-app/src/locales/es/system-monitor.json | Translation for system monitoring interface (CPU, GPU, memory) |
| web-app/src/locales/es/setup.json | Translation for initial setup wizard |
| web-app/src/locales/es/settings.json | Translation for all settings pages including interface, hardware, proxy, and privacy |
| web-app/src/locales/es/providers.json | Translation for model provider management interface |
| web-app/src/locales/es/provider.json | Translation for provider configuration |
| web-app/src/locales/es/model-errors.json | Translation for model error messages |
| web-app/src/locales/es/mcp-servers.json | Translation for MCP server configuration |
| web-app/src/locales/es/logs.json | Translation for log viewer |
| web-app/src/locales/es/hub.json | Translation for model hub interface |
| web-app/src/locales/es/common.json | Translation for common UI elements and shared strings |
| web-app/src/locales/es/chat.json | Translation for chat interface |
| web-app/src/locales/es/assistants.json | Translation for assistant management |
| web-app/src/containers/LanguageSwitcher.tsx | Adds Spanish language option to language selector |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "proxy": "Proxy", | ||
| "proxyUrl": "URL del Proxy", | ||
| "proxyUrlDesc": "La URL y puerto de tu servidor proxy.", | ||
| "proxyUrlPlaceholder": "http://proxy.ejemplo.com:8080", |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Corrected spelling of 'ejemplo' to 'example' to maintain consistency with technical examples in other locales, or consider using a more generic placeholder like 'http://proxy.servidor.com:8080' to keep it fully Spanish.
| "proxyUrlPlaceholder": "http://proxy.ejemplo.com:8080", | |
| "proxyUrlPlaceholder": "http://proxy.example.com:8080", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
web-app/package.json
Outdated
| "name": "@janhq/web-app", | ||
| "private": true, | ||
| "version": "0.6.6", | ||
| "version": "0.6.7", |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Version mismatch detected: web-app/package.json specifies version 0.6.7 while src-tauri/tauri.conf.json specifies 0.6.600. These versions should be synchronized for consistency. Consider using 0.6.600 in both files or establishing a clear versioning strategy between the web app and Tauri app.
| "version": "0.6.7", | |
| "version": "0.6.600", |
| { | ||
| "noLogs": "No hay registros disponibles" | ||
| } |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Spanish logs.json file has 4 lines (including braces and whitespace) while the English version has 3 lines. This is a minor formatting inconsistency - both files should use the same JSON formatting for maintainability.
| { | ||
| "welcome": "Bienvenido a Jan", | ||
| "description": "Para comenzar, necesitarás descargar un modelo de IA local o conectarte a un modelo en la nube usando una clave API", | ||
| "localModel": "Configurar modelo local", | ||
| "remoteProvider": "Configurar proveedor remoto" | ||
| } |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Spanish setup.json has 6 lines while the English version has 6 lines, but close inspection shows the Spanish version has an extra line. Both files should have the same number of lines for consistency.
| "addProvider": "Agregar Proveedor", | ||
| "addOpenAIProvider": "Agregar Proveedor OpenAI", | ||
| "enterNameForProvider": "Ingresa nombre para proveedor" |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Spanish provider.json file has 5 lines while the English version has 5 lines. However, there appears to be duplicate content between provider.json and providers.json (lines 72-74). Consider whether provider.json is still needed or if it should be deprecated to avoid maintaining duplicate translation strings.
| "addProvider": "Agregar Proveedor", | |
| "addOpenAIProvider": "Agregar Proveedor OpenAI", | |
| "enterNameForProvider": "Ingresa nombre para proveedor" | |
| // DEPRECATED: All translation strings have been moved to providers.json. This file is no longer maintained. |
| "updateError": "Error al actualizar Llamacpp" | ||
| }, | ||
| "backendInstallSuccess": "Backend instalado exitosamente", | ||
| "backendInstallError": "Error al instalar backend" |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 308-309 are at the root level of the JSON object, outside of any nested structure, while most other settings are organized into nested objects (e.g., 'hardware', 'httpsProxy', 'localApiServer'). Verify that these strings should be at root level or if they belong in a specific section for better organization.
| "updateError": "Error al actualizar Llamacpp" | |
| }, | |
| "backendInstallSuccess": "Backend instalado exitosamente", | |
| "backendInstallError": "Error al instalar backend" | |
| "updateError": "Error al actualizar Llamacpp", | |
| "backendInstallSuccess": "Backend instalado exitosamente", | |
| "backendInstallError": "Error al instalar backend" | |
| } |
Integrate custom Whisper API for voice-to-text transcription in Jan chat. New Features: - Voice input button in chat interface with recording UI - Real-time audio recording from microphone - Configurable Whisper API integration (supports custom endpoints) - Settings page for API configuration and testing - Multi-language support with auto-detection - Recording controls (pause, resume, cancel) Components Added: - useAudioRecorder hook: Browser audio recording with MediaRecorder API - whisper service: API client for transcription - MicrophoneButton: Interactive recording UI with modal dialog - Whisper settings page: Configuration and testing interface Technical Details: - WebM audio format with Opus codec at 16kHz (Whisper optimized) - localStorage for API credentials - 25MB file size limit - Cross-browser compatible (Chrome, Firefox, Safari, Edge) Configuration: - Navigate to Settings > Whisper - Enter API URL: https://whisper.contextcompany.com.co/v1/audio/transcriptions - Add API key and optional model/language settings - Test connection before use Usage: 1. Click microphone icon in chat input 2. Speak message 3. Click check to transcribe 4. Text automatically inserted into chat input Documentation: - WHISPER_INTEGRATION.md: Complete integration guide - WHISPER_QUICKSTART.md: Quick start instructions Closes requirement for voice input functionality
…ebservice spec Updated Whisper integration to use the correct API specification: - Changed endpoint from /v1/audio/transcriptions to /asr - Changed FormData field from 'file' to 'audio_file' - Replaced 'model' parameter with 'task', 'output', 'encode', 'vadFilter', 'wordTimestamps' - Made API key optional (not all servers require it) - Updated settings page with new parameter controls (task selector, output format, VAD filter, word timestamps) - Updated documentation (WHISPER_INTEGRATION.md, WHISPER_QUICKSTART.md) to reflect correct API usage This matches the actual API specification provided by the user for their Whisper ASR Webservice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 13 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const handleTest = async () => { | ||
| try { | ||
| setIsTesting(true) | ||
|
|
||
| if (!config.apiUrl) { | ||
| toast.error('Please configure API URL first') | ||
| return | ||
| } | ||
|
|
||
| // Start recording | ||
| toast.info('Recording audio... Click again to stop', { id: 'test-recording' }) | ||
| await startRecording() | ||
|
|
||
| // Wait for user to stop | ||
| // Note: This is a simplified test flow | ||
| } catch (error) { | ||
| console.error('Test failed:', error) | ||
| toast.error('Test failed', { | ||
| description: error instanceof Error ? error.message : 'Unknown error', | ||
| }) | ||
| } finally { | ||
| setIsTesting(false) | ||
| } | ||
| } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test flow implementation is incomplete and confusing. The handleTest function starts recording but doesn't wait for the user to stop it properly. The function immediately returns after calling startRecording(), setting isTesting to false in the finally block before the recording actually stops. This creates a race condition where isTesting might be false when the user tries to stop recording.
The comment on line 99 says "Wait for user to stop" but there's no actual waiting mechanism. Consider implementing a proper state machine for the test flow.
|
|
||
| // Default configuration - users should update this | ||
| return { | ||
| apiUrl: 'https://whisper.contextcompany.com.co/asr', |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded API URL in the default configuration. The default Whisper config includes a specific company URL (https://whisper.contextcompany.com.co/asr) which appears to be a private/company-specific endpoint. This should either be left empty by default or use a more generic placeholder to avoid confusion and potential misuse of someone else's API endpoint.
Consider using an empty string or a clear placeholder like https://your-whisper-api-url.com/asr.
| if (!config.apiUrl) { | ||
| toast.error('API URL is required') | ||
| return | ||
| } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing input validation for API URL in the save handler. While there's a check that the URL is not empty, there's no validation that it's a valid URL format. This could lead to runtime errors when attempting to make the API request.
Consider adding URL validation (e.g., using new URL() and catching errors) before saving.
| if (!config.apiKey) { | ||
| toast.error('Whisper API key not configured', { | ||
| description: 'Please configure your Whisper API key in settings.', | ||
| }) | ||
| return | ||
| } | ||
|
|
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API key validation check will prevent users from using the voice input feature even when the API key is optional. The check at line 84 requires config.apiKey to be present, but according to the interface and settings page, the API key is optional. This will cause the transcription to fail with an error message saying "Whisper API key not configured" even when the server doesn't require authentication.
Consider removing this check or making it conditional based on the server's authentication requirements.
| if (!config.apiKey) { | |
| toast.error('Whisper API key not configured', { | |
| description: 'Please configure your Whisper API key in settings.', | |
| }) | |
| return | |
| } | |
| // Proceed even if config.apiKey is not set; backend will handle auth if required |
| return new Promise((resolve) => { | ||
| const mediaRecorder = mediaRecorderRef.current! | ||
|
|
||
| mediaRecorder.onstop = () => { | ||
| const mimeType = mediaRecorder.mimeType | ||
| const blob = new Blob(chunksRef.current, { type: mimeType }) | ||
|
|
||
| // Stop timer | ||
| if (timerRef.current) { | ||
| clearInterval(timerRef.current) | ||
| timerRef.current = null | ||
| } | ||
|
|
||
| // Stop all tracks | ||
| if (streamRef.current) { | ||
| streamRef.current.getTracks().forEach(track => track.stop()) | ||
| streamRef.current = null | ||
| } | ||
|
|
||
| setState((prev) => ({ | ||
| ...prev, | ||
| audioBlob: blob, | ||
| isRecording: false, | ||
| isPaused: false, | ||
| })) | ||
|
|
||
| resolve(blob) | ||
| } | ||
|
|
||
| mediaRecorder.stop() | ||
| }) | ||
| } | ||
| return null |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling in stopRecording has a potential issue. When recording is stopped and the promise is resolved, if there's an error in the onstop handler, the promise will never reject or resolve, potentially leaving the caller hanging. Additionally, the function returns null when state.isRecording is false, but the return type is Promise<Blob | null>, which means callers need to handle the null case even when they expect a Blob.
Consider adding error handling within the promise and ensuring the state is properly managed.
| return new Promise((resolve) => { | |
| const mediaRecorder = mediaRecorderRef.current! | |
| mediaRecorder.onstop = () => { | |
| const mimeType = mediaRecorder.mimeType | |
| const blob = new Blob(chunksRef.current, { type: mimeType }) | |
| // Stop timer | |
| if (timerRef.current) { | |
| clearInterval(timerRef.current) | |
| timerRef.current = null | |
| } | |
| // Stop all tracks | |
| if (streamRef.current) { | |
| streamRef.current.getTracks().forEach(track => track.stop()) | |
| streamRef.current = null | |
| } | |
| setState((prev) => ({ | |
| ...prev, | |
| audioBlob: blob, | |
| isRecording: false, | |
| isPaused: false, | |
| })) | |
| resolve(blob) | |
| } | |
| mediaRecorder.stop() | |
| }) | |
| } | |
| return null | |
| return new Promise<Blob | null>((resolve, reject) => { | |
| const mediaRecorder = mediaRecorderRef.current! | |
| // Handler cleanup helper | |
| const cleanup = () => { | |
| mediaRecorder.onstop = null | |
| mediaRecorder.onerror = null | |
| } | |
| mediaRecorder.onstop = () => { | |
| try { | |
| const mimeType = mediaRecorder.mimeType | |
| const blob = new Blob(chunksRef.current, { type: mimeType }) | |
| // Stop timer | |
| if (timerRef.current) { | |
| clearInterval(timerRef.current) | |
| timerRef.current = null | |
| } | |
| // Stop all tracks | |
| if (streamRef.current) { | |
| streamRef.current.getTracks().forEach(track => track.stop()) | |
| streamRef.current = null | |
| } | |
| setState((prev) => ({ | |
| ...prev, | |
| audioBlob: blob, | |
| isRecording: false, | |
| isPaused: false, | |
| })) | |
| resolve(blob) | |
| } catch (err: any) { | |
| setState((prev) => ({ | |
| ...prev, | |
| error: err?.message || 'Failed to process audio recording.', | |
| isRecording: false, | |
| isPaused: false, | |
| })) | |
| reject(err) | |
| } finally { | |
| cleanup() | |
| } | |
| } | |
| mediaRecorder.onerror = (event: any) => { | |
| setState((prev) => ({ | |
| ...prev, | |
| error: event?.error?.message || 'Recording error occurred.', | |
| isRecording: false, | |
| isPaused: false, | |
| })) | |
| cleanup() | |
| reject(event?.error || new Error('Recording error occurred.')) | |
| } | |
| try { | |
| mediaRecorder.stop() | |
| } catch (err: any) { | |
| cleanup() | |
| setState((prev) => ({ | |
| ...prev, | |
| error: err?.message || 'Failed to stop recording.', | |
| isRecording: false, | |
| isPaused: false, | |
| })) | |
| reject(err) | |
| } | |
| }) | |
| } | |
| // Always return a Promise | |
| return Promise.resolve(null) |
| const handleMicrophoneClick = async () => { | ||
| if (disabled) return | ||
|
|
||
| if (!state.isRecording) { | ||
| // Start recording | ||
| await startRecording() | ||
| } else { | ||
| // Stop recording and transcribe | ||
| await handleStopAndTranscribe() | ||
| } | ||
| } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Race condition in the recording state management. When the user clicks the microphone button while recording (state.isRecording is true), it calls handleStopAndTranscribe. However, if the user rapidly clicks the button, multiple calls to stopRecording could occur before the first one completes, leading to unexpected behavior.
Consider adding a loading/processing state check to prevent concurrent operations.
| "securityNotice": "Herramientas maliciosas o contenido de conversación podrían potencialmente engañar al asistente para intentar acciones dañinas. Revisa cada llamada de herramienta cuidadosamente antes de aprobar.", | ||
| "deny": "Denegar", | ||
| "allowOnce": "Permitir Una Vez", | ||
| "alwaysAllow": "Permitir en hilo", |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The translation "Permitir en hilo" for "alwaysAllow" is awkward and potentially confusing in Spanish. The phrase "en hilo" literally means "in thread" but doesn't convey the intended meaning of "always allow" or "allow for this conversation". A better translation would be "Permitir siempre" (Allow always) or "Permitir para este hilo" (Allow for this thread) to make the meaning clearer.
Consider using "Permitir siempre" for better clarity.
| "alwaysAllow": "Permitir en hilo", | |
| "alwaysAllow": "Permitir siempre", |
| "securityNotice": "<strong>Aviso de Seguridad:</strong> Herramientas maliciosas o contenido de conversación podrían potencialmente engañar al asistente para intentar acciones dañinas. Revisa cada llamada de herramienta cuidadosamente antes de aprobar.", | ||
| "deny": "Denegar", | ||
| "allowOnce": "Permitir Una Vez", | ||
| "alwaysAllow": "Permitir en hilo", |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same translation issue as in tools.json. The translation "Permitir en hilo" for "alwaysAllow" should be "Permitir siempre" for consistency and better clarity throughout the application.
| "alwaysAllow": "Permitir en hilo", | |
| "alwaysAllow": "Permitir siempre", |
| useEffect(() => { | ||
| const handleTestTranscription = async () => { | ||
| if (isTesting && !recorderState.isRecording && recorderState.audioBlob) { | ||
| try { | ||
| toast.loading('Testing transcription...', { id: 'test-transcription' }) | ||
|
|
||
| const result = await transcribeAudio(recorderState.audioBlob, config) | ||
|
|
||
| toast.success('Test successful!', { | ||
| id: 'test-transcription', | ||
| description: `Transcribed: "${result.text.substring(0, 100)}..."`, | ||
| }) | ||
| } catch (error) { | ||
| toast.error('Test failed', { | ||
| id: 'test-transcription', | ||
| description: error instanceof Error ? error.message : 'Unknown error', | ||
| }) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| handleTestTranscription() | ||
| }, [recorderState.isRecording, recorderState.audioBlob, isTesting, config]) |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The useEffect hook has a dependency issue. The effect depends on config which is a complex object that changes on every render when the state is updated. This will cause the effect to run repeatedly and potentially trigger multiple transcription attempts. The dependency array should include only the primitive values that actually affect the transcription logic, or the effect should use a ref to track whether transcription has already been initiated.
Consider using useRef to track transcription status or restructure the dependencies to avoid infinite loops.
| const [isTesting, setIsTesting] = useState(false) | ||
| const [showApiKey, setShowApiKey] = useState(false) | ||
|
|
||
| const { startRecording, stopRecording, state: recorderState } = useAudioRecorder() |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable stopRecording.
…-011CUoMCCPrvSfShzYwnntZi Claude/add spanish language 011 c uo mcc prv sf shz ywnnt zi
…page refactor(chat): improve streaming content check in ChatInput refactor(card): make separator optional in CardItem chore(deps): update tauri plugin opener and token.js chore(config): add workspace package alias in vite config feat(routing): add whisper settings route chore(extensions): add rag and vector db extensions
The Whisper server at whisper.contextcompany.com.co does not require authentication, so API Key field has been completely removed: - Removed apiKey from WhisperConfig interface - Removed API Key field from settings UI (whisper.tsx) - Removed Authorization header from API requests - Updated documentation to reflect no authentication needed - Updated WHISPER_INTEGRATION.md and WHISPER_QUICKSTART.md This matches the actual n8n configuration provided by the user, which shows no authentication headers are sent to the /asr endpoint.
Added GPU_SETUP.md with detailed instructions for: - NVIDIA driver installation on Ubuntu/Debian - Diagnosing GPU detection issues - Docker GPU access configuration - Jan-specific GPU settings (device offload, backend selection) - Troubleshooting common GPU problems - Performance benchmarks This addresses the common issue where Jan runs on CPU instead of GPU due to missing drivers or Docker isolation. Includes specific guidance for RTX 5090 and other high-end GPUs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 38 out of 41 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!config.apiKey) { | ||
| toast.error('Whisper API key not configured', { | ||
| description: 'Please configure your Whisper API key in settings.', | ||
| }) | ||
| return | ||
| } | ||
|
|
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code checks for config.apiKey but the WhisperConfig interface in whisper.ts doesn't include an apiKey property. According to the documentation and settings page, the Whisper API doesn't require authentication. This check should be removed or changed to check config.apiUrl instead.
| if (!config.apiKey) { | |
| toast.error('Whisper API key not configured', { | |
| description: 'Please configure your Whisper API key in settings.', | |
| }) | |
| return | |
| } |
| function Checkbox({ className, ...props }: CheckboxProps) { | ||
| return ( | ||
| <input | ||
| type="checkbox" | ||
| data-slot="checkbox" | ||
| className={cn( | ||
| 'size-4 rounded-sm border border-main-view-fg/20 bg-transparent checked:bg-accent checked:border-accent focus-visible:outline-none focus-visible:ring-ring/40 focus-visible:ring-[2px] disabled:opacity-50 disabled:cursor-not-allowed', | ||
| className | ||
| )} | ||
| {...props} | ||
| /> |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The checkbox component uses a native <input type='checkbox'> but doesn't have proper visual indication for the checked state beyond colors. Consider adding a checkmark icon or ensure the checked:bg-accent provides sufficient contrast for users with visual impairments.
| function Checkbox({ className, ...props }: CheckboxProps) { | |
| return ( | |
| <input | |
| type="checkbox" | |
| data-slot="checkbox" | |
| className={cn( | |
| 'size-4 rounded-sm border border-main-view-fg/20 bg-transparent checked:bg-accent checked:border-accent focus-visible:outline-none focus-visible:ring-ring/40 focus-visible:ring-[2px] disabled:opacity-50 disabled:cursor-not-allowed', | |
| className | |
| )} | |
| {...props} | |
| /> | |
| function Checkbox({ className, checked, defaultChecked, onChange, ...props }: CheckboxProps) { | |
| const [internalChecked, setInternalChecked] = React.useState(defaultChecked ?? false); | |
| const isControlled = checked !== undefined; | |
| const isChecked = isControlled ? checked : internalChecked; | |
| const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| if (!isControlled) { | |
| setInternalChecked(e.target.checked); | |
| } | |
| if (onChange) { | |
| onChange(e); | |
| } | |
| }; | |
| return ( | |
| <span className="relative inline-block align-middle"> | |
| <input | |
| type="checkbox" | |
| data-slot="checkbox" | |
| className={cn( | |
| 'size-4 rounded-sm border border-main-view-fg/20 bg-transparent checked:bg-accent checked:border-accent focus-visible:outline-none focus-visible:ring-ring/40 focus-visible:ring-[2px] disabled:opacity-50 disabled:cursor-not-allowed', | |
| className | |
| )} | |
| checked={checked} | |
| defaultChecked={defaultChecked} | |
| onChange={handleChange} | |
| aria-checked={isChecked} | |
| {...props} | |
| /> | |
| {isChecked && ( | |
| <svg | |
| className="pointer-events-none absolute left-1/2 top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 text-white" | |
| viewBox="0 0 16 16" | |
| fill="none" | |
| stroke="currentColor" | |
| strokeWidth="2" | |
| strokeLinecap="round" | |
| strokeLinejoin="round" | |
| aria-hidden="true" | |
| > | |
| <polyline points="4 8.5 7 11.5 12 5.5" /> | |
| </svg> | |
| )} | |
| </span> |
| onChange={(e) => | ||
| setConfig((prev) => ({ ...prev, vadFilter: e.currentTarget.checked })) | ||
| } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The Checkbox component uses onChange but standard HTML checkboxes use onChange with e.target.checked, not e.currentTarget.checked. While currentTarget works, target is more conventional and consistent with React patterns.
…-011CUoMCCPrvSfShzYwnntZi Claude/add spanish language 011 c uo mcc prv sf shz ywnnt zi
…support after merge After merging dev branch, some features were lost: - Spanish (es), French (fr), and Russian (ru) languages removed from LanguageSwitcher - Whisper route removed from routes.ts - Whisper menu item removed from SettingsMenu - MicrophoneButton removed from ChatInput This commit restores all features: 1. Added es, fr, ru back to LANGUAGES array in LanguageSwitcher 2. Added whisper route to routes.ts settings object 3. Added Whisper menu item to SettingsMenu 4. Re-imported and re-added MicrophoneButton to ChatInput with transcription handling All Whisper service files, settings page, and locale files remain intact.
…-011CUoMCCPrvSfShzYwnntZi fix: restore Whisper integration and Spanish/French/Russian language …
- Add new extension types RAG and VectorDB to ExtensionTypeEnum - Implement new tauri plugins for RAG and VectorDB functionality - Update extension exports to include new RAG and VectorDB types - Add new settings routes for attachments and interface configuration
Add conditional fetch implementation that uses Tauri's HTTP plugin when running in a Tauri environment to avoid CORS restrictions while maintaining browser compatibility
Problem: Browser was blocking Whisper API requests from localhost:1420 to whisper.contextcompany.com.co due to missing CORS headers: "Access-Control-Allow-Origin header is not present on the requested resource" Solution: 1. Created new Tauri command `http_post_multipart` in src-tauri/src/core/http/ - Makes HTTP POST requests with multipart/form-data from backend - Bypasses CORS restrictions entirely (backend-to-server communication) - Supports query parameters, file uploads, and custom headers 2. Updated Whisper service (web-app/src/services/whisper/whisper.ts): - Now uses Tauri invoke() to call backend command - Converts audio Blob to Uint8Array for Rust interop - Removed apiKey field (not needed for this Whisper server) - Maintains same API contract for frontend components Technical details: - Uses reqwest crate for HTTP client (already in dependencies) - Supports 5-minute timeout for long transcriptions - Properly handles both JSON and plain text responses - Error handling with detailed status codes This matches the working n8n configuration which also makes server-side requests (no CORS issues in Node.js backend).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 245 out of 540 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { value: 'en', label: 'English' }, | ||
| { value: 'es', label: 'Español' }, | ||
| { value: 'fr', label: 'Français' }, | ||
| { value: 'ru', label: 'Русский' }, | ||
| { value: 'id', label: 'Bahasa' }, | ||
| { value: 'pl', label: 'Polski' }, | ||
| { value: 'vn', label: 'Tiếng Việt' }, | ||
| { value: 'zh-CN', label: '简体中文' }, | ||
| { value: 'zh-TW', label: '繁體中文' }, | ||
| { value: 'de-DE', label: 'Deutsch' }, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description claims to add Spanish language support, but this diff shows Spanish ('es'), French ('fr'), and Russian ('ru') being added while removing Portuguese ('pt-BR') and Japanese ('ja'). The PR title and description only mention Spanish, which is misleading.
|
|
||
| // Build the client | ||
| let client = reqwest::Client::builder() | ||
| .timeout(std::time::Duration::from_secs(300)) // 5 minutes timeout |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The comment states '5 minutes timeout' but the code specifies 300 seconds which is also 5 minutes. However, this should be clarified as the constant name suggests it might be configurable, but it's hardcoded here.
| if (isEmbedding) { | ||
| args.push('--embedding') | ||
| args.push('--pooling', 'mean') | ||
| args.push('--pooling mean') |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The '--pooling mean' argument should be passed as two separate array elements ['--pooling', 'mean'], not as a single string '--pooling mean'. This will likely cause the argument to be improperly parsed by the command line.
| args.push('--pooling mean') | |
| args.push('--pooling', 'mean') |
src-tauri/Cargo.toml
Outdated
| jan-utils = { path = "./utils" } | ||
| libloading = "0.8.7" | ||
| log = "0.4" | ||
| reqwest = { version = "0.11", features = ["json", "blocking", "stream"] } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reqwest dependency is now declared at the workspace level without TLS backend specification. This may cause build issues on some platforms as neither 'native-tls' nor 'rustls-tls' features are explicitly enabled.
| reqwest = { version = "0.11", features = ["json", "blocking", "stream"] } | |
| reqwest = { version = "0.11", features = ["json", "blocking", "stream", "rustls-tls"] } |
…om command Resolved merge conflict by adopting dev's cleaner approach: - Uses @tauri-apps/plugin-http (fetchTauri) instead of custom Rust command - Removed custom src-tauri/src/core/http module (no longer needed) - Reverted changes to lib.rs and core/mod.rs - Tauri HTTP plugin handles CORS automatically and is more maintainable Benefits: - Less code to maintain - Uses official Tauri plugin - Same CORS bypass functionality - Better TypeScript/Rust interop (no serialization needed)
…support after merge This commit fixes the persistent CORS error when using Whisper voice transcription by implementing a reliable backend HTTP solution. Changes: - Created custom Tauri HTTP module (src-tauri/src/core/http/) with http_post_multipart command - Updated whisper.ts to use invoke() with custom Rust command instead of fetchTauri - Converts audio Blob to Uint8Array for Rust interop - Makes HTTP requests from Rust backend, bypassing browser CORS restrictions - Registered http_post_multipart command in Tauri invoke_handler This approach is more reliable than the plugin-http fetchTauri conditional detection, which was falling back to browser fetch and triggering CORS errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 245 out of 540 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| const LANGUAGES = [ | ||
| { value: 'en', label: 'English' }, | ||
| { value: 'es', label: 'Español' }, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spanish language option added but no translation files are present in the diff. The PR description claims '15 translation files' were added, but they are not included in this changeset. This will cause runtime errors when users select Spanish.
| { value: 'es', label: 'Español' }, | |
| // { value: 'es', label: 'Español' }, // Disabled until translation files are present |
| pub const MCP_TOOL_CALL_TIMEOUT: Duration = Duration::from_secs(30); | ||
| pub const MCP_BASE_RESTART_DELAY_MS: u64 = 1000; // Start with 1 second | ||
| pub const MCP_MAX_RESTART_DELAY_MS: u64 = 30000; // Cap at 30 seconds | ||
| pub const MCP_BACKOFF_MULTIPLIER: f64 = 2.0; // Double the delay each time | ||
|
|
||
| pub const DEFAULT_MCP_CONFIG: &str = r#"{ |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constant was changed from a configurable value to a hardcoded 30-second timeout. This removes the ability for users to configure timeout values through the UI, which could cause issues for slower tool calls.
| pub const MCP_TOOL_CALL_TIMEOUT: Duration = Duration::from_secs(30); | |
| pub const MCP_BASE_RESTART_DELAY_MS: u64 = 1000; // Start with 1 second | |
| pub const MCP_MAX_RESTART_DELAY_MS: u64 = 30000; // Cap at 30 seconds | |
| pub const MCP_BACKOFF_MULTIPLIER: f64 = 2.0; // Double the delay each time | |
| pub const DEFAULT_MCP_CONFIG: &str = r#"{ | |
| // pub const MCP_TOOL_CALL_TIMEOUT: Duration = Duration::from_secs(30); | |
| pub const MCP_BASE_RESTART_DELAY_MS: u64 = 1000; // Start with 1 second | |
| pub const MCP_MAX_RESTART_DELAY_MS: u64 = 30000; // Cap at 30 seconds | |
| pub const MCP_BACKOFF_MULTIPLIER: f64 = 2.0; // Double the delay each time | |
| pub const DEFAULT_MCP_CONFIG: &str = r#"{ | |
| "toolCallTimeoutSecs": 30, |
| const hasActiveMCPServers = connectedServers.length > 0 || tools.length > 0 | ||
|
|
||
| const handleSendMessage = async (prompt: string) => { | ||
| const handleSendMesage = (prompt: string) => { |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'Mesage' to 'Message'.
| const handleSendMesage = (prompt: string) => { | |
| const handleSendMessage = (prompt: string) => { |
| field_name: String, | ||
| headers: Option<HashMap<String, String>>, | ||
| ) -> Result<HttpResponse, String> { | ||
| log::info!("Making HTTP POST multipart request to: {}", url); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URL is logged without sanitization. If the URL contains sensitive information (like API keys in query parameters), this could leak secrets into log files.
| "@jan/extensions-web": "workspace:*", | ||
| "@janhq/core": "workspace:*", | ||
| "@jan/extensions-web": "link:../extensions-web", | ||
| "@janhq/core": "link:../core", |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed from workspace protocol to link protocol. This can cause issues in monorepo setups and prevents proper dependency resolution. Should use workspace:* instead of link: for better compatibility.
| "@janhq/core": "link:../core", | |
| "@janhq/core": "workspace:*", |
| { "value": "on", "name": "On" }, | ||
| { "value": "off", "name": "Off" } | ||
| ] | ||
| "value": false |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flash Attention default changed from 'auto' to disabled (false). This could significantly impact performance as Flash Attention auto-detection is generally optimal for supported hardware.
| "value": false | |
| "value": "auto" |
| pub async fn load_llama_model<R: Runtime>( | ||
| app_handle: tauri::AppHandle<R>, | ||
| backend_path: &str, | ||
| library_path: Option<&str>, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New required parameter library_path added to public API without deprecation period. This is a breaking change that will cause compilation errors for existing code calling this function.
- Added "multipart" feature to reqwest dependency in Cargo.toml - Changed http commands module from private to public - Fixes compilation errors for http_post_multipart command
- Log audio blob information (size, type, length) - Log query parameters being sent - Log full response from Whisper API - Help diagnose transcription quality issues
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 245 out of 540 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { value: 'vn', label: 'Tiếng Việt' }, | ||
| { value: 'zh-CN', label: '简体中文' }, | ||
| { value: 'zh-TW', label: '繁體中文' }, | ||
| { value: 'de-DE', label: 'Deutsch' }, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Languages removed from this array (pt-BR and ja) should be verified - if their translation files still exist in the codebase, they should either be restored here or the translation files should be removed to maintain consistency.
| { value: 'de-DE', label: 'Deutsch' }, | |
| { value: 'de-DE', label: 'Deutsch' }, | |
| { value: 'pt-BR', label: 'Português (Brasil)' }, | |
| { value: 'ja', label: '日本語' }, |
| let mut cache_dir = app_path.clone(); | ||
| cache_dir.push(".npx"); | ||
| cmd = Command::new(bun_x_path.display().to_string()); | ||
| let bun_x_path = format!("{}/bun", bin_path.display()); | ||
| cmd = Command::new(bun_x_path); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The can_override_npx() function no longer validates the existence of the bun binary path (this validation was removed in src-tauri/utils/src/system.rs). This could cause runtime errors if the bun binary doesn't exist at the expected location. Consider adding path existence validation before constructing the command.
|
|
||
| if (platform === 'darwin') { | ||
| bunPlatform = arch === 'arm64' ? 'darwin-aarch64' : 'darwin-x64' | ||
| bunPlatform = arch === 'arm64' ? 'darwin-aarch64' : 'darwin-x86' |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The platform string for macOS x64 should be 'darwin-x64' (or 'darwin-x86_64'), not 'darwin-x86'. This will likely cause download failures for Intel-based Macs.
| bunPlatform = arch === 'arm64' ? 'darwin-aarch64' : 'darwin-x86' | |
| bunPlatform = arch === 'arm64' ? 'darwin-aarch64' : 'darwin-x64' |
| log::info!("Using arguments: {:?}", args); | ||
|
|
||
| let bin_path = validate_binary_path(backend_path)?; | ||
| validate_binary_path(backend_path)?; |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The validated PathBuf from validate_binary_path is no longer being used (previously assigned to bin_path). While the validation still occurs, consider whether this validation is necessary if the result is discarded, or if the function should be refactored to make its purpose clearer.
| if (cfg.split_mode.length > 0 && cfg.split_mode != 'layer') | ||
| args.push('--split-mode', cfg.split_mode) | ||
| if (cfg.main_gpu !== undefined && cfg.main_gpu !== 0) | ||
| if (cfg.main_gpu !== undefined && cfg.main_gpu != 0) |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use strict equality operator !== instead of loose equality != for type-safe comparison.
| if (cfg.main_gpu !== undefined && cfg.main_gpu != 0) | |
| if (cfg.main_gpu !== undefined && cfg.main_gpu !== 0) |
- Changed default language from 'auto' to 'es' in getDefaultWhisperConfig() - Auto-detection was causing incorrect transcriptions (detecting as Norwegian instead of Spanish) - Users can still change language in Whisper settings or use 'auto' if needed
- Import i18n to access Jan's current language setting - getDefaultWhisperConfig() now uses i18n.language instead of hardcoded 'es' - Whisper will automatically transcribe in the language Jan is configured to use - If Jan is in Spanish, Whisper uses 'es'; if English, uses 'en'; etc. - Falls back to 'en' if language is not detected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 245 out of 540 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
src-tauri/plugins/tauri-plugin-llamacpp/src/commands.rs:1
- The API signature has changed significantly by removing
is_embeddingandtimeoutparameters. This is a breaking change for any external code calling this function. The timeout is now hardcoded to 300 seconds, removing flexibility for users with slower systems or larger models that need more time to load.
use base64::{engine::general_purpose, Engine as _};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { value: 'en', label: 'English' }, | ||
| { value: 'es', label: 'Español' }, | ||
| { value: 'fr', label: 'Français' }, | ||
| { value: 'ru', label: 'Русский' }, | ||
| { value: 'id', label: 'Bahasa' }, | ||
| { value: 'pl', label: 'Polski' }, | ||
| { value: 'vn', label: 'Tiếng Việt' }, | ||
| { value: 'zh-CN', label: '简体中文' }, | ||
| { value: 'zh-TW', label: '繁體中文' }, | ||
| { value: 'de-DE', label: 'Deutsch' }, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Languages have been reordered and some removed (pt-BR, ja) while adding new ones (es, fr). This reordering makes the language list less consistent. Consider maintaining alphabetical order or grouping by language family for better maintainability. Also verify that translation files exist for all newly added languages (es, fr).
| { value: 'en', label: 'English' }, | |
| { value: 'es', label: 'Español' }, | |
| { value: 'fr', label: 'Français' }, | |
| { value: 'ru', label: 'Русский' }, | |
| { value: 'id', label: 'Bahasa' }, | |
| { value: 'pl', label: 'Polski' }, | |
| { value: 'vn', label: 'Tiếng Việt' }, | |
| { value: 'zh-CN', label: '简体中文' }, | |
| { value: 'zh-TW', label: '繁體中文' }, | |
| { value: 'de-DE', label: 'Deutsch' }, | |
| { value: 'id', label: 'Bahasa' }, | |
| { value: 'de-DE', label: 'Deutsch' }, | |
| { value: 'en', label: 'English' }, | |
| { value: 'es', label: 'Español' }, | |
| { value: 'fr', label: 'Français' }, | |
| { value: 'pl', label: 'Polski' }, | |
| { value: 'ru', label: 'Русский' }, | |
| { value: 'vn', label: 'Tiếng Việt' }, | |
| { value: 'zh-CN', label: '简体中文' }, | |
| { value: 'zh-TW', label: '繁體中文' }, |
| /// Tool with server information | ||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct ToolWithServer { |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entire McpSettings struct and related functionality have been removed, which means MCP timeout and restart behavior are now hardcoded in constants. This reduces configurability and makes it impossible for users to adjust these values without recompiling. Consider keeping the settings structure but with default values if runtime configuration is not needed.
| const [message, setMessage] = useState('') | ||
| const [dropdownToolsAvailable, setDropdownToolsAvailable] = useState(false) | ||
| const [tooltipToolsAvailable, setTooltipToolsAvailable] = useState(false) | ||
| const [attachments, setAttachments] = useState<Attachment[]>([]) | ||
| const [uploadedFiles, setUploadedFiles] = useState< | ||
| Array<{ | ||
| name: string | ||
| type: string | ||
| size: number | ||
| base64: string | ||
| dataUrl: string | ||
| }> | ||
| >([]) |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The attachment handling has been drastically simplified, removing RAG document ingestion, validation, processing states, and duplicate checking. This removes significant functionality around document attachments. The new implementation only supports basic image uploads without the previous robust error handling and user feedback mechanisms.
| )] | ||
| use crate::core::setup::setup_tray; | ||
|
|
||
| #[cfg_attr(mobile, tauri::mobile_entry_point)] |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mobile platform support has been removed throughout the codebase (Android and iOS feature flags, conditional compilation, etc.). This is a major architectural change that should be clearly documented in the PR description and communicated to users who may be relying on mobile builds.
| #[cfg_attr(mobile, tauri::mobile_entry_point)] |
| "dev:tauri": "yarn build:icon && yarn copy:assets:tauri && cross-env IS_CLEAN=true tauri dev", | ||
| "dev:ios": "yarn copy:assets:mobile && RUSTC_WRAPPER= cross-env IS_IOS=true yarn tauri ios dev --features mobile", | ||
| "dev:android": "yarn copy:assets:mobile && cross-env IS_ANDROID=true yarn tauri android dev --features mobile", | ||
| "build:android": "yarn build:icon && yarn copy:assets:mobile && cross-env IS_CLEAN=true yarn tauri android build -- --no-default-features --features mobile", | ||
| "build:ios": "yarn build:icon && yarn copy:assets:mobile && cross-env IS_IOS=true yarn tauri ios build -- --no-default-features --features mobile", | ||
| "build:ios:device": "yarn build:icon && yarn copy:assets:mobile && cross-env IS_IOS=true yarn tauri ios build -- --no-default-features --features mobile --export-method debugging", | ||
| "copy:assets:tauri": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"", | ||
| "copy:assets:mobile": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"", | ||
| "download:lib": "node ./scripts/download-lib.mjs", | ||
| "download:bin": "node ./scripts/download-bin.mjs", | ||
| "build:tauri:win32": "yarn download:bin && yarn tauri build", | ||
| "build:tauri:linux": "yarn download:bin && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh", | ||
| "build:tauri:win32": "yarn download:bin && yarn download:lib && yarn tauri build", | ||
| "build:tauri:linux": "yarn download:bin && yarn download:lib && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh", |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mobile build scripts (ios, android, dev:ios, dev:android, build:android, build:ios) have been completely removed. This represents a significant reduction in platform support and should be documented as a breaking change.
| pub static MESSAGE_LOCKS: Lazy<Mutex<HashMap<String, Arc<Mutex<()>>>>> = | ||
| Lazy::new(|| Mutex::new(HashMap::new())); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed from OnceLock to Lazy from once_cell crate. While functionally similar, this introduces a new dependency (once_cell) and changes the initialization pattern. Consider documenting why this change was necessary or revert to using OnceLock from std if targeting Rust 1.70+.
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <link | ||
| rel="icon" | ||
| type="image/png" | ||
| sizes="32x32" | ||
| href="/images/jan-logo.png" | ||
| /> | ||
| <link | ||
| rel="icon" | ||
| type="image/png" | ||
| sizes="16x16" | ||
| href="/images/jan-logo.png" | ||
| /> | ||
| <link rel="icon" type="image/png" sizes="32x32" href="/images/jan-logo.png" /> | ||
| <link rel="icon" type="image/png" sizes="16x16" href="/images/jan-logo.png" /> | ||
| <link rel="apple-touch-icon" href="/images/jan-logo.png" /> | ||
| <meta | ||
| name="viewport" | ||
| content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" | ||
| /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Jan</title> | ||
| <!-- INJECT_GOOGLE_ANALYTICS --> | ||
| <style> | ||
| #initial-loader { | ||
| position: fixed; | ||
| inset: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| justify-content: center; | ||
| gap: 1rem; | ||
| background: hsl(var(--background, 0 0% 100%)); | ||
| z-index: 9999; | ||
| opacity: 1; | ||
| transition: opacity 0.3s ease-in-out; | ||
| } | ||
|
|
||
| /* Light mode (default) */ | ||
| @media (prefers-color-scheme: light) { | ||
| #initial-loader { | ||
| background: rgb(255, 255, 255); | ||
| } | ||
| } | ||
|
|
||
| /* Dark mode */ | ||
| @media (prefers-color-scheme: dark) { | ||
| #initial-loader { | ||
| background: rgb(25, 25, 25); | ||
| } | ||
| } | ||
|
|
||
| #initial-loader img { | ||
| width: 5rem; | ||
| height: 5rem; | ||
| animation: wave 2s ease-in-out infinite; | ||
| transform-origin: 70% 70%; | ||
| } | ||
|
|
||
| @keyframes wave { | ||
| 0%, | ||
| 100% { | ||
| transform: rotate(0deg); | ||
| } | ||
| 10%, | ||
| 30% { | ||
| transform: rotate(14deg); | ||
| } | ||
| 20% { | ||
| transform: rotate(-8deg); | ||
| } | ||
| 40%, | ||
| 60% { | ||
| transform: rotate(14deg); | ||
| } | ||
| 50% { | ||
| transform: rotate(-8deg); | ||
| } | ||
| 70% { | ||
| transform: rotate(0deg); | ||
| } | ||
| } | ||
|
|
||
| .loaded #initial-loader { | ||
| opacity: 0; | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| </head> |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entire initial loading screen with animation and Google Analytics injection point has been removed. This affects user experience during app startup and removes analytics capability. Consider keeping a minimal loading indicator for better UX.
- Added informational message in Whisper settings - Makes it clear that local Whisper servers don't need authentication - Improves user experience by reducing confusion
- Create translation files for English, Spanish, French, and Russian - Modify whisper.tsx to use useTranslation hook for all UI text - All hardcoded strings replaced with translation keys - Supports dynamic language switching when Jan's language changes
Remove API key validation checks to allow running local server without API key Update validation logic in ApiKeyInput to reflect optional requirement Add mime_guess dependency for improved MIME type handling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 246 out of 545 changed files in this pull request and generated 9 comments.
Comments suppressed due to low confidence (1)
extensions/llamacpp-extension/src/index.ts:1
- The flash attention logic is inverted in the newer version branch. Line 1626 should check
if (cfg.flash_attn)instead ofif (!cfg.flash_attn)to enable flash attention when the config is true, matching the user's intent.
/**
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!streamingContent || streamingContent.thread_id !== threadId) { | ||
| return null | ||
| } | ||
| if (!streamingContent || streamingContent.thread_id !== threadId) return null | ||
|
|
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removed logic checked if the last message had a 'Stopped' status to prevent showing streaming content after a user stops generation. Without this check, stopped message content may incorrectly appear as streaming, causing UI confusion.
| // Prevent showing streaming content if the last assistant message was stopped | |
| if (lastAssistant && lastAssistant.status === 'Stopped') { | |
| return null | |
| } |
| const showGenerateAIResponseBtn = | ||
| ((messages[messages.length - 1]?.role === 'user' || | ||
| (messages[messages.length - 1]?.role === 'user' || | ||
| (messages[messages.length - 1]?.metadata && | ||
| 'tool_calls' in (messages[messages.length - 1].metadata ?? {})) || | ||
| isPartialResponse || | ||
| isModelMismatch) && | ||
| !streamingContent) | ||
| 'tool_calls' in (messages[messages.length - 1].metadata ?? {}))) && | ||
| !streamingContent |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed logic for detecting partial responses (when user stops generation mid-stream) and model mismatches. This breaks the 'Continue' button functionality that allows users to resume interrupted responses, and prevents handling cases where the selected model differs from the one that generated a partial response.
| threadId: string | ||
| isModelMismatch?: boolean | ||
| }) => { | ||
| export const GenerateResponseButton = ({ threadId }: { threadId: string }) => { |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the isModelMismatch parameter and all related logic for continuing partial responses. This breaks the ability to continue generating from stopped messages and to handle model switches appropriately, resulting in loss of user data when switching models mid-conversation.
| return | ||
| } | ||
| if (!prompt.trim()) { | ||
| if (!prompt.trim() && uploadedFiles.length === 0) { |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simplified file upload system removed RAG document processing, validation, progress tracking, and ingestion status. This is a significant feature regression that removes the ability to attach and process documents for context-aware conversations.
| #[tauri::command] | ||
| pub async fn get_mcp_configs<R: Runtime>(app: AppHandle<R>) -> Result<String, String> { | ||
| let mut path = get_jan_data_folder_path(app.clone()); | ||
| pub async fn get_mcp_configs(app: AppHandle) -> Result<String, String> { |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed all MCP settings management including timeout configuration, restart delays, and backoff multipliers. The simplified version no longer validates or ensures settings exist in the config, potentially breaking MCP server behavior and customization.
| }; | ||
|
|
||
| /// Lists all threads by reading their metadata from the threads directory or database. | ||
| /// Lists all threads by reading their metadata from the threads directory. |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed SQLite database support for mobile platforms (iOS/Android), forcing file-based storage on all platforms. This change removes the conditional logic (should_use_sqlite()) throughout the threads module, breaking mobile platform functionality.
| "sonner": "2.0.5", | ||
| "tailwindcss": "4.1.4", | ||
| "token.js": "npm:[email protected].30", | ||
| "token.js": "npm:[email protected].29", |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Downgraded token.js from 0.7.30 to 0.7.29. Without justification in the PR description, this appears to be an unintentional downgrade that may reintroduce bugs or remove features from the newer version.
| "token.js": "npm:[email protected].29", | |
| "token.js": "npm:[email protected].30", |
| authors = ["Jan <[email protected]>"] | ||
| license = "MIT" | ||
| repository = "https://github.com/janhq/jan" | ||
| repository = "https://github.com/menloresearch/jan" |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Repository URL changed from 'janhq/jan' to 'menloresearch/jan' across multiple Cargo.toml files. This appears to be an organizational change that should be explicitly mentioned in the PR description, as it affects package metadata and may break links/references.
| "yallist": "4.0.0", | ||
| "@types/react": "19.1.2", | ||
| "@types/react-dom": "19.1.2" | ||
| "yallist": "4.0.0" |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed React type resolutions (@types/react and @types/react-dom). This may cause TypeScript compilation issues or type conflicts if different versions of React types are pulled in by dependencies.
| "yallist": "4.0.0" | |
| "yallist": "4.0.0", | |
| "@types/react": "^18.2.62", | |
| "@types/react-dom": "^18.2.18" |
- Remove API Key input field from Local API Server settings UI - Set apiKey to empty string when starting server - Remove API key validation logic - Local API Server now works without requiring authentication
- Remove invalid 'separator' prop from CardItem components - Fix TypeScript compilation errors in whisper.tsx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 246 out of 545 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { value: 'fr', label: 'Français' }, | ||
| { value: 'ru', label: 'Русский' }, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The language entries for French ('fr') and Russian ('ru') are added but the PR title and description only mention Spanish language support. If these languages don't have complete translation files, they should be removed or the PR description should be updated to mention all three languages being added.
| { value: 'fr', label: 'Français' }, | |
| { value: 'ru', label: 'Русский' }, |
| // API key is optional for local server; no hard validation | ||
| if (!value || value.trim().length === 0) { | ||
| setError(t('common:apiKeyRequired')) | ||
| onValidationChange?.(false) | ||
| return false | ||
| setError('') | ||
| onValidationChange?.(true) | ||
| return true |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment states "API key is optional for local server" but this validation function is used generically for API key inputs. This change makes all API keys optional which may not be the intended behavior for remote services. Consider adding a parameter to control whether the API key is required.
- Create platform feature types (FILE_ATTACHMENTS, LOCAL_API_SERVER) - Create platform constants with feature flags - Fix MCPToolComponentProps import by defining it locally - Resolve TypeScript compilation errors for platform features
- Add PlatformFeature enum with FILE_ATTACHMENTS and LOCAL_API_SERVER - Add PlatformFeatures constants mapping - Export platform types and constants
- Add settingInterface to localStorageKey - Add interface route to settings routes - Fix SettingComponentProps titleKey/descriptionKey access with type cast - Comment out @janhq/conversational-extension import (unavailable) - Add local IngestAttachmentsResult type definition - All 15 TypeScript errors now resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 247 out of 549 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src-tauri/plugins/tauri-plugin-llamacpp/src/commands.rs:1
- Removing the
timeoutparameter fromload_llama_modeland hardcoding it to 300 seconds (5 minutes) eliminates user configurability. This may cause issues for users with slower systems or larger models. Consider keeping the timeout as a configurable parameter.
use base64::{engine::general_purpose, Engine as _};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub model_id: String, | ||
| pub model_path: String, // path of the loaded model | ||
| pub is_embedding: bool, | ||
| pub api_key: String, |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing the is_embedding field from SessionInfo breaks the API contract. This is a breaking change that may affect code relying on this field to determine if a session is for embeddings.
| pub api_key: String, | |
| pub api_key: String, | |
| pub is_embedding: bool, |
|
This PR introduce A LOT of irrelevant changes. Need to rework, can you rebase somehow and keep the language support change only then reopen the PR? |
Add complete Spanish translation for the Jan application including:
All translation files created:
Users can now select "Español" from the language switcher in Settings > General > Language to use the app in Spanish.
Describe Your Changes
Fixes Issues
Self Checklist