From a1978d3c1cd93db4a8e56da35d4ed00e69242566 Mon Sep 17 00:00:00 2001 From: dangehub Date: Thu, 1 Jan 2026 12:23:24 +0800 Subject: [PATCH] =?UTF-8?q?pref(UI):=20=E4=BC=98=E5=8C=96=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E7=AB=AFUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lang/locale/en.ts | 21 ++- src/lang/locale/es.ts | 9 +- src/lang/locale/fr.ts | 3 + src/lang/locale/ja.ts | 11 +- src/lang/locale/ko.ts | 7 +- src/lang/locale/ru.ts | 3 + src/lang/locale/zh_cn.ts | 19 +- src/modal/manager-modal.ts | 346 ++++++++++++++++++++++++++++++++----- styles.css | 159 +++++++++++++++-- 9 files changed, 493 insertions(+), 85 deletions(-) diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 309a4dc..d2a83d8 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -15,8 +15,9 @@ export default { 通用_禁用_文本: 'Disable', 通用_关闭_文本: 'Disable', 通用_开启_文本: 'Enable', - 通用_保存_文本: 'Save settings', - 通用_取消_文本: 'Cancel', + 通用_保存_文本: 'Save settings', + 通用_取消_文本: 'Cancel', + 通用_返回_文本: 'Back', 导出_正文提示: 'Body section: you can edit or replace this content.', @@ -90,13 +91,14 @@ export default { 管理器_重载插件_描述: 'Reload plugins to take effect immediately', 管理器_重载插件_开始提示: 'Refreshing plugin manifests…', 管理器_重载插件_失败提示: 'Reload failed. Please try again (see console for details).', - 管理器_检查更新_描述: 'Check for plugin updates', - 管理器_一键禁用_描述: 'Disable all plugins at once', - 管理器_一键启用_描述: 'Enable all plugins at once', - 管理器_插件设置_描述: 'Manage plugin settings', - 管理器_仅启用_描述: 'Only display enabled plugins', - 管理器_未分组_描述: 'Filter all ungrouped plugins', - 管理器_打开设置_描述: 'Open the settings interface', + 管理器_检查更新_描述: 'Check for plugin updates', + 管理器_更多操作_描述: 'More actions', + 管理器_一键禁用_描述: 'Disable all plugins at once', + 管理器_一键启用_描述: 'Enable all plugins at once', + 管理器_插件设置_描述: 'Manage plugin settings', + 管理器_仅启用_描述: 'Only display enabled plugins', + 管理器_未分组_描述: 'Filter all ungrouped plugins', + 管理器_打开设置_描述: 'Open the settings interface', 管理器_还原内容_描述: 'Restore to the initial state', 管理器_打开目录_描述: 'Open the plugin directory', 管理器_删除插件_描述: 'Completely delete the plugin', @@ -106,6 +108,7 @@ export default { 管理器_重启中_提示: 'Restarting… please wait', 管理器_仓库检测中_提示: 'Detecting repository…', 管理器_打开仓库_提示: 'Open repo: {repo}', + 管理器_打开仓库_标题: 'Open repository', 管理器_仓库未记录_提示: 'Repository not recorded', 管理器_仓库需手动添加_提示: 'This plugin is not installed via official/BPM. Please set the source manually.', 下载更新_缺少仓库提示: 'Missing repository source; cannot download update.', diff --git a/src/lang/locale/es.ts b/src/lang/locale/es.ts index 7ef3c84..c84dbb9 100644 --- a/src/lang/locale/es.ts +++ b/src/lang/locale/es.ts @@ -11,15 +11,17 @@ export default { 通用_无延迟_文本: 'Sin retraso', 通用_过滤_文本: 'Filtrar', 通用_总计_文本: 'Total', - 通用_启用_文本: 'Habilitar', - 通用_禁用_文本: 'Deshabilitar', + 通用_启用_文本: 'Habilitar', + 通用_禁用_文本: 'Deshabilitar', + 通用_返回_文本: 'Volver', 管理器_GITHUB_描述: 'Visite la página de GitHub del autor para ver detalles del proyecto, registros de actualizaciones, participar en discusiones y contribuir con código.', 管理器_视频教程_描述: 'Acceder a tutoriales en video', 管理器_编辑模式_描述: 'Habilitar modo de edición para una personalización profunda de la configuración del plugin', 管理器_重载插件_描述: 'Recargar plugins para que surtan efecto inmediatamente', - 管理器_检查更新_描述: 'Comprobar actualizaciones de plugins', + 管理器_检查更新_描述: 'Comprobar actualizaciones de plugins', + 管理器_更多操作_描述: 'Más acciones', 管理器_一键禁用_描述: 'Deshabilitar todos los plugins a la vez', 管理器_一键启用_描述: 'Habilitar todos los plugins a la vez', 管理器_插件设置_描述: 'Administrar configuración de plugins', @@ -64,6 +66,7 @@ export default { 管理器_重启中_提示: 'Reiniciando… espera un momento', 管理器_仓库检测中_提示: 'Detectando repositorio…', 管理器_打开仓库_提示: 'Abrir repo: {repo}', + 管理器_打开仓库_标题: 'Abrir repositorio', 管理器_仓库未记录_提示: 'Repositorio no registrado', 管理器_仓库需手动添加_提示: 'Este plugin no es oficial/instalado por BPM. Añade el origen manualmente.', 下载更新_缺少仓库提示: 'Falta el repositorio; no se puede descargar la actualización.', diff --git a/src/lang/locale/fr.ts b/src/lang/locale/fr.ts index 2d07462..c2f91cd 100644 --- a/src/lang/locale/fr.ts +++ b/src/lang/locale/fr.ts @@ -17,6 +17,7 @@ export default { 通用_开启_文本: 'Ouvrir', 通用_保存_文本: 'Enregistrer', 通用_取消_文本: 'Annuler', + 通用_返回_文本: 'Retour', 导出_正文提示: 'Section corps : vous pouvez modifier ou remplacer ce contenu.', @@ -196,4 +197,6 @@ export default { 安装_错误_通用: 'Échec de l’installation. Vérifiez dépôt/version ou réseau.', 设置_基础设置_调试模式_标题: 'Mode debug', 设置_基础设置_调试模式_描述: 'Désactivé : erreurs uniquement. Activé : logs de debug.', + 管理器_更多操作_描述: 'Plus d’actions', + 管理器_打开仓库_标题: 'Ouvrir le dépôt', } diff --git a/src/lang/locale/ja.ts b/src/lang/locale/ja.ts index 55bfa5e..e7e02b7 100644 --- a/src/lang/locale/ja.ts +++ b/src/lang/locale/ja.ts @@ -12,7 +12,8 @@ export default { 通用_过滤_文本: 'フィルター', 通用_总计_文本: '合計', 通用_启用_文本: '有効', - 通用_禁用_文本: '無効', + 通用_禁用_文本: '無効', + 通用_返回_文本: '戻る', 管理器_GITHUB_描述: '著者のGitHubページを訪れ、プロジェクトの詳細、更新ログ、議論への参加、コードへの貢献を確認してください。', @@ -125,6 +126,8 @@ export default { 筛选_分组_全部: 'グループ:全部', 筛选_标签_全部: 'タグ:全部', 筛选_延迟_全部: '遅延:全部', - - 命令_管理面板_描述: 'プラグインマネージャーを開く', -} + + 命令_管理面板_描述: 'プラグインマネージャーを開く', + 管理器_更多操作_描述: 'その他の操作', + 管理器_打开仓库_标题: 'リポジトリを開く', +} diff --git a/src/lang/locale/ko.ts b/src/lang/locale/ko.ts index c09cd7c..8b76521 100644 --- a/src/lang/locale/ko.ts +++ b/src/lang/locale/ko.ts @@ -1,4 +1,4 @@ -export default { +export default { 通用_管理器_文本: '플러그인 관리자', 通用_成功_文本: '성공', 通用_失败_文本: '실패', @@ -12,7 +12,8 @@ export default { 通用_过滤_文本: '필터', 通用_总计_文本: '총계', 通用_启用_文本: '활성화', - 通用_禁用_文本: '비활성화', + 通用_禁用_文本: '비활성화', + 通用_返回_文本: '뒤로', 管理器_GITHUB_描述: '저자의 GitHub 페이지를 방문하여 프로젝트 세부 정보, 업데이트 로그, 토론 참여, 코드 기여를 확인하세요.', @@ -125,4 +126,6 @@ export default { 管理器_仓库未记录_提示: '저장소가 기록되지 않았습니다', 管理器_仓库需手动添加_提示: '이 플러그인은 공식/BPM 설치가 아닙니다. 소스를 수동으로 설정하세요.', 下载更新_缺少仓库提示: '저장소 정보가 없어 업데이트를 다운로드할 수 없습니다.', + 管理器_更多操作_描述: '더 많은 작업', + 管理器_打开仓库_标题: '저장소 열기', } diff --git a/src/lang/locale/ru.ts b/src/lang/locale/ru.ts index 261e4d1..5ff4d79 100644 --- a/src/lang/locale/ru.ts +++ b/src/lang/locale/ru.ts @@ -17,6 +17,7 @@ export default { 通用_开启_文本: 'Открыть', 通用_保存_文本: 'Сохранить', 通用_取消_文本: 'Отмена', + 通用_返回_文本: 'Назад', 导出_正文提示: 'Основной текст: вы можете изменить или заменить этот контент.', @@ -195,4 +196,6 @@ export default { 管理器_仓库未记录_提示: 'Репозиторий не указан', 管理器_仓库需手动添加_提示: 'Плагин не из официального/BPM источника. Укажите источник вручную.', 下载更新_缺少仓库提示: 'Нет источника репозитория — невозможно скачать обновление.', + 管理器_更多操作_描述: 'Дополнительные действия', + 管理器_打开仓库_标题: 'Открыть репозиторий', } diff --git a/src/lang/locale/zh_cn.ts b/src/lang/locale/zh_cn.ts index 28fe90d..c998722 100644 --- a/src/lang/locale/zh_cn.ts +++ b/src/lang/locale/zh_cn.ts @@ -15,8 +15,9 @@ export default { 通用_禁用_文本: '禁用', 通用_关闭_文本: '关闭', 通用_开启_文本: '开启', - 通用_保存_文本: '保存设置', - 通用_取消_文本: '取消', + 通用_保存_文本: '保存设置', + 通用_取消_文本: '取消', + 通用_返回_文本: '返回', 导出_正文提示: '正文区:这里的内容可自行编辑或替换。', @@ -32,12 +33,13 @@ export default { 管理器_重载插件_描述: '重载插件,即时生效', 管理器_重载插件_开始提示: '正在重新读取插件清单…', 管理器_重载插件_失败提示: '重载失败,请稍后重试(详情见控制台)。', - 管理器_检查更新_描述: '检查插件更新', - 管理器_一键禁用_描述: '一键禁用所有插件', - 管理器_一键启用_描述: '一键启用所有插件', - 管理器_插件设置_描述: '管理插件设置', - 管理器_仅启用_描述: '仅显示已启用插件', - 管理器_未分组_描述: '筛选所有未分组插件', + 管理器_检查更新_描述: '检查插件更新', + 管理器_更多操作_描述: '更多操作', + 管理器_一键禁用_描述: '一键禁用所有插件', + 管理器_一键启用_描述: '一键启用所有插件', + 管理器_插件设置_描述: '管理插件设置', + 管理器_仅启用_描述: '仅显示已启用插件', + 管理器_未分组_描述: '筛选所有未分组插件', 管理器_打开设置_描述: '打开设置界面', 管理器_还原内容_描述: '还原初始状态', 管理器_打开目录_描述: '打开插件目录', @@ -48,6 +50,7 @@ export default { 管理器_重启中_提示: '重启中,请稍等', 管理器_仓库检测中_提示: '正在检测仓库地址...', 管理器_打开仓库_提示: '打开仓库:{repo}', + 管理器_打开仓库_标题: '打开仓库', 管理器_仓库未记录_提示: '未记录仓库地址', 管理器_仓库需手动添加_提示: '本插件非官方/bpm安装,请手动添加来源', 下载更新_缺少仓库提示: '缺少仓库来源,无法下载更新', diff --git a/src/modal/manager-modal.ts b/src/modal/manager-modal.ts index a5bb343..56d3b9c 100644 --- a/src/modal/manager-modal.ts +++ b/src/modal/manager-modal.ts @@ -76,6 +76,7 @@ export class ManagerModal extends Modal { actionCollapsed = false; filterCollapsed = false; private reloadingManifests = false; + private mobileFiltersCollapsed = true; private showInlineProgress(text: string, subText?: string) { const notice = new Notice("", 0); @@ -184,12 +185,12 @@ export class ManagerModal extends Modal { return plugins; } - public async showHead() { - //@ts-ignore - const modalEl: HTMLElement = this.contentEl.parentElement; - this.modalContainer = modalEl; - modalEl.addClass("manager-container"); - if (Platform.isMobileApp) modalEl.addClass("manager-container--mobile"); + public async showHead() { + //@ts-ignore + const modalEl: HTMLElement = this.contentEl.parentElement; + this.modalContainer = modalEl; + modalEl.addClass("manager-container"); + if (Platform.isMobileApp) modalEl.addClass("manager-container--mobile"); // 靠上 if (!this.settings.CENTER && !Platform.isMobileApp) modalEl.addClass("manager-container__top"); if (this.editorMode) modalEl.addClass("manager-container--editing"); @@ -200,10 +201,15 @@ export class ManagerModal extends Modal { // 添加页尾 this.footEl = document.createElement("div"); this.footEl.addClass("manager-food"); - this.modalEl.appendChild(this.footEl); - - // [操作行] - const actionWrapper = this.titleEl.createDiv("manager-section manager-section--row"); + this.modalEl.appendChild(this.footEl); + + if (Platform.isMobileApp) { + this.showHeadMobile(); + return; + } + + // [操作行] + const actionWrapper = this.titleEl.createDiv("manager-section manager-section--row"); const actionHeader = actionWrapper.createDiv("manager-section__header"); const actionArrow = actionHeader.createSpan({ text: this.actionCollapsed ? "▼" : "▲" }); actionArrow.addClass("manager-section__arrow"); @@ -414,7 +420,7 @@ export class ManagerModal extends Modal { }); // [操作行] 插件设置 - const settingsButton = new ButtonComponent(actionBar.controlEl); + const settingsButton = new ButtonComponent(actionBar.controlEl); settingsButton.setIcon("settings"); settingsButton.setTooltip(this.manager.translator.t("管理器_插件设置_描述")); this.bindLongPressTooltip(settingsButton.buttonEl, this.manager.translator.t("管理器_插件设置_描述")); @@ -568,8 +574,182 @@ export class ManagerModal extends Modal { this.reloadShowData(); }); } + + private showHeadMobile() { + const t = (k: any) => this.manager.translator.t(k); + this.titleEl.empty(); + + const header = this.titleEl.createDiv("bpm-mobile-header"); + const topRow = header.createDiv("bpm-mobile-header__top"); + const title = topRow.createDiv("bpm-mobile-header__title"); + title.setText(this.manager.manifest.name); + + const topActions = topRow.createDiv("bpm-mobile-header__actions"); + + // 编辑模式(常驻) + const editorBtn = new ButtonComponent(topActions); + editorBtn.setIcon(this.editorMode ? "pen-off" : "pen"); + editorBtn.setTooltip(t("管理器_编辑模式_描述")); + this.bindLongPressTooltip(editorBtn.buttonEl, t("管理器_编辑模式_描述")); + editorBtn.onClick(async () => { + this.editorMode = !this.editorMode; + this.applyEditingStyle(); + // 退出编辑模式需要刷新筛选项统计,但要保留滚动位置 + if (!this.editorMode) { + await this.refreshFilterOptions(true); + } else { + this.renderContent(); + } + this.showHeadMobile(); + }); + + // 安装/返回(切换安装模式) + const installBtn = new ButtonComponent(topActions); + installBtn.setIcon(this.installMode ? "arrow-left" : "download"); + installBtn.setTooltip(this.installMode ? t("通用_返回_文本") : t("管理器_安装_GITHUB_描述")); + this.bindLongPressTooltip(installBtn.buttonEl, this.installMode ? t("通用_返回_文本") : t("管理器_安装_GITHUB_描述")); + installBtn.onClick(() => { + this.installMode = !this.installMode; + if (this.searchBarEl) { + this.installMode ? this.searchBarEl.addClass("manager-display-none") : this.searchBarEl.removeClass("manager-display-none"); + } + this.renderContent(); + this.showHeadMobile(); + }); + + // 更多操作菜单 + const moreBtn = new ButtonComponent(topActions); + moreBtn.setIcon("more-vertical"); + moreBtn.setTooltip(t("管理器_更多操作_描述")); + this.bindLongPressTooltip(moreBtn.buttonEl, t("管理器_更多操作_描述")); + moreBtn.buttonEl.addEventListener("click", (ev) => { + const menu = new Menu(); + menu.addItem((item) => item.setTitle(t("管理器_检查更新_描述")).setIcon("rss").onClick(async () => { + await this.manager.checkUpdatesWithNotice(); + await this.reloadShowData(); + })); + menu.addItem((item) => item.setTitle(t("管理器_重载插件_描述")).setIcon("refresh-ccw").onClick(async () => { + await this.appPlugins.loadManifests(); + await this.reloadShowData(); + })); + menu.addSeparator(); + menu.addItem((item) => item.setTitle(t("管理器_插件设置_描述")).setIcon("settings").onClick(() => { + this.appSetting.open(); + this.appSetting.openTabById(this.manager.manifest.id); + })); + menu.addItem((item) => item.setTitle(t("管理器_GITHUB_描述")).setIcon("github").onClick(() => { + window.open("https://github.com/zenozero-dev/obsidian-manager"); + })); + menu.addItem((item) => item.setTitle(t("管理器_视频教程_描述")).setIcon("book-open").onClick(() => { + window.open("https://www.bilibili.com/video/BV1WyrkYMEce/"); + })); + menu.showAtMouseEvent(ev as MouseEvent); + }); + + const searchWrap = header.createDiv("bpm-mobile-header__search"); + this.searchEl = new SearchComponent(searchWrap); + if (this.settings.PERSISTENCE && typeof this.settings.FILTER_SEARCH === "string") { + this.searchText = this.settings.FILTER_SEARCH; + this.searchEl.inputEl.value = this.searchText; + } + this.searchEl.onChange((value: string) => { + this.searchText = value; + if (this.settings.PERSISTENCE) { + this.settings.FILTER_SEARCH = value; + this.manager.saveSettings(); + } + this.reloadShowData(); + }); + + const filterHeader = header.createDiv("bpm-mobile-header__filters-toggle"); + const arrow = filterHeader.createSpan({ cls: "bpm-mobile-header__filters-arrow" }); + arrow.setText(this.mobileFiltersCollapsed ? "▼" : "▲"); + filterHeader.createSpan({ text: t("通用_过滤_文本") }); + filterHeader.toggleClass("is-open", !this.mobileFiltersCollapsed); + filterHeader.addEventListener("click", () => { + this.mobileFiltersCollapsed = !this.mobileFiltersCollapsed; + filterPanel.toggleClass("is-collapsed", this.mobileFiltersCollapsed); + filterHeader.toggleClass("is-open", !this.mobileFiltersCollapsed); + arrow.setText(this.mobileFiltersCollapsed ? "▼" : "▲"); + }); + + const filterPanel = header.createDiv(`bpm-mobile-header__filters${this.mobileFiltersCollapsed ? " is-collapsed" : ""}`); + + // 状态 + const statusSetting = new Setting(filterPanel).setName(t("筛选_状态_全部")); + statusSetting.addDropdown((dd) => { + dd.addOptions({ + "all": t("筛选_状态_全部"), + "enabled": t("筛选_仅启用_描述"), + "disabled": t("筛选_仅禁用_描述"), + "grouped": t("筛选_已分组_描述"), + "ungrouped": t("筛选_未分组_描述"), + "tagged": t("筛选_有标签_描述"), + "untagged": t("筛选_无标签_描述"), + "noted": t("筛选_有笔记_描述"), + }); + dd.setValue(this.filter || "all"); + dd.onChange((v) => { this.filter = v; this.reloadShowData(); }); + }); + + // 分组 + const groupCounts = this.settings.Plugins.reduce((acc: { [key: string]: number }, plugin) => { const groupId = plugin.group || ""; acc[groupId] = (acc[groupId] || 0) + 1; return acc; }, { "": 0 }); + const groups = this.settings.GROUPS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} [${groupCounts[item.id] || 0}]`; return acc; }, { "": t("筛选_分组_全部") }); + const groupSetting = new Setting(filterPanel).setName(t("筛选_分组_全部")); + groupSetting.addDropdown((dd) => { + dd.addOptions(groups); + dd.setValue(this.settings.PERSISTENCE ? this.settings.FILTER_GROUP : this.group); + dd.onChange((value) => { + if (this.settings.PERSISTENCE) { + this.settings.FILTER_GROUP = value; + this.manager.saveSettings(); + } else { + this.group = value; + } + this.reloadShowData(); + }); + }); + + // 标签 + const tagCounts: { [key: string]: number } = this.settings.Plugins.reduce((acc, plugin) => { plugin.tags.forEach((tag) => { acc[tag] = (acc[tag] || 0) + 1; }); return acc; }, {} as { [key: string]: number }); + const tags = this.settings.TAGS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} [${tagCounts[item.id] || 0}]`; return acc; }, { "": t("筛选_标签_全部") }); + const tagSetting = new Setting(filterPanel).setName(t("筛选_标签_全部")); + tagSetting.addDropdown((dd) => { + dd.addOptions(tags); + dd.setValue(this.settings.PERSISTENCE ? this.settings.FILTER_TAG : this.tag); + dd.onChange((value) => { + if (this.settings.PERSISTENCE) { + this.settings.FILTER_TAG = value; + this.manager.saveSettings(); + } else { + this.tag = value; + } + this.reloadShowData(); + }); + }); + + // 延迟 + if (this.settings.DELAY) { + const delayCounts = this.settings.Plugins.reduce((acc: { [key: string]: number }, plugin) => { const delay = plugin.delay || ""; acc[delay] = (acc[delay] || 0) + 1; return acc; }, { "": 0 }); + const delays = this.settings.DELAYS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} (${delayCounts[item.id] || 0})`; return acc; }, { "": t("筛选_延迟_全部") }); + const delaySetting = new Setting(filterPanel).setName(t("筛选_延迟_全部")); + delaySetting.addDropdown((dd) => { + dd.addOptions(delays); + dd.setValue(this.settings.PERSISTENCE ? this.settings.FILTER_DELAY : this.delay); + dd.onChange((value) => { + if (this.settings.PERSISTENCE) { + this.settings.FILTER_DELAY = value; + this.manager.saveSettings(); + } else { + this.delay = value; + } + this.reloadShowData(); + }); + }); + } + } - public async showData() { + public async showData() { // 使用 manifests 按 id 去重,防止重复渲染 const manifestMap = this.appPlugins.manifests; if (this.settings.DEBUG) console.log("[BPM] render showData manifests size:", Object.keys(manifestMap).length); @@ -922,20 +1102,20 @@ export class ManagerModal extends Modal { const arrow = createSpan({ text: "→", cls: ["manager-item__name-remote-arrow"] }); versionWrap.appendChild(arrow); const remote = createSpan({ text: `${updateInfo.remoteVersion}`, cls: ["manager-item__name-remote"] }); - versionWrap.appendChild(remote); - if (!this.editorMode) { - const downloadBtn = new ExtraButtonComponent(itemEl.controlEl); - downloadBtn.setIcon("download"); - downloadBtn.setTooltip(this.manager.translator.t("管理器_下载更新_描述")); - downloadBtn.onClick(() => { - const versions = updateInfo.versions && updateInfo.versions.length > 0 - ? updateInfo.versions - : [{ version: updateInfo.remoteVersion!, prerelease: false }]; - new UpdateModal(this.app, this.manager, plugin.id, versions, updateInfo.remoteVersion, updateInfo.repo || undefined).open(); - }); - } - } - itemEl.nameEl.appendChild(versionWrap); + versionWrap.appendChild(remote); + if (!this.editorMode && !Platform.isMobileApp) { + const downloadBtn = new ExtraButtonComponent(itemEl.controlEl); + downloadBtn.setIcon("download"); + downloadBtn.setTooltip(this.manager.translator.t("管理器_下载更新_描述")); + downloadBtn.onClick(() => { + const versions = updateInfo.versions && updateInfo.versions.length > 0 + ? updateInfo.versions + : [{ version: updateInfo.remoteVersion!, prerelease: false }]; + new UpdateModal(this.app, this.manager, plugin.id, versions, updateInfo.remoteVersion, updateInfo.repo || undefined).open(); + }); + } + } + itemEl.nameEl.appendChild(versionWrap); // [默认] 笔记图标 if (ManagerPlugin.note?.length > 0) { @@ -993,8 +1173,89 @@ export class ManagerModal extends Modal { tags.appendChild(tag); } - if (!this.editorMode) { - // [按钮] 打开仓库 + if (!this.editorMode) { + const isMobile = Platform.isMobileApp; + + let openPluginSetting: ExtraButtonComponent | null = null; + let openPluginSettingEl: HTMLElement | undefined; + + if (isMobile) { + const moreButton = new ExtraButtonComponent(itemEl.controlEl); + moreButton.setIcon("more-vertical"); + moreButton.setTooltip(this.manager.translator.t("管理器_更多操作_描述")); + const moreEl = ((moreButton as any).extraSettingsEl || (moreButton as any).buttonEl) as HTMLElement | undefined; + this.bindLongPressTooltip(moreEl, this.manager.translator.t("管理器_更多操作_描述")); + moreEl?.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + const menu = new Menu(); + menu.addItem((item) => item + .setTitle(this.manager.translator.t("管理器_检查更新_描述")) + .setIcon("rss") + .onClick(async () => { + await this.manager.checkUpdateForPlugin(plugin.id); + await this.reloadShowData(); + })); + if (updateInfo?.hasUpdate && updateInfo.remoteVersion) { + menu.addItem((item) => item + .setTitle(this.manager.translator.t("管理器_下载更新_描述")) + .setIcon("download") + .onClick(() => { + const versions = updateInfo.versions && updateInfo.versions.length > 0 + ? updateInfo.versions + : [{ version: updateInfo.remoteVersion!, prerelease: false }]; + new UpdateModal(this.app, this.manager, plugin.id, versions, updateInfo.remoteVersion, updateInfo.repo || undefined).open(); + })); + } + menu.addSeparator(); + menu.addItem((item) => item + .setTitle(this.manager.translator.t("管理器_打开设置_描述")) + .setIcon("settings") + .setDisabled(!isEnabled) + .onClick(() => { + this.appSetting.open(); + this.appSetting.openTabById(plugin.id); + })); + menu.addItem((item) => item + .setTitle(this.manager.translator.t("管理器_打开目录_描述")) + .setIcon("folder-open") + .onClick(() => { + managerOpen(pluginDir, this.manager); + })); + menu.addItem((item) => item + .setTitle(this.manager.translator.t("管理器_打开仓库_标题")) + .setIcon("github") + .onClick(async () => { + const repo = await this.manager.repoResolver.resolveRepo(plugin.id); + if (repo) { + window.open(`https://github.com/${repo}`); + } else { + const isBpmInstall = this.manager.settings.BPM_INSTALLED.includes(plugin.id); + new Notice(isBpmInstall + ? this.manager.translator.t("管理器_仓库未记录_提示") + : this.manager.translator.t("管理器_仓库需手动添加_提示")); + } + })); + menu.addSeparator(); + menu.addItem((item) => item + .setTitle(this.manager.translator.t("管理器_删除插件_描述")) + .setIcon("trash") + .setDisabled(isSelf) + .onClick(async () => { + if (isSelf) return; + new DeleteModal(this.app, this.manager, async () => { + await this.appPlugins.uninstallPlugin(plugin.id); + await this.appPlugins.loadManifests(); + this.reloadShowData(); + Commands(this.app, this.manager); + this.manager.synchronizePlugins(Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manager.manifest.id) as PluginManifest[]); + new Notice(this.manager.translator.t("卸载_通知_一")); + }).open(); + })); + menu.showAtMouseEvent(event as MouseEvent); + }); + } else { + // [按钮] 打开仓库 const openRepoButton = new ExtraButtonComponent(itemEl.controlEl); openRepoButton.setIcon("github"); openRepoButton.setTooltip(this.manager.translator.t("管理器_仓库检测中_提示")); @@ -1012,23 +1273,23 @@ export class ManagerModal extends Modal { } // [按钮] 打开设置 - const openPluginSetting = new ExtraButtonComponent(itemEl.controlEl); + openPluginSetting = new ExtraButtonComponent(itemEl.controlEl); openPluginSetting.setIcon("settings"); openPluginSetting.setTooltip(this.manager.translator.t("管理器_打开设置_描述")); openPluginSetting.onClick(() => { - openPluginSetting.setDisabled(true); + openPluginSetting?.setDisabled(true); this.appSetting.open(); this.appSetting.openTabById(plugin.id); - openPluginSetting.setDisabled(false); + openPluginSetting?.setDisabled(false); }); - const openPluginSettingEl = ((openPluginSetting as any).extraSettingsEl || (openPluginSetting as any).buttonEl) as HTMLElement | undefined; + openPluginSettingEl = ((openPluginSetting as any).extraSettingsEl || (openPluginSetting as any).buttonEl) as HTMLElement | undefined; if (!isEnabled) { openPluginSetting.setDisabled(true); if (openPluginSettingEl) openPluginSettingEl.style.display = "none"; } - - // [按钮] 打开目录 - const openPluginDirButton = new ExtraButtonComponent(itemEl.controlEl); + + // [按钮] 打开目录 + const openPluginDirButton = new ExtraButtonComponent(itemEl.controlEl); openPluginDirButton.setIcon("folder-open"); openPluginDirButton.setTooltip(this.manager.translator.t("管理器_打开目录_描述")); openPluginDirButton.onClick(() => { @@ -1052,10 +1313,11 @@ export class ManagerModal extends Modal { Commands(this.app, this.manager); // 删除同理 this.manager.synchronizePlugins(Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manager.manifest.id) as PluginManifest[]); - new Notice(this.manager.translator.t("卸载_通知_一")); - }).open(); - }); - + new Notice(this.manager.translator.t("卸载_通知_一")); + }).open(); + }); + } + // [按钮] 切换状态 const toggleSwitch = new ToggleComponent(itemEl.controlEl); toggleSwitch.setTooltip(this.manager.translator.t("管理器_切换状态_描述")); @@ -1073,8 +1335,10 @@ export class ManagerModal extends Modal { itemEl.settingEl.toggleClass("inactive", !targetEnabled); } // 同步“打开设置”按钮(启用后出现,禁用后隐藏) - openPluginSetting.setDisabled(!targetEnabled); - if (openPluginSettingEl) openPluginSettingEl.style.display = targetEnabled ? "" : "none"; + if (openPluginSetting) { + openPluginSetting.setDisabled(!targetEnabled); + if (openPluginSettingEl) openPluginSettingEl.style.display = targetEnabled ? "" : "none"; + } // 按需从当前视图移除,避免全量重绘 if (removeByFilter) { itemEl.settingEl.detach(); diff --git a/styles.css b/styles.css index 7ea2d06..01c1552 100644 --- a/styles.css +++ b/styles.css @@ -217,20 +217,40 @@ body.theme-dark { gap: 6px; } -.manager-container--mobile .manager-item { - flex-direction: column; - align-items: flex-start; - padding: clamp(10px, 2vw, 14px); - border: 1px solid var(--background-modifier-border); - border-radius: 12px; - background-color: var(--background-secondary); -} - -.manager-container--mobile .manager-item__name-container { - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; +.manager-container--mobile .manager-item { + flex-direction: column; + align-items: flex-start; + padding: clamp(10px, 2vw, 14px); + border: 1px solid var(--background-modifier-border); + border-radius: 12px; + background-color: var(--background-secondary); +} + +.manager-container--mobile .manager-item__controls.setting-item-control { + width: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + flex-wrap: nowrap; +} + +.manager-container--mobile .manager-item__controls .clickable-icon { + width: 40px; + height: 40px; + border-radius: 12px; + background: var(--background-primary); + border: 1px solid var(--background-modifier-border); + display: inline-flex; + align-items: center; + justify-content: center; +} + +.manager-container--mobile .manager-item__name-container { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; } .manager-container--mobile .manager-item__description-container { @@ -390,7 +410,7 @@ body.theme-dark { } } -@media (min-width: 600px) and (orientation: landscape) { +@media (min-width: 600px) and (orientation: landscape) { .manager-container--mobile { top: calc(3vh + env(safe-area-inset-top, 0px)); height: calc(100vh - 6vh - env(safe-area-inset-top, 0px)); @@ -406,9 +426,112 @@ body.theme-dark { .manager-container--mobile .manager-item-container { max-height: calc(100vh - 200px); } -} - -/* 目录容器 */ +} + +/* mobile header (separate UI) */ +.manager-container--mobile .bpm-mobile-header { + display: flex; + flex-direction: column; + gap: 10px; + padding: 10px 12px 8px; + margin: 0 8px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 12px; + background: var(--background-secondary); +} + +.manager-container--mobile .bpm-mobile-header__top { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.manager-container--mobile .bpm-mobile-header__title { + flex: 1 1 auto; + min-width: 0; + font-size: 16px; + font-weight: 700; + color: var(--text-normal); + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.manager-container--mobile .bpm-mobile-header__actions { + flex: 0 0 auto; + display: flex; + align-items: center; + gap: 8px; +} + +.manager-container--mobile .bpm-mobile-header__actions button { + width: 40px; + height: 40px; + padding: 0; + border-radius: 12px; + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--background-primary); + border: 1px solid var(--background-modifier-border); +} + +.manager-container--mobile .bpm-mobile-header__actions button svg { + width: 18px; + height: 18px; +} + +.manager-container--mobile .bpm-mobile-header__search .search-input-container, +.manager-container--mobile .bpm-mobile-header__search .search-input-container input { + width: 100%; +} + +.manager-container--mobile .bpm-mobile-header__filters-toggle { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + border: 1px solid var(--background-modifier-border); + border-radius: 12px; + background: var(--background-primary); + cursor: pointer; + font-weight: 600; + user-select: none; +} + +.manager-container--mobile .bpm-mobile-header__filters-toggle.is-open { + border-color: color-mix(in srgb, var(--text-accent) 55%, var(--background-modifier-border)); + background: var(--background-secondary-alt); + color: var(--text-accent); +} + +.manager-container--mobile .bpm-mobile-header__filters-arrow { + font-size: 12px; + line-height: 1; +} + +.manager-container--mobile .bpm-mobile-header__filters { + padding: 8px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 12px; + background: var(--background-primary); +} + +.manager-container--mobile .bpm-mobile-header__filters.is-collapsed { + display: none !important; +} + +.manager-container--mobile .bpm-mobile-header__filters .setting-item { + padding: 8px 0; +} + +.manager-container--mobile .bpm-mobile-header__filters .setting-item + .setting-item { + border-top: 1px solid var(--background-modifier-border); +} + +/* 目录容器 */ .manager-item-container { overflow: auto; padding: 8px;