feat: add startup launch support (#1421)
Add launch-at-startup support across the Electron desktop app and the web CLI. Electron now supports macOS launch-at-login through the native login item API. Login launches start OpenChamber in the background without opening a window, while Dock activation, deep links, and second-instance launches still open or focus the normal app window. The desktop Settings UI now exposes a localized launch-at-login toggle in Desktop Network Access. The web CLI now includes `openchamber startup status|enable|disable`, backed by native user services: - macOS: launchd LaunchAgent - Linux: systemd --user service - Windows: Task Scheduler Startup services run `openchamber serve --foreground` so the OS service manager owns process lifetime and restarts. Foreground service updates now defer restarts to the service manager instead of spawning duplicate CLI restarts. Startup services snapshot useful environment variables by default so provider tokens, PATH, SSH agent settings, and OpenCode configuration survive login/reboot starts. The snapshot avoids shell/session-only state, uses systemd-compatible env quoting on Linux, and avoids unused env artifacts on macOS. Also adds localized docs for startup services and environment variables.
This commit is contained in:
committed by
GitHub
parent
e97bf0d9dc
commit
2014303bc0
@@ -3,7 +3,14 @@ import * as React from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { getDesktopLanAddress, isDesktopLocalOriginActive, isDesktopShell, restartDesktopApp } from '@/lib/desktop';
|
||||
import {
|
||||
getDesktopLanAddress,
|
||||
getDesktopLaunchAtLogin,
|
||||
isDesktopLocalOriginActive,
|
||||
isDesktopShell,
|
||||
restartDesktopApp,
|
||||
setDesktopLaunchAtLogin,
|
||||
} from '@/lib/desktop';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
export const DesktopNetworkSettings: React.FC = () => {
|
||||
@@ -15,6 +22,9 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
const [draftPassword, setDraftPassword] = React.useState('');
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
const [isSaving, setIsSaving] = React.useState(false);
|
||||
const [launchAtLoginSupported, setLaunchAtLoginSupported] = React.useState(false);
|
||||
const [launchAtLoginEnabled, setLaunchAtLoginEnabled] = React.useState(false);
|
||||
const [isSavingLaunchAtLogin, setIsSavingLaunchAtLogin] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const [lanAddress, setLanAddress] = React.useState<string | null>(null);
|
||||
|
||||
@@ -66,6 +76,27 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
};
|
||||
}, [isLocalDesktop, t]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isLocalDesktop) {
|
||||
setLaunchAtLoginSupported(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
const status = await getDesktopLaunchAtLogin();
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
setLaunchAtLoginSupported(status?.supported === true);
|
||||
setLaunchAtLoginEnabled(status?.enabled === true);
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [isLocalDesktop]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isLocalDesktop || !draftValue) {
|
||||
setLanAddress(null);
|
||||
@@ -101,6 +132,30 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
setDraftValue((current) => !current);
|
||||
}, []);
|
||||
|
||||
const handleLaunchAtLoginToggle = React.useCallback(async () => {
|
||||
if (!launchAtLoginSupported || isSavingLaunchAtLogin) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextValue = !launchAtLoginEnabled;
|
||||
setLaunchAtLoginEnabled(nextValue);
|
||||
setIsSavingLaunchAtLogin(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const status = await setDesktopLaunchAtLogin(nextValue);
|
||||
if (!status?.supported) {
|
||||
throw new Error(t('settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported'));
|
||||
}
|
||||
setLaunchAtLoginEnabled(status.enabled);
|
||||
} catch (cause) {
|
||||
setLaunchAtLoginEnabled(!nextValue);
|
||||
setError(cause instanceof Error ? cause.message : t('settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed'));
|
||||
} finally {
|
||||
setIsSavingLaunchAtLogin(false);
|
||||
}
|
||||
}, [isSavingLaunchAtLogin, launchAtLoginEnabled, launchAtLoginSupported, t]);
|
||||
|
||||
const handleSaveAndRestart = React.useCallback(async () => {
|
||||
if (!isDirty) {
|
||||
return;
|
||||
@@ -150,6 +205,34 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<section className="space-y-2 px-2 pb-2 pt-0">
|
||||
{launchAtLoginSupported ? (
|
||||
<div
|
||||
className="group flex cursor-pointer items-start gap-2 py-1.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleLaunchAtLoginToggle}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
handleLaunchAtLoginToggle();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={launchAtLoginEnabled}
|
||||
onChange={handleLaunchAtLoginToggle}
|
||||
ariaLabel={t('settings.openchamber.desktopNetwork.field.launchAtLoginAria')}
|
||||
disabled={isSavingLaunchAtLogin}
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="typography-ui-label text-foreground">{t('settings.openchamber.desktopNetwork.field.launchAtLogin')}</div>
|
||||
<div className="typography-micro text-muted-foreground/70">
|
||||
{t('settings.openchamber.desktopNetwork.field.launchAtLoginDescription')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-1 py-1.5">
|
||||
<label className="typography-ui-label text-foreground" htmlFor="desktop-ui-password">
|
||||
{t('settings.openchamber.desktopPassword.field.password')}
|
||||
|
||||
@@ -40,16 +40,16 @@ export const OpenChamberPage: React.FC<OpenChamberPageProps> = ({ section }) =>
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<DefaultsSettings />
|
||||
</div>
|
||||
{!isVSCode && (
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<OpenCodeCliSettings />
|
||||
</div>
|
||||
)}
|
||||
{showDesktopNetworkSettings && (
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<DesktopNetworkSettings />
|
||||
</div>
|
||||
)}
|
||||
{!isVSCode && (
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<OpenCodeCliSettings />
|
||||
</div>
|
||||
)}
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<SessionRetentionSettings />
|
||||
</div>
|
||||
@@ -139,16 +139,16 @@ const SessionsSectionContent: React.FC = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<DefaultsSettings />
|
||||
{!isVSCode && (
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<OpenCodeCliSettings />
|
||||
</div>
|
||||
)}
|
||||
{showDesktopNetworkSettings && (
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<DesktopNetworkSettings />
|
||||
</div>
|
||||
)}
|
||||
{!isVSCode && (
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<OpenCodeCliSettings />
|
||||
</div>
|
||||
)}
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<SessionRetentionSettings />
|
||||
</div>
|
||||
|
||||
@@ -225,6 +225,45 @@ export const invokeDesktop = async <T = unknown>(command: string, args?: Record<
|
||||
return tauri.core.invoke(command, args ?? {}) as Promise<T>;
|
||||
};
|
||||
|
||||
type LaunchAtLoginStatus = {
|
||||
supported: boolean;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export const getDesktopLaunchAtLogin = async (): Promise<LaunchAtLoginStatus | null> => {
|
||||
if (!canUseElectronDesktopIPC() || !isDesktopLocalOriginActive()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await invokeDesktop<LaunchAtLoginStatus>('desktop_get_launch_at_login');
|
||||
if (!result || typeof result.supported !== 'boolean' || typeof result.enabled !== 'boolean') {
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.warn('Failed to get launch at login status', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const setDesktopLaunchAtLogin = async (enabled: boolean): Promise<LaunchAtLoginStatus | null> => {
|
||||
if (!canUseElectronDesktopIPC() || !isDesktopLocalOriginActive()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await invokeDesktop<LaunchAtLoginStatus>('desktop_set_launch_at_login', { enabled });
|
||||
if (!result || typeof result.supported !== 'boolean' || typeof result.enabled !== 'boolean') {
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.warn('Failed to set launch at login status', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeOrigin = (raw: string): string | null => {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
@@ -758,6 +758,9 @@ export const settingsDict = {
|
||||
'settings.openchamber.sessionRetention.toast.failedArchiveCount': 'Failed to archive {count} session(s)',
|
||||
'settings.openchamber.sessionRetention.toast.failedDeleteCount': 'Failed to delete {count} session(s)',
|
||||
'settings.openchamber.desktopNetwork.title': 'Desktop Network Access',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Start OpenChamber at login',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Start OpenChamber when you log in',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Starts the app in the background without opening a window. Click the Dock icon to open it.',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessAria': 'Allow LAN access to desktop sidecar',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccess': 'Let other devices on your local network open this app',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessDescription': 'Restarts the app so phones, tablets, and other computers on your Wi-Fi can open it.',
|
||||
@@ -771,6 +774,8 @@ export const settingsDict = {
|
||||
'settings.openchamber.desktopNetwork.error.loadFailed': 'Failed to load desktop settings',
|
||||
'settings.openchamber.desktopNetwork.error.saveFailed': 'Failed to save desktop settings',
|
||||
'settings.openchamber.desktopNetwork.error.savedRestartFailed': 'Saved, but failed to restart app',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': 'Launch at login is not supported on this system',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': 'Failed to update launch at login setting',
|
||||
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
|
||||
'settings.openchamber.opencodeCli.tooltipPrefix': 'Optional absolute path to the',
|
||||
'settings.openchamber.opencodeCli.tooltipSuffix': 'binary.',
|
||||
|
||||
@@ -725,6 +725,9 @@ export const settingsDict = {
|
||||
"settings.openchamber.sessionRetention.toast.failedArchiveCount": "No se pudo archivar {count} sesión(es)",
|
||||
"settings.openchamber.sessionRetention.toast.failedDeleteCount": "No se pudo eliminar {count} sesión(es)",
|
||||
"settings.openchamber.desktopNetwork.title": "Acceso de red de escritorio",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Iniciar OpenChamber al iniciar sesión",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Iniciar OpenChamber al iniciar sesión",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia la app en segundo plano sin abrir una ventana. Haz clic en el icono del Dock para abrirla.",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccessAria": "Permitir acceso LAN al lado de escritorio",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccess": "Permitir que otros dispositivos en tu red local abran esta aplicación",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccessDescription": "Reinicia la aplicación para que los teléfonos, tablets y otros ordenadores en tu Wi-Fi puedan abrirla.",
|
||||
@@ -738,6 +741,8 @@ export const settingsDict = {
|
||||
"settings.openchamber.desktopNetwork.error.loadFailed": "No se pudo cargar la configuración de escritorio",
|
||||
"settings.openchamber.desktopNetwork.error.saveFailed": "No se pudo guardar la configuración de escritorio",
|
||||
"settings.openchamber.desktopNetwork.error.savedRestartFailed": "Guardado, pero no se pudo reiniciar la aplicación",
|
||||
"settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported": "El inicio al iniciar sesión no es compatible con este sistema",
|
||||
"settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed": "No se pudo actualizar la opción de inicio al iniciar sesión",
|
||||
"settings.openchamber.opencodeCli.title": "CLI de OpenCode",
|
||||
"settings.openchamber.opencodeCli.tooltipPrefix": "Ruta absoluta opcional al",
|
||||
"settings.openchamber.opencodeCli.tooltipSuffix": "ejecutable.",
|
||||
|
||||
@@ -725,6 +725,9 @@ export const settingsDict = {
|
||||
'settings.openchamber.sessionRetention.toast.failedArchiveCount': '세션 {count}개를 보관하지 못했습니다',
|
||||
'settings.openchamber.sessionRetention.toast.failedDeleteCount': '세션 {count}개를 삭제하지 못했습니다',
|
||||
'settings.openchamber.desktopNetwork.title': 'Desktop 네트워크 접속',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '로그인 시 OpenChamber 시작',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLogin': '로그인 시 OpenChamber 시작',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '창을 열지 않고 백그라운드에서 앱을 시작합니다. Dock 아이콘을 클릭하면 열립니다.',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessAria': 'desktop sidecar에 LAN 접속 허용',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccess': '로컬 네트워크의 다른 기기에서 이 앱 열기 허용',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessDescription': '휴대폰, 태블릿, Wi-Fi의 다른 컴퓨터에서 열 수 있도록 앱을 다시 시작합니다.',
|
||||
@@ -738,6 +741,8 @@ export const settingsDict = {
|
||||
'settings.openchamber.desktopNetwork.error.loadFailed': 'desktop 설정을 로드하지 못했습니다',
|
||||
'settings.openchamber.desktopNetwork.error.saveFailed': 'desktop 설정을 저장하지 못했습니다',
|
||||
'settings.openchamber.desktopNetwork.error.savedRestartFailed': '저장했지만 앱을 다시 시작하지 못했습니다',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': '이 시스템에서는 로그인 시 시작을 지원하지 않습니다',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': '로그인 시 시작 설정을 업데이트하지 못했습니다',
|
||||
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
|
||||
'settings.openchamber.opencodeCli.tooltipPrefix': '선택적 절대 경로:',
|
||||
'settings.openchamber.opencodeCli.tooltipSuffix': 'binary.',
|
||||
|
||||
@@ -643,12 +643,17 @@ export const settingsDict = {
|
||||
'settings.openchamber.defaults.summaryPrefix': 'Nowe sesje będą rozpoczynać się od:',
|
||||
'settings.openchamber.defaults.title': 'Domyślne ustawienia sesji',
|
||||
'settings.openchamber.desktopNetwork.actions.saveAndRestart': 'Zapisz + Zrestartuj',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': 'Uruchamianie przy logowaniu nie jest obsługiwane w tym systemie',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': 'Nie udało się zaktualizować ustawienia uruchamiania przy logowaniu',
|
||||
'settings.openchamber.desktopNetwork.error.loadFailed': 'Nie udało się załadować ustawień pulpitu',
|
||||
'settings.openchamber.desktopNetwork.error.saveFailed': 'Nie udało się zapisać ustawień pulpitu',
|
||||
'settings.openchamber.desktopNetwork.error.savedRestartFailed': 'Zapisano, ale restart aplikacji nie powiódł się',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccess': 'Pozwól innym urządzeniom w sieci lokalnej otwierać tę aplikację',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessAria': 'Zezwól na dostęp LAN do sidecara pulpitu',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessDescription': 'Restartuje aplikację, aby telefony, tablety i inne komputery w Twojej sieci Wi-Fi mogły ją otworzyć.',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Uruchamiaj OpenChamber przy logowaniu',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Uruchamiaj OpenChamber przy logowaniu',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Uruchamia aplikację w tle bez otwierania okna. Kliknij ikonę w Docku, aby ją otworzyć.',
|
||||
'settings.openchamber.desktopNetwork.field.warning': 'Ostrzeżenie: po włączeniu aplikacja jest dostępna dla każdego w tej samej sieci lokalnej.',
|
||||
'settings.openchamber.desktopPassword.field.password': 'Hasło UI pulpitu',
|
||||
'settings.openchamber.desktopPassword.field.passwordPlaceholder': 'Hasło nie jest wymagane',
|
||||
|
||||
@@ -725,6 +725,9 @@ export const settingsDict = {
|
||||
"settings.openchamber.sessionRetention.toast.failedArchiveCount": "Não foi possível arquivar {count} sessão(es)",
|
||||
"settings.openchamber.sessionRetention.toast.failedDeleteCount": "Não foi possível excluir {count} sessão(es)",
|
||||
"settings.openchamber.desktopNetwork.title": "Acesso de rede do desktop",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Iniciar o OpenChamber ao fazer login",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Iniciar o OpenChamber ao fazer login",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia o app em segundo plano sem abrir uma janela. Clique no ícone do Dock para abrir.",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccessAria": "Permitir acesso LAN ao lado de desktop",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccess": "Permitir que outros dispositivos na sua rede local abram este aplicativo",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccessDescription": "Reinicia o aplicativo para que os telefones, tablets e outros computadores em seu Wi-Fi possam abri-lo.",
|
||||
@@ -738,6 +741,8 @@ export const settingsDict = {
|
||||
"settings.openchamber.desktopNetwork.error.loadFailed": "Não foi possível carregar as configurações de desktop",
|
||||
"settings.openchamber.desktopNetwork.error.saveFailed": "Não foi possível salvar a configuração de desktop",
|
||||
"settings.openchamber.desktopNetwork.error.savedRestartFailed": "Salvo, mas não foi possível reiniciar o aplicativo",
|
||||
"settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported": "Iniciar ao fazer login não é compatível com este sistema",
|
||||
"settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed": "Não foi possível atualizar a opção de iniciar ao fazer login",
|
||||
"settings.openchamber.opencodeCli.title": "CLI do OpenCode",
|
||||
"settings.openchamber.opencodeCli.tooltipPrefix": "Caminho absoluto opcional para o",
|
||||
"settings.openchamber.opencodeCli.tooltipSuffix": "executável.",
|
||||
|
||||
@@ -725,6 +725,9 @@ export const settingsDict = {
|
||||
"settings.openchamber.sessionRetention.toast.failedArchiveCount": "Не вдалося заархівувати сесій: {count}",
|
||||
"settings.openchamber.sessionRetention.toast.failedDeleteCount": "Не вдалося видалити сесій: {count}",
|
||||
"settings.openchamber.desktopNetwork.title": "Мережевий доступ десктопного застосунку",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Запускати OpenChamber під час входу в систему",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Запускати OpenChamber під час входу в систему",
|
||||
"settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Запускає застосунок у фоні без відкриття вікна. Щоб відкрити його, натисніть іконку в Dock.",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccessAria": "Дозволити LAN-доступ до десктопного застосунку",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccess": "Дозволити іншим пристроям у локальній мережі відкривати цей застосунок",
|
||||
"settings.openchamber.desktopNetwork.field.allowLanAccessDescription": "Перезапускає застосунок, щоб телефони, планшети та інші комп’ютери в мережі Wi-Fi могли його відкрити.",
|
||||
@@ -738,6 +741,8 @@ export const settingsDict = {
|
||||
"settings.openchamber.desktopNetwork.error.loadFailed": "Не вдалося завантажити налаштування десктопного застосунку",
|
||||
"settings.openchamber.desktopNetwork.error.saveFailed": "Не вдалося зберегти налаштування десктопного застосунку",
|
||||
"settings.openchamber.desktopNetwork.error.savedRestartFailed": "Збережено, але не вдалося перезапустити застосунок",
|
||||
"settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported": "Запуск під час входу в систему не підтримується на цій системі",
|
||||
"settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed": "Не вдалося оновити налаштування запуску під час входу в систему",
|
||||
"settings.openchamber.opencodeCli.title": "OpenCode CLI",
|
||||
"settings.openchamber.opencodeCli.tooltipPrefix": "Додатковий абсолютний шлях до",
|
||||
"settings.openchamber.opencodeCli.tooltipSuffix": "бінарного файлу.",
|
||||
|
||||
@@ -725,6 +725,9 @@ export const settingsDict = {
|
||||
'settings.openchamber.sessionRetention.toast.failedArchiveCount': '归档 {count} 个会话失败',
|
||||
'settings.openchamber.sessionRetention.toast.failedDeleteCount': '删除 {count} 个会话失败',
|
||||
'settings.openchamber.desktopNetwork.title': '桌面端网络访问',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '登录时启动 OpenChamber',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLogin': '登录时启动 OpenChamber',
|
||||
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '在后台启动应用且不打开窗口。点击 Dock 图标即可打开。',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessAria': '允许桌面 sidecar 局域网访问',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccess': '允许你本地网络中的其他设备打开此应用',
|
||||
'settings.openchamber.desktopNetwork.field.allowLanAccessDescription': '会重启应用,以便手机、平板和同一 Wi‑Fi 下的其他电脑访问。',
|
||||
@@ -738,6 +741,8 @@ export const settingsDict = {
|
||||
'settings.openchamber.desktopNetwork.error.loadFailed': '加载桌面设置失败',
|
||||
'settings.openchamber.desktopNetwork.error.saveFailed': '保存桌面设置失败',
|
||||
'settings.openchamber.desktopNetwork.error.savedRestartFailed': '已保存,但重启应用失败',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginUnsupported': '此系统不支持登录时启动',
|
||||
'settings.openchamber.desktopNetwork.error.launchAtLoginSaveFailed': '更新登录时启动设置失败',
|
||||
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
|
||||
'settings.openchamber.opencodeCli.tooltipPrefix': '可选的',
|
||||
'settings.openchamber.opencodeCli.tooltipSuffix': '二进制绝对路径。',
|
||||
|
||||
Reference in New Issue
Block a user