fix: improve mode toggle robustness and add i18n support

- Make mode detection more robust (handle ::1, 0.0.0.0)
- Add getProxyPort() to parse port from ANTHROPIC_BASE_URL dynamically
- Add i18n translation keys for mode toggle in all 5 languages
- Update settings.html to use translation keys and dynamic port

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Badri Narayanan S
2026-02-01 16:47:30 +05:30
parent cf2af0ba4b
commit f80e60668c
8 changed files with 83 additions and 16 deletions

View File

@@ -16,6 +16,20 @@ window.Components.claudeConfig = () => ({
currentMode: 'proxy', // 'proxy' or 'paid' currentMode: 'proxy', // 'proxy' or 'paid'
modeLoading: false, modeLoading: false,
/**
* Extract port from ANTHROPIC_BASE_URL for display
* @returns {string} Port number or '8080' as fallback
*/
getProxyPort() {
const baseUrl = this.config?.env?.ANTHROPIC_BASE_URL || '';
try {
const url = new URL(baseUrl);
return url.port || '8080';
} catch {
return '8080';
}
},
// Presets state // Presets state
presets: [], presets: [],
selectedPresetName: '', selectedPresetName: '',

View File

@@ -364,4 +364,14 @@ window.translations.en = {
mustBeAtMost: "{fieldName} must be at most {max}", mustBeAtMost: "{fieldName} must be at most {max}",
cannotBeEmpty: "{fieldName} cannot be empty", cannotBeEmpty: "{fieldName} cannot be empty",
mustBeTrueOrFalse: "Value must be true or false", mustBeTrueOrFalse: "Value must be true or false",
// Mode Toggle (Proxy/Paid)
connectionMode: "Connection Mode",
proxyMode: "Proxy Mode",
paidMode: "Paid Mode",
usingLocalProxy: "Using local proxy server (localhost:{port})",
usingOfficialApi: "Using official Anthropic API (requires subscription)",
paidModeTitle: "Claude CLI is using the official Anthropic API",
paidModeDesc: "All proxy configuration has been removed. Claude CLI uses your Anthropic subscription directly.",
paidModeHint: "Switch to Proxy mode to configure model routing and presets.",
modeToggleFailed: "Failed to switch mode",
}; };

View File

@@ -409,4 +409,14 @@ window.translations.id = {
strategyUpdated: "Strategi diubah ke: {strategy}", strategyUpdated: "Strategi diubah ke: {strategy}",
failedToUpdateStrategy: "Gagal memperbarui strategi", failedToUpdateStrategy: "Gagal memperbarui strategi",
invalidStrategy: "Strategi tidak valid dipilih", invalidStrategy: "Strategi tidak valid dipilih",
// Mode Toggle (Proxy/Paid)
connectionMode: "Mode Koneksi",
proxyMode: "Mode Proxy",
paidMode: "Mode Berbayar",
usingLocalProxy: "Menggunakan server proxy lokal (localhost:{port})",
usingOfficialApi: "Menggunakan API resmi Anthropic (memerlukan langganan)",
paidModeTitle: "Claude CLI menggunakan API resmi Anthropic",
paidModeDesc: "Semua konfigurasi proxy telah dihapus. Claude CLI menggunakan langganan Anthropic Anda secara langsung.",
paidModeHint: "Beralih ke mode Proxy untuk mengonfigurasi routing model dan preset.",
modeToggleFailed: "Gagal beralih mode",
}; };

View File

@@ -305,4 +305,14 @@ window.translations.pt = {
strategyUpdated: "Estratégia atualizada para: {strategy}", strategyUpdated: "Estratégia atualizada para: {strategy}",
failedToUpdateStrategy: "Falha ao atualizar estratégia", failedToUpdateStrategy: "Falha ao atualizar estratégia",
invalidStrategy: "Estratégia inválida selecionada", invalidStrategy: "Estratégia inválida selecionada",
// Mode Toggle (Proxy/Paid)
connectionMode: "Modo de Conexão",
proxyMode: "Modo Proxy",
paidMode: "Modo Pago",
usingLocalProxy: "Usando servidor proxy local (localhost:{port})",
usingOfficialApi: "Usando API oficial da Anthropic (requer assinatura)",
paidModeTitle: "Claude CLI está usando a API oficial da Anthropic",
paidModeDesc: "Toda configuração de proxy foi removida. Claude CLI usa sua assinatura Anthropic diretamente.",
paidModeHint: "Mude para modo Proxy para configurar roteamento de modelos e presets.",
modeToggleFailed: "Falha ao alternar modo",
}; };

