Merge pull request #107 from YasinKose/main
Feat(ui): add Turkish language support and UI enhancements Introduces Turkish language support and several UI/UX improvements to the web management interface.
This commit is contained in:
@@ -509,6 +509,12 @@
|
|||||||
@apply skeleton h-12 w-full mb-2;
|
@apply skeleton h-12 w-full mb-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix DaisyUI toggle hover background override */
|
||||||
|
.toggle-success:checked,
|
||||||
|
.toggle-success[aria-checked="true"] {
|
||||||
|
background-color: oklch(var(--su)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Desktop Sidebar Collapsed State */
|
/* Desktop Sidebar Collapsed State */
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
body .sidebar-collapsed {
|
body .sidebar-collapsed {
|
||||||
|
|||||||
2
public/css/style.css
generated
2
public/css/style.css
generated
File diff suppressed because one or more lines are too long
@@ -300,7 +300,7 @@
|
|||||||
|
|
||||||
<div class="modal-action mt-6">
|
<div class="modal-action mt-6">
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<button type="button" class="btn btn-ghost hover:bg-white/10" x-text="$store.global.t('close')">Close</button>
|
<button type="submit" class="btn btn-ghost hover:bg-white/10" x-text="$store.global.t('close')">Close</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ window.Components.accountManager = () => ({
|
|||||||
toggling: false,
|
toggling: false,
|
||||||
deleting: false,
|
deleting: false,
|
||||||
reloading: false,
|
reloading: false,
|
||||||
|
selectedAccountEmail: '',
|
||||||
|
selectedAccountLimits: {},
|
||||||
|
|
||||||
get filteredAccounts() {
|
get filteredAccounts() {
|
||||||
const accounts = Alpine.store('data').accounts || [];
|
const accounts = Alpine.store('data').accounts || [];
|
||||||
@@ -174,6 +176,12 @@ window.Components.accountManager = () => ({
|
|||||||
}, this, 'reloading', { errorMessage: 'Failed to reload accounts' });
|
}, this, 'reloading', { errorMessage: 'Failed to reload accounts' });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openQuotaModal(account) {
|
||||||
|
this.selectedAccountEmail = account.email;
|
||||||
|
this.selectedAccountLimits = account.limits || {};
|
||||||
|
document.getElementById('quota_modal').showModal();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get main model quota for display
|
* Get main model quota for display
|
||||||
* Prioritizes flagship models (Opus > Sonnet > Flash)
|
* Prioritizes flagship models (Opus > Sonnet > Flash)
|
||||||
|
|||||||
@@ -235,8 +235,9 @@ document.addEventListener('alpine:init', () => {
|
|||||||
isHidden = (family === 'other' || family === 'unknown');
|
isHidden = (family === 'other' || family === 'unknown');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Models Page: ALWAYS hide hidden models (use Settings to restore)
|
// Models Page: Check settings for visibility
|
||||||
if (isHidden) return;
|
const showHidden = Alpine.store('settings')?.showHiddenModels ?? false;
|
||||||
|
if (isHidden && !showHidden) return;
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
if (this.filters.family !== 'all' && this.filters.family !== family) return;
|
if (this.filters.family !== 'all' && this.filters.family !== family) return;
|
||||||
|
|||||||
@@ -114,6 +114,9 @@ document.addEventListener('alpine:init', () => {
|
|||||||
modelsDesc: "Configure model visibility, pinning, and request routing.",
|
modelsDesc: "Configure model visibility, pinning, and request routing.",
|
||||||
modelsPageDesc: "Real-time quota and status for all available models.",
|
modelsPageDesc: "Real-time quota and status for all available models.",
|
||||||
showHidden: "Show Hidden Models",
|
showHidden: "Show Hidden Models",
|
||||||
|
hideHidden: "Hide Hidden Models",
|
||||||
|
hiddenOn: "Hidden: ON",
|
||||||
|
hiddenOff: "Hidden: OFF",
|
||||||
modelId: "Model ID",
|
modelId: "Model ID",
|
||||||
actions: "Actions",
|
actions: "Actions",
|
||||||
pinToTop: "Pin to top",
|
pinToTop: "Pin to top",
|
||||||
@@ -315,6 +318,11 @@ document.addEventListener('alpine:init', () => {
|
|||||||
gemini1mDesc: "Appends [1m] suffix to Gemini models for 1M context window support.",
|
gemini1mDesc: "Appends [1m] suffix to Gemini models for 1M context window support.",
|
||||||
gemini1mWarning: "⚠ Large context may reduce Gemini-3-Pro performance.",
|
gemini1mWarning: "⚠ Large context may reduce Gemini-3-Pro performance.",
|
||||||
clickToSet: "Click to configure...",
|
clickToSet: "Click to configure...",
|
||||||
|
none: "None",
|
||||||
|
// Quota Distribution
|
||||||
|
quotaDistribution: "Quota Distribution",
|
||||||
|
resetsIn: "Resets in {time}",
|
||||||
|
noQuotaData: "No quota data available for this account yet.",
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
dashboard: "仪表盘",
|
dashboard: "仪表盘",
|
||||||
@@ -391,6 +399,9 @@ document.addEventListener('alpine:init', () => {
|
|||||||
modelsDesc: "配置模型的可见性、置顶和请求路由。",
|
modelsDesc: "配置模型的可见性、置顶和请求路由。",
|
||||||
modelsPageDesc: "所有可用模型的实时配额和状态。",
|
modelsPageDesc: "所有可用模型的实时配额和状态。",
|
||||||
showHidden: "显示隐藏模型",
|
showHidden: "显示隐藏模型",
|
||||||
|
hideHidden: "隐藏被屏蔽模型",
|
||||||
|
hiddenOn: "隐藏模型: 显示",
|
||||||
|
hiddenOff: "隐藏模型: 隐藏",
|
||||||
modelId: "模型 ID",
|
modelId: "模型 ID",
|
||||||
actions: "操作",
|
actions: "操作",
|
||||||
pinToTop: "置顶",
|
pinToTop: "置顶",
|
||||||
@@ -592,7 +603,278 @@ document.addEventListener('alpine:init', () => {
|
|||||||
gemini1mDesc: "为 Gemini 模型添加 [1m] 后缀以支持 1M 上下文窗口。",
|
gemini1mDesc: "为 Gemini 模型添加 [1m] 后缀以支持 1M 上下文窗口。",
|
||||||
gemini1mWarning: "⚠ 大上下文可能降低 Gemini-3-Pro 性能。",
|
gemini1mWarning: "⚠ 大上下文可能降低 Gemini-3-Pro 性能。",
|
||||||
clickToSet: "点击进行配置...",
|
clickToSet: "点击进行配置...",
|
||||||
}
|
none: "无",
|
||||||
|
// Quota Distribution
|
||||||
|
quotaDistribution: "配额分布",
|
||||||
|
resetsIn: "{time} 后重置",
|
||||||
|
noQuotaData: "暂无此账号的配额数据。",
|
||||||
|
},
|
||||||
|
tr: {
|
||||||
|
dashboard: "Panel",
|
||||||
|
models: "Modeller",
|
||||||
|
accounts: "Hesaplar",
|
||||||
|
logs: "Günlükler",
|
||||||
|
settings: "Ayarlar",
|
||||||
|
online: "ÇEVRİMİÇİ",
|
||||||
|
offline: "ÇEVRİMDIŞI",
|
||||||
|
totalAccounts: "TOPLAM HESAP",
|
||||||
|
active: "AKTİF",
|
||||||
|
operational: "Çalışıyor",
|
||||||
|
rateLimited: "HIZ SINIRLANDI",
|
||||||
|
quotasDepleted: "{count}/{total} Kota Tükendi",
|
||||||
|
quotasDepletedTitle: "KOTA TÜKENDİ",
|
||||||
|
outOfTracked: "{total} Takip Edilen İçinden",
|
||||||
|
cooldown: "Soğuma",
|
||||||
|
searchPlaceholder: "Modelleri ara...",
|
||||||
|
allAccounts: "Tüm Hesaplar",
|
||||||
|
stat: "DURUM",
|
||||||
|
modelIdentity: "MODEL KİMLİĞİ",
|
||||||
|
globalQuota: "GENEL KOTA",
|
||||||
|
nextReset: "SONRAKİ SIFIRLAMA",
|
||||||
|
distribution: "HESAP DAĞILIMI",
|
||||||
|
systemConfig: "Sistem Yapılandırması",
|
||||||
|
language: "Dil",
|
||||||
|
pollingInterval: "Veri Güncelleme Aralığı",
|
||||||
|
maxDisplayLogs: "Maksimum Görüntülenen Günlük",
|
||||||
|
showExhausted: "Tükenmiş Modelleri Göster",
|
||||||
|
showExhaustedDesc: "%0 kotası kalan modelleri de göster.",
|
||||||
|
compactMode: "Kompakt Mod",
|
||||||
|
compactModeDesc: "Daha fazla bilgi göstermek için tablo boşluklarını azalt.",
|
||||||
|
saveChanges: "Değişiklikleri Kaydet",
|
||||||
|
autoScroll: "Otomatik Kaydır",
|
||||||
|
clearLogs: "Günlükleri Temizle",
|
||||||
|
accountManagement: "Hesap Yönetimi",
|
||||||
|
manageTokens: "Google Hesap jetonlarını ve yetkilendirme durumlarını yönet",
|
||||||
|
addAccount: "Hesap Ekle",
|
||||||
|
status: "DURUM",
|
||||||
|
enabled: "ETKİN",
|
||||||
|
health: "SAĞLIK",
|
||||||
|
accountEmail: "HESAP (E-POSTA)",
|
||||||
|
source: "KAYNAK",
|
||||||
|
projectId: "PROJE ID",
|
||||||
|
sessionState: "OTURUM DURUMU",
|
||||||
|
operations: "İŞLEMLER",
|
||||||
|
delete: "Sil",
|
||||||
|
confirmDelete: "Bu hesabı kaldırmak istediğinizden emin misiniz?",
|
||||||
|
cannotDeleteDatabase: "Silinemez: Bu hesap Antigravity veritabanından (salt okunur)",
|
||||||
|
connectGoogle: "Google Hesabı Bağla",
|
||||||
|
reauthenticated: "yeniden doğrulandı",
|
||||||
|
added: "eklendi",
|
||||||
|
successfully: "başarıyla",
|
||||||
|
accountAddedSuccess: "Hesap başarıyla eklendi",
|
||||||
|
accountReauthSuccess: "Hesap başarıyla yeniden doğrulandı",
|
||||||
|
failedToGetAuthUrl: "Yetkilendirme URL'si alınamadı",
|
||||||
|
failedToStartOAuth: "OAuth akışı başlatılamadı",
|
||||||
|
oauthInProgress: "OAuth devam ediyor. Lütfen açılır pencerede kimlik doğrulamayı tamamlayın...",
|
||||||
|
family: "Aile",
|
||||||
|
model: "Model",
|
||||||
|
activeSuffix: "Aktif",
|
||||||
|
manualReload: "Yapılandırmayı Yeniden Yükle",
|
||||||
|
// Tabs
|
||||||
|
tabInterface: "Arayüz",
|
||||||
|
tabClaude: "Claude CLI",
|
||||||
|
tabModels: "Modeller",
|
||||||
|
tabServer: "Sunucu Ayarları",
|
||||||
|
// Dashboard
|
||||||
|
linkedAccounts: "Bağlı Hesaplar",
|
||||||
|
noSignal: "SİNYAL YOK",
|
||||||
|
establishingUplink: "BAĞLANTI KURULUYOR...",
|
||||||
|
goToAccounts: "Hesaplara Git",
|
||||||
|
// Settings - Models
|
||||||
|
modelsDesc: "Model görünürlüğünü, sabitlemeyi ve istek yönlendirmeyi yapılandırın.",
|
||||||
|
modelsPageDesc: "Tüm mevcut modeller için gerçek zamanlı kota ve durum.",
|
||||||
|
showHidden: "Gizli Modelleri Göster",
|
||||||
|
hideHidden: "Gizli Modelleri Gizle",
|
||||||
|
hiddenOn: "Gizli: AÇIK",
|
||||||
|
hiddenOff: "Gizli: KAPALI",
|
||||||
|
modelId: "Model ID",
|
||||||
|
actions: "İşlemler",
|
||||||
|
pinToTop: "En Üste Sabitle",
|
||||||
|
toggleVisibility: "Görünürlüğü Değiştir",
|
||||||
|
noModels: "MODEL ALGILANMADI",
|
||||||
|
modelMappingHint: "Sunucu tarafı model yönlendirme. Claude Code kullanıcıları: istemci tarafı kurulumu için 'Claude CLI' sekmesine bakın.",
|
||||||
|
modelMapping: "Eşleştirme (Hedef Model ID)",
|
||||||
|
// Settings - Claude
|
||||||
|
proxyConnection: "Proxy Bağlantısı",
|
||||||
|
modelSelection: "Model Seçimi",
|
||||||
|
defaultModelAliases: "VARSAYILAN MODEL TAKMA ADLARI",
|
||||||
|
opusAlias: "Opus Takma Adı",
|
||||||
|
sonnetAlias: "Sonnet Takma Adı",
|
||||||
|
haikuAlias: "Haiku Takma Adı",
|
||||||
|
claudeSettingsAlertPrefix: "Aşağıdaki ayarlar doğrudan değiştirir",
|
||||||
|
claudeSettingsAlertSuffix: "Uygulamak için Claude CLI'yı yeniden başlatın.",
|
||||||
|
applyToClaude: "Claude CLI'ya Uygula",
|
||||||
|
// Settings - Server
|
||||||
|
port: "Port",
|
||||||
|
uiVersion: "Arayüz Sürümü",
|
||||||
|
debugMode: "Hata Ayıklama Modu",
|
||||||
|
environment: "Ortam",
|
||||||
|
serverReadOnly: "Ayarlar config.json üzerinden yönetilir. Değişiklikleri uygulamak için sunucuyu yeniden başlatın.",
|
||||||
|
advancedSettings: "Gelişmiş Ayarlar",
|
||||||
|
reloadConfigTitle: "Hesap Yapılandırmasını Yeniden Yükle",
|
||||||
|
reloadConfigDesc: "accounts.json dosyasını diskten yeniden yüklemeye zorla",
|
||||||
|
reload: "Yeniden Yükle",
|
||||||
|
// Config Specific
|
||||||
|
primaryModel: "Birincil Model",
|
||||||
|
subAgentModel: "Alt Ajan Modeli",
|
||||||
|
advancedOverrides: "Varsayılan Model Geçersiz Kılmaları",
|
||||||
|
opusModel: "Opus Modeli",
|
||||||
|
sonnetModel: "Sonnet Modeli",
|
||||||
|
haikuModel: "Haiku Modeli",
|
||||||
|
authToken: "Yetkilendirme Jetonu",
|
||||||
|
saveConfig: "Claude CLI ayarlarına kaydet",
|
||||||
|
envVar: "Ortam Değişkeni",
|
||||||
|
// New Keys
|
||||||
|
systemName: "ANTIGRAVITY",
|
||||||
|
systemDesc: "CLAUDE PROXY SİSTEMİ",
|
||||||
|
connectGoogleDesc: "API kota limitinizi artırmak için bir Google Workspace hesabı bağlayın. Bu hesap, Claude isteklerini Antigravity üzerinden vekil sunucu olarak kullanmak için kullanılacaktır.",
|
||||||
|
useCliCommand: "CLI Komutunu Kullan",
|
||||||
|
close: "Kapat",
|
||||||
|
requestVolume: "İstek Hacmi",
|
||||||
|
filter: "Filtrele",
|
||||||
|
all: "Tümü",
|
||||||
|
none: "Hiçbiri",
|
||||||
|
noDataTracked: "Henüz veri izlenmedi",
|
||||||
|
selectFamilies: "Görüntülenecek aileleri seçin",
|
||||||
|
selectModels: "Görüntülenecek modelleri seçin",
|
||||||
|
noLogsMatch: "Filtreyle eşleşen günlük yok",
|
||||||
|
connecting: "BAĞLANILIYOR",
|
||||||
|
main: "Ana",
|
||||||
|
system: "Sistem",
|
||||||
|
refreshData: "Verileri Yenile",
|
||||||
|
connectionLost: "Bağlantı Kaybedildi",
|
||||||
|
lastUpdated: "Son Güncelleme",
|
||||||
|
grepLogs: "günlükleri ara...",
|
||||||
|
noMatchingModels: "Eşleşen model yok",
|
||||||
|
typeToSearch: "Aramak veya seçmek için yazın...",
|
||||||
|
or: "VEYA",
|
||||||
|
refreshingAccount: "{email} yenileniyor...",
|
||||||
|
refreshedAccount: "{email} yenilendi",
|
||||||
|
refreshFailed: "Yenileme başarısız",
|
||||||
|
accountToggled: "Hesap {email} {status}",
|
||||||
|
toggleFailed: "Değiştirme başarısız",
|
||||||
|
reauthenticating: "{email} yeniden doğrulanıyor...",
|
||||||
|
authUrlFailed: "Yetkilendirme URL'si alınamadı",
|
||||||
|
deletedAccount: "{email} silindi",
|
||||||
|
deleteFailed: "Silme başarısız",
|
||||||
|
accountsReloaded: "Hesaplar yeniden yüklendi",
|
||||||
|
reloadFailed: "Yeniden yükleme başarısız",
|
||||||
|
claudeConfigSaved: "Claude yapılandırması kaydedildi",
|
||||||
|
claudeConfigRestored: "Claude CLI varsayılanlara geri yüklendi",
|
||||||
|
saveConfigFailed: "Yapılandırma kaydedilemedi",
|
||||||
|
restoreConfigFailed: "Yapılandırma geri yüklenemedi",
|
||||||
|
restoreDefault: "Varsayılana Dön",
|
||||||
|
confirmRestoreTitle: "Geri Yüklemeyi Onayla",
|
||||||
|
confirmRestoreMessage: "Claude CLI'yı varsayılan ayarlara geri yüklemek istediğinizden emin misiniz? Bu işlem proxy yapılandırmasını kaldıracaktır.",
|
||||||
|
confirmRestore: "Geri Yüklemeyi Onayla",
|
||||||
|
claudeActive: "Claude Aktif",
|
||||||
|
claudeEmpty: "Claude Boş",
|
||||||
|
geminiActive: "Gemini Aktif",
|
||||||
|
geminiEmpty: "Gemini Boş",
|
||||||
|
synced: "EŞİTLENDİ",
|
||||||
|
syncing: "EŞİTLENİYOR...",
|
||||||
|
// Time range labels
|
||||||
|
last1Hour: "Son 1S",
|
||||||
|
last6Hours: "Son 6S",
|
||||||
|
last24Hours: "Son 24S",
|
||||||
|
last7Days: "Son 7G",
|
||||||
|
allTime: "Tüm Zamanlar",
|
||||||
|
groupBy: "Gruplama Ölçütü",
|
||||||
|
// Additional
|
||||||
|
reloading: "Yeniden yükleniyor...",
|
||||||
|
reloaded: "Yeniden yüklendi",
|
||||||
|
lines: "satır",
|
||||||
|
enabledSeeLogs: "Etkin (Günlüklere Bak)",
|
||||||
|
production: "Üretim",
|
||||||
|
configSaved: "Yapılandırma Kaydedildi",
|
||||||
|
enterPassword: "Web UI Parolasını Girin:",
|
||||||
|
ready: "HAZIR",
|
||||||
|
depleted: "Tükendi",
|
||||||
|
timeH: "S",
|
||||||
|
timeM: "D",
|
||||||
|
familyClaude: "Claude",
|
||||||
|
familyGemini: "Gemini",
|
||||||
|
familyOther: "Diğer",
|
||||||
|
enabledStatus: "etkin",
|
||||||
|
disabledStatus: "devre dışı",
|
||||||
|
logLevelInfo: "BİLGİ",
|
||||||
|
logLevelSuccess: "BAŞARILI",
|
||||||
|
logLevelWarn: "UYARI",
|
||||||
|
logLevelError: "HATA",
|
||||||
|
totalColon: "Toplam:",
|
||||||
|
todayColon: "Bugün:",
|
||||||
|
hour1Colon: "1S:",
|
||||||
|
frequentModels: "Sık Kullanılan",
|
||||||
|
smartTitle: "En çok kullanılan 5 modeli otomatik seç (24s)",
|
||||||
|
activeCount: "{count} Aktif",
|
||||||
|
allCaps: "TÜMÜ",
|
||||||
|
claudeCaps: "CLAUDE",
|
||||||
|
geminiCaps: "GEMINI",
|
||||||
|
modelMapping: "Eşleştirme (Hedef Model ID)",
|
||||||
|
systemInfo: "Sistem Bilgisi",
|
||||||
|
refresh: "Yenile",
|
||||||
|
runtimeConfig: "Çalışma Zamanı Yapılandırması",
|
||||||
|
debugDesc: "Ayrıntılı günlük kaydını etkinleştir (Günlükler sekmesine bakın)",
|
||||||
|
networkRetry: "Ağ Yeniden Deneme Ayarları",
|
||||||
|
maxRetries: "Maksimum Deneme",
|
||||||
|
retryBaseDelay: "Yeniden Deneme Temel Gecikmesi (ms)",
|
||||||
|
retryMaxDelay: "Yeniden Deneme Maksimum Gecikmesi (ms)",
|
||||||
|
persistentSessions: "Kalıcı Oturumlar",
|
||||||
|
persistTokenDesc: "Daha hızlı yeniden başlatmalar için OAuth oturumlarını diske kaydet",
|
||||||
|
rateLimiting: "Hesap Hız Sınırlama ve Zaman Aşımları",
|
||||||
|
defaultCooldown: "Varsayılan Soğuma Süresi",
|
||||||
|
maxWaitThreshold: "Maksimum Bekleme Eşiği (Yapışkan)",
|
||||||
|
maxWaitDesc: "Yapışkan bir hesabın değiştirmeden önce sıfırlanması için beklenecek maksimum süre.",
|
||||||
|
saveConfigServer: "Yapılandırmayı Kaydet",
|
||||||
|
serverRestartAlert: "Değişiklikler {path} konumuna kaydedildi. Bazı ayarları uygulamak için sunucuyu yeniden başlatın.",
|
||||||
|
changePassword: "WebUI Parolasını Değiştir",
|
||||||
|
changePasswordDesc: "Bu panoya erişim parolasını güncelle",
|
||||||
|
currentPassword: "Mevcut Parola",
|
||||||
|
newPassword: "Yeni Parola",
|
||||||
|
confirmNewPassword: "Yeni Parolayı Onayla",
|
||||||
|
passwordEmptyDesc: "Parola ayarlanmamışsa boş bırakın",
|
||||||
|
passwordLengthDesc: "En az 6 karakter",
|
||||||
|
passwordConfirmDesc: "Yeni parolayı tekrar girin",
|
||||||
|
cancel: "İptal",
|
||||||
|
passwordsNotMatch: "Parolalar eşleşmiyor",
|
||||||
|
passwordTooShort: "Parola en az 6 karakter olmalıdır",
|
||||||
|
// Dashboard drill-down
|
||||||
|
clickToViewAllAccounts: "Tüm hesapları görüntülemek için tıklayın",
|
||||||
|
clickToViewModels: "Modeller sayfasını görüntülemek için tıklayın",
|
||||||
|
clickToViewLimitedAccounts: "Hız sınırı olan hesapları görüntülemek için tıklayın",
|
||||||
|
clickToFilterClaude: "Claude modellerini filtrelemek için tıklayın",
|
||||||
|
clickToFilterGemini: "Gemini modellerini filtrelemek için tıklayın",
|
||||||
|
// Accounts page
|
||||||
|
searchAccounts: "Hesap ara...",
|
||||||
|
noAccountsYet: "Henüz Hesap Yok",
|
||||||
|
noAccountsDesc: "OAuth üzerinden bir Google hesabı ekleyerek veya kimlik bilgilerini içe aktarmak için CLI komutunu kullanarak başlayın.",
|
||||||
|
addFirstAccount: "İlk Hesabınızı Ekleyin",
|
||||||
|
noSearchResults: "Aramanızla eşleşen hesap yok",
|
||||||
|
clearSearch: "Aramayı Temizle",
|
||||||
|
disabledAccountsNote: "<strong>Devre dışı bırakılan hesaplar</strong> istek yönlendirmesi için kullanılmayacak ancak yapılandırmada kalacaktır. Pano istatistikleri yalnızca etkin hesapları içerir.",
|
||||||
|
dangerousOperation: "⚠️ Tehlikeli İşlem",
|
||||||
|
confirmDeletePrompt: "Hesabı silmek istediğinizden emin misiniz",
|
||||||
|
deleteWarning: "⚠️ Bu işlem geri alınamaz. Tüm yapılandırma ve geçmiş kayıtları kalıcı olarak silinecektir.",
|
||||||
|
// OAuth progress
|
||||||
|
oauthWaiting: "OAuth yetkilendirmesi bekleniyor...",
|
||||||
|
oauthWaitingDesc: "Lütfen açılır pencerede kimlik doğrulamasını tamamlayın. Bu işlem 2 dakikaya kadar sürebilir.",
|
||||||
|
oauthCancelled: "OAuth yetkilendirmesi iptal edildi",
|
||||||
|
oauthTimeout: "⏱️ OAuth yetkilendirmesi zaman aşımına uğradı. Lütfen tekrar deneyin.",
|
||||||
|
oauthWindowClosed: "OAuth penceresi kapatıldı. Yetkilendirme tamamlanmamış olabilir.",
|
||||||
|
cancelOAuth: "İptal",
|
||||||
|
// MCP CLI & Gemini 1M
|
||||||
|
mcpCliExperimental: "Deneysel MCP CLI",
|
||||||
|
mcpCliDesc: "Daha az bağlam tüketimi ile güvenilir araç kullanımı için deneysel MCP entegrasyonunu etkinleştirir.",
|
||||||
|
gemini1mMode: "Gemini 1M Bağlam Modu",
|
||||||
|
gemini1mDesc: "1M bağlam penceresi desteği için Gemini modellerine [1m] son eki ekler.",
|
||||||
|
gemini1mWarning: "⚠ Büyük bağlam, Gemini-3-Pro performansını düşürebilir.",
|
||||||
|
clickToSet: "Yapılandırmak için tıklayın...",
|
||||||
|
none: "Hiçbiri",
|
||||||
|
// Quota Distribution
|
||||||
|
quotaDistribution: "Kota Dağılımı",
|
||||||
|
resetsIn: "{time} içinde sıfırlanır",
|
||||||
|
noQuotaData: "Bu hesap için henüz kota verisi yok.",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Toast Messages
|
// Toast Messages
|
||||||
|
|||||||
@@ -129,7 +129,7 @@
|
|||||||
x-text="(acc.subscription?.tier || 'free').toUpperCase()">
|
x-text="(acc.subscription?.tier || 'free').toUpperCase()">
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-4">
|
<td class="py-4 cursor-pointer" @click="openQuotaModal(acc)">
|
||||||
<div x-data="{ quota: getMainModelQuota(acc) }">
|
<div x-data="{ quota: getMainModelQuota(acc) }">
|
||||||
<template x-if="quota.percent !== null">
|
<template x-if="quota.percent !== null">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -268,4 +268,62 @@
|
|||||||
<button>close</button>
|
<button>close</button>
|
||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<!-- Quota Distribution Modal -->
|
||||||
|
<dialog id="quota_modal" class="modal backdrop-blur-sm">
|
||||||
|
<div class="modal-box max-w-2xl w-full bg-space-900 border border-space-border text-gray-300 shadow-2xl p-6">
|
||||||
|
<h3 class="font-bold text-xl text-white mb-2 flex items-center gap-2">
|
||||||
|
<svg class="w-6 h-6 text-neon-cyan" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||||
|
</svg>
|
||||||
|
<span x-text="$store.global.t('quotaDistribution')">Quota Distribution</span>
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-gray-500 font-mono mb-6" x-text="selectedAccountEmail"></p>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 overflow-y-auto max-h-[60vh] pr-2 custom-scrollbar">
|
||||||
|
<template x-for="(limit, modelId) in selectedAccountLimits" :key="modelId">
|
||||||
|
<div class="p-3 bg-space-800/50 border border-space-border/30 rounded-lg">
|
||||||
|
<div class="flex justify-between items-center mb-2">
|
||||||
|
<span class="text-sm font-semibold text-gray-200" x-text="modelId"></span>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="text-xs font-mono"
|
||||||
|
:class="limit.remainingFraction > 0.5 ? 'text-neon-green' : (limit.remainingFraction > 0.2 ? 'text-yellow-500' : 'text-red-500')"
|
||||||
|
x-text="Math.round(limit.remainingFraction * 100) + '%'"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full bg-gray-700 rounded-full h-2.5 mb-2 overflow-hidden">
|
||||||
|
<div class="h-full rounded-full transition-all duration-500"
|
||||||
|
:class="limit.remainingFraction > 0.5 ? 'bg-neon-green' : (limit.remainingFraction > 0.2 ? 'bg-yellow-500' : 'bg-red-500')"
|
||||||
|
:style="`width: ${limit.remainingFraction * 100}%`">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-[10px] text-gray-500 uppercase font-bold" x-text="$store.data.getModelFamily(modelId)"></span>
|
||||||
|
<template x-if="limit.resetTime">
|
||||||
|
<span class="text-[10px] text-yellow-500/80 font-mono italic"
|
||||||
|
x-text="$store.global.t('resetsIn', { time: window.utils.formatTimeUntil(limit.resetTime) })"></span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template x-if="Object.keys(selectedAccountLimits).length === 0">
|
||||||
|
<div class="text-center py-8 text-gray-500" x-text="$store.global.t('noQuotaData')">
|
||||||
|
No quota data available for this account yet.
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-action">
|
||||||
|
<form method="dialog">
|
||||||
|
<button class="btn btn-ghost hover:bg-white/10" x-text="$store.global.t('close')">Close</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form method="dialog" class="modal-backdrop">
|
||||||
|
<button>close</button>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<div class="text-[10px] font-mono text-gray-600 hidden lg:block">
|
<div class="text-[10px] font-mono text-gray-600 hidden lg:block">
|
||||||
<span x-text="filteredLogs.length"></span>/<span x-text="logs.length"></span>
|
<span x-text="filteredLogs.length"></span>/<span x-text="logs.length"></span>
|
||||||
</div>
|
</div>
|
||||||
<label class="cursor-pointer flex items-center gap-2" title="Auto-scroll to bottom">
|
<label class="cursor-pointer flex items-center gap-2">
|
||||||
<span class="text-[10px] font-mono text-gray-500 uppercase hidden sm:inline-block"
|
<span class="text-[10px] font-mono text-gray-500 uppercase hidden sm:inline-block"
|
||||||
x-text="$store.global.t('autoScroll')">Auto-Scroll</span>
|
x-text="$store.global.t('autoScroll')">Auto-Scroll</span>
|
||||||
<input type="checkbox" class="toggle toggle-xs toggle-success" x-model="isAutoScroll">
|
<input type="checkbox" class="toggle toggle-xs toggle-success" x-model="isAutoScroll">
|
||||||
|
|||||||
@@ -86,6 +86,18 @@
|
|||||||
@click="$store.data.filters.family = 'gemini'; $store.data.computeQuotaRows()"
|
@click="$store.data.filters.family = 'gemini'; $store.data.computeQuotaRows()"
|
||||||
x-text="$store.global.t('geminiCaps')">GEMINI</button>
|
x-text="$store.global.t('geminiCaps')">GEMINI</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Show Hidden Toggle -->
|
||||||
|
<button
|
||||||
|
class="btn h-9 px-4 ml-2 border border-space-border/50 bg-space-800 hover:bg-space-700 hover:border-space-border hover:text-white transition-all gap-2 min-h-0"
|
||||||
|
:class="$store.settings.showHiddenModels ? 'text-neon-purple border-neon-purple/50 bg-neon-purple/10' : 'text-gray-400'"
|
||||||
|
@click="$store.settings.toggle('showHiddenModels'); $store.data.computeQuotaRows()"
|
||||||
|
:title="$store.settings.showHiddenModels ? $store.global.t('hideHidden') : $store.global.t('showHidden')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
|
||||||
|
</svg>
|
||||||
|
<span class="text-[10px] font-medium uppercase tracking-wide" x-text="$store.settings.showHiddenModels ? $store.global.t('hiddenOn') : $store.global.t('hiddenOff')"></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -156,7 +168,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="text-sm">
|
<tbody class="text-sm">
|
||||||
<template x-for="row in $store.data.quotaRows" :key="row.modelId">
|
<template x-for="row in $store.data.quotaRows" :key="row.modelId">
|
||||||
<tr class="group">
|
<tr class="group transition-all duration-300" :class="row.hidden ? 'opacity-40 grayscale hover:opacity-100 hover:grayscale-0 bg-space-900/50' : ''">
|
||||||
<td class="pl-4">
|
<td class="pl-4">
|
||||||
<div class="w-2 h-2 rounded-full transition-all duration-500"
|
<div class="w-2 h-2 rounded-full transition-all duration-500"
|
||||||
:class="row.avgQuota > 0 ? 'bg-neon-green shadow-[0_0_8px_rgba(34,197,94,0.6)]' : 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.6)]'">
|
:class="row.avgQuota > 0 ? 'bg-neon-green shadow-[0_0_8px_rgba(34,197,94,0.6)]' : 'bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.6)]'">
|
||||||
|
|||||||
@@ -71,20 +71,14 @@
|
|||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="label-text text-gray-300" x-text="$store.global.t('language')">Language</span>
|
<span class="label-text text-gray-300" x-text="$store.global.t('language')">Language</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="join w-full grid grid-cols-2">
|
<select
|
||||||
<button
|
class="select select-bordered select-sm w-full bg-space-800 border-space-border/50 text-gray-300 focus:border-neon-purple focus:ring-1 focus:ring-neon-purple/50 font-medium transition-all !py-0 leading-tight"
|
||||||
class="join-item btn btn-sm border-space-border/50 bg-space-800 transition-all font-medium"
|
:value="$store.global.lang"
|
||||||
:class="$store.global.lang === 'en'
|
@change="$store.global.setLang($event.target.value)">
|
||||||
? 'bg-neon-purple/20 text-neon-purple border-neon-purple/60 shadow-lg shadow-neon-purple/10'
|
<option value="en">English</option>
|
||||||
: 'text-gray-400 hover:text-white hover:bg-space-700 hover:border-space-border'"
|
<option value="zh">中文</option>
|
||||||
@click="$store.global.setLang('en')">English</button>
|
<option value="tr">Türkçe</option>
|
||||||
<button
|
</select>
|
||||||
class="join-item btn btn-sm border-space-border/50 bg-space-800 transition-all font-medium"
|
|
||||||
:class="$store.global.lang === 'zh'
|
|
||||||
? 'bg-neon-purple/20 text-neon-purple border-neon-purple/60 shadow-lg shadow-neon-purple/10'
|
|
||||||
: 'text-gray-400 hover:text-white hover:bg-space-700 hover:border-space-border'"
|
|
||||||
@click="$store.global.setLang('zh')">中文</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Polling Interval -->
|
<!-- Polling Interval -->
|
||||||
@@ -753,15 +747,19 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div x-show="isEditing(modelId)" class="flex items-center gap-1">
|
<div x-show="isEditing(modelId)" class="flex items-center gap-1">
|
||||||
<input type="text" x-model="newMapping"
|
<select x-model="newMapping"
|
||||||
:x-ref="'input-' + modelId"
|
:x-ref="'input-' + modelId"
|
||||||
class="input input-xs bg-space-800 border-space-border text-white focus:outline-none focus:border-neon-cyan flex-1 font-mono text-xs"
|
class="select select-sm bg-space-800 border-space-border text-white focus:outline-none focus:border-neon-cyan flex-1 font-mono text-xs !h-8 min-h-0"
|
||||||
placeholder="e.g. claude-sonnet-4-5 or gemini-3-flash"
|
|
||||||
@keydown.enter="await updateModelConfig(modelId, { mapping: newMapping }); stopEditing()"
|
@keydown.enter="await updateModelConfig(modelId, { mapping: newMapping }); stopEditing()"
|
||||||
@keydown.escape="newMapping = config.mapping || ''; stopEditing()">
|
@keydown.escape="newMapping = config.mapping || ''; stopEditing()">
|
||||||
|
<option value="" x-text="$store.global.t('none')">None</option>
|
||||||
|
<template x-for="mId in $store.data.models" :key="mId">
|
||||||
|
<option :value="mId" x-text="mId" :selected="mId === newMapping"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
<button class="btn-action-success"
|
<button class="btn-action-success"
|
||||||
@click="await updateModelConfig(modelId, { mapping: newMapping }); stopEditing()"
|
@click="await updateModelConfig(modelId, { mapping: newMapping }); stopEditing()"
|
||||||
title="Save">
|
:title="$store.global.t('saveChanges')">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
|
||||||
viewBox="0 0 24 24" stroke="currentColor">
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
@@ -770,7 +768,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button class="btn-action-neutral"
|
<button class="btn-action-neutral"
|
||||||
@click="newMapping = config.mapping || ''; stopEditing()"
|
@click="newMapping = config.mapping || ''; stopEditing()"
|
||||||
title="Cancel">
|
:title="$store.global.t('cancel')">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
|
||||||
viewBox="0 0 24 24" stroke="currentColor">
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
@@ -780,7 +778,7 @@
|
|||||||
<button x-show="config.mapping"
|
<button x-show="config.mapping"
|
||||||
class="btn-action-danger"
|
class="btn-action-danger"
|
||||||
@click="await updateModelConfig(modelId, { mapping: '' }); stopEditing()"
|
@click="await updateModelConfig(modelId, { mapping: '' }); stopEditing()"
|
||||||
title="Clear mapping">
|
:title="$store.global.t('delete')">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none"
|
||||||
viewBox="0 0 24 24" stroke="currentColor">
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
|||||||
Reference in New Issue
Block a user