refactor IPC (#341)

This commit is contained in:
Lingxuan Zuo
2026-03-08 11:54:49 +08:00
committed by GitHub
Unverified
parent c03d92e9a2
commit 3d804a9f5e
52 changed files with 3121 additions and 336 deletions

View File

@@ -8,6 +8,8 @@ import {
Moon,
Monitor,
RefreshCw,
ChevronDown,
ChevronRight,
Terminal,
ExternalLink,
Key,
@@ -28,6 +30,8 @@ import { useGatewayStore } from '@/stores/gateway';
import { useUpdateStore } from '@/stores/update';
import { ProvidersSettings } from '@/components/settings/ProvidersSettings';
import { UpdateSettings } from '@/components/settings/UpdateSettings';
import { invokeIpc, toUserMessage } from '@/lib/api-client';
import { trackUiEvent } from '@/lib/telemetry';
import { useTranslation } from 'react-i18next';
import { SUPPORTED_LANGUAGES } from '@/i18n';
type ControlUiInfo = {
@@ -36,6 +40,8 @@ type ControlUiInfo = {
port: number;
};
type GatewayTransportPreference = 'ws-first' | 'http-first' | 'ws-only' | 'http-only' | 'ipc-only';
export function Settings() {
const { t } = useTranslation('settings');
const {
@@ -51,12 +57,14 @@ export function Settings() {
proxyHttpsServer,
proxyAllServer,
proxyBypassRules,
gatewayTransportPreference,
setProxyEnabled,
setProxyServer,
setProxyHttpServer,
setProxyHttpsServer,
setProxyAllServer,
setProxyBypassRules,
setGatewayTransportPreference,
autoCheckUpdate,
setAutoCheckUpdate,
autoDownloadUpdate,
@@ -77,8 +85,17 @@ export function Settings() {
const [proxyAllServerDraft, setProxyAllServerDraft] = useState('');
const [proxyBypassRulesDraft, setProxyBypassRulesDraft] = useState('');
const [proxyEnabledDraft, setProxyEnabledDraft] = useState(false);
const [showAdvancedProxy, setShowAdvancedProxy] = useState(false);
const [savingProxy, setSavingProxy] = useState(false);
const transportOptions: Array<{ value: GatewayTransportPreference; labelKey: string; descKey: string }> = [
{ value: 'ws-first', labelKey: 'advanced.transport.options.wsFirst', descKey: 'advanced.transport.descriptions.wsFirst' },
{ value: 'http-first', labelKey: 'advanced.transport.options.httpFirst', descKey: 'advanced.transport.descriptions.httpFirst' },
{ value: 'ws-only', labelKey: 'advanced.transport.options.wsOnly', descKey: 'advanced.transport.descriptions.wsOnly' },
{ value: 'http-only', labelKey: 'advanced.transport.options.httpOnly', descKey: 'advanced.transport.descriptions.httpOnly' },
{ value: 'ipc-only', labelKey: 'advanced.transport.options.ipcOnly', descKey: 'advanced.transport.descriptions.ipcOnly' },
];
const isWindows = window.electron.platform === 'win32';
const showCliTools = true;
const [showLogs, setShowLogs] = useState(false);
@@ -86,7 +103,7 @@ export function Settings() {
const handleShowLogs = async () => {
try {
const logs = await window.electron.ipcRenderer.invoke('log:readFile', 100) as string;
const logs = await invokeIpc<string>('log:readFile', 100);
setLogContent(logs);
setShowLogs(true);
} catch {
@@ -97,9 +114,9 @@ export function Settings() {
const handleOpenLogDir = async () => {
try {
const logDir = await window.electron.ipcRenderer.invoke('log:getDir') as string;
const logDir = await invokeIpc<string>('log:getDir');
if (logDir) {
await window.electron.ipcRenderer.invoke('shell:showItemInFolder', logDir);
await invokeIpc('shell:showItemInFolder', logDir);
}
} catch {
// ignore
@@ -109,15 +126,16 @@ export function Settings() {
// Open developer console
const openDevConsole = async () => {
try {
const result = await window.electron.ipcRenderer.invoke('gateway:getControlUiUrl') as {
const result = await invokeIpc<{
success: boolean;
url?: string;
token?: string;
port?: number;
error?: string;
};
}>('gateway:getControlUiUrl');
if (result.success && result.url && result.token && typeof result.port === 'number') {
setControlUiInfo({ url: result.url, token: result.token, port: result.port });
trackUiEvent('settings.open_dev_console');
window.electron.openExternal(result.url);
} else {
console.error('Failed to get Dev Console URL:', result.error);
@@ -129,12 +147,12 @@ export function Settings() {
const refreshControlUiInfo = async () => {
try {
const result = await window.electron.ipcRenderer.invoke('gateway:getControlUiUrl') as {
const result = await invokeIpc<{
success: boolean;
url?: string;
token?: string;
port?: number;
};
}>('gateway:getControlUiUrl');
if (result.success && result.url && result.token && typeof result.port === 'number') {
setControlUiInfo({ url: result.url, token: result.token, port: result.port });
}
@@ -159,11 +177,11 @@ export function Settings() {
(async () => {
try {
const result = await window.electron.ipcRenderer.invoke('openclaw:getCliCommand') as {
const result = await invokeIpc<{
success: boolean;
command?: string;
error?: string;
};
}>('openclaw:getCliCommand');
if (cancelled) return;
if (result.success && result.command) {
setOpenclawCliCommand(result.command);
@@ -235,7 +253,7 @@ export function Settings() {
const normalizedHttpsServer = proxyHttpsServerDraft.trim();
const normalizedAllServer = proxyAllServerDraft.trim();
const normalizedBypassRules = proxyBypassRulesDraft.trim();
await window.electron.ipcRenderer.invoke('settings:setMany', {
await invokeIpc('settings:setMany', {
proxyEnabled: proxyEnabledDraft,
proxyServer: normalizedProxyServer,
proxyHttpServer: normalizedHttpServer,
@@ -252,8 +270,9 @@ export function Settings() {
setProxyEnabled(proxyEnabledDraft);
toast.success(t('gateway.proxySaved'));
trackUiEvent('settings.proxy_saved', { enabled: proxyEnabledDraft });
} catch (error) {
toast.error(`${t('gateway.proxySaveFailed')}: ${String(error)}`);
toast.error(`${t('gateway.proxySaveFailed')}: ${toUserMessage(error)}`);
} finally {
setSavingProxy(false);
}
@@ -438,7 +457,22 @@ export function Settings() {
</div>
{devModeUnlocked && (
<>
<div className="rounded-md border border-border/60 p-3">
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => setShowAdvancedProxy((prev) => !prev)}
>
{showAdvancedProxy ? (
<ChevronDown className="h-4 w-4 mr-2" />
) : (
<ChevronRight className="h-4 w-4 mr-2" />
)}
{showAdvancedProxy ? t('gateway.hideAdvancedProxy') : t('gateway.showAdvancedProxy')}
</Button>
{showAdvancedProxy && (
<div className="mt-3 space-y-4">
<div className="space-y-2">
<Label htmlFor="proxy-http-server">{t('gateway.proxyHttpServer')}</Label>
<Input
@@ -477,7 +511,9 @@ export function Settings() {
{t('gateway.proxyAllServerHelp')}
</p>
</div>
</>
</div>
)}
</div>
)}
<div className="space-y-2">
@@ -585,6 +621,34 @@ export function Settings() {
<CardDescription>{t('developer.description')}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div>
<Label>{t('advanced.transport.label')}</Label>
<p className="text-sm text-muted-foreground">
{t('advanced.transport.desc')}
</p>
</div>
<div className="grid gap-2">
{transportOptions.map((option) => (
<Button
key={option.value}
type="button"
variant={gatewayTransportPreference === option.value ? 'default' : 'outline'}
className="justify-between"
onClick={() => {
setGatewayTransportPreference(option.value);
toast.success(t('advanced.transport.saved'));
}}
>
<span>{t(option.labelKey)}</span>
<span className="text-xs opacity-80">{t(option.descKey)}</span>
</Button>
))}
</div>
</div>
<Separator />
<div className="space-y-2">
<Label>{t('developer.console')}</Label>
<p className="text-sm text-muted-foreground">