View File

@@ -355,4 +355,14 @@ window.translations.tr = {
strategyUpdated: "Strateji şu şekilde güncellendi: {strategy}", strategyUpdated: "Strateji şu şekilde güncellendi: {strategy}",
failedToUpdateStrategy: "Strateji güncellenemedi", failedToUpdateStrategy: "Strateji güncellenemedi",
invalidStrategy: "Geçersiz strateji seçildi", invalidStrategy: "Geçersiz strateji seçildi",
// Mode Toggle (Proxy/Paid)
connectionMode: "Bağlantı Modu",
proxyMode: "Proxy Modu",
paidMode: "Ücretli Mod",
usingLocalProxy: "Yerel proxy sunucusu kullanılıyor (localhost:{port})",
usingOfficialApi: "Resmi Anthropic API kullanılıyor (abonelik gerektirir)",
paidModeTitle: "Claude CLI resmi Anthropic API kullanıyor",
paidModeDesc: "Tüm proxy yapılandırması kaldırıldı. Claude CLI doğrudan Anthropic aboneliğinizi kullanır.",
paidModeHint: "Model yönlendirme ve ön ayarları yapılandırmak için Proxy moduna geçin.",
modeToggleFailed: "Mod değiştirilemedi",
}; };

View File

@@ -370,4 +370,14 @@ window.translations.zh = {
strategyUpdated: "策略已更新为: {strategy}", strategyUpdated: "策略已更新为: {strategy}",
failedToUpdateStrategy: "更新策略失败", failedToUpdateStrategy: "更新策略失败",
invalidStrategy: "选择了无效的策略", invalidStrategy: "选择了无效的策略",
// Mode Toggle (Proxy/Paid)
connectionMode: "连接模式",
proxyMode: "代理模式",
paidMode: "付费模式",
usingLocalProxy: "使用本地代理服务器 (localhost:{port})",
usingOfficialApi: "使用官方 Anthropic API (需要订阅)",
paidModeTitle: "Claude CLI 正在使用官方 Anthropic API",
paidModeDesc: "所有代理配置已移除。Claude CLI 直接使用您的 Anthropic 订阅。",
paidModeHint: "切换到代理模式以配置模型路由和预设。",
modeToggleFailed: "切换模式失败",
}; };

View File

