Compare commits

..
Author SHA1 Message Date
Anthony Stirling 4fc5f4a14b Localize OCR language labels 2025-11-27 23:45:39 +00:00
James BruntonandGitHub 731743b618 Don't block desktop app on backend starting up (#5041)
# Description of Changes
Start bundled backend instantly on startup of app and don't wait on it
being fully up to spawn app. This is techincally wasteful curently on
self-hosted mode where everything runs remotely, but in the future we'll
probably route simple operations to the local machine regardless of
connection, and it stops unnecessary waiting in the offline mode.
2025-11-27 15:54:35 +00:00
ConnorYohandGitHub 04c4aec0d8 Disable admin plan section when no login (#5039)
Admin plan section matches other admin sections

<img width="1013" height="629" alt="image"
src="https://github.com/user-attachments/assets/39e9fad7-461c-491d-99cb-4b140292f2f4"
/>
<img width="730" height="595" alt="image"
src="https://github.com/user-attachments/assets/b26354d2-5401-40b3-8ca7-3b48b26b644e"
/>
2025-11-27 13:57:04 +00:00
5 changed files with 30 additions and 48 deletions
@@ -398,10 +398,6 @@ pub async fn start_backend(
e
})?;
// Wait for the backend to start
println!("⏳ Waiting for backend startup...");
tokio::time::sleep(std::time::Duration::from_millis(10000)).await;
// Reset the starting flag since startup is complete
reset_starting_flag();
add_log("✅ Backend startup sequence completed, starting flag cleared".to_string());
+12 -1
View File
@@ -66,7 +66,7 @@ pub fn run() {
// Emit a generic notification that files were added (frontend will re-read storage)
let _ = app.emit("files-changed", ());
}))
.setup(|_app| {
.setup(|app| {
add_log("🚀 Tauri app setup started".to_string());
// Process command line arguments on first launch
@@ -78,6 +78,17 @@ pub fn run() {
}
}
// Start backend immediately, non-blocking
let app_handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
add_log("🚀 Starting bundled backend in background".to_string());
let connection_state = app_handle.state::<AppConnectionState>();
if let Err(e) = commands::backend::start_backend(app_handle.clone(), connection_state).await {
add_log(format!("⚠️ Backend start failed: {}", e));
}
});
add_log("🔍 DEBUG: Setup completed".to_string());
Ok(())
})
@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import { Text, Loader } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { tempOcrLanguages, getAutoOcrLanguage } from '@app/utils/languageMapping';
import { getAutoOcrLanguage, getOcrDisplayName } from '@app/utils/languageMapping';
import apiClient from '@app/services/apiClient';
import DropdownListWithFooter, { DropdownItem } from '@app/components/shared/DropdownListWithFooter';
export interface LanguageOption {
@@ -37,32 +38,22 @@ const LanguagePicker: React.FC<LanguagePickerProps> = ({
// Fetch available languages from backend
const fetchLanguages = async () => {
try {
const response = await fetch(languagesEndpoint);
const { data } = await apiClient.get<{ languages: string[] }>(languagesEndpoint);
if (response.ok) {
const data: { languages: string[] } = await response.json();
const languages = data.languages;
const languageOptions = languages.map(lang => {
// TODO: Use actual language translations when they become available
// For now, use temporary English translations
const translatedName = tempOcrLanguages.lang[lang as keyof typeof tempOcrLanguages.lang] || lang;
const displayName = translatedName;
const languageOptions = [...new Set(data.languages)]
.map((lang) => {
const displayName = getOcrDisplayName(lang);
const translatedName = t(`ocr.languages.${lang}`, displayName);
return {
value: lang,
name: displayName
name: translatedName,
label: translatedName
};
});
})
.sort((a, b) => a.name.localeCompare(b.name, i18n.language));
setAvailableLanguages(languageOptions);
} else {
console.error('[LanguagePicker] Response not OK:', response.status, response.statusText);
const errorText = await response.text();
console.error('[LanguagePicker] Error response body:', errorText);
}
setAvailableLanguages(languageOptions);
} catch (error) {
console.error('[LanguagePicker] Fetch failed with error:', error);
console.error('[LanguagePicker] Error details:', {
@@ -31,10 +31,10 @@ export function AppProviders({ children }: { children: ReactNode }) {
}
}, [setupComplete, isFirstLaunch, connectionMode]);
// Only start bundled backend if in SaaS mode (local backend) and setup is complete
// Self-hosted mode connects to remote server so doesn't need local backend
const shouldStartBackend = setupComplete && !isFirstLaunch && connectionMode === 'saas';
useBackendInitializer(shouldStartBackend);
// Initialize monitoring for bundled backend (already started in Rust)
// This sets up port detection and health checks
const shouldMonitorBackend = setupComplete && !isFirstLaunch && connectionMode === 'saas';
useBackendInitializer(shouldMonitorBackend);
// Show setup wizard on first launch
if (isFirstLaunch && !setupComplete) {
@@ -51,23 +51,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
}}
>
<SetupWizard
onComplete={async () => {
// Wait for backend to become healthy before reloading
// This prevents reloading mid-startup which would interrupt the backend
const maxWaitTime = 60000; // 60 seconds max
const checkInterval = 1000; // Check every second
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
if (tauriBackendService.isBackendHealthy()) {
window.location.reload();
return;
}
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
// If we timeout, reload anyway
console.warn('[AppProviders] Backend health check timeout, reloading anyway...');
onComplete={() => {
window.location.reload();
}}
/>
@@ -61,7 +61,7 @@ export const SetupWizard: React.FC<SetupWizardProps> = ({ onComplete }) => {
}
await connectionModeService.switchToSaaS(serverConfig.url);
await tauriBackendService.startBackend();
tauriBackendService.startBackend().catch(console.error);
onComplete();
} catch (err) {
console.error('SaaS login failed:', err);