Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 102 additions & 41 deletions src/components/Settings/General/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========

import { Button } from '@/components/ui/button';
import { DsText } from '@/components/ui/ds-text';
import { Input } from '@/components/ui/input';
import { LocaleEnum, switchLanguage } from '@/i18n';
import { SITE_URL } from '@/lib';
Expand Down Expand Up @@ -67,9 +68,16 @@ export default function SettingGeneral({

// Proxy configuration state
const [proxyUrl, setProxyUrl] = useState('');
const [savedProxyUrl, setSavedProxyUrl] = useState('');
const [proxyLoading, setProxyLoading] = useState(true);
const [isProxySaving, setIsProxySaving] = useState(false);
const [proxyNeedsRestart, setProxyNeedsRestart] = useState(false);
// One action button drives the field: it saves while the input differs from
// the persisted baseline, and clears the saved proxy once the two match.
// Compared untrimmed so a whitespace-only edit still reads as "Save" — the
// destructive Reset must never sit under a field the user has just typed in.
const isProxyDirty = proxyUrl !== savedProxyUrl;
const canClearSavedProxy = savedProxyUrl.trim() !== '';

const languageList = [
{
Expand Down Expand Up @@ -124,9 +132,9 @@ export default function SettingGeneral({
try {
if (host?.electronAPI?.readGlobalEnv) {
const result = await host.electronAPI.readGlobalEnv('HTTP_PROXY');
if (result?.value) {
setProxyUrl(result.value);
}
const loadedProxyUrl = result?.value ?? '';
setProxyUrl(loadedProxyUrl);
setSavedProxyUrl(loadedProxyUrl);
}
} catch (_error) {
console.log('No proxy configured');
Expand All @@ -137,6 +145,21 @@ export default function SettingGeneral({
void loadProxyConfig();
}, [host]);

// The restart affordance lives in the toast rather than on the action button,
// so the button is only ever Save or Reset. The note under the input repeats
// it for anyone who dismisses or misses the toast.
const notifyProxyRestartRequired = (message: string) => {
const restartApp = host?.electronAPI?.restartApp;
toast.success(message, {
action: restartApp
? {
label: t('setting.restart-to-apply'),
onClick: () => restartApp(),
}
: undefined,
});
};

// Save proxy configuration
const handleSaveProxy = async () => {
if (!authStore.email) {
Expand Down Expand Up @@ -182,8 +205,10 @@ export default function SettingGeneral({
);
if (!result?.success) throw new Error('envRemove returned no success');
}
setProxyUrl(trimmed);
setSavedProxyUrl(trimmed);
setProxyNeedsRestart(true);
toast.success(t('setting.proxy-saved-restart-required'));
notifyProxyRestartRequired(t('setting.proxy-saved-restart-required'));
} catch (error) {
console.error('Failed to save proxy:', error);
toast.error(t('setting.proxy-save-failed'));
Expand All @@ -192,6 +217,33 @@ export default function SettingGeneral({
}
};

// Clear the saved proxy. Only reachable once the input matches the persisted
// baseline, so it never discards an edit the user can still see.
const handleResetProxy = async () => {
if (!authStore.email || !host?.electronAPI?.envRemove) {
toast.error(t('setting.proxy-reset-failed'));
return;
}

setIsProxySaving(true);
try {
const result = await host.electronAPI.envRemove(
authStore.email,
'HTTP_PROXY'
);
if (!result?.success) throw new Error('envRemove returned no success');
setProxyUrl('');
setSavedProxyUrl('');
setProxyNeedsRestart(true);
notifyProxyRestartRequired(t('setting.proxy-cleared-restart-required'));
} catch (error) {
console.error('Failed to reset proxy:', error);
toast.error(t('setting.proxy-reset-failed'));
} finally {
setIsProxySaving(false);
}
};

return (
<SettingsSectionPage>
<SettingsRowGroup>
Expand Down Expand Up @@ -295,45 +347,54 @@ export default function SettingGeneral({
description={t('setting.network-proxy-description')}
actionClassName="w-[280px]"
action={
<Input
placeholder={t('setting.proxy-placeholder')}
value={proxyUrl}
onChange={(e) => {
setProxyUrl(e.target.value);
setProxyNeedsRestart(false);
}}
className="w-[280px]"
size="default"
disabled={proxyLoading}
note={
proxyNeedsRestart
? t('setting.proxy-restart-hint')
: undefined
}
trailingButton={
<Button
variant={proxyNeedsRestart ? 'outline' : 'primary'}
size="sm"
buttonRadius="full"
onClick={
proxyNeedsRestart
? () => host?.electronAPI?.restartApp()
: handleSaveProxy
}
disabled={
proxyLoading || (!proxyNeedsRestart && isProxySaving)
}
>
{proxyLoading
? t('setting.loading')
: proxyNeedsRestart
? t('setting.restart-to-apply')
<div className="flex w-full flex-col gap-ds-stack-related">
<Input
placeholder={t('setting.proxy-placeholder')}
value={proxyUrl}
onChange={(e) => {
setProxyUrl(e.target.value);
}}
size="default"
disabled={proxyLoading}
// Reserve room for the nested action so a long proxy URL
// scrolls behind the field edge instead of under the button.
className="pr-24"
trailingButton={
<Button
variant={isProxyDirty ? 'primary' : 'outline'}
size="sm"
buttonRadius="full"
onClick={
isProxyDirty ? handleSaveProxy : handleResetProxy
}
disabled={
proxyLoading ||
isProxySaving ||
(!isProxyDirty && !canClearSavedProxy)
}
>
{proxyLoading
? t('setting.loading')
: isProxySaving
? t('setting.saving')
: t('setting.save')}
</Button>
}
/>
: isProxyDirty
? t('setting.save')
: t('setting.reset')}
</Button>
}
/>
{/* Rendered here rather than via Input's `note` so it can carry
the error tone without marking the field itself invalid. */}
{proxyNeedsRestart ? (
<DsText
as="span"
role="meta"
className="text-ds-text-status-error-strong-default"
>
{t('setting.proxy-restart-hint')}
</DsText>
) : null}
</div>
}
/>
)}
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/ar/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "تم حفظ تكوين الوكيل. أعد تشغيل التطبيق لتطبيق التغييرات.",
"proxy-save-failed": "فشل حفظ تكوين الوكيل.",
"proxy-cleared-restart-required": "تم مسح تكوين الوكيل. أعد تشغيل التطبيق لتطبيق التغييرات.",
"proxy-reset-failed": "فشل إعادة تعيين تكوين الوكيل.",
"proxy-invalid-url": "عنوان URL للوكيل غير صالح. يجب أن يبدأ بـ http:// أو https:// أو socks4:// أو socks5://.",
"proxy-restart-hint": "يجب إعادة التشغيل لتطبيق تغييرات الوكيل.",
"cloud-not-available-in-local-proxy": "إصدار السحابة غير متاح في وضع الوكيل المحلي",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/de/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "Proxy-Konfiguration gespeichert. Starten Sie die App neu, um die Änderungen anzuwenden.",
"proxy-save-failed": "Proxy-Konfiguration konnte nicht gespeichert werden.",
"proxy-cleared-restart-required": "Proxy-Konfiguration gelöscht. Starten Sie die App neu, um die Änderungen anzuwenden.",
"proxy-reset-failed": "Proxy-Konfiguration konnte nicht zurückgesetzt werden.",
"proxy-invalid-url": "Ungültige Proxy-URL. Muss mit http://, https://, socks4:// oder socks5:// beginnen.",
"proxy-restart-hint": "Neustart erforderlich, um Proxy-Änderungen anzuwenden.",
"cloud-not-available-in-local-proxy": "Cloud-Version ist im lokalen Proxy-Modus nicht verfügbar",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/en-us/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "Proxy configuration saved. Restart the app to apply changes.",
"proxy-save-failed": "Failed to save proxy configuration.",
"proxy-cleared-restart-required": "Proxy configuration cleared. Restart the app to apply changes.",
"proxy-reset-failed": "Failed to reset proxy configuration.",
"proxy-invalid-url": "Invalid proxy URL. Must start with http://, https://, socks4://, or socks5://.",
"proxy-restart-hint": "Restart required to apply proxy changes.",
"cloud-not-available-in-local-proxy": "Cloud version is not available in local proxy mode",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/es/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "Configuración de proxy guardada. Reinicie la aplicación para aplicar los cambios.",
"proxy-save-failed": "Error al guardar la configuración del proxy.",
"proxy-cleared-restart-required": "Configuración de proxy borrada. Reinicie la aplicación para aplicar los cambios.",
"proxy-reset-failed": "Error al restablecer la configuración del proxy.",
"proxy-invalid-url": "URL de proxy no válida. Debe comenzar con http://, https://, socks4:// o socks5://.",
"proxy-restart-hint": "Es necesario reiniciar para aplicar los cambios del proxy.",
"cloud-not-available-in-local-proxy": "La versión en la nube no está disponible en modo proxy local",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/fr/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "Configuration du proxy enregistrée. Redémarrez l'application pour appliquer les modifications.",
"proxy-save-failed": "Échec de l'enregistrement de la configuration du proxy.",
"proxy-cleared-restart-required": "Configuration du proxy effacée. Redémarrez l'application pour appliquer les modifications.",
"proxy-reset-failed": "Échec de la réinitialisation de la configuration du proxy.",
"proxy-invalid-url": "URL de proxy invalide. Doit commencer par http://, https://, socks4:// ou socks5://.",
"proxy-restart-hint": "Redémarrage nécessaire pour appliquer les modifications du proxy.",
"cloud-not-available-in-local-proxy": "La version cloud n'est pas disponible en mode proxy local",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/it/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "Configurazione proxy salvata. Riavvia l'app per applicare le modifiche.",
"proxy-save-failed": "Impossibile salvare la configurazione del proxy.",
"proxy-cleared-restart-required": "Configurazione proxy cancellata. Riavvia l'app per applicare le modifiche.",
"proxy-reset-failed": "Impossibile reimpostare la configurazione del proxy.",
"proxy-invalid-url": "URL proxy non valido. Deve iniziare con http://, https://, socks4:// o socks5://.",
"proxy-restart-hint": "Riavvio necessario per applicare le modifiche del proxy.",
"cloud-not-available-in-local-proxy": "La versione cloud non è disponibile in modalità proxy locale",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/ja/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "プロキシ設定が保存されました。変更を適用するにはアプリを再起動してください。",
"proxy-save-failed": "プロキシ設定の保存に失敗しました。",
"proxy-cleared-restart-required": "プロキシ設定が消去されました。変更を適用するにはアプリを再起動してください。",
"proxy-reset-failed": "プロキシ設定のリセットに失敗しました。",
"proxy-invalid-url": "無効なプロキシURLです。http://、https://、socks4://、またはsocks5://で始まる必要があります。",
"proxy-restart-hint": "プロキシの変更を適用するには再起動が必要です。",
"cloud-not-available-in-local-proxy": "ローカルプロキシモードではクラウド版は利用できません",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/ko/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "프록시 설정이 저장되었습니다. 변경 사항을 적용하려면 앱을 다시 시작하세요.",
"proxy-save-failed": "프록시 설정 저장에 실패했습니다.",
"proxy-cleared-restart-required": "프록시 설정이 지워졌습니다. 변경 사항을 적용하려면 앱을 다시 시작하세요.",
"proxy-reset-failed": "프록시 설정 초기화에 실패했습니다.",
"proxy-invalid-url": "잘못된 프록시 URL입니다. http://, https://, socks4://, 또는 socks5://로 시작해야 합니다.",
"proxy-restart-hint": "프록시 변경 사항을 적용하려면 다시 시작해야 합니다.",
"cloud-not-available-in-local-proxy": "로컬 프록시 모드에서는 클라우드 버전을 사용할 수 없습니다",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/ru/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "Конфигурация прокси сохранена. Перезапустите приложение для применения изменений.",
"proxy-save-failed": "Не удалось сохранить конфигурацию прокси.",
"proxy-cleared-restart-required": "Конфигурация прокси очищена. Перезапустите приложение для применения изменений.",
"proxy-reset-failed": "Не удалось сбросить конфигурацию прокси.",
"proxy-invalid-url": "Недопустимый URL прокси. Должен начинаться с http://, https://, socks4:// или socks5://.",
"proxy-restart-hint": "Для применения изменений прокси требуется перезапуск.",
"cloud-not-available-in-local-proxy": "Облачная версия недоступна в режиме локального прокси",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-Hans/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "代理配置已保存。请重启应用以应用更改。",
"proxy-save-failed": "保存代理配置失败。",
"proxy-cleared-restart-required": "代理配置已清除。请重启应用以应用更改。",
"proxy-reset-failed": "重置代理配置失败。",
"proxy-invalid-url": "无效的代理 URL。必须以 http://、https://、socks4:// 或 socks5:// 开头。",
"proxy-restart-hint": "需要重启应用以应用代理更改。",

Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-Hant/setting.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@
"proxy-placeholder": "http://127.0.0.1:7890",
"proxy-saved-restart-required": "代理設定已儲存。請重新啟動應用程式以套用變更。",
"proxy-save-failed": "儲存代理設定失敗。",
"proxy-cleared-restart-required": "代理設定已清除。請重新啟動應用程式以套用變更。",
"proxy-reset-failed": "重設代理設定失敗。",
"proxy-invalid-url": "無效的代理 URL。必須以 http://、https://、socks4:// 或 socks5:// 開頭。",
"proxy-restart-hint": "需要重新啟動應用程式以套用代理變更。",
"preferred-ide": "偏好 IDE",
Expand Down
Loading
Loading