feat(settings): add configurable default terminal for CLI agents
- Add default terminal selector under Advanced Settings > Software Settings - Detect available terminals across macOS, Windows, and Linux - Persist and migrate the default-terminal configuration - Apply the selected terminal when launching CLI agents - Add localized labels and regression tests Fixes #179
This commit is contained in:
1 parent
270ad2869c
commit
79b50b7a2b
10 files changed
+288
-26
No files matched your search
@@ -372,6 +372,9 @@ export const en: Record<MessageKey, string> = {
|
||||
'config.software.startCoreOnLaunchDescription': 'Automatically start the CPA core whenever EasyCLIProxyAPI launches',
|
||||
'config.software.silentStart': 'Silent Launch',
|
||||
'config.software.silentStartDescription': 'Start without showing the main window and run directly in the system tray',
|
||||
'config.software.defaultTerminal': 'Default terminal',
|
||||
'config.software.defaultTerminalDescription': 'Terminal program used when launching CLI agents',
|
||||
'config.software.terminal.auto': 'Automatic',
|
||||
'config.software.closeBehavior': 'Close behavior',
|
||||
'config.software.closeBehaviorDescription': 'Choose whether closing the window exits the app or keeps it running in the system tray',
|
||||
'config.software.behavior.ask': 'Ask every time',
|
||||
|
||||
@@ -370,6 +370,9 @@ export const zhCN = {
|
||||
'config.software.startCoreOnLaunchDescription': '每次启动 EasyCLIProxyAPI 时自动启动 CPA 内核',
|
||||
'config.software.silentStart': '静默启动',
|
||||
'config.software.silentStartDescription': '启动应用时不显示主窗口,直接在系统托盘运行',
|
||||
'config.software.defaultTerminal': '默认终端',
|
||||
'config.software.defaultTerminalDescription': '启动 CLI 智能体时使用的终端程序',
|
||||
'config.software.terminal.auto': '自动选择',
|
||||
'config.software.closeBehavior': '关闭行为',
|
||||
'config.software.closeBehaviorDescription': '决定关闭窗口时是退出程序还是继续在系统托盘运行',
|
||||
'config.software.behavior.ask': '每次询问',
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ShieldCheck,
|
||||
Sparkles,
|
||||
Power,
|
||||
Terminal,
|
||||
Trash2,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
@@ -79,11 +80,18 @@ type DraftRefreshMode = 'replace' | 'preserve';
|
||||
|
||||
type NetworkDraftDirty = Record<NetworkDraftField, boolean>;
|
||||
|
||||
type AgentTerminalOption = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
type SoftwareSettings = {
|
||||
closeBehavior: CloseBehavior;
|
||||
autostartEnabled: boolean;
|
||||
startCoreOnLaunch: boolean;
|
||||
silentStartEnabled: boolean;
|
||||
defaultTerminal: string;
|
||||
availableTerminals: AgentTerminalOption[];
|
||||
};
|
||||
|
||||
type CoreTlsSettings = {
|
||||
@@ -119,6 +127,7 @@ export function ConfigPanelPage() {
|
||||
const [softwareAutostartDraft, setSoftwareAutostartDraft] = useState(false);
|
||||
const [softwareStartCoreDraft, setSoftwareStartCoreDraft] = useState(true);
|
||||
const [softwareSilentStartDraft, setSoftwareSilentStartDraft] = useState(false);
|
||||
const [softwareDefaultTerminalDraft, setSoftwareDefaultTerminalDraft] = useState('auto');
|
||||
const [softwareSavedStatusVisible, setSoftwareSavedStatusVisible] = useState(false);
|
||||
const [tlsSettings, setTlsSettings] = useState<CoreTlsSettings | null>(null);
|
||||
const [tlsSettingsLoading, setTlsSettingsLoading] = useState(true);
|
||||
@@ -264,6 +273,7 @@ export function ConfigPanelPage() {
|
||||
setSoftwareAutostartDraft(result.autostartEnabled);
|
||||
setSoftwareStartCoreDraft(result.startCoreOnLaunch);
|
||||
setSoftwareSilentStartDraft(result.silentStartEnabled);
|
||||
setSoftwareDefaultTerminalDraft(result.defaultTerminal);
|
||||
} catch (error) {
|
||||
setSoftwareSettings(null);
|
||||
showNotice(t('config.error.saveFailed', { error: String(error) }), 'error');
|
||||
@@ -690,6 +700,7 @@ export function ConfigPanelPage() {
|
||||
&& softwareAutostartDraft === softwareSettings.autostartEnabled
|
||||
&& softwareStartCoreDraft === softwareSettings.startCoreOnLaunch
|
||||
&& softwareSilentStartDraft === softwareSettings.silentStartEnabled
|
||||
&& softwareDefaultTerminalDraft === softwareSettings.defaultTerminal
|
||||
) return;
|
||||
|
||||
setBusyAction('software');
|
||||
@@ -700,6 +711,7 @@ export function ConfigPanelPage() {
|
||||
autostartEnabled: softwareAutostartDraft,
|
||||
startCoreOnLaunch: softwareStartCoreDraft,
|
||||
silentStartEnabled: softwareSilentStartDraft,
|
||||
defaultTerminal: softwareDefaultTerminalDraft,
|
||||
},
|
||||
});
|
||||
setSoftwareSettings(result);
|
||||
@@ -707,6 +719,7 @@ export function ConfigPanelPage() {
|
||||
setSoftwareAutostartDraft(result.autostartEnabled);
|
||||
setSoftwareStartCoreDraft(result.startCoreOnLaunch);
|
||||
setSoftwareSilentStartDraft(result.silentStartEnabled);
|
||||
setSoftwareDefaultTerminalDraft(result.defaultTerminal);
|
||||
setSoftwareSavedStatusVisible(true);
|
||||
showNotice(t('config.notice.softwareUpdated'), 'success');
|
||||
} catch (error) {
|
||||
@@ -714,6 +727,7 @@ export function ConfigPanelPage() {
|
||||
setSoftwareAutostartDraft(softwareSettings.autostartEnabled);
|
||||
setSoftwareStartCoreDraft(softwareSettings.startCoreOnLaunch);
|
||||
setSoftwareSilentStartDraft(softwareSettings.silentStartEnabled);
|
||||
setSoftwareDefaultTerminalDraft(softwareSettings.defaultTerminal);
|
||||
setSoftwareSavedStatusVisible(false);
|
||||
showNotice(t('config.error.saveFailed', { error: String(error) }), 'error');
|
||||
void loadSoftwareSettings();
|
||||
@@ -746,10 +760,13 @@ export function ConfigPanelPage() {
|
||||
&& softwareSilentStartDraft !== softwareSettings.silentStartEnabled;
|
||||
const softwareStartCoreDirty = softwareSettings !== null
|
||||
&& softwareStartCoreDraft !== softwareSettings.startCoreOnLaunch;
|
||||
const softwareDefaultTerminalDirty = softwareSettings !== null
|
||||
&& softwareDefaultTerminalDraft !== softwareSettings.defaultTerminal;
|
||||
const softwareSettingsDirty = softwareCloseBehaviorDirty
|
||||
|| softwareAutostartDirty
|
||||
|| softwareStartCoreDirty
|
||||
|| softwareSilentStartDirty;
|
||||
|| softwareSilentStartDirty
|
||||
|| softwareDefaultTerminalDirty;
|
||||
const softwareStatusLabel = softwareSettingsLoading
|
||||
? t('common.loading')
|
||||
: softwareSettings === null
|
||||
@@ -1660,6 +1677,35 @@ export function ConfigPanelPage() {
|
||||
<span className="switch-track" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="config-software-setting-row config-software-close-row">
|
||||
<div className="config-software-setting-copy">
|
||||
<span className="config-software-setting-icon" aria-hidden="true">
|
||||
<Terminal size={18} />
|
||||
</span>
|
||||
<div>
|
||||
<strong>{t('config.software.defaultTerminal')}</strong>
|
||||
<small>{t('config.software.defaultTerminalDescription')}</small>
|
||||
</div>
|
||||
</div>
|
||||
<label className="config-software-select">
|
||||
<span className="sr-only">{t('config.software.defaultTerminal')}</span>
|
||||
<select
|
||||
className="config-network-input"
|
||||
value={softwareDefaultTerminalDraft}
|
||||
disabled={softwareSettingsLoading || softwareSettings === null || busyAction !== null}
|
||||
onChange={(event) => {
|
||||
setSoftwareSavedStatusVisible(false);
|
||||
setSoftwareDefaultTerminalDraft(event.currentTarget.value);
|
||||
}}
|
||||
>
|
||||
{(softwareSettings?.availableTerminals ?? []).map((terminal) => (
|
||||
<option value={terminal.id} key={terminal.id}>
|
||||
{terminal.id === 'auto' ? t('config.software.terminal.auto') : terminal.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div className="config-software-setting-row config-software-close-row">
|
||||
<div className="config-software-setting-copy">
|
||||
<span className="config-software-setting-icon" aria-hidden="true">
|
||||
|
||||
Reference in new issue
Block a user