diff --git a/src/webui/src/i18n/en-US.ts b/src/webui/src/i18n/en-US.ts index b3d961d..929eb37 100644 --- a/src/webui/src/i18n/en-US.ts +++ b/src/webui/src/i18n/en-US.ts @@ -393,7 +393,7 @@ const enUS: Record = { 'uid.toast_removed': 'Removed', 'uid.toast_remove_failed': 'Failed to remove: ', 'uid.toast_load_apps_failed': 'Failed to load app list: ', - 'uid.btn_add_selected': 'Add Selected ({count})', + 'uid.btn_add_selected': 'Confirm ({count})', 'uid.toast_added_count': 'Successfully added {count} apps', 'uid.no_apps_found': 'No apps found', diff --git a/src/webui/src/i18n/ur-PK.ts b/src/webui/src/i18n/ur-PK.ts index 793b7ae..073fad6 100644 --- a/src/webui/src/i18n/ur-PK.ts +++ b/src/webui/src/i18n/ur-PK.ts @@ -366,7 +366,7 @@ const urPK: Record = { 'uid.toast_removed': 'ہٹا دیا گیا', 'uid.toast_remove_failed': 'ہٹانے میں ناکام: ', 'uid.toast_load_apps_failed': 'ایپ لسٹ لوڈ کرنے میں ناکام: ', - 'uid.btn_add_selected': 'منتخب شامل کریں ({count})', + 'uid.btn_add_selected': 'تصدیق کریں ({count})', 'uid.toast_added_count': '{count} ایپس کامیابی سے شامل ہو گئیں', 'uid.no_apps_found': 'کوئی ایپس نہیں ملیں', diff --git a/src/webui/src/i18n/zh-CN.ts b/src/webui/src/i18n/zh-CN.ts index 57b6095..858d981 100644 --- a/src/webui/src/i18n/zh-CN.ts +++ b/src/webui/src/i18n/zh-CN.ts @@ -393,7 +393,7 @@ const zhCN: Record = { 'uid.toast_removed': '已移除', 'uid.toast_remove_failed': '移除失败: ', 'uid.toast_load_apps_failed': '加载应用列表失败: ', - 'uid.btn_add_selected': '添加选中 ({count})', + 'uid.btn_add_selected': '确认 ({count})', 'uid.toast_added_count': '成功添加 {count} 个应用', 'uid.no_apps_found': '没有找到应用', diff --git a/src/webui/src/styles/components/mdui-overrides.css b/src/webui/src/styles/components/mdui-overrides.css index 48c42f2..629d548 100644 --- a/src/webui/src/styles/components/mdui-overrides.css +++ b/src/webui/src/styles/components/mdui-overrides.css @@ -190,6 +190,24 @@ mdui-dialog#confirm-dialog::part(panel) { width: 90% !important; } +/* 关于对话框 - 从底部滑入动画 */ +mdui-dialog#about-dialog::part(panel) { + background-color: var(--monet-surface-container-high, var(--mdui-color-surface-container-high)) !important; + animation: slideUpIn 250ms cubic-bezier(0.2, 0, 0, 1) forwards; + transform-origin: center bottom; +} + +@keyframes slideUpIn { + from { + opacity: 0; + transform: translateY(100%); + } + to { + opacity: 1; + transform: translateY(0); + } +} + /* ==================== 列表 ==================== */ mdui-list { @@ -567,4 +585,27 @@ mdui-divider { /* 将 snackbar 提升到底部导航栏上方 (底部导航栏高度约 80px) */ mdui-snackbar[placement="bottom"] { bottom: 88px !important; +} + +/* ==================== 当前节点标签文字颜色 ==================== */ + +/* 应用浅色模式:半透明白色文字 */ +.mdui-theme-light .current-tag-text { + color: rgba(255, 255, 255, 0.85) !important; +} + +/* 应用深色模式:半透明黑色文字 */ +.mdui-theme-dark .current-tag-text { + color: rgba(0, 0, 0, 0.75) !important; +} + +/* 应用自动模式:跟随系统深浅色模式 */ +.mdui-theme-auto .current-tag-text { + color: rgba(255, 255, 255, 0.85) !important; +} + +@media (prefers-color-scheme: dark) { + .mdui-theme-auto .current-tag-text { + color: rgba(0, 0, 0, 0.75) !important; + } } \ No newline at end of file diff --git a/src/webui/src/ui/app-page.ts b/src/webui/src/ui/app-page.ts index 65faf2c..a4e7688 100644 --- a/src/webui/src/ui/app-page.ts +++ b/src/webui/src/ui/app-page.ts @@ -31,6 +31,7 @@ export class AppPageManager { proxyMode: string; proxyApps: ProxyAppConfig[]; selectedApps: Map; + originalProxyApps: Map; users: UserInfo[]; currentUserId: string; showSystemApps: boolean; @@ -42,7 +43,8 @@ export class AppPageManager { this.allApps = []; this.proxyMode = 'blacklist'; this.proxyApps = []; - this.selectedApps = new Map(); // 用于多选, key: "userId:packageName" + this.selectedApps = new Map(); + this.originalProxyApps = new Map(); this.users = []; this.currentUserId = '0'; this.showSystemApps = false; // 默认不显示系统应用 (-3 only) @@ -326,6 +328,12 @@ export class AppPageManager { const listEl = document.getElementById('app-selector-list'); const addSelectedBtn = document.getElementById('app-selector-add-selected'); + // 清空搜索输入框并重置筛选状态 + const filterInput = document.getElementById('app-selector-search') as HTMLInputElement | null; + if (filterInput) { + filterInput.value = ''; + } + // 加载用户列表 const userSelect = document.getElementById('app-selector-user') as any; if (userSelect) { @@ -343,6 +351,21 @@ export class AppPageManager { // 清空选中状态 this.selectedApps.clear(); + this.originalProxyApps.clear(); + + // 预填充已在代理列表中的应用 + for (const proxyApp of this.proxyApps) { + const key = `${proxyApp.userId}:${proxyApp.packageName}`; + const appInfo: AppInfo = { + packageName: proxyApp.packageName, + userId: proxyApp.userId, + appLabel: proxyApp.appLabel, + icon: proxyApp.icon + }; + this.selectedApps.set(key, appInfo); + this.originalProxyApps.set(key, appInfo); + } + this.updateAddSelectedButton(); // 绑定批量添加按钮事件(每次打开都重新绑定) @@ -360,7 +383,9 @@ export class AppPageManager { if (btn) { const count = this.selectedApps.size; btn.textContent = I18nService.t('uid.btn_add_selected', { count: String(count) }); - btn.disabled = count === 0; + const hasChanges = count > 0 || + this.originalProxyApps.size !== this.selectedApps.size; + btn.disabled = !hasChanges; } } @@ -387,11 +412,28 @@ export class AppPageManager { } async addSelectedApps(): Promise { - if (this.selectedApps.size === 0) return; + // 找出需要移除的应用(在原始列表中但不在选中列表中) + const appsToRemove: AppInfo[] = []; + for (const [key, app] of this.originalProxyApps) { + if (!this.selectedApps.has(key)) { + appsToRemove.push(app); + } + } - const apps = Array.from(this.selectedApps.values()); + // 找出需要添加的应用 + const appsToAdd = Array.from(this.selectedApps.values()); - for (const app of apps) { + // 移除被取消勾选的应用 + for (const app of appsToRemove) { + try { + await AppService.removeProxyApp(app.packageName, app.userId); + } catch (error) { + // 忽略错误 + } + } + + // 添加新选中的应用 + for (const app of appsToAdd) { try { await AppService.addProxyApp(app.packageName, app.userId); } catch (error) { @@ -399,12 +441,20 @@ export class AppPageManager { } } - toast(I18nService.t('uid.toast_added_count', { count: String(apps.length) })); + // 根据操作显示提示 + if (appsToRemove.length > 0 && appsToAdd.length > 0) { + toast(I18nService.t('uid.toast_added_count', { count: String(appsToAdd.length) })); + } else if (appsToRemove.length > 0) { + toast(I18nService.t('uid.toast_removed')); + } else if (appsToAdd.length > 0) { + toast(I18nService.t('uid.toast_added_count', { count: String(appsToAdd.length) })); + } + // 关闭对话框并刷新列表 const dialog = document.getElementById('app-selector-dialog') as any; if (dialog) dialog.open = false; this.selectedApps.clear(); - // 清除缓存并强制刷新 + this.originalProxyApps.clear(); this.proxyApps = []; await this.update(true); } diff --git a/src/webui/src/ui/config-page.ts b/src/webui/src/ui/config-page.ts index 31756b3..c3ff374 100644 --- a/src/webui/src/ui/config-page.ts +++ b/src/webui/src/ui/config-page.ts @@ -524,7 +524,8 @@ export class ConfigPageManager { if (isCurrent) { const currentTag = document.createElement('span'); currentTag.textContent = I18nService.t('config.status.current'); - currentTag.style.cssText = 'font-size: 10px; padding: 1px 4px; border-radius: 4px; background: var(--mdui-color-primary); color: #ffffff;'; + currentTag.className = 'current-tag-text'; + currentTag.style.cssText = 'font-size: 10px; padding: 1px 4px; border-radius: 4px; background: var(--mdui-color-primary);'; protocolLine.appendChild(currentTag); } diff --git a/src/webui/src/ui/settings-page.ts b/src/webui/src/ui/settings-page.ts index 37b4b29..a61e578 100644 --- a/src/webui/src/ui/settings-page.ts +++ b/src/webui/src/ui/settings-page.ts @@ -50,6 +50,7 @@ export class SettingsPageManager { _logsAutoRefreshEnabled: boolean; _logsAutoRefreshInterval: ReturnType | null; _logsAutoRefreshMs: number; + lastAppliedThemeMode: string; constructor(ui: UI) { this.ui = ui; @@ -1037,6 +1038,7 @@ export class SettingsPageManager { loadThemeSettings(): void { const savedTheme = localStorage.getItem('theme') || 'auto'; const savedColor = localStorage.getItem('themeColor') || '#6750A4'; + this.lastAppliedThemeMode = savedTheme; // 设置模式选择 const modeGroup = document.getElementById('theme-mode-group') as any; @@ -1063,6 +1065,10 @@ export class SettingsPageManager { } applyThemeMode(mode) { + if (mode === this.lastAppliedThemeMode) { + return; + } + this.lastAppliedThemeMode = mode; localStorage.setItem('theme', mode); setTheme(mode); @@ -1264,7 +1270,7 @@ export class SettingsPageManager { const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const html = document.documentElement; - if (savedMonet === 'true' && savedTheme === 'auto') { + if (savedMonet !== 'false' && savedTheme === 'auto') { // 自动模式 + 莫奈取色开启:使用 KernelSU 注入的变量 html.classList.add('mdui-theme-auto'); html.classList.remove('mdui-theme-light', 'mdui-theme-dark'); @@ -1336,6 +1342,7 @@ export class SettingsPageManager { showAboutDialog() { const dialog = document.createElement('mdui-dialog') as any; + dialog.id = 'about-dialog'; dialog.headline = I18nService.t('settings.about.title'); dialog.innerHTML = `
diff --git a/src/webui/src/ui/ui-core.ts b/src/webui/src/ui/ui-core.ts index 077519a..22c2019 100644 --- a/src/webui/src/ui/ui-core.ts +++ b/src/webui/src/ui/ui-core.ts @@ -223,15 +223,31 @@ export class UI { applyTheme(theme: string): void { const html = document.documentElement; + const savedMonet = localStorage.getItem('monetEnabled'); + const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; - // 首先移除所有主题类 + // 移除所有主题类 html.classList.remove('mdui-theme-light', 'mdui-theme-dark', 'mdui-theme-auto'); - // 添加对应的主题类 - html.classList.add(`mdui-theme-${theme}`); - - // 同时调用MDUI的setTheme确保组件内部状态正确 - setTheme(theme as any); + if (theme === 'light') { + html.classList.add('mdui-theme-light'); + setTheme('light'); + } else if (theme === 'dark') { + html.classList.add('mdui-theme-dark'); + setTheme('dark'); + } else { + // 自动模式 + const monetEnabled = savedMonet !== 'false'; + if (monetEnabled) { + // 莫奈取色开启:使用 mdui-theme-auto + html.classList.add('mdui-theme-auto'); + setTheme('auto'); + } else { + // 莫奈取色关闭:根据系统偏好设置主题 + html.classList.add(isDark ? 'mdui-theme-dark' : 'mdui-theme-light'); + setTheme(isDark ? 'dark' : 'light'); + } + } } setupDialogs(): void {