From 7fc22bc69baa2f097c852ddef76c9a6911d10789 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 5 May 2026 00:41:30 +0300 Subject: [PATCH] feat: auto-detect OpenCode CLI on onboarding screen Polls every 2.5s and transitions automatically once the CLI is reachable Advanced settings and troubleshooting moved into collapsible accordions New i18n keys added for status indicator and section titles across 6 locales --- .../components/onboarding/ChooserScreen.tsx | 476 +++++++++--------- packages/ui/src/lib/i18n/messages/en.ts | 12 +- packages/ui/src/lib/i18n/messages/es.ts | 10 +- packages/ui/src/lib/i18n/messages/ko.ts | 10 +- packages/ui/src/lib/i18n/messages/pt-BR.ts | 10 +- packages/ui/src/lib/i18n/messages/uk.ts | 10 +- packages/ui/src/lib/i18n/messages/zh-CN.ts | 10 +- 7 files changed, 298 insertions(+), 240 deletions(-) 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')}
-
    -
  1. {t('onboarding.localSetup.windows.stepInstallWsl')} wsl --install {t('onboarding.localSetup.windows.stepInstallWslSuffix')}
  2. -
  3. {t('onboarding.localSetup.windows.stepRunInstallInWsl')}
  4. -
  5. {t('onboarding.localSetup.windows.stepSetBinaryPath')}
  6. -
-
- )} +
+ setActiveTab('local')} + showBackButton={false} + onSwitchToLocal={() => setActiveTab('local')} + /> +
+ ) : null} -
-
- {copied ? ( -
- - {t('onboarding.common.status.copiedToClipboard')} -
- ) : ( - - )} -
+ {showLocal && ( +
+ {platform === 'windows' && ( +
+
{t('onboarding.localSetup.windows.title')}
+
    +
  1. {t('onboarding.localSetup.windows.stepInstallWsl')} wsl --install {t('onboarding.localSetup.windows.stepInstallWslSuffix')}
  2. +
  3. {t('onboarding.localSetup.windows.stepRunInstallInWsl')}
  4. +
  5. {t('onboarding.localSetup.windows.stepSetBinaryPath')}
  6. +
+
+ )} + +

+ {t('onboarding.localSetup.intro')} +

+ +
+ {copied ? ( +
+ + {t('onboarding.common.status.copiedToClipboard')}
+ ) : ( + + )} +
- - {platform === 'windows' ? t('onboarding.localSetup.docs.windows') : t('onboarding.localSetup.docs.default')} - - +
+ + {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 配置。',