@@ -199,35 +199,36 @@
<!-- Mode Toggle (Proxy/Paid) --> <!-- Mode Toggle (Proxy/Paid) -->
<div class="card bg-space-900/30 border border-space-border/50 p-5"> <div class="card bg-space-900/30 border border-space-border/50 p-5">
<label class="label text-xs uppercase text-gray-500 font-semibold mb-3">Connection Mode</label> <label class="label text-xs uppercase text-gray-500 font-semibold mb-3"
x-text="$store.global.t('connectionMode')">Connection Mode</label>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-sm font-semibold" <span class="text-sm font-semibold"
:class="currentMode === 'proxy' ? 'text-neon-purple' : 'text-neon-green'"> :class="currentMode === 'proxy' ? 'text-neon-purple' : 'text-neon-green'">
<template x-if="currentMode === 'proxy'"><span>🔌 Proxy Mode</span></template> <template x-if="currentMode === 'proxy'"><span>🔌 <span x-text="$store.global.t('proxyMode')">Proxy Mode</span></span></template>
<template x-if="currentMode === 'paid'"><span>💳 Paid Mode</span></template> <template x-if="currentMode === 'paid'"><span>💳 <span x-text="$store.global.t('paidMode')">Paid Mode</span></span></template>
</span> </span>
<span x-show="modeLoading" <span x-show="modeLoading"
class="loading loading-spinner loading-xs text-gray-400"></span> class="loading loading-spinner loading-xs text-gray-400"></span>
</div> </div>
<span class="text-[11px] text-gray-500"> <span class="text-[11px] text-gray-500">
<template x-if="currentMode === 'proxy'"><span>Using local proxy server <template x-if="currentMode === 'proxy'"><span x-text="$store.global.t('usingLocalProxy', {port: getProxyPort()})">Using local proxy server</span></template>
(localhost:8080)</span></template> <template x-if="currentMode === 'paid'"><span x-text="$store.global.t('usingOfficialApi')">Using official Anthropic API (requires subscription)</span></template>
<template x-if="currentMode === 'paid'"><span>Using official Anthropic API (requires
subscription)</span></template>
</span> </span>
</div> </div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="text-xs font-medium" <span class="text-xs font-medium"
:class="currentMode === 'proxy' ? 'text-neon-purple' : 'text-gray-500'">Proxy</span> :class="currentMode === 'proxy' ? 'text-neon-purple' : 'text-gray-500'"
x-text="$store.global.t('proxyMode').split(' ')[0]">Proxy</span>
<input type="checkbox" class="toggle toggle-sm" <input type="checkbox" class="toggle toggle-sm"
:class="currentMode === 'paid' ? 'toggle-success' : 'toggle-secondary'" :class="currentMode === 'paid' ? 'toggle-success' : 'toggle-secondary'"
:checked="currentMode === 'paid'" :disabled="modeLoading" :checked="currentMode === 'paid'" :disabled="modeLoading"
@change="toggleMode($event.target.checked ? 'paid' : 'proxy')" @change="toggleMode($event.target.checked ? 'paid' : 'proxy')"
aria-label="Toggle between Proxy and Paid mode"> aria-label="Toggle between Proxy and Paid mode">
<span class="text-xs font-medium" <span class="text-xs font-medium"
:class="currentMode === 'paid' ? 'text-neon-green' : 'text-gray-500'">Paid</span> :class="currentMode === 'paid' ? 'text-neon-green' : 'text-gray-500'"
x-text="$store.global.t('paidMode').split(' ')[0]">Paid</span>
</div> </div>
</div> </div>
</div> </div>
@@ -241,12 +242,9 @@
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg> </svg>
<div> <div>
<p class="text-sm font-semibold text-neon-green">Claude CLI is using the official Anthropic <p class="text-sm font-semibold text-neon-green" x-text="$store.global.t('paidModeTitle')">Claude CLI is using the official Anthropic API</p>
API</p> <p class="text-[11px] text-gray-400 mt-1" x-text="$store.global.t('paidModeDesc')">All proxy configuration has been removed. Claude CLI uses your Anthropic subscription directly.</p>
<p class="text-[11px] text-gray-400 mt-1">All proxy configuration has been removed. Claude <p class="text-[10px] text-gray-500 mt-2" x-text="$store.global.t('paidModeHint')">Switch to Proxy mode to configure model routing and presets.</p>
CLI uses your Anthropic subscription directly.</p>
<p class="text-[10px] text-gray-500 mt-2">Switch to Proxy mode to configure model routing
and presets.</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -628,7 +628,12 @@ export function mountWebUI(app, dirname, accountManager) {
const baseUrl = claudeConfig.env?.ANTHROPIC_BASE_URL || ''; const baseUrl = claudeConfig.env?.ANTHROPIC_BASE_URL || '';
// Determine mode based on ANTHROPIC_BASE_URL // Determine mode based on ANTHROPIC_BASE_URL
const isProxy = baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1'); const isProxy = baseUrl && (
baseUrl.includes('localhost') ||
baseUrl.includes('127.0.0.1') ||
baseUrl.includes('::1') ||
baseUrl.includes('0.0.0.0')
);
res.json({ res.json({
status: 'ok', status: 'ok',