diff --git a/packages/ui/src/components/onboarding/ChooserScreen.tsx b/packages/ui/src/components/onboarding/ChooserScreen.tsx index 30f5668f..955e027c 100644 --- a/packages/ui/src/components/onboarding/ChooserScreen.tsx +++ b/packages/ui/src/components/onboarding/ChooserScreen.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { RiFileCopyLine, RiCheckLine, RiExternalLinkLine } from '@remixicon/react'; -import { isDesktopShell, isTauriShell } from '@/lib/desktop'; +import { RiFileCopyLine, RiCheckLine, RiExternalLinkLine, RiArrowDownSLine } from '@remixicon/react'; +import { isDesktopShell, isTauriShell, startDesktopWindowDrag } from '@/lib/desktop'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { updateDesktopSettings } from '@/lib/persistence'; @@ -14,6 +14,7 @@ import { useI18n } from '@/lib/i18n'; const INSTALL_COMMAND = 'curl -fsSL https://opencode.ai/install | bash'; const DOCS_URL = 'https://opencode.ai/docs'; const WINDOWS_WSL_DOCS_URL = 'https://opencode.ai/docs/windows-wsl'; +const POLL_INTERVAL_MS = 2500; type OnboardingPlatform = 'macos' | 'linux' | 'windows' | 'unknown'; @@ -24,8 +25,8 @@ type ChooserScreenProps = { function BashCommand({ onCopy, copyTitle }: { onCopy: () => void; copyTitle: string }) { return ( -
+
+
curl
-fsSL
https://opencode.ai/install
@@ -34,8 +35,9 @@ function BashCommand({ onCopy, copyTitle }: { onCopy: () => void; copyTitle: str
@@ -43,24 +45,17 @@ function BashCommand({ onCopy, copyTitle }: { onCopy: () => void; copyTitle: str
);
}
-const HINT_DELAY_MS = 30000;
-
export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
const { t } = useI18n();
const [copied, setCopied] = React.useState(false);
- const [showHint, setShowHint] = React.useState(false);
const [isDesktopApp, setIsDesktopApp] = React.useState(false);
- const [isRetrying, setIsRetrying] = React.useState(false);
- const [isChecking, setIsChecking] = React.useState(false);
- const [checkError, setCheckError] = React.useState(null);
+ const [isApplyingPath, setIsApplyingPath] = React.useState(false);
+ const [isManualChecking, setIsManualChecking] = React.useState(false);
const [opencodeBinary, setOpencodeBinary] = React.useState('');
const [platform, setPlatform] = React.useState('unknown');
const [activeTab, setActiveTab] = React.useState<'local' | 'remote'>('local');
-
- React.useEffect(() => {
- const timer = setTimeout(() => setShowHint(true), HINT_DELAY_MS);
- return () => clearTimeout(timer);
- }, []);
+ const [advancedOpen, setAdvancedOpen] = React.useState(false);
+ const [troubleOpen, setTroubleOpen] = React.useState(false);
React.useEffect(() => {
setIsDesktopApp(isDesktopShell());
@@ -73,19 +68,10 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
}
const ua = navigator.userAgent || '';
- if (/Windows/i.test(ua)) {
- setPlatform('windows');
- return;
- }
- if (/Macintosh|Mac OS X/i.test(ua)) {
- setPlatform('macos');
- return;
- }
- if (/Linux/i.test(ua)) {
- setPlatform('linux');
- return;
- }
- setPlatform('unknown');
+ if (/Windows/i.test(ua)) setPlatform('windows');
+ else if (/Macintosh|Mac OS X/i.test(ua)) setPlatform('macos');
+ else if (/Linux/i.test(ua)) setPlatform('linux');
+ else setPlatform('unknown');
}, []);
React.useEffect(() => {
@@ -97,9 +83,7 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
const data = (await response.json().catch(() => null)) as null | { opencodeBinary?: unknown };
if (!data || cancelled) return;
const value = typeof data.opencodeBinary === 'string' ? data.opencodeBinary.trim() : '';
- if (value) {
- setOpencodeBinary(value);
- }
+ if (value) setOpencodeBinary(value);
} catch {
// ignore
}
@@ -110,18 +94,12 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
}, []);
const handleDragStart = React.useCallback(async (e: React.MouseEvent) => {
- if ((e.target as HTMLElement).closest('button, a, input, select, textarea, code')) {
- return;
- }
+ const target = e.target as HTMLElement;
+ if (target.closest('.app-region-no-drag')) return;
+ if (target.closest('button, a, input, select, textarea, code, summary, details')) return;
if (e.button !== 0) return;
- if (isDesktopApp && isTauriShell()) {
- try {
- const { getCurrentWindow } = await import('@tauri-apps/api/window');
- const window = getCurrentWindow();
- await window.startDragging();
- } catch (error) {
- console.error('Failed to start window dragging:', error);
- }
+ if (isDesktopApp) {
+ await startDesktopWindowDrag();
}
}, [isDesktopApp]);
@@ -136,18 +114,74 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
}
}, []);
+ const persistFirstChoice = React.useCallback(async (choice: 'local' | 'remote') => {
+ if (!isTauriShell()) return;
+
+ const config = await desktopHostsGet();
+ await desktopHostsSet({
+ ...config,
+ ...(choice === 'local' ? { defaultHostId: 'local' } : {}),
+ initialHostChoiceCompleted: true,
+ });
+ }, []);
+
+ const announceAvailable = React.useCallback(async () => {
+ if (isTauriShell()) {
+ await persistFirstChoice('local');
+ }
+ onCliAvailable?.();
+ }, [onCliAvailable, persistFirstChoice]);
+
+ // Background polling: while the local tab is visible, periodically check
+ // whether the OpenCode CLI is reachable. As soon as it is, transition
+ // automatically — the user doesn't have to click anything.
+ React.useEffect(() => {
+ if (activeTab !== 'local') return;
+
+ let cancelled = false;
+ let timer: ReturnType | null = null;
+
+ const tick = async () => {
+ if (cancelled) return;
+ try {
+ const available = await checkCliAvailability();
+ if (cancelled) return;
+ if (available) {
+ await announceAvailable();
+ return;
+ }
+ } catch {
+ // ignore
+ }
+ if (!cancelled) {
+ timer = setTimeout(tick, POLL_INTERVAL_MS);
+ }
+ };
+
+ timer = setTimeout(tick, POLL_INTERVAL_MS);
+
+ return () => {
+ cancelled = true;
+ if (timer) clearTimeout(timer);
+ };
+ }, [activeTab, checkCliAvailability, announceAvailable]);
+
+ const handleManualCheck = React.useCallback(async () => {
+ setIsManualChecking(true);
+ try {
+ const available = await checkCliAvailability();
+ if (available) await announceAvailable();
+ } finally {
+ setIsManualChecking(false);
+ }
+ }, [checkCliAvailability, announceAvailable]);
+
const handleBrowse = React.useCallback(async () => {
- if (typeof window === 'undefined') {
- return;
- }
- if (!isDesktopApp || !isTauriShell()) {
- return;
- }
+ if (typeof window === 'undefined') return;
+ if (!isDesktopApp || !isTauriShell()) return;
const tauri = (window as unknown as { __TAURI__?: { dialog?: { open?: (opts: Record) => Promise } } }).__TAURI__;
- if (!tauri?.dialog?.open) {
- return;
- }
+ if (!tauri?.dialog?.open) return;
try {
const selected = await tauri.dialog.open({
@@ -163,41 +197,18 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
}
}, [isDesktopApp, t]);
- // Persist the user's first choice (local or remote)
- const persistFirstChoice = React.useCallback(async (choice: 'local' | 'remote') => {
- if (!isTauriShell()) return;
-
- const config = await desktopHostsGet();
- await desktopHostsSet({
- ...config,
- // Only change defaultHostId when switching to local; remote keeps
- // whatever was there (or null) until a successful connect.
- ...(choice === 'local' ? { defaultHostId: 'local' } : {}),
- initialHostChoiceCompleted: true,
- });
- }, []);
-
const handleApplyPath = React.useCallback(async () => {
- setIsRetrying(true);
+ setIsApplyingPath(true);
try {
await updateDesktopSettings({ opencodeBinary: opencodeBinary.trim() });
-
- // In first-launch mode, persist the local choice when user manually
- // sets the binary path, so the choice is remembered after restart.
if (isTauriShell()) {
await persistFirstChoice('local');
- }
-
- // In desktop boot flow, always restart the entire Tauri app so Rust
- // can re-evaluate the boot outcome with the updated binary path.
- if (isTauriShell()) {
await restartDesktopApp();
return;
}
-
await fetch('/api/config/reload', { method: 'POST' });
} finally {
- setTimeout(() => setIsRetrying(false), 1000);
+ setTimeout(() => setIsApplyingPath(false), 1000);
}
}, [opencodeBinary, persistFirstChoice]);
@@ -211,32 +222,6 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
}
}, []);
- const handleChooseRemote = React.useCallback(() => {
- setActiveTab('remote');
- }, []);
-
- const handleCheckAndContinue = React.useCallback(async () => {
- setIsChecking(true);
- setCheckError(null);
- try {
- const available = await checkCliAvailability();
- if (available) {
- // In first-launch mode, persist the local choice when CLI becomes
- // available, so the choice is remembered after restart.
- if (isTauriShell()) {
- await persistFirstChoice('local');
- }
- onCliAvailable?.();
- } else {
- setCheckError(t('onboarding.localSetup.errors.cliNotReady'));
- }
- } catch (err) {
- setCheckError(err instanceof Error ? err.message : t('onboarding.localSetup.errors.detectionFailed'));
- } finally {
- setIsChecking(false);
- }
- }, [checkCliAvailability, onCliAvailable, persistFirstChoice, t]);
-
const docsUrl = platform === 'windows' ? WINDOWS_WSL_DOCS_URL : DOCS_URL;
const binaryPlaceholder =
platform === 'windows'
@@ -245,27 +230,29 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
? '/home/you/.bun/bin/opencode'
: '/Users/you/.bun/bin/opencode';
+ const showLocal = !isDesktopApp || !isTauriShell() || activeTab === 'local';
+
return (
-
-
-
+
+
+
{t('onboarding.chooser.title')}
-
+
{t('onboarding.chooser.description')}
-
+
{isDesktopApp && isTauriShell() && (
-
+
@@ -290,133 +277,168 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
)}
{isDesktopApp && isTauriShell() && activeTab === 'remote' ? (
- setActiveTab('local')}
- showBackButton={false}
- onSwitchToLocal={() => setActiveTab('local')}
- />
- ) : (
- <>
- {(!isDesktopApp || !isTauriShell() || activeTab === 'local') && (
- <>
- {platform === 'windows' && (
-
- {t('onboarding.localSetup.windows.title')}
-
- - {t('onboarding.localSetup.windows.stepInstallWsl')}
wsl --install {t('onboarding.localSetup.windows.stepInstallWslSuffix')}
- - {t('onboarding.localSetup.windows.stepRunInstallInWsl')}
- - {t('onboarding.localSetup.windows.stepSetBinaryPath')}
-
-
- )}
+
+ setActiveTab('local')}
+ showBackButton={false}
+ onSwitchToLocal={() => setActiveTab('local')}
+ />
+
+ ) : null}
-
-
- {copied ? (
-
-
- {t('onboarding.common.status.copiedToClipboard')}
-
- ) : (
-
- )}
-
+ {showLocal && (
+
+ {platform === 'windows' && (
+
+ {t('onboarding.localSetup.windows.title')}
+
+ - {t('onboarding.localSetup.windows.stepInstallWsl')}
wsl --install {t('onboarding.localSetup.windows.stepInstallWslSuffix')}
+ - {t('onboarding.localSetup.windows.stepRunInstallInWsl')}
+ - {t('onboarding.localSetup.windows.stepSetBinaryPath')}
+
+
+ )}
+
+
+ {t('onboarding.localSetup.intro')}
+
+
+
+ {copied ? (
+
+
+ {t('onboarding.common.status.copiedToClipboard')}
+ ) : (
+
+ )}
+
-
- {platform === 'windows' ? t('onboarding.localSetup.docs.windows') : t('onboarding.localSetup.docs.default')}
-
-
+
- {checkError && (
-
- {checkError}
-
- )}
+
+
+
+
+
+
+
+ {t('onboarding.localSetup.status.watching')}
+
+
+ {t('onboarding.localSetup.status.autoContinue')}
+
+
+
-
+ setAdvancedOpen((e.currentTarget as HTMLDetailsElement).open)}
+ >
+
+ {t('onboarding.localSetup.advanced.title')}
+
+
+
+
+ setOpencodeBinary(e.target.value)}
+ placeholder={binaryPlaceholder}
+ disabled={isApplyingPath}
+ className="flex-1 font-mono text-xs"
+ />
+
-
-
- {t('onboarding.localSetup.helper.checkAndContinue')}
-
+
+ {t('onboarding.localSetup.helper.saveAndReload')}
+
+
+
-
-
- {t('onboarding.localSetup.field.alreadyInstalled')}
-
- setOpencodeBinary(e.target.value)}
- placeholder={binaryPlaceholder}
- disabled={isRetrying}
- className="flex-1 font-mono text-xs"
- />
-
-
-
- {t('onboarding.localSetup.helper.saveAndReload')}
-
-
- >
- )}
- >
+ setTroubleOpen((e.currentTarget as HTMLDetailsElement).open)}
+ >
+
+ {t('onboarding.localSetup.troubleshoot.title')}
+
+
+
+ {platform === 'windows' ? (
+ <>
+ - {t('onboarding.localSetup.windows.hintInstallInWsl')}
+ - {t('onboarding.localSetup.windows.hintDetectionFailed')}
+ >
+ ) : (
+ <>
+ - {t('onboarding.localSetup.hint.ensurePath')}
+ - {t('onboarding.localSetup.hint.setEnv')}
+ - {t('onboarding.localSetup.hint.missingRuntime')}
+ >
+ )}
+
+
+
)}
-
- {showHint && activeTab === 'local' && (
-
- {platform === 'windows' ? (
- <>
-
- {t('onboarding.localSetup.windows.hintInstallInWsl')}
-
-
- {t('onboarding.localSetup.windows.hintDetectionFailed')}
-
- >
- ) : (
- <>
-
- {t('onboarding.localSetup.hint.ensurePath')}
-
-
- {t('onboarding.localSetup.hint.setEnv')}
-
-
- {t('onboarding.localSetup.hint.missingRuntime')}
-
- >
- )}
-
- )}
);
}
diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts
index a7fe8533..779662aa 100644
--- a/packages/ui/src/lib/i18n/messages/en.ts
+++ b/packages/ui/src/lib/i18n/messages/en.ts
@@ -1951,12 +1951,18 @@ export const dict = {
'onboarding.localSetup.windows.stepInstallWslSuffix': 'in PowerShell.',
'onboarding.localSetup.windows.stepRunInstallInWsl': 'Run the install command below inside your WSL terminal.',
'onboarding.localSetup.windows.stepSetBinaryPath': 'If OpenChamber does not detect OpenCode automatically, set the binary path below.',
- 'onboarding.localSetup.docs.windows': 'View Windows + WSL documentation',
- 'onboarding.localSetup.docs.default': 'View documentation',
- 'onboarding.localSetup.actions.checking': 'Checking...',
+ 'onboarding.localSetup.intro': 'OpenCode is the heart of OpenChamber — install it to get started.',
+ 'onboarding.localSetup.docs.windows': 'OpenCode Windows + WSL docs',
+ 'onboarding.localSetup.docs.default': 'OpenCode docs',
+ 'onboarding.localSetup.actions.checking': 'Checking…',
'onboarding.localSetup.actions.checkAndContinue': "I've completed installation, check and continue",
+ 'onboarding.localSetup.actions.checkNow': 'Check now',
'onboarding.localSetup.helper.checkAndContinue': 'Click to check if OpenCode CLI is available. If successful, you\'ll automatically enter the main screen.',
+ 'onboarding.localSetup.status.watching': 'Waiting for OpenCode',
+ 'onboarding.localSetup.status.autoContinue': "We'll continue automatically once it's detected.",
'onboarding.localSetup.field.alreadyInstalled': 'Already installed? Set the OpenCode CLI path:',
+ 'onboarding.localSetup.advanced.title': 'Set a custom binary path',
+ 'onboarding.localSetup.troubleshoot.title': 'Having trouble?',
'onboarding.localSetup.actions.browse': 'Browse',
'onboarding.localSetup.actions.apply': 'Apply',
'onboarding.localSetup.helper.saveAndReload': 'Saves to OpenChamber settings and reloads OpenCode configuration.',
diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts
index a01e3e63..4931df1e 100644
--- a/packages/ui/src/lib/i18n/messages/es.ts
+++ b/packages/ui/src/lib/i18n/messages/es.ts
@@ -1917,12 +1917,18 @@ export const dict: Record = {
"onboarding.localSetup.windows.stepInstallWslSuffix": "en PowerShell.",
"onboarding.localSetup.windows.stepRunInstallInWsl": "Ejecuta el comando de instalación a continuación dentro de tu terminal de WSL.",
"onboarding.localSetup.windows.stepSetBinaryPath": "Si OpenChamber no detecta OpenCode automáticamente, establece la ruta del ejecutable a continuación.",
- "onboarding.localSetup.docs.windows": "Ver documentación de Windows + WSL",
- "onboarding.localSetup.docs.default": "Ver documentación",
+ "onboarding.localSetup.intro": "OpenCode es el corazón de OpenChamber. Instálalo para empezar.",
+ "onboarding.localSetup.docs.windows": "Documentación de OpenCode para Windows + WSL",
+ "onboarding.localSetup.docs.default": "Documentación de OpenCode",
"onboarding.localSetup.actions.checking": "Verificando...",
"onboarding.localSetup.actions.checkAndContinue": "He completado la instalación, verificar y continuar",
+ "onboarding.localSetup.actions.checkNow": "Verificar ahora",
"onboarding.localSetup.helper.checkAndContinue": "Haz clic para verificar si la CLI de OpenCode está disponible. Si la verificación funciona, entrarás automáticamente en la pantalla principal.",
+ "onboarding.localSetup.status.watching": "Esperando OpenCode",
+ "onboarding.localSetup.status.autoContinue": "Continuaremos automáticamente al detectarlo.",
"onboarding.localSetup.field.alreadyInstalled": "¿Ya está instalado? Establece la ruta de la CLI de OpenCode:",
+ "onboarding.localSetup.advanced.title": "Establecer una ruta personalizada del binario",
+ "onboarding.localSetup.troubleshoot.title": "¿Tienes problemas?",
"onboarding.localSetup.actions.browse": "Explorar",
"onboarding.localSetup.actions.apply": "Aplicar",
"onboarding.localSetup.helper.saveAndReload": "Guarda en la configuración de OpenChamber y recarga la configuración de OpenCode.",
diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts
index 98f4df6c..66c15393 100644
--- a/packages/ui/src/lib/i18n/messages/ko.ts
+++ b/packages/ui/src/lib/i18n/messages/ko.ts
@@ -1951,12 +1951,18 @@ export const dict: Record = {
'onboarding.localSetup.windows.stepInstallWslSuffix': 'PowerShell에서 실행하세요.',
'onboarding.localSetup.windows.stepRunInstallInWsl': '아래 설치 명령을 WSL 터미널에서 실행하세요.',
'onboarding.localSetup.windows.stepSetBinaryPath': 'OpenChamber가 OpenCode를 자동으로 찾지 못하면 아래에 바이너리 경로를 설정하세요.',
- 'onboarding.localSetup.docs.windows': 'Windows + WSL 문서 보기',
- 'onboarding.localSetup.docs.default': '문서 보기',
+ 'onboarding.localSetup.intro': 'OpenCode는 OpenChamber의 심장입니다. 먼저 설치하고 시작하세요.',
+ 'onboarding.localSetup.docs.windows': 'OpenCode Windows + WSL 문서',
+ 'onboarding.localSetup.docs.default': 'OpenCode 문서',
'onboarding.localSetup.actions.checking': '확인 중…',
'onboarding.localSetup.actions.checkAndContinue': '설치를 마쳤습니다. 확인 후 계속',
+ 'onboarding.localSetup.actions.checkNow': '지금 확인',
'onboarding.localSetup.helper.checkAndContinue': 'OpenCode CLI를 사용할 수 있으면 확인을 누르세요. 성공하면 자동으로 메인 화면으로 이동합니다.',
+ 'onboarding.localSetup.status.watching': 'OpenCode 대기 중',
+ 'onboarding.localSetup.status.autoContinue': '감지되면 자동으로 계속됩니다.',
'onboarding.localSetup.field.alreadyInstalled': '이미 설치되어 있나요? OpenCode CLI 경로 설정:',
+ 'onboarding.localSetup.advanced.title': '사용자 지정 바이너리 경로 설정',
+ 'onboarding.localSetup.troubleshoot.title': '문제가 있나요?',
'onboarding.localSetup.actions.browse': '찾아보기',
'onboarding.localSetup.actions.apply': '적용',
'onboarding.localSetup.helper.saveAndReload': 'OpenChamber 설정에 저장하고 OpenCode 구성을 다시 로드합니다.',
diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts
index e55f5144..b0faf229 100644
--- a/packages/ui/src/lib/i18n/messages/pt-BR.ts
+++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts
@@ -1917,12 +1917,18 @@ export const dict: Record = {
"onboarding.localSetup.windows.stepInstallWslSuffix": "no PowerShell.",
"onboarding.localSetup.windows.stepRunInstallInWsl": "Execute o comando de instalação a seguir dentro de seu terminal de WSL.",
"onboarding.localSetup.windows.stepSetBinaryPath": "Se o OpenChamber não detectar o OpenCode automaticamente, defina o caminho do executável a seguir.",
- "onboarding.localSetup.docs.windows": "Ver documentação de Windows + WSL",
- "onboarding.localSetup.docs.default": "Ver documentação",
+ "onboarding.localSetup.intro": "OpenCode é o coração do OpenChamber. Instale-o para começar.",
+ "onboarding.localSetup.docs.windows": "Documentação do OpenCode para Windows + WSL",
+ "onboarding.localSetup.docs.default": "Documentação do OpenCode",
"onboarding.localSetup.actions.checking": "Verificando...",
"onboarding.localSetup.actions.checkAndContinue": "Concluí a instalação, verificar e continuar",
+ "onboarding.localSetup.actions.checkNow": "Verificar agora",
"onboarding.localSetup.helper.checkAndContinue": "Clique para verificar se a CLI do OpenCode está disponível. Se a verificação funcionar, você entrará automaticamente na tela principal.",
+ "onboarding.localSetup.status.watching": "Aguardando OpenCode",
+ "onboarding.localSetup.status.autoContinue": "Continuaremos automaticamente assim que detectado.",
"onboarding.localSetup.field.alreadyInstalled": "Já está instalado? Defina o caminho da CLI do OpenCode:",
+ "onboarding.localSetup.advanced.title": "Definir um caminho personalizado do binário",
+ "onboarding.localSetup.troubleshoot.title": "Está com problemas?",
"onboarding.localSetup.actions.browse": "Explorar",
"onboarding.localSetup.actions.apply": "Aplicar",
"onboarding.localSetup.helper.saveAndReload": "Salve nas configurações do OpenChamber e recarregue configurações do OpenCode.",
diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts
index 1ea5e01e..d9f144e4 100644
--- a/packages/ui/src/lib/i18n/messages/uk.ts
+++ b/packages/ui/src/lib/i18n/messages/uk.ts
@@ -1917,12 +1917,18 @@ export const dict: Record = {
"onboarding.localSetup.windows.stepInstallWslSuffix": "в PowerShell.",
"onboarding.localSetup.windows.stepRunInstallInWsl": "Виконайте наведену нижче команду встановлення всередині терміналу WSL.",
"onboarding.localSetup.windows.stepSetBinaryPath": "Якщо OpenChamber не визначає OpenCode автоматично, укажіть шлях до виконуваного файла нижче.",
- "onboarding.localSetup.docs.windows": "Перегляньте документацію Windows + WSL",
- "onboarding.localSetup.docs.default": "Переглянути документацію",
+ "onboarding.localSetup.intro": "OpenCode — серце OpenChamber. Встановіть його, щоб почати.",
+ "onboarding.localSetup.docs.windows": "Документація OpenCode для Windows + WSL",
+ "onboarding.localSetup.docs.default": "Документація OpenCode",
"onboarding.localSetup.actions.checking": "Перевірка...",
"onboarding.localSetup.actions.checkAndContinue": "Я завершив встановлення, перевірте та продовжуйте",
+ "onboarding.localSetup.actions.checkNow": "Перевірити зараз",
"onboarding.localSetup.helper.checkAndContinue": "Натисніть, щоб перевірити, чи доступний OpenCode CLI. У разі успіху ви автоматично перейдете на головний екран.",
+ "onboarding.localSetup.status.watching": "Очікування OpenCode",
+ "onboarding.localSetup.status.autoContinue": "Ми автоматично продовжимо після виявлення.",
"onboarding.localSetup.field.alreadyInstalled": "Вже встановлено? Встановіть шлях OpenCode CLI:",
+ "onboarding.localSetup.advanced.title": "Вказати власний шлях до бінарного файлу",
+ "onboarding.localSetup.troubleshoot.title": "Виникли проблеми?",
"onboarding.localSetup.actions.browse": "Огляд",
"onboarding.localSetup.actions.apply": "Застосувати",
"onboarding.localSetup.helper.saveAndReload": "Зберігає налаштування в OpenChamber і перезавантажує конфігурацію OpenCode.",
diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts
index ffce19df..a154c846 100644
--- a/packages/ui/src/lib/i18n/messages/zh-CN.ts
+++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts
@@ -1917,12 +1917,18 @@ export const dict: Record = {
'onboarding.localSetup.windows.stepInstallWslSuffix': '安装 WSL。',
'onboarding.localSetup.windows.stepRunInstallInWsl': '在 WSL 终端中运行下方安装命令。',
'onboarding.localSetup.windows.stepSetBinaryPath': '如果 OpenChamber 未自动检测到 OpenCode,请在下方设置可执行文件路径。',
- 'onboarding.localSetup.docs.windows': '查看 Windows + WSL 文档',
- 'onboarding.localSetup.docs.default': '查看文档',
+ 'onboarding.localSetup.intro': 'OpenCode 是 OpenChamber 的核心,先安装它即可开始。',
+ 'onboarding.localSetup.docs.windows': 'OpenCode Windows + WSL 文档',
+ 'onboarding.localSetup.docs.default': 'OpenCode 文档',
'onboarding.localSetup.actions.checking': '检测中...',
'onboarding.localSetup.actions.checkAndContinue': '我已完成安装,检测并继续',
+ 'onboarding.localSetup.actions.checkNow': '立即检测',
'onboarding.localSetup.helper.checkAndContinue': '点击检测 OpenCode CLI 是否可用。成功后将自动进入主界面。',
+ 'onboarding.localSetup.status.watching': '正在等待 OpenCode',
+ 'onboarding.localSetup.status.autoContinue': '检测到后将自动继续。',
'onboarding.localSetup.field.alreadyInstalled': '已安装?设置 OpenCode CLI 路径:',
+ 'onboarding.localSetup.advanced.title': '设置自定义二进制路径',
+ 'onboarding.localSetup.troubleshoot.title': '遇到问题?',
'onboarding.localSetup.actions.browse': '浏览',
'onboarding.localSetup.actions.apply': '应用',
'onboarding.localSetup.helper.saveAndReload': '将保存到 OpenChamber 设置并重新加载 OpenCode 配置。',