├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── README_CN.md ├── esbuild.config.mjs ├── img ├── 1.png ├── 2.png ├── 3.png └── index.png ├── main.ts ├── manifest.json ├── package-lock.json ├── package.json ├── src ├── agreement.ts ├── command.ts ├── data │ ├── data.ts │ └── types.ts ├── lang │ ├── inxdex.ts │ └── locale │ │ ├── en.ts │ │ ├── es.ts │ │ ├── fr.ts │ │ ├── ja.ts │ │ ├── ko.ts │ │ ├── ru.ts │ │ └── zh_cn.ts ├── main.ts ├── modal │ ├── delete-modal.ts │ ├── disable-modal.ts │ ├── group-modal.ts │ ├── hide-modal.ts │ ├── manager-modal.ts │ ├── note-modal.ts │ ├── share-modal.ts │ ├── share-t-modal.ts │ └── tags-modal.ts ├── settings │ ├── base-setting.ts │ ├── data.ts │ ├── index.ts │ └── ui │ │ ├── manager-basis.ts │ │ ├── manager-delay.ts │ │ ├── manager-group.ts │ │ ├── manager-style.ts │ │ └── manager-tag.ts └── utils.ts ├── styles.css ├── tsconfig.json ├── version-bump.mjs └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = tab 9 | indent_size = 4 10 | tab_width = 4 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | main.js 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "env": { "node": true }, 5 | "plugins": [ 6 | "@typescript-eslint" 7 | ], 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "parserOptions": { 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-unused-vars": "off", 18 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 19 | "@typescript-eslint/ban-ts-comment": "off", 20 | "no-prototype-builtins": "off", 21 | "@typescript-eslint/no-empty-function": "off" 22 | } 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # 不要将编译后的 `main.js` 文件放入代码库中。应该将其上传到 GitHub 发布版中。 12 | main.js 13 | 14 | # Exclude sourcemaps 15 | *.map 16 | 17 | # obsidian 18 | data.json 19 | 20 | # 排除macOS Finder (System Explorer)视图状态 21 | .DS_Store 22 | 23 | # 无 24 | 笔记.md 25 | repealed.ts 26 | README.md 27 | i18n -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 zero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Better Plugins Manager

3 | GitHub Downloads (all assets, all releases) 4 | GitHub release (latest by date) 5 | GitHub last commit 6 | GitHub issues 7 | GitHub stars 8 |

9 | 10 |
11 | Obsidian Iconize 12 |
13 | 14 | 15 | [简体中文](https://github.com/0011000000110010/obsidian-manager/blob/main/README_CN.md) | English 16 | 17 | In the ecosystem of Obsidian, plugins play a key role in expanding functionality and enhancing workflow efficiency. Although Obsidian provides basic plugin management capabilities, for users who pursue higher management efficiency and personalized experiences, these may not be sufficient. Better Plugins Manager (BPM) is born out of this need, aiming to surpass basic functionalities and offer a more refined and powerful plugin management solution. 18 | 19 | ## Features 20 | 21 | - **Plugin Delayed Startup**: BPM allows you to set personalized delay times for each plugin, reducing startup time and enhancing application performance. 22 | - **Intuitive and User-Friendly Interface**: BPM's clear and intuitive interface design simplifies the plugin management and configuration process, making your operations more intuitive and convenient. 23 | - **Customizable Interface**: BPM offers personalized settings, allowing you to customize the interface according to your preferences and workflow. 24 | - **Convenient Directory Access**: BPM lets you access the plugin directory with one click, making it easy to manage and edit at a deeper level, meeting your advanced management needs. 25 | - **Quick Plugin Toggle**: BPM simplifies the process of enabling and disabling plugins with one-click operations, allowing you to respond to changes quickly without going through complicated setup steps. 26 | - **Plugin Renaming**: BPM allows you to rename plugins based on their actual function or personal preference, making your plugin list more intuitive and personalized. 27 | - **Description Editing**: BPM allows you to edit plugin descriptions based on their actual use or personal experience, making your plugin list more aligned with your personal workflow. 28 | - **Group Management**: BPM introduces group management functionality, allowing you to logically group plugins for easier management 和 quick access, enhancing work efficiency. 29 | - **Tagging Management**: BPM enables you to add personalized tags to plugins, whether by function, frequency of use, or personal categorization, making your plugin management more organized. 30 | - **Advanced Search**: BPM offers an advanced search feature, allowing you to quickly locate the plugins you need, whether by name, group, or tag, making it easy to find them. -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 |
2 |

Better Plugins Manager

3 | GitHub Downloads (all assets, all releases) 4 | GitHub release (latest by date) 5 | GitHub last commit 6 | GitHub issues 7 | GitHub stars 8 |

9 | 10 |
11 | Obsidian Iconize 12 |
13 | 14 | 15 | 简体中文 | [English](https://github.com/0011000000110010/obsidian-manager/blob/main/README.md) 16 | 17 | 在Obsidian的生态中,插件扮演着扩展功能和提升工作流效率的关键角色。尽管Obsidian提供了基本的插件管理功能,但对于追求更高管理效率和个性化体验的用户来说,这些可能还远远不够。Better Plugins Manager(BPM)因此而生,旨在超越基础功能,提供更精致、更强大的插件管理解决方案。 18 | 19 | ## 特色功能 20 | 21 | - 插件延时启动:BPM允许您为每个插件设定个性化的延时启动时间。从而减少启动时间,提升应用性能。 22 | - 界面直观易用:BPM以其清晰、直观的界面设计,简化了插件管理和配置流程,让您的操作更加直观和便捷。 23 | - 界面个性设置:BPM提供了个性化设置,让您根据个人喜好和工作流程定制界面。 24 | - 便捷目录访问:BPM让您能够一键直达插件目录,轻松进行深层次管理和编辑,满足您对高级管理的需求。 25 | - 插件快速切换:BPM通过一键操作简化了插件的开启和关闭过程,让您能够迅速响应变化,无需经历繁琐的设置步骤。 26 | - 名称编辑功能:BPM允许您根据插件的实际作用或个人喜好,重新命名插件,使您的插件列表更加直观和个性化。 27 | - 描述编辑功能:BPM允许您根据插件的实际用途或个人使用体验,编辑插件描述。让您的插件列表更加贴合个人工作方式。 28 | - 分组管理功能:BPM引入了分组管理功能,让您能够将插件逻辑分组,便于管理和快速访问,提升工作效率。 29 | - 添加管理功能:BPM允许您为插件添加个性化标签,无论是按功能、使用频率还是个人分类,都能让您的插件管理更加有序。 30 | - 丰富搜索功能:BPM提供了更丰富的搜索功能,让您能够快速定位到所需的插件,无论是通过名称、分组还是标签,都能轻松找到。 31 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | 5 | const banner = ``; 6 | 7 | const prod = (process.argv[2] === "production"); 8 | 9 | const context = await esbuild.context({ 10 | banner: { 11 | js: banner, 12 | }, 13 | // 修改启动路径 14 | entryPoints: ["main.ts"], 15 | bundle: true, 16 | external: [ 17 | "obsidian", 18 | "electron", 19 | "@codemirror/autocomplete", 20 | "@codemirror/collab", 21 | "@codemirror/commands", 22 | "@codemirror/language", 23 | "@codemirror/lint", 24 | "@codemirror/search", 25 | "@codemirror/state", 26 | "@codemirror/view", 27 | "@lezer/common", 28 | "@lezer/highlight", 29 | "@lezer/lr", 30 | ...builtins], 31 | format: "cjs", 32 | target: "es2018", 33 | logLevel: "info", 34 | sourcemap: prod ? false : "inline", 35 | treeShaking: true, 36 | outfile: "main.js", 37 | }); 38 | 39 | if (prod) { 40 | await context.rebuild(); 41 | process.exit(0); 42 | } else { 43 | await context.watch(); 44 | } -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Obsidian-Forge/obsidian-manager/3da2ea26900103deff8a1e1a2943bbcb23406c6c/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Obsidian-Forge/obsidian-manager/3da2ea26900103deff8a1e1a2943bbcb23406c6c/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Obsidian-Forge/obsidian-manager/3da2ea26900103deff8a1e1a2943bbcb23406c6c/img/3.png -------------------------------------------------------------------------------- /img/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Obsidian-Forge/obsidian-manager/3da2ea26900103deff8a1e1a2943bbcb23406c6c/img/index.png -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import Manager from './src/main' 2 | 3 | export default Manager 4 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "better-plugins-manager", 3 | "name": "Better Plugins Manager", 4 | "version": "0.2.0", 5 | "minAppVersion": "1.5.8", 6 | "description": "Plugin Manager: Simplify, Enhance, Personalize | 插件管理器:简化操作、增强功能、个性化设置", 7 | "author": "zero", 8 | "authorUrl": "https://github.com/0011000000110010/", 9 | "isDesktopOnly": true 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-sample-plugin", 3 | "version": "1.0.0", 4 | "description": "This is a sample plugin for Obsidian (https://obsidian.md)", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "version": "node version-bump.mjs && git add manifest.json versions.json" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/node": "^16.11.6", 16 | "@typescript-eslint/eslint-plugin": "5.29.0", 17 | "@typescript-eslint/parser": "5.29.0", 18 | "builtin-modules": "3.3.0", 19 | "esbuild": "0.17.3", 20 | "obsidian": "latest", 21 | "tslib": "2.4.0", 22 | "typescript": "4.7.4" 23 | } 24 | } -------------------------------------------------------------------------------- /src/agreement.ts: -------------------------------------------------------------------------------- 1 | import ShareMyPlugin from "main"; 2 | import { Notice, ObsidianProtocolData, debounce } from "obsidian"; 3 | 4 | // 导出一个全局的 communityPlugins 变量,可在其他模块中使用 5 | export let communityPlugins: any; 6 | 7 | /** 8 | * 插件安装器类,负责处理插件的安装和解析安装参数等操作 9 | */ 10 | 11 | 12 | export default class Agreement { 13 | // 引用 ShareMyPlugin 实例,方便访问主插件的属性和方法 14 | plugin: ShareMyPlugin; 15 | // 存储社区插件信息的对象,键为插件 ID,值为插件详细信息 16 | communityPlugins: Record; 17 | // 标记是否已经加载了社区插件列表 18 | loaded: boolean = false; 19 | // 防抖函数,用于定时刷新社区插件列表,每小时执行一次 20 | debounceFetch = debounce(async () => { await this.fetchCommunityPlugins() }, 1000 * 60 * 60); 21 | 22 | /** 23 | * 从远程获取社区插件列表,并将其转换为以插件 ID 为键的对象 24 | */ 25 | async fetchCommunityPlugins() { 26 | // 从指定的 URL 获取社区插件列表的 JSON 数据 27 | const pluginList = await fetch(`https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugins.json`).then(r => r.json()); 28 | // if (!pluginList.ok) { new Notice(`[插件管理器] 无法连接到Github(跳转主页及下载不可用)`); } 29 | // 创建一个空对象,用于存储以插件 ID 为键的插件信息 30 | const keyedPluginList: Record = {}; 31 | // 遍历插件列表,将每个插件的信息存储到 keyedPluginList 中 32 | for (const item of pluginList) keyedPluginList[item.id] = item; 33 | // 将处理后的插件列表赋值给 communityPlugins 属性 34 | this.communityPlugins = keyedPluginList; 35 | // 标记社区插件列表已加载 36 | this.loaded = true; 37 | } 38 | 39 | /** 40 | * 构造函数,初始化插件安装器 41 | * @param SMPL - ShareMyPlugin 实例 42 | */ 43 | constructor(SMPL: ShareMyPlugin) { 44 | // 保存 ShareMyPlugin 实例 45 | this.plugin = SMPL; 46 | // 调用 fetchCommunityPlugins 方法获取社区插件列表 47 | this.fetchCommunityPlugins(); 48 | } 49 | 50 | /** 51 | * 获取指定的插件 52 | * @param id - 要获取的插件的 ID 53 | */ 54 | public async pluginGithub(id: string) { 55 | // 如果社区插件列表未加载,则先加载 56 | if (!this.loaded) { 57 | await this.fetchCommunityPlugins(); 58 | } 59 | // 从社区插件列表中查找对应插件的 repo 信息 60 | const pluginInfo = this.communityPlugins[id]; 61 | 62 | if (!pluginInfo) { 63 | new Notice(`[插件管理器] 未知插件ID: ${id}`); 64 | return null; 65 | } 66 | window.open(`https://github.com/${pluginInfo.repo}`); 67 | } 68 | 69 | /** 70 | * 安装指定的插件 71 | * @param id - 要安装的插件的 ID 72 | * @param version - 要安装的插件的版本,默认为空字符串,表示不检查版本 73 | * @param enable - 安装后是否启用插件,默认为 false 74 | * @param github - 插件的 GitHub 仓库地址,默认为空字符串 75 | */ 76 | public async pluginInstall(id: string, version: string = "", enable: boolean = false, github: string = "") { 77 | // 打印日志,记录开始安装插件的信息 78 | // console.log(`[插件管理器] 开始安装插件 -- ${id} - ${version} - ${enable} - ${github}`); 79 | // 如果社区插件列表未加载,则先加载 否则,触发防抖函数,定时刷新社区插件列表 80 | if (!this.loaded) await this.fetchCommunityPlugins(); else this.debounceFetch(); 81 | 82 | // 获取 Obsidian 应用的插件注册表 83 | // @ts-ignore 84 | const pluginRegistry = this.plugin.app.plugins; 85 | 86 | // 标记是否需要安装插件 87 | let installFlag = false; 88 | // 获取插件的仓库地址,如果提供了 github 参数,则使用该参数,否则从社区插件列表中获取 89 | const repo = github !== "" ? github : this.communityPlugins[id]?.repo; 90 | console.log(repo) 91 | // 如果找不到插件的仓库地址,显示提示信息并返回 92 | if (!repo) { 93 | new Notice(`[插件管理器] 未知插件ID: ${id}`); 94 | return; 95 | } 96 | 97 | // 检查插件是否已经安装 98 | if (pluginRegistry.manifests[id]) { 99 | // 插件已安装,显示提示信息 100 | new Notice(`[插件管理器] 插件 ${pluginRegistry.manifests[id].name} 已安装`); 101 | // 如果指定了版本且与已安装的版本不同,则标记为需要安装 102 | if (version !== "" && version !== pluginRegistry.manifests[id]?.version) installFlag = true; 103 | } else { 104 | // 插件未安装,标记为需要安装 105 | installFlag = true; 106 | } 107 | 108 | // 如果需要安装插件 109 | if (installFlag) { 110 | // 从 GitHub 仓库获取插件的 manifest.json 文件 111 | const manifest = await fetch(`https://raw.githubusercontent.com/${repo}/HEAD/manifest.json`).then(r => r.json()); 112 | // 如果版本为 "latest" 或空字符串,则使用 manifest 中的版本 113 | if (version.toLowerCase() === "latest" || version === "") version = manifest.version; 114 | // 调用插件注册表的 installPlugin 方法安装插件 115 | await pluginRegistry.installPlugin(repo, version, manifest); 116 | } 117 | 118 | // 根据 enable 参数决定是否启用或禁用插件 119 | if (enable) { 120 | // 启用插件 121 | await pluginRegistry.loadPlugin(id); 122 | await pluginRegistry.enablePluginAndSave(id); 123 | } else { 124 | // 禁用插件 125 | await pluginRegistry.disablePlugin(id); 126 | } 127 | } 128 | 129 | /** 130 | * 解析安装参数并调用 installPlugin 方法安装插件 131 | * @param params - 包含插件安装参数的对象 132 | */ 133 | public async parsePluginInstall(params: ObsidianProtocolData) { 134 | // 解析参数,设置默认值 135 | let args = { 136 | id: params.id, 137 | version: params?.version ?? "", 138 | enable: ["", "true", "1"].includes(params.enable.toLowerCase()), 139 | github: params.github ?? "", 140 | }; 141 | // 调用 installPlugin 方法安装插件 142 | this.pluginInstall(args.id, args.version, args.enable); 143 | } 144 | 145 | /** 146 | * 解析包含插件信息的字符串或对象,获取插件的相关信息 147 | * @param input - 包含插件信息的字符串或对象 148 | * @return - 返回解析后的插件信息对象,如果解析失败则返回 null 149 | */ 150 | public async parsePluginGithub(params: ObsidianProtocolData) { 151 | // 解析参数,设置默认值 152 | let args = { id: params.id }; 153 | await this.pluginGithub(args.id); 154 | } 155 | } -------------------------------------------------------------------------------- /src/command.ts: -------------------------------------------------------------------------------- 1 | import { App, PluginManifest } from "obsidian"; 2 | import Manager from "./main"; 3 | import { ManagerModal } from "./modal/manager-modal"; 4 | 5 | const Commands = (app: App, manager: Manager) => { 6 | manager.addCommand({ 7 | id: 'manager-view', 8 | name: manager.translator.t('命令_管理面板_描述'), 9 | hotkeys: [ 10 | { 11 | modifiers: ['Ctrl'], 12 | key: 'M', 13 | } 14 | ], 15 | callback: () => { new ManagerModal(app, manager).open() } 16 | }); 17 | 18 | if (manager.settings.DELAY) { 19 | // 单行命令 20 | if (manager.settings.COMMAND_ITEM) { 21 | const plugins: PluginManifest[] = Object.values(manager.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== manager.manifest.id) as PluginManifest[]; 22 | plugins.forEach(plugin => { 23 | const mp = manager.settings.Plugins.find(mp => mp.id === plugin.id); 24 | if (mp) { 25 | manager.addCommand({ 26 | id: `manager-${mp.id}`, 27 | name: `${mp.enabled ? manager.translator.t('通用_关闭_文本') : manager.translator.t('通用_开启_文本')} ${mp.name} `, 28 | callback: async () => { 29 | if (mp.enabled) { 30 | mp.enabled = false; 31 | manager.saveSettings(); 32 | await manager.appPlugins.disablePlugin(plugin.id); 33 | Commands(app, manager); 34 | } else { 35 | mp.enabled = true; 36 | manager.saveSettings(); 37 | await manager.appPlugins.enablePlugin(plugin.id); 38 | Commands(app, manager); 39 | } 40 | } 41 | }); 42 | } 43 | }); 44 | } 45 | // 分组命令 46 | if (manager.settings.COMMAND_GROUP) { 47 | manager.settings.GROUPS.forEach((group) => { 48 | manager.addCommand({ 49 | id: `manager-${group.id}-enabled`, 50 | name: `${manager.translator.t('命令行_一键启用_文本')} ${group.name}`, 51 | callback: async () => { 52 | const filteredPlugins = manager.settings.Plugins.filter(plugin => plugin.group === group.id); 53 | filteredPlugins.forEach(async plugin => { 54 | if (plugin && !plugin.enabled) { 55 | await manager.appPlugins.enablePlugin(plugin.id); 56 | plugin.enabled = true; 57 | manager.saveSettings(); 58 | } 59 | }); 60 | Commands(app, manager); 61 | } 62 | }); 63 | manager.addCommand({ 64 | id: `manager-${group.id}-disable`, 65 | name: `${manager.translator.t('命令行_一键禁用_文本')} ${group.name}`, 66 | callback: async () => { 67 | const filteredPlugins = manager.settings.Plugins.filter(plugin => plugin.group === group.id); 68 | filteredPlugins.forEach(async plugin => { 69 | if (plugin && plugin.enabled) { 70 | await manager.appPlugins.disablePlugin(plugin.id); 71 | plugin.enabled = false; 72 | manager.saveSettings(); 73 | } 74 | }); 75 | Commands(app, manager); 76 | } 77 | }); 78 | }); 79 | } 80 | } else { 81 | // 单行命令 82 | if (manager.settings.COMMAND_ITEM) { 83 | const plugins: PluginManifest[] = Object.values(manager.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== manager.manifest.id) as PluginManifest[]; 84 | plugins.forEach(plugin => { 85 | const enabled = manager.appPlugins.enabledPlugins.has(plugin.id); 86 | manager.addCommand({ 87 | id: `manager-${plugin.id}`, 88 | name: `${enabled ? manager.translator.t('命令行_禁用_文本') : manager.translator.t('命令行_启用_文本')} ${plugin.name} `, 89 | callback: async () => { 90 | if (enabled) { 91 | await manager.appPlugins.disablePluginAndSave(plugin.id); 92 | Commands(app, manager); 93 | } else { 94 | await manager.appPlugins.enablePluginAndSave(plugin.id); 95 | Commands(app, manager); 96 | } 97 | } 98 | }); 99 | 100 | }); 101 | } 102 | // 分组命令 103 | if (manager.settings.COMMAND_GROUP) { 104 | manager.settings.GROUPS.forEach((group) => { 105 | manager.addCommand({ 106 | id: `manager-${group.id}-enabled`, 107 | name: `${manager.translator.t('命令行_一键启用_文本')} ${group.name} ${manager.translator.t('命令行_分组_文本')}`, 108 | callback: async () => { 109 | const filteredPlugins = manager.settings.Plugins.filter(plugin => plugin.group === group.id); 110 | filteredPlugins.forEach(async plugin => { await manager.appPlugins.enablePluginAndSave(plugin.id); }); 111 | Commands(app, manager); 112 | } 113 | }); 114 | manager.addCommand({ 115 | id: `manager-${group.id}-disable`, 116 | name: `${manager.translator.t('命令行_一键禁用_文本')} ${group.name} ${manager.translator.t('命令行_分组_文本')}`, 117 | callback: async () => { 118 | const filteredPlugins = manager.settings.Plugins.filter(plugin => plugin.group === group.id); 119 | filteredPlugins.forEach(async plugin => { await manager.appPlugins.disablePluginAndSave(plugin.id); }); 120 | Commands(app, manager); 121 | } 122 | }); 123 | }); 124 | } 125 | } 126 | } 127 | 128 | export default Commands -------------------------------------------------------------------------------- /src/data/data.ts: -------------------------------------------------------------------------------- 1 | export const ITEM_STYLE = { 2 | 'alwaysExpand': '始终展开', 3 | 'neverExpand': '永不展开', 4 | 'hoverExpand': '悬浮展开', 5 | 'clickExpand': '单击展开' 6 | } 7 | 8 | export const GROUP_STYLE = { 9 | 'a': '样式一', 10 | 'b': '样式二', 11 | 'c': '样式三', 12 | 'd': '样式四' 13 | } 14 | 15 | export const TAG_STYLE = { 16 | 'a': '样式一', 17 | 'b': '样式二', 18 | 'c': '样式三', 19 | 'd': '样式四' 20 | } -------------------------------------------------------------------------------- /src/data/types.ts: -------------------------------------------------------------------------------- 1 | export interface ManagerPlugin { 2 | id: string; 3 | name: string; 4 | desc: string; 5 | group: string; 6 | tags: string[]; 7 | enabled: boolean; 8 | delay: string; 9 | note:string; 10 | } 11 | 12 | export interface Type { 13 | id: string; 14 | name: string; 15 | color: string; 16 | } 17 | 18 | export interface Tag { 19 | id: string; 20 | name: string; 21 | color: string; 22 | } 23 | 24 | export interface Delay { 25 | id: string; 26 | name: string; 27 | time: number; 28 | } -------------------------------------------------------------------------------- /src/lang/inxdex.ts: -------------------------------------------------------------------------------- 1 | import Manager from "main"; 2 | import zh_cn from './locale/zh_cn'; 3 | import en from "./locale/en"; 4 | import ru from "./locale/ru"; 5 | import ja from "./locale/ja"; 6 | import ko from "./locale/ko"; 7 | import fr from "./locale/fr"; 8 | import es from "./locale/es"; 9 | 10 | export class Translator { 11 | private manager: Manager; 12 | public language = { 13 | 'zh-cn': '简体中文', 14 | 'en': 'English', 15 | 'ru': 'Русский язык', 16 | 'ja': '日本語', 17 | 'ko': '한국어', 18 | 'fr': 'Français', 19 | 'es': 'Español', 20 | }; 21 | 22 | private localeMap: { [k: string]: Partial } = { 23 | 'zh-cn': zh_cn, 24 | 'en': en, 25 | 'ru': ru, 26 | 'ja': ja, 27 | 'ko': ko, 28 | 'fr': fr, 29 | 'es': es, 30 | }; 31 | 32 | constructor(manager: Manager) { 33 | this.manager = manager; 34 | } 35 | 36 | // 方法用于获取翻译后的字符串 37 | public t(str: keyof typeof zh_cn): string { 38 | const language = this.manager.settings.LANGUAGE || 'zh-cn'; // 默认使用 'zh-cn' 39 | const locale = this.localeMap[language] || zh_cn; // 如果 language 不存在,则使用 zh_cn 40 | return locale[str] || zh_cn[str]; // 如果 str 在 locale 中不存在,则使用 zh_cn 中的默认值 41 | } 42 | } 43 | 44 | // import { moment } from "obsidian"; 45 | // import zh_cn from './locale/zh_cn'; 46 | // import en from "./locale/en"; 47 | // import ja_jp from "./locale/ja_jp"; 48 | // import ko_kr from "./locale/ko_kr"; 49 | // import ru_ru from "./locale/ru_ru"; 50 | 51 | // export const LANGUAGE = { 52 | // 'zh-cn': '简体中文', 53 | // 'en': '永不展开' 54 | // } 55 | 56 | // const localeMap: { [k: string]: Partial } = { 57 | // 'zh-cn': zh_cn, 58 | // 'en-us': en, 59 | // 'ja-jp': ja_jp, 60 | // 'ko-kr': ko_kr, 61 | // 'ru-ru': ru_ru 62 | // }; 63 | 64 | // // const locales = moment.locales(); 65 | // // console.log(locales); 66 | // // console.log(moment.locale()) 67 | // const locale = localeMap[moment.locale()]; 68 | 69 | // export function t(str: keyof typeof zh_cn): string { 70 | // return (locale && locale[str]) || zh_cn[str]; 71 | // } 72 | -------------------------------------------------------------------------------- /src/lang/locale/en.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: 'Plugin Manager', 3 | 通用_成功_文本: 'Success', 4 | 通用_失败_文本: 'Failure', 5 | 通用_新增_文本: 'Add', 6 | 通用_操作_文本: 'Operation', 7 | 通用_搜索_文本: 'Search', 8 | 通用_名称_文本: 'Name', 9 | 通用_无分组_文本: 'ALL', 10 | 通用_无标签_文本: 'ALL', 11 | 通用_无延迟_文本: 'No Delay', 12 | 通用_总计_文本: 'Total', 13 | 通用_启用_文本: 'Enable', 14 | 通用_禁用_文本: 'Disable', 15 | 通用_关闭_文本: 'Disable', 16 | 通用_开启_文本: 'Enable', 17 | 18 | 命令行_启用_文本: 'Enable', 19 | 命令行_禁用_文本: 'Disable', 20 | 命令行_分组_文本: 'Group', 21 | 命令行_一键启用_文本: 'One - click Enable', 22 | 命令行_一键禁用_文本: 'One - click Disable', 23 | 24 | 菜单_笔记_标题: 'Note', 25 | 菜单_快捷键_标题: 'Hotkeys', 26 | 菜单_GitHub_标题: 'GitHub', 27 | 菜单_单次启动_描述: 'Single start', 28 | 菜单_重启插件_描述: 'Restart plugin', 29 | 30 | 筛选_全部_描述: 'All', 31 | 筛选_仅启用_描述: 'Enabled only', 32 | 筛选_仅禁用_描述: 'Disabled only', 33 | 筛选_已分组_描述: 'Grouped', 34 | 筛选_未分组_描述: 'Ungrouped', 35 | 筛选_有标签_描述: 'With tags', 36 | 筛选_无标签_描述: 'Without tags', 37 | 筛选_有笔记_描述: 'With notes', 38 | 39 | 管理器_GITHUB_描述: 'Visit the author\'s GitHub page to view project details, update logs, participate in discussions, and contribute code.', 40 | 管理器_视频教程_描述: 'Access video tutorials', 41 | 管理器_编辑模式_描述: 'Enable edit mode for in-depth plugin configuration customization', 42 | 管理器_重载插件_描述: 'Reload plugins to take effect immediately', 43 | 管理器_检查更新_描述: 'Check for plugin updates', 44 | 管理器_一键禁用_描述: 'Disable all plugins at once', 45 | 管理器_一键启用_描述: 'Enable all plugins at once', 46 | 管理器_插件设置_描述: 'Manage plugin settings', 47 | 管理器_仅启用_描述: 'Only display enabled plugins', 48 | 管理器_未分组_描述: 'Filter all ungrouped plugins', 49 | 管理器_打开设置_描述: 'Open the settings interface', 50 | 管理器_还原内容_描述: 'Restore to the initial state', 51 | 管理器_打开目录_描述: 'Open the plugin directory', 52 | 管理器_删除插件_描述: 'Completely delete the plugin', 53 | 管理器_切换状态_描述: 'Toggle the plugin status', 54 | 55 | 卸载_标题: 'Uninstall Plugin', 56 | 卸载_提示: 'Are you sure you want to uninstall this plugin? This will delete the plugin\'s folder.', 57 | 卸载_卸载: 'Uninstall', 58 | 卸载_取消: 'Cancel', 59 | 卸载_通知_一: 'Uninstalled successfully', 60 | 61 | 一键_标题: 'One-click Enable/Disable Plugins', 62 | 一键_提示: 'Are you sure you want to enable/disable the plugins on this page with one click? This action cannot be undone. (Please wait patiently during the enable/disable process)', 63 | 一键_启禁: 'Enable/Disable', 64 | 一键_取消: 'Cancel', 65 | 一键_通知_一: 'Enable/Disable Successful', 66 | 67 | 设置_基础设置_前缀: 'Basic', 68 | 设置_样式设置_前缀: 'Style', 69 | 设置_分组设置_前缀: 'Group', 70 | 设置_标签设置_前缀: 'Tag', 71 | 设置_延迟设置_前缀: 'Delay', 72 | 73 | 设置_基础设置_语言_标题: 'Language Settings', 74 | 设置_基础设置_语言_描述: 'Choose your preferred language.', 75 | 设置_基础设置_界面居中_标题: 'Center the interface', 76 | 设置_基础设置_界面居中_描述: 'Set whether the manager interface is centered', 77 | 78 | 设置_基础设置_目录样式_标题: 'Directory Style', 79 | 设置_基础设置_目录样式_描述: 'Select the style of the group to enhance the browsing experience.', 80 | 设置_基础设置_目录样式_选项_一: 'Always Expanded', 81 | 设置_基础设置_目录样式_选项_二: 'Never Expanded', 82 | 设置_基础设置_目录样式_选项_三: 'Hover to Expand', 83 | 设置_基础设置_目录样式_选项_四: 'Click to Expand', 84 | 85 | 设置_基础设置_分组样式_标题: 'Group Style', 86 | 设置_基础设置_分组样式_描述: 'Select the style of the group to make it more noticeable and easy to identify.', 87 | 设置_基础设置_分组样式_选项_一: 'Style One', 88 | 设置_基础设置_分组样式_选项_二: 'Style Two', 89 | 设置_基础设置_分组样式_选项_三: 'Style Three', 90 | 设置_基础设置_分组样式_选项_四: 'Style Four', 91 | 92 | 设置_基础设置_标签样式_标题: 'Tag Style', 93 | 设置_基础设置_标签样式_描述: 'Select the style of the tag to make it more noticeable and easy to identify.', 94 | 设置_基础设置_标签样式_选项_一: 'Style One', 95 | 设置_基础设置_标签样式_选项_二: 'Style Two', 96 | 设置_基础设置_标签样式_选项_三: 'Style Three', 97 | 设置_基础设置_标签样式_选项_四: 'Style Four', 98 | 99 | 设置_基础设置_延时启动_标题: 'Delayed Startup', 100 | 设置_基础设置_延时启动_描述: 'Enabling the delayed startup feature can optimize the loading order, but please note that this may cause compatibility issues with some plugins.', 101 | 设置_基础设置_淡化插件_标题: 'Fade Plugins', 102 | 设置_基础设置_淡化插件_描述: 'Provide a visual fade effect for disabled plugins to clearly distinguish between enabled and disabled plugins.', 103 | 104 | 设置_基础设置_筛选持久化_标题: 'Filter Persistence', 105 | 设置_基础设置_筛选持久化_描述: 'After enabling, you will see the same plugin list every time you open the manager.', 106 | 107 | 设置_基础设置_单独命令_标题: 'Control Plugin Commands Separately', 108 | 设置_基础设置_单独命令_描述: 'Enable this option to control the enabled and disabled state of each plugin separately. (Restart Obsidian to take effect)', 109 | 设置_基础设置_分组命令_标题: 'Control Plugin Commands by Group', 110 | 设置_基础设置_分组命令_描述: 'Enable this option to enable or disable all plugins in a specified group with one click. (Restart Obsidian to take effect)', 111 | 112 | 设置_延迟设置_通知_一: '[Delay] Added', 113 | 设置_延迟设置_通知_二: '[Delay] ID already exists or is empty', 114 | 设置_延迟设置_通知_三: '[Delay] Deleted successfully', 115 | 设置_延迟设置_通知_四: '[Delay] Deletion failed, plugins exist under this delay', 116 | 117 | 设置_分组设置_通知_一: '[Group] Added', 118 | 设置_分组设置_通知_二: '[Group] ID already exists or is empty', 119 | 设置_分组设置_通知_三: '[Group] Deleted successfully', 120 | 设置_分组设置_通知_四: '[Group] Deletion failed, plugins exist under this group', 121 | 122 | 设置_标签设置_通知_一: '[Tag] Added', 123 | 设置_标签设置_通知_二: '[Tag] ID already exists or is empty', 124 | 设置_标签设置_通知_三: '[Tag] Deleted successfully', 125 | 设置_标签设置_通知_四: '[Tag] Deletion failed, plugins exist under this tag', 126 | 127 | 设置_提示_一_标题: 'If You Encounter Conflicts with Other Plugins', 128 | 设置_提示_一_描述: 'Due to limited capabilities, I cannot fix this issue. Please disable delayed startup to resolve all conflict issues.', 129 | 130 | 命令_管理面板_描述: 'Open the plugin manager', 131 | } 132 | -------------------------------------------------------------------------------- /src/lang/locale/es.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: 'Administrador de plugins', 3 | 通用_成功_文本: 'Éxito', 4 | 通用_失败_文本: 'Fallo', 5 | 通用_新增_文本: 'Agregar', 6 | 通用_操作_文本: 'Operación', 7 | 通用_搜索_文本: 'Buscar', 8 | 通用_名称_文本: 'Nombre', 9 | 通用_无分组_文本: 'Sin grupo', 10 | 通用_无标签_文本: 'Sin etiqueta', 11 | 通用_无延迟_文本: 'Sin retraso', 12 | 通用_总计_文本: 'Total', 13 | 通用_启用_文本: 'Habilitar', 14 | 通用_禁用_文本: 'Deshabilitar', 15 | 16 | 17 | 管理器_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.', 18 | 管理器_视频教程_描述: 'Acceder a tutoriales en video', 19 | 管理器_编辑模式_描述: 'Habilitar modo de edición para una personalización profunda de la configuración del plugin', 20 | 管理器_重载插件_描述: 'Recargar plugins para que surtan efecto inmediatamente', 21 | 管理器_检查更新_描述: 'Comprobar actualizaciones de plugins', 22 | 管理器_一键禁用_描述: 'Deshabilitar todos los plugins a la vez', 23 | 管理器_一键启用_描述: 'Habilitar todos los plugins a la vez', 24 | 管理器_插件设置_描述: 'Administrar configuración de plugins', 25 | 管理器_仅启用_描述: 'Mostrar solo plugins habilitados', 26 | 管理器_打开设置_描述: 'Abrir la interfaz de configuración', 27 | 管理器_还原内容_描述: 'Restaurar al estado inicial', 28 | 管理器_打开目录_描述: 'Abrir el directorio de plugins', 29 | 管理器_删除插件_描述: 'Eliminar completamente el plugin', 30 | 管理器_切换状态_描述: 'Alternar el estado del plugin', 31 | 32 | 卸载_标题: 'Desinstalar Plugin', 33 | 卸载_提示: '¿Está seguro de que desea desinstalar este plugin? Esto eliminará la carpeta del plugin.', 34 | 卸载_卸载: 'Desinstalar', 35 | 卸载_取消: 'Cancelar', 36 | 卸载_通知_一: 'Desinstalado correctamente', 37 | 38 | 设置_基础设置_前缀: 'Configuración básica', 39 | 设置_分组设置_前缀: 'Grupo', 40 | 设置_标签设置_前缀: 'Etiqueta', 41 | 设置_延迟设置_前缀: 'Retraso', 42 | 43 | 44 | 设置_基础设置_语言_标题: 'Configuración de idioma', 45 | 设置_基础设置_语言_描述: 'Seleccione su idioma preferido.', 46 | 设置_基础设置_目录样式_标题: 'Estilo del directorio', 47 | 设置_基础设置_目录样式_描述: 'Seleccione el estilo del grupo para mejorar la experiencia de navegación.', 48 | 设置_基础设置_分组样式_标题: 'Estilo del grupo', 49 | 设置_基础设置_分组样式_描述: 'Seleccione el estilo del grupo para hacerlo más visible y fácil de identificar.', 50 | 设置_基础设置_标签样式_标题: 'Estilo de la etiqueta', 51 | 设置_基础设置_标签样式_描述: 'Seleccione el estilo de la etiqueta para hacerlo más visible y fácil de identificar.', 52 | 53 | 设置_基础设置_延时启动_标题: 'Inicio con retraso', 54 | 设置_基础设置_延时启动_描述: 'Habilitar la función de inicio con retraso puede optimizar el orden de carga, pero tenga en cuenta que esto puede causar problemas de compatibilidad con algunos plugins.', 55 | 设置_基础设置_淡化插件_标题: 'Atenuar plugins', 56 | 设置_基础设置_淡化插件_描述: 'Proporcione un efecto de atenuación visual para plugins deshabilitados para distinguir claramente entre plugins habilitados y deshabilitados.', 57 | 设置_基础设置_单独命令_标题: 'Controlar comandos de plugins por separado', 58 | 设置_基础设置_单独命令_描述: 'Habilite esta opción para controlar el estado habilitado y deshabilitado de cada plugin por separado. (Reinicie Obsidian para que surtan efecto)', 59 | 设置_基础设置_分组命令_标题: 'Controlar comandos de plugins por grupo', 60 | 设置_基础设置_分组命令_描述: 'Habilite esta opción para habilitar o deshabilitar todos los plugins de un grupo específico con un solo clic. (Reinicie Obsidian para que surtan efecto)', 61 | 62 | 设置_延迟设置_通知_一: '[Retraso] Añadido', 63 | 设置_延迟设置_通知_二: '[Retraso] El ID ya existe o está vacío', 64 | 设置_延迟设置_通知_三: '[Retraso] Eliminado correctamente', 65 | 设置_延迟设置_通知_四: '[Retraso] Fallo al eliminar, existen plugins bajo este retraso', 66 | 67 | 设置_分组设置_通知_一: '[Grupo] Añadido', 68 | 设置_分组设置_通知_二: '[Grupo] El ID ya existe o está vacío', 69 | 设置_分组设置_通知_三: '[Grupo] Eliminado correctamente', 70 | 设置_分组设置_通知_四: '[Grupo] Fallo al eliminar, existen plugins bajo este grupo', 71 | 72 | 设置_标签设置_通知_一: '[Etiqueta] Añadido', 73 | 设置_标签设置_通知_二: '[Etiqueta] El ID ya existe o está vacío', 74 | 设置_标签设置_通知_三: '[Etiqueta] Eliminado correctamente', 75 | 设置_标签设置_通知_四: '[Etiqueta] Fallo al eliminar, existen plugins bajo esta etiqueta', 76 | 77 | 设置_提示_一_标题: 'Si encuentra conflictos con otros plugins', 78 | 设置_提示_一_描述: 'Debido a capacidades limitadas, no puedo solucionar este problema. Por favor, deshabilite el inicio con retraso para resolver todos los problemas de conflicto.', 79 | 80 | 命令_管理面板_描述: 'Abrir el administrador de plugins', 81 | } -------------------------------------------------------------------------------- /src/lang/locale/fr.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: 'Gestionnaire de plugins', 3 | 通用_成功_文本: 'Succès', 4 | 通用_失败_文本: 'Échec', 5 | 通用_新增_文本: 'Ajouter', 6 | 通用_操作_文本: 'Opération', 7 | 通用_搜索_文本: 'Recherche', 8 | 通用_名称_文本: 'Nom', 9 | 通用_无分组_文本: 'Aucun groupe', 10 | 通用_无标签_文本: 'Aucun tag', 11 | 通用_无延迟_文本: 'Aucun retard', 12 | 通用_总计_文本: 'Total', 13 | 通用_启用_文本: 'Activer', 14 | 通用_禁用_文本: 'Désactiver', 15 | 16 | 17 | 管理器_GITHUB_描述: 'Visitez la page GitHub de l\'auteur pour voir les détails du projet, les journaux de mise à jour, participer aux discussions et contribuer du code.', 18 | 管理器_视频教程_描述: 'Accédez aux tutoriels vidéo', 19 | 管理器_编辑模式_描述: 'Activez le mode édition pour une personnalisation approfondie de la configuration des plugins', 20 | 管理器_重载插件_描述: 'Rechargez les plugins pour qu\'ils prennent effet immédiatement', 21 | 管理器_检查更新_描述: 'Vérifiez les mises à jour des plugins', 22 | 管理器_一键禁用_描述: 'Désactivez tous les plugins en une fois', 23 | 管理器_一键启用_描述: 'Activez tous les plugins en une fois', 24 | 管理器_插件设置_描述: 'Gérez les paramètres des plugins', 25 | 管理器_仅启用_描述: 'Affichez uniquement les plugins activés', 26 | 管理器_打开设置_描述: 'Ouvrez l\'interface de paramètres', 27 | 管理器_还原内容_描述: 'Rétablissez l\'état initial', 28 | 管理器_打开目录_描述: 'Ouvrez le répertoire des plugins', 29 | 管理器_删除插件_描述: 'Supprimez complètement le plugin', 30 | 管理器_切换状态_描述: 'Basculer l\'état du plugin', 31 | 32 | 卸载_标题: 'Désinstaller le plugin', 33 | 卸载_提示: 'Êtes-vous sûr de vouloir désinstaller ce plugin ? Cela supprimera le dossier du plugin.', 34 | 卸载_卸载: 'Désinstaller', 35 | 卸载_取消: 'Annuler', 36 | 卸载_通知_一: 'Désinstallé avec succès', 37 | 38 | 设置_基础设置_前缀: 'Paramètres de base', 39 | 设置_分组设置_前缀: 'Groupe', 40 | 设置_标签设置_前缀: 'Tag', 41 | 设置_延迟设置_前缀: 'Retard', 42 | 43 | 44 | 设置_基础设置_语言_标题: 'Paramètres de langue', 45 | 设置_基础设置_语言_描述: 'Choisissez votre langue préférée.', 46 | 设置_基础设置_目录样式_标题: 'Style du répertoire', 47 | 设置_基础设置_目录样式_描述: 'Choisissez le style du groupe pour améliorer l\'expérience de navigation.', 48 | 设置_基础设置_分组样式_标题: 'Style du groupe', 49 | 设置_基础设置_分组样式_描述: 'Choisissez le style du groupe pour le rendre plus visible et facile à identifier.', 50 | 设置_基础设置_标签样式_标题: 'Style du tag', 51 | 设置_基础设置_标签样式_描述: 'Choisissez le style du tag pour le rendre plus visible et facile à identifier.', 52 | 53 | 设置_基础设置_延时启动_标题: 'Démarrage différé', 54 | 设置_基础设置_延时启动_描述: 'L\'activation de la fonction de démarrage différé peut optimiser l\'ordre de chargement, mais veuillez noter que cela peut causer des problèmes de compatibilité avec certains plugins.', 55 | 设置_基础设置_淡化插件_标题: 'Estomper les plugins', 56 | 设置_基础设置_淡化插件_描述: 'Appliquez un effet de transparence visuel aux plugins désactivés pour distinguer clairement les plugins activés et désactivés.', 57 | 设置_基础设置_单独命令_标题: 'Contrôler les commandes des plugins séparément', 58 | 设置_基础设置_单独命令_描述: 'Activez cette option pour contrôler l\'état activé et désactivé de chaque plugin séparément. (Redémarrez Obsidian pour que les modifications prennent effet)', 59 | 设置_基础设置_分组命令_标题: 'Contrôler les commandes des plugins par groupe', 60 | 设置_基础设置_分组命令_描述: 'Activez cette option pour activer ou désactiver tous les plugins d\'un groupe spécifique avec un seul clic. (Redémarrez Obsidian pour que les modifications prennent effet)', 61 | 62 | 设置_延迟设置_通知_一: '[Retard] Ajouté', 63 | 设置_延迟设置_通知_二: '[Retard] L\'ID existe déjà ou est vide', 64 | 设置_延迟设置_通知_三: '[Retard] Supprimé avec succès', 65 | 设置_延迟设置_通知_四: '[Retard] Échec de la suppression, des plugins existent sous ce retard', 66 | 67 | 设置_分组设置_通知_一: '[Groupe] Ajouté', 68 | 设置_分组设置_通知_二: '[Groupe] L\'ID existe déjà ou est vide', 69 | 设置_分组设置_通知_三: '[Groupe] Supprimé avec succès', 70 | 设置_分组设置_通知_四: '[Groupe] Échec de la suppression, des plugins existent sous ce groupe', 71 | 72 | 设置_标签设置_通知_一: '[Tag] Ajouté', 73 | 设置_标签设置_通知_二: '[Tag] L\'ID existe déjà ou est vide', 74 | 设置_标签设置_通知_三: '[Tag] Supprimé avec succès', 75 | 设置_标签设置_通知_四: '[Tag] Échec de la suppression, des plugins existent sous ce tag', 76 | 77 | 设置_提示_一_标题: 'Si vous rencontrez des conflits avec d\'autres plugins', 78 | 设置_提示_一_描述: 'En raison de capacités limitées, je ne peux pas résoudre ce problème. Veuillez désactiver le démarrage différé pour résoudre tous les problèmes de conflit.', 79 | 80 | 命令_管理面板_描述: 'Ouvrez le gestionnaire de plugins', 81 | } -------------------------------------------------------------------------------- /src/lang/locale/ja.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: 'プラグインマネージャー', 3 | 通用_成功_文本: '成功', 4 | 通用_失败_文本: '失敗', 5 | 通用_新增_文本: '追加', 6 | 通用_操作_文本: '操作', 7 | 通用_搜索_文本: '検索', 8 | 通用_名称_文本: '名前', 9 | 通用_无分组_文本: 'グループなし', 10 | 通用_无标签_文本: 'タグなし', 11 | 通用_无延迟_文本: '遅延なし', 12 | 通用_总计_文本: '合計', 13 | 通用_启用_文本: '有効', 14 | 通用_禁用_文本: '無効', 15 | 16 | 17 | 管理器_GITHUB_描述: '著者のGitHubページを訪れ、プロジェクトの詳細、更新ログ、議論への参加、コードへの貢献を確認してください。', 18 | 管理器_视频教程_描述: 'ビデオチュートリアルにアクセス', 19 | 管理器_编辑模式_描述: '編集モードを有効にして、プラグインの設定をカスタマイズします', 20 | 管理器_重载插件_描述: 'プラグインをリロードして即座に効果を発揮します', 21 | 管理器_检查更新_描述: 'プラグインの更新を確認する', 22 | 管理器_一键禁用_描述: '一度にすべてのプラグインを無効にします', 23 | 管理器_一键启用_描述: '一度にすべてのプラグインを有効にします', 24 | 管理器_插件设置_描述: 'プラグインの設定を管理する', 25 | 管理器_仅启用_描述: '有効なプラグインのみを表示する', 26 | 管理器_打开设置_描述: '設定インターフェースを開く', 27 | 管理器_还原内容_描述: '初期状態に戻す', 28 | 管理器_打开目录_描述: 'プラグインディレクトリを開く', 29 | 管理器_删除插件_描述: 'プラグインを完全に削除する', 30 | 管理器_切换状态_描述: 'プラグインのステータスを切り替える', 31 | 32 | 卸载_标题: 'プラグインのアンインストール', 33 | 卸载_提示: 'このプラグインをアンインストールしてもよろしいですか?プラグインのフォルダが削除されます。', 34 | 卸载_卸载: 'アンインストール', 35 | 卸载_取消: 'キャンセル', 36 | 卸载_通知_一: 'アンインストールに成功しました', 37 | 38 | 设置_基础设置_前缀: '基本', 39 | 设置_分组设置_前缀: 'グループ', 40 | 设置_标签设置_前缀: 'タグ', 41 | 设置_延迟设置_前缀: '遅延', 42 | 43 | 44 | 设置_基础设置_语言_标题: '言語設定', 45 | 设置_基础设置_语言_描述: 'お好みの言語を選択してください。', 46 | 设置_基础设置_目录样式_标题: 'ディレクトリスタイル', 47 | 设置_基础设置_目录样式_描述: 'グループのスタイルを選択して、ブラウジング体験を向上させます。', 48 | 设置_基础设置_分组样式_标题: 'グループスタイル', 49 | 设置_基础设置_分组样式_描述: 'グループのスタイルを選択して、より目立たせやすく識別しやすくします。', 50 | 设置_基础设置_标签样式_标题: 'タグスタイル', 51 | 设置_基础设置_标签样式_描述: 'タグのスタイルを選択して、より目立たせやすく識別しやすくします。', 52 | 53 | 设置_基础设置_延时启动_标题: '遅延スタート', 54 | 设置_基础设置_延时启动_描述: '遅延スタート機能を有効にすると、読み込み順序を最適化できますが、一部のプラグインで互換性問題が発生する場合があります。', 55 | 设置_基础设置_淡化插件_标题: 'プラグインのフェード', 56 | 设置_基础设置_淡化插件_描述: '無効なプラグインに視覚的なフェード効果を提供して、有効と無効のプラグインを明確に区別します。', 57 | 设置_基础设置_单独命令_标题: 'プラグインコマンドを個別に制御', 58 | 设置_基础设置_单独命令_描述: 'このオプションを有効にすると、各プラグインの有効/無効状態を個別に制御できます。(Obsidianを再起動する必要があります)', 59 | 设置_基础设置_分组命令_标题: 'グループごとにプラグインコマンドを制御', 60 | 设置_基础设置_分组命令_描述: 'このオプションを有効にすると、指定されたグループ内のすべてのプラグインをワンクリックで有効または無効にできます。(Obsidianを再起動する必要があります)', 61 | 62 | 设置_延迟设置_通知_一: '[遅延] 追加されました', 63 | 设置_延迟设置_通知_二: '[遅延] IDが既に存在するか、空です', 64 | 设置_延迟设置_通知_三: '[遅延] 削除に成功しました', 65 | 设置_延迟设置_通知_四: '[遅延] 削除に失敗しました、この遅延の下にプラグインが存在します', 66 | 67 | 设置_分组设置_通知_一: '[グループ] 追加されました', 68 | 设置_分组设置_通知_二: '[グループ] IDが既に存在するか、空です', 69 | 设置_分组设置_通知_三: '[グループ] 削除に成功しました', 70 | 设置_分组设置_通知_四: '[グループ] 削除に失敗しました、このグループの下にプラグインが存在します', 71 | 72 | 设置_标签设置_通知_一: '[タグ] 追加されました', 73 | 设置_标签设置_通知_二: '[タグ] IDが既に存在するか、空です', 74 | 设置_标签设置_通知_三: '[タグ] 削除に成功しました', 75 | 设置_标签设置_通知_四: '[タグ] 削除に失敗しました、このタグの下にプラグインが存在します', 76 | 77 | 设置_提示_一_标题: '他のプラグインとのコンフリクトが発生した場合', 78 | 设置_提示_一_描述: '能力に限りがあるため、この問題を修正できません。遅延スタートを無効にすることで、すべてのコンフリクト問題を解決してください。', 79 | 80 | 命令_管理面板_描述: 'プラグインマネージャーを開く', 81 | } -------------------------------------------------------------------------------- /src/lang/locale/ko.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: '플러그인 관리자', 3 | 通用_成功_文本: '성공', 4 | 通用_失败_文本: '실패', 5 | 通用_新增_文本: '추가', 6 | 通用_操作_文本: '작업', 7 | 通用_搜索_文本: '검색', 8 | 通用_名称_文本: '이름', 9 | 通用_无分组_文本: '그룹 없음', 10 | 通用_无标签_文本: '태그 없음', 11 | 通用_无延迟_文本: '딜레이 없음', 12 | 通用_总计_文本: '총계', 13 | 通用_启用_文本: '활성화', 14 | 通用_禁用_文本: '비활성화', 15 | 16 | 17 | 管理器_GITHUB_描述: '저자의 GitHub 페이지를 방문하여 프로젝트 세부 정보, 업데이트 로그, 토론 참여, 코드 기여를 확인하세요.', 18 | 管理器_视频教程_描述: '비디오 튜토리얼에 액세스', 19 | 管理器_编辑模式_描述: '편집 모드를 활성화하여 플러그인 설정을 자세히 커스터마이징하세요', 20 | 管理器_重载插件_描述: '플러그인을 다시 로드하여 즉시 적용하세요', 21 | 管理器_检查更新_描述: '플러그인 업데이트를 확인하세요', 22 | 管理器_一键禁用_描述: '한 번에 모든 플러그인을 비활성화하세요', 23 | 管理器_一键启用_描述: '한 번에 모든 플러그인을 활성화하세요', 24 | 管理器_插件设置_描述: '플러그인 설정을 관리하세요', 25 | 管理器_仅启用_描述: '활성화된 플러그인만 표시하세요', 26 | 管理器_打开设置_描述: '설정 인터페이스를 엽니다', 27 | 管理器_还原内容_描述: '초기 상태로 복원하세요', 28 | 管理器_打开目录_描述: '플러그인 디렉토리를 엽니다', 29 | 管理器_删除插件_描述: '플러그인을 완전히 삭제하세요', 30 | 管理器_切换状态_描述: '플러그인 상태를 전환하세요', 31 | 32 | 卸载_标题: '플러그인 제거', 33 | 卸载_提示: '이 플러그인을 제거하시겠습니까? 이 작업은 플러그인 폴더를 삭제합니다.', 34 | 卸载_卸载: '제거', 35 | 卸载_取消: '취소', 36 | 卸载_通知_一: '성공적으로 제거되었습니다', 37 | 38 | 设置_基础设置_前缀: '기본', 39 | 设置_分组设置_前缀: '그룹', 40 | 设置_标签设置_前缀: '태그', 41 | 设置_延迟设置_前缀: '딜레이', 42 | 43 | 44 | 设置_基础设置_语言_标题: '언어 설정', 45 | 设置_基础设置_语言_描述: '선호하는 언어를 선택하세요.', 46 | 设置_基础设置_目录样式_标题: '디렉토리 스타일', 47 | 设置_基础设置_目录样式_描述: '그룹의 스타일을 선택하여 브라우징 경험을 향상하세요.', 48 | 设置_基础设置_分组样式_标题: '그룹 스타일', 49 | 设置_基础设置_分组样式_描述: '그룹의 스타일을 선택하여 더 눈에 띄고 식별하기 쉽게 만드세요.', 50 | 设置_基础设置_标签样式_标题: '태그 스타일', 51 | 设置_基础设置_标签样式_描述: '태그의 스타일을 선택하여 더 눈에 띄고 식별하기 쉽게 만드세요.', 52 | 53 | 设置_基础设置_延时启动_标题: '지연 시작', 54 | 设置_基础设置_延时启动_描述: '지연 시작 기능을 활성화하면 로딩 순서를 최적화할 수 있지만, 일부 플러그인에서 호환성 문제가 발생할 수 있으므로 유의하세요.', 55 | 设置_基础设置_淡化插件_标题: '플러그인 흐리게 표시', 56 | 设置_基础设置_淡化插件_描述: '비활성화된 플러그인에 시각적인 흐림 효과를 제공하여 활성화된 플러그인과 비활성화된 플러그인을 명확히 구분하세요.', 57 | 设置_基础设置_单独命令_标题: '플러그인 명령을 별도로 제어', 58 | 设置_基础设置_单独命令_描述: '이 옵션을 활성화하면 각 플러그인의 활성화/비활성화 상태를 별도로 제어할 수 있습니다. (Obsidian을 다시 시작해야 적용됩니다)', 59 | 设置_基础设置_分组命令_标题: '그룹별 플러그인 명령 제어', 60 | 设置_基础设置_分组命令_描述: '이 옵션을 활성화하면 지정된 그룹의 모든 플러그인을 한 번 클릭으로 활성화하거나 비활성화할 수 있습니다. (Obsidian을 다시 시작해야 적용됩니다)', 61 | 62 | 设置_延迟设置_通知_一: '[딜레이] 추가됨', 63 | 设置_延迟设置_通知_二: '[딜레이] ID가 이미 존재하거나 비어 있음', 64 | 设置_延迟设置_通知_三: '[딜레이] 성공적으로 삭제됨', 65 | 设置_延迟设置_通知_四: '[딜레이] 삭제 실패, 이 딜레이하에 플러그인이 존재함', 66 | 67 | 设置_分组设置_通知_一: '[그룹] 추가됨', 68 | 设置_分组设置_通知_二: '[그룹] ID가 이미 존재하거나 비어 있음', 69 | 设置_分组设置_通知_三: '[그룹] 성공적으로 삭제됨', 70 | 设置_分组设置_通知_四: '[그룹] 삭제 실패, 이 그룹하에 플러그인이 존재함', 71 | 72 | 设置_标签设置_通知_一: '[태그] 추가됨', 73 | 设置_标签设置_通知_二: '[태그] ID가 이미 존재하거나 비어 있음', 74 | 设置_标签设置_通知_三: '[태그] 성공적으로 삭제됨', 75 | 设置_标签设置_通知_四: '[태그] 삭제 실패, 이 태그하에 플러그인이 존재함', 76 | 77 | 设置_提示_一_标题: '다른 플러그인과의 충돌이 발생할 경우', 78 | 设置_提示_一_描述: '능력이 제한되어 있어 이 문제를 해결할 수 없습니다. 지연 시작을 비활성화하여 모든 충돌 문제를 해결하세요.', 79 | 80 | 命令_管理面板_描述: '플러그인 관리자를 엽니다', 81 | } -------------------------------------------------------------------------------- /src/lang/locale/ru.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: 'Менеджер плагинов', 3 | 通用_成功_文本: 'Успех', 4 | 通用_失败_文本: 'Неудача', 5 | 通用_新增_文本: 'Добавить', 6 | 通用_操作_文本: 'Операция', 7 | 通用_搜索_文本: 'Поиск', 8 | 通用_名称_文本: 'Название', 9 | 通用_无分组_文本: 'Без группы', 10 | 通用_无标签_文本: 'Без метки', 11 | 通用_无延迟_文本: 'Без задержки', 12 | 通用_总计_文本: 'Всего', 13 | 通用_启用_文本: 'Включить', 14 | 通用_禁用_文本: 'Отключить', 15 | 16 | 管理器_GITHUB_描述: 'Посетите страницу автора на GitHub, чтобы просмотреть подробности проекта, журнал обновлений, принять участие в обсуждении и внести свой вклад в код.', 17 | 管理器_视频教程_描述: 'Доступ к видеоурокам', 18 | 管理器_编辑模式_描述: 'Включите режим редактирования для глубокой настройки конфигурации плагинов', 19 | 管理器_重载插件_描述: 'Перезагрузите плагины для немедленного вступления в силу', 20 | 管理器_检查更新_描述: 'Проверьте обновления плагинов', 21 | 管理器_一键禁用_描述: 'Отключите все плагины одним кликом', 22 | 管理器_一键启用_描述: 'Включите все плагины одним кликом', 23 | 管理器_插件设置_描述: 'Управление настройками плагинов', 24 | 管理器_仅启用_描述: 'Показывать только включенные плагины', 25 | 管理器_打开设置_描述: 'Откройте интерфейс настроек', 26 | 管理器_还原内容_描述: 'Верните начальное состояние', 27 | 管理器_打开目录_描述: 'Откройте каталог плагинов', 28 | 管理器_删除插件_描述: 'Полностью удалите плагин', 29 | 管理器_切换状态_描述: 'Переключите статус плагина', 30 | 31 | 卸载_标题: 'Удалить плагин', 32 | 卸载_提示: 'Вы уверены, что хотите удалить этот плагин? Это удалит папку плагина.', 33 | 卸载_卸载: 'Удалить', 34 | 卸载_取消: 'Отмена', 35 | 卸载_通知_一: 'Успешно удалено', 36 | 37 | 设置_基础设置_前缀: 'Основные', 38 | 设置_分组设置_前缀: 'Группа', 39 | 设置_标签设置_前缀: 'Метка', 40 | 设置_延迟设置_前缀: 'Задержка', 41 | 42 | 43 | 设置_基础设置_语言_标题: 'Настройки языка', 44 | 设置_基础设置_语言_描述: 'Выберите предпочитаемый язык.', 45 | 设置_基础设置_目录样式_标题: 'Стиль каталога', 46 | 设置_基础设置_目录样式_描述: 'Выберите стиль группы для улучшения просмотра.', 47 | 设置_基础设置_分组样式_标题: 'Стиль группы', 48 | 设置_基础设置_分组样式_描述: 'Выберите стиль группы для лучшей видимости и идентификации.', 49 | 设置_基础设置_标签样式_标题: 'Стиль метки', 50 | 设置_基础设置_标签样式_描述: 'Выберите стиль метки для лучшей видимости и идентификации.', 51 | 52 | 设置_基础设置_延时启动_标题: 'Задержка при запуске', 53 | 设置_基础设置_延时启动_描述: 'Включение функции задержки при запуске может оптимизировать порядок загрузки, но обратите внимание, что это может вызвать проблемы совместимости с некоторыми плагинами.', 54 | 设置_基础设置_淡化插件_标题: 'Слабо видимые плагины', 55 | 设置_基础设置_淡化插件_描述: 'Предоставьте визуальный эффект слабой видимости для отключенных плагинов, чтобы четко различать включенные и отключенные плагины.', 56 | 设置_基础设置_单独命令_标题: 'Отдельное управление командами плагинов', 57 | 设置_基础设置_单独命令_描述: 'Включите этот параметр для отдельного управления состоянием включения и отключения каждого плагина. (Перезапустите Obsidian, чтобы внести изменения)', 58 | 设置_基础设置_分组命令_标题: 'Управление командами плагинов по группам', 59 | 设置_基础设置_分组命令_描述: 'Включите этот параметр для включения или отключения всех плагинов в указанной группе одним кликом. (Перезапустите Obsidian, чтобы внести изменения)', 60 | 61 | 设置_延迟设置_通知_一: '[Задержка] Добавлено', 62 | 设置_延迟设置_通知_二: '[Задержка] ID уже существует или пуст', 63 | 设置_延迟设置_通知_三: '[Задержка] Успешно удалено', 64 | 设置_延迟设置_通知_四: '[Задержка] Не удалось удалить, существуют плагины с этой задержкой', 65 | 66 | 设置_分组设置_通知_一: '[Группа] Добавлено', 67 | 设置_分组设置_通知_二: '[Группа] ID уже существует или пуст', 68 | 设置_分组设置_通知_三: '[Группа] Успешно удалено', 69 | 设置_分组设置_通知_四: '[Группа] Не удалось удалить, существуют плагины в этой группе', 70 | 71 | 设置_标签设置_通知_一: '[Метка] Добавлено', 72 | 设置_标签设置_通知_二: '[Метка] ID уже существует или пуст', 73 | 设置_标签设置_通知_三: '[Метка] Успешно удалено', 74 | 设置_标签设置_通知_四: '[Метка] Не удалось удалить, существуют плагины с этой меткой', 75 | 76 | 设置_提示_一_标题: 'Если возникают конфликты с другими плагинами', 77 | 设置_提示_一_描述: 'Из-за ограниченных возможностей я не могу исправить эту проблему. Пожалуйста, отключите задержку при запуске, чтобы решить все проблемы конфликта.', 78 | 79 | 命令_管理面板_描述: 'Откройте менеджер плагинов', 80 | } -------------------------------------------------------------------------------- /src/lang/locale/zh_cn.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 通用_管理器_文本: '插件管理器', 3 | 通用_成功_文本: '成功', 4 | 通用_失败_文本: '失败', 5 | 通用_新增_文本: '新增', 6 | 通用_操作_文本: '操作', 7 | 通用_搜索_文本: '搜索', 8 | 通用_名称_文本: '名称', 9 | 通用_无分组_文本: '全部', 10 | 通用_无标签_文本: '全部', 11 | 通用_无延迟_文本: '无', 12 | 通用_总计_文本: '总计', 13 | 通用_启用_文本: '启用', 14 | 通用_禁用_文本: '禁用', 15 | 通用_关闭_文本: '关闭', 16 | 通用_开启_文本: '开启', 17 | 18 | 命令行_启用_文本: '启用', 19 | 命令行_禁用_文本: '禁用', 20 | 命令行_分组_文本: '分组', 21 | 命令行_一键启用_文本: '一键启用', 22 | 命令行_一键禁用_文本: '一键禁用', 23 | 24 | 管理器_GITHUB_描述: '访问作者的GitHub页面,查看项目详情、更新日志、参与讨论和贡献代码。', 25 | 管理器_视频教程_描述: '访问视频教程', 26 | 管理器_编辑模式_描述: '启用编辑模式,深度自定义插件配置', 27 | 管理器_重载插件_描述: '重载插件,即时生效', 28 | 管理器_检查更新_描述: '检查插件更新', 29 | 管理器_一键禁用_描述: '一键禁用所有插件', 30 | 管理器_一键启用_描述: '一键启用所有插件', 31 | 管理器_插件设置_描述: '管理插件设置', 32 | 管理器_仅启用_描述: '仅显示已启用插件', 33 | 管理器_未分组_描述: '筛选所有未分组插件', 34 | 管理器_打开设置_描述: '打开设置界面', 35 | 管理器_还原内容_描述: '还原初始状态', 36 | 管理器_打开目录_描述: '打开插件目录', 37 | 管理器_删除插件_描述: '彻底删除插件', 38 | 管理器_切换状态_描述: '切换插件状态', 39 | 40 | 卸载_标题: '卸载插件', 41 | 卸载_提示: '你确定要卸载此插件吗?这将删除插件的文件夹。', 42 | 卸载_卸载: '卸载', 43 | 卸载_取消: '取消', 44 | 卸载_通知_一: '卸载成功', 45 | 46 | 一键_标题: '一键启用/禁用插件', 47 | 一键_提示: '你确定要一键启用/禁用此页面插件吗?这将无法恢复。(启用/禁用过程中请耐心等待)', 48 | 一键_启禁: '启用/禁用', 49 | 一键_取消: '取消', 50 | 一键_通知_一: '启用/禁用成功', 51 | 52 | 菜单_笔记_标题: '笔记', 53 | 菜单_快捷键_标题: '快捷键', 54 | 菜单_GitHub_标题: 'GitHub', 55 | 菜单_单次启动_描述: '单次启动', 56 | 菜单_重启插件_描述: '重启插件', 57 | 菜单_隐藏插件_标题: '隐藏插件', 58 | 菜单_复制ID_标题: '复制ID', 59 | 60 | 通知_ID已复制: 'ID已复制', 61 | 62 | 筛选_全部_描述: '全部', 63 | 筛选_仅启用_描述: '仅启用', 64 | 筛选_仅禁用_描述: '仅禁用', 65 | 筛选_已分组_描述: '已分组', 66 | 筛选_未分组_描述: '未分组', 67 | 筛选_有标签_描述: '有标签', 68 | 筛选_无标签_描述: '无标签', 69 | 筛选_有笔记_描述: '有笔记', 70 | 71 | 设置_基础设置_前缀: '基础', 72 | 设置_样式设置_前缀: '样式', 73 | 设置_分组设置_前缀: '分组', 74 | 设置_标签设置_前缀: '标签', 75 | 设置_延迟设置_前缀: '延迟', 76 | 77 | 设置_基础设置_语言_标题: '语言设置', 78 | 设置_基础设置_语言_描述: '选择您喜欢的语言。', 79 | 设置_基础设置_界面居中_标题: '界面居中', 80 | 设置_基础设置_界面居中_描述: '设置管理器界面是否居中', 81 | 82 | 设置_基础设置_目录样式_标题: '目录样式', 83 | 设置_基础设置_目录样式_描述: '选择分组的样式,以提升浏览体验。', 84 | 设置_基础设置_目录样式_选项_一: '始终展开', 85 | 设置_基础设置_目录样式_选项_二: '永不展开', 86 | 设置_基础设置_目录样式_选项_三: '悬浮展开', 87 | 设置_基础设置_目录样式_选项_四: '单击展开', 88 | 89 | 设置_基础设置_分组样式_标题: '分组样式', 90 | 设置_基础设置_分组样式_描述: '选择分组的样式,使分组更加明显,便于识别。', 91 | 设置_基础设置_分组样式_选项_一: '样式一', 92 | 设置_基础设置_分组样式_选项_二: '样式二', 93 | 设置_基础设置_分组样式_选项_三: '样式三', 94 | 设置_基础设置_分组样式_选项_四: '样式四', 95 | 96 | 设置_基础设置_标签样式_标题: '标签样式', 97 | 设置_基础设置_标签样式_描述: '选择标签的样式,使标签更加明显,便于识别。', 98 | 设置_基础设置_标签样式_选项_一: '样式一', 99 | 设置_基础设置_标签样式_选项_二: '样式二', 100 | 设置_基础设置_标签样式_选项_三: '样式三', 101 | 设置_基础设置_标签样式_选项_四: '样式四', 102 | 103 | 设置_基础设置_延时启动_标题: '延时启动', 104 | 设置_基础设置_延时启动_描述: '启用延时启动功能可以优化加载顺序,但请注意,这可能会导致某些插件出现兼容性问题。', 105 | 设置_基础设置_淡化插件_标题: '淡化插件', 106 | 设置_基础设置_淡化插件_描述: '为未启用的插件提供视觉淡化效果,以便清晰地区分启用和未启用的插件。', 107 | 108 | 设置_基础设置_筛选持久化_标题: '筛选持久化', 109 | 设置_基础设置_筛选持久化_描述: '启用后,您将在每次打开管理器时看到相同的插件列表。', 110 | 111 | 设置_基础设置_单独命令_标题: '单独控制插件命令', 112 | 设置_基础设置_单独命令_描述: '启用此选项可以单独控制每个插件的启用和禁用状态。(重启Obsidian生效)', 113 | 设置_基础设置_分组命令_标题: '分组控制插件命令', 114 | 设置_基础设置_分组命令_描述: '启用此选项可以一键启用或禁用指定分组中的所有插件。(重启Obsidian生效)', 115 | 116 | 设置_延迟设置_通知_一: '[延迟] 已添加', 117 | 设置_延迟设置_通知_二: '[延迟] ID已存在或为空', 118 | 设置_延迟设置_通知_三: '[延迟] 删除成功', 119 | 设置_延迟设置_通知_四: '[延迟] 删除失败,此延迟下存在插件', 120 | 121 | 设置_分组设置_通知_一: '[分组] 已添加', 122 | 设置_分组设置_通知_二: '[分组] ID已存在或为空', 123 | 设置_分组设置_通知_三: '[分组] 删除成功', 124 | 设置_分组设置_通知_四: '[分组] 删除失败,此分组下存在插件', 125 | 126 | 设置_标签设置_通知_一: '[标签] 已添加', 127 | 设置_标签设置_通知_二: '[标签] ID已存在或为空', 128 | 设置_标签设置_通知_三: '[标签] 删除成功', 129 | 设置_标签设置_通知_四: '[标签] 删除失败,此标签下存在插件', 130 | 131 | 设置_提示_一_标题: '如果遇到本插件与其他插件冲突', 132 | 设置_提示_一_描述: '个人能力有限,无法修复此问题,请关闭延时启动,即可解决一切冲突问题。', 133 | 134 | 命令_管理面板_描述: '开启插件管理器', 135 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { ObsidianProtocolData, Plugin, PluginManifest, Workspace } from 'obsidian'; 2 | import { DEFAULT_SETTINGS, ManagerSettings } from './settings/data'; 3 | import { ManagerSettingTab } from './settings'; 4 | import { Translator } from './lang/inxdex'; 5 | import { ManagerModal } from './modal/manager-modal'; 6 | import Commands from './command'; 7 | import Agreement from 'src/agreement'; 8 | 9 | export default class Manager extends Plugin { 10 | public settings: ManagerSettings; 11 | public managerModal: ManagerModal; 12 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 13 | public appPlugins: any; 14 | public appWorkspace: Workspace; 15 | public translator: Translator; 16 | 17 | public agreement: Agreement; 18 | 19 | public async onload() { 20 | // @ts-ignore 21 | this.appPlugins = this.app.plugins; 22 | this.appWorkspace = this.app.workspace; 23 | 24 | console.log(`%c ${this.manifest.name} %c v${this.manifest.version} `, `padding: 2px; border-radius: 2px 0 0 2px; color: #fff; background: #5B5B5B;`, `padding: 2px; border-radius: 0 2px 2px 0; color: #fff; background: #409EFF;`); 25 | await this.loadSettings(); 26 | // 初始化语言系统 27 | this.translator = new Translator(this); 28 | // 初始化侧边栏图标 29 | this.addRibbonIcon('folder-cog', this.translator.t('通用_管理器_文本'), () => { this.managerModal = new ManagerModal(this.app, this); this.managerModal.open(); }); 30 | // 初始化设置界面 31 | this.addSettingTab(new ManagerSettingTab(this.app, this)); 32 | this.settings.DELAY ? this.enableDelay() : this.disableDelay(); 33 | Commands(this.app, this); 34 | 35 | this.agreement = new Agreement(this); 36 | 37 | this.registerObsidianProtocolHandler("BPM-plugin-install", async (params: ObsidianProtocolData) => { 38 | await this.agreement.parsePluginInstall(params); 39 | }); 40 | this.registerObsidianProtocolHandler("BPM-plugin-github", async (params: ObsidianProtocolData) => { 41 | await this.agreement.parsePluginGithub(params); 42 | }); 43 | } 44 | 45 | public async onunload() { 46 | if (this.settings.DELAY) this.disableDelaysForAllPlugins(); 47 | } 48 | 49 | public async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } 50 | public async saveSettings() { await this.saveData(this.settings); } 51 | 52 | // 关闭延时 调用 53 | public disableDelay() { 54 | const plugins = Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manifest.id) as PluginManifest[]; 55 | this.synchronizePlugins(plugins); 56 | } 57 | 58 | // 开启延时 调用 59 | public enableDelay() { 60 | const plugins = Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manifest.id) as PluginManifest[]; 61 | // 同步插件 62 | this.synchronizePlugins(plugins); 63 | // 开始延时启动插件 64 | plugins.forEach((plugin: PluginManifest) => this.startPluginWithDelay(plugin.id)); 65 | } 66 | 67 | // 为所有插件启动延迟 68 | public enableDelaysForAllPlugins() { 69 | // 获取所有插件 70 | const plugins = Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manifest.id) as PluginManifest[]; 71 | // 同步插件 72 | this.synchronizePlugins(plugins); 73 | 74 | plugins.forEach(async (plugin: PluginManifest) => { 75 | // 插件状态 76 | const isEnabled = this.appPlugins.enabledPlugins.has(plugin.id); 77 | if (isEnabled) { 78 | // 1. 关闭插件 79 | await this.appPlugins.disablePluginAndSave(plugin.id); 80 | // 2. 开启插件 81 | await this.appPlugins.enablePlugin(plugin.id); 82 | // 3. 切换配置状态 83 | const mp = this.settings.Plugins.find(p => p.id === plugin.id); 84 | if (mp) mp.enabled = true; 85 | // 4. 保存状态 86 | this.saveSettings(); 87 | } else { 88 | // 1. 切换配置文件 89 | const mp = this.settings.Plugins.find(p => p.id === plugin.id); 90 | if (mp) mp.enabled = false; 91 | // 2. 保存状态 92 | this.saveSettings(); 93 | } 94 | }); 95 | } 96 | 97 | // 为所有插件关闭延迟 98 | public disableDelaysForAllPlugins() { 99 | const plugins = Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manifest.id); 100 | plugins.forEach(async (pm: PluginManifest) => { 101 | const plugin = this.settings.Plugins.find(p => p.id === pm.id) 102 | if (plugin) { 103 | if (plugin.enabled) { 104 | await this.appPlugins.disablePlugin(pm.id); 105 | await this.appPlugins.enablePluginAndSave(pm.id); 106 | } 107 | } 108 | }); 109 | } 110 | 111 | // 延时启动指定插件 112 | private startPluginWithDelay(id: string) { 113 | const plugin = this.settings.Plugins.find(p => p.id === id); 114 | if (plugin && plugin.enabled) { 115 | const delay = this.settings.DELAYS.find(item => item.id === plugin.delay); 116 | const time = delay ? delay.time : 0; 117 | setTimeout(() => { this.appPlugins.enablePlugin(id); }, time * 1000); 118 | } 119 | } 120 | 121 | // 同步插件到配置文件 122 | public synchronizePlugins(p1: PluginManifest[]) { 123 | const p2 = this.settings.Plugins; 124 | p2.forEach(p2Item => { 125 | if (!p1.some(p1Item => p1Item.id === p2Item.id)) { 126 | this.settings.Plugins = this.settings.Plugins.filter(pm => pm.id !== p2Item.id); 127 | } 128 | }); 129 | p1.forEach(p1Item => { 130 | if (!p2.some(p2Item => p2Item.id === p1Item.id)) { 131 | const isEnabled = this.appPlugins.enabledPlugins.has(p1Item.id); 132 | this.settings.Plugins.push({ 133 | 'id': p1Item.id, 134 | 'name': p1Item.name, 135 | 'desc': p1Item.description, 136 | 'group': '', 137 | 'tags': [], 138 | 'enabled': isEnabled, 139 | 'delay': '', 140 | 'note': '' 141 | }); 142 | } 143 | }); 144 | // 保存设置 145 | this.saveSettings(); 146 | } 147 | 148 | // 工具函数 149 | public createTag(text: string, color: string, type: string) { 150 | const style = this.generateTagStyle(color, type); 151 | const tag = createEl('span', { 152 | text: text, 153 | cls: 'manager-tag', 154 | attr: { 'style': style } 155 | }) 156 | return tag; 157 | } 158 | public generateTagStyle(color: string, type: string) { 159 | let style; 160 | const [r, g, b] = this.hexToRgbArray(color); 161 | switch (type) { 162 | case 'a': 163 | style = `color: #fff; background-color: ${color}; border-color: ${color};`; 164 | break; 165 | case 'b': 166 | style = `color: ${color}; background-color: transparent; border-color: ${color};`; 167 | break; 168 | case 'c': 169 | style = `color: ${color}; background-color: rgba(${r}, ${g}, ${b}, 0.3); border-color: ${color};`; 170 | break; 171 | case 'd': 172 | style = `color: ${color}; background-color: ${this.adjustColorBrightness(color, 50)}; border-color: ${this.adjustColorBrightness(color, 50)};`; 173 | break; 174 | default: 175 | style = `background-color: transparent;border-style: dashed;`; 176 | } 177 | return style; 178 | } 179 | public hexToRgbArray(hex: string) { 180 | const rgb = parseInt(hex.slice(1), 16); 181 | const r = (rgb >> 16); 182 | const g = ((rgb >> 8) & 0x00FF); 183 | const b = (rgb & 0x0000FF); 184 | return [r, g, b]; 185 | } 186 | public adjustColorBrightness(hex: string, amount: number) { 187 | const rgb = parseInt(hex.slice(1), 16); 188 | const r = Math.min(255, Math.max(0, ((rgb >> 16) & 0xFF) + amount)); 189 | const g = Math.min(255, Math.max(0, ((rgb >> 8) & 0xFF) + amount)); 190 | const b = Math.min(255, Math.max(0, (rgb & 0xFF) + amount)); 191 | return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`; 192 | } 193 | } 194 | 195 | -------------------------------------------------------------------------------- /src/modal/delete-modal.ts: -------------------------------------------------------------------------------- 1 | import { App, ExtraButtonComponent, Modal, Setting } from 'obsidian'; 2 | import { ManagerSettings } from '../settings/data'; 3 | import Manager from 'main'; 4 | 5 | export class DeleteModal extends Modal { 6 | settings: ManagerSettings; 7 | manager: Manager; 8 | 9 | private deleteCallback: () => void; 10 | 11 | constructor(app: App, manager: Manager, deleteCallback: () => void) { 12 | super(app); 13 | this.manager = manager; 14 | this.deleteCallback = deleteCallback; 15 | } 16 | 17 | private async showHead() { 18 | //@ts-ignore 19 | const modalEl: HTMLElement = this.contentEl.parentElement; 20 | modalEl.addClass('manager-editor__container'); 21 | modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]); 22 | this.titleEl.parentElement?.addClass('manager-container__header'); 23 | this.contentEl.addClass('manager-item-container'); 24 | 25 | // [标题行] 26 | const titleBar = new Setting(this.titleEl) 27 | titleBar.setClass('manager-delete__title') 28 | titleBar.setName(this.manager.translator.t('卸载_标题')); 29 | 30 | // [标题行] 关闭按钮 31 | const closeButton = new ExtraButtonComponent(titleBar.controlEl) 32 | closeButton.setIcon('circle-x') 33 | closeButton.onClick(() => this.close()); 34 | } 35 | 36 | private async showData() { 37 | const titleBar = new Setting(this.titleEl) 38 | titleBar.setName(this.manager.translator.t('卸载_提示')); 39 | const actionBar = new Setting(this.titleEl) 40 | actionBar.setClass('manager-delete__action') 41 | actionBar.addButton(cb => cb 42 | .setWarning() 43 | .setButtonText(this.manager.translator.t('卸载_卸载')) 44 | .onClick(() => { 45 | this.deleteCallback(); 46 | this.close(); 47 | }) 48 | ); 49 | actionBar.addButton(cb => cb 50 | .setButtonText(this.manager.translator.t('卸载_取消')) 51 | .onClick(() => { this.close(); }) 52 | ); 53 | } 54 | 55 | async onOpen() { 56 | await this.showHead(); 57 | await this.showData(); 58 | } 59 | 60 | async onClose() { 61 | this.contentEl.empty(); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/modal/disable-modal.ts: -------------------------------------------------------------------------------- 1 | import { App, ExtraButtonComponent, Modal, Setting } from 'obsidian'; 2 | import { ManagerSettings } from '../settings/data'; 3 | import Manager from 'main'; 4 | 5 | export class DisableModal extends Modal { 6 | settings: ManagerSettings; 7 | manager: Manager; 8 | 9 | private deleteCallback: () => void; 10 | 11 | constructor(app: App, manager: Manager, deleteCallback: () => void) { 12 | super(app); 13 | this.manager = manager; 14 | this.deleteCallback = deleteCallback; 15 | } 16 | 17 | private async showHead() { 18 | //@ts-ignore 19 | const modalEl: HTMLElement = this.contentEl.parentElement; 20 | modalEl.addClass('manager-editor__container'); 21 | modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]); 22 | this.titleEl.parentElement?.addClass('manager-container__header'); 23 | this.contentEl.addClass('manager-item-container'); 24 | 25 | // [标题行] 26 | const titleBar = new Setting(this.titleEl) 27 | titleBar.setClass('manager-delete__title') 28 | titleBar.setName(this.manager.translator.t('一键_标题')); 29 | 30 | // [标题行] 关闭按钮 31 | const closeButton = new ExtraButtonComponent(titleBar.controlEl) 32 | closeButton.setIcon('circle-x') 33 | closeButton.onClick(() => this.close()); 34 | } 35 | 36 | private async showData() { 37 | const titleBar = new Setting(this.titleEl) 38 | titleBar.setName(this.manager.translator.t('一键_提示')); 39 | const actionBar = new Setting(this.titleEl) 40 | actionBar.setClass('manager-delete__action') 41 | actionBar.addButton(cb => cb 42 | .setCta() 43 | .setButtonText(this.manager.translator.t('一键_启禁')) 44 | .onClick(() => { 45 | this.deleteCallback(); 46 | this.close(); 47 | }) 48 | ); 49 | actionBar.addButton(cb => cb 50 | .setButtonText(this.manager.translator.t('一键_取消')) 51 | .onClick(() => { 52 | this.close(); 53 | }) 54 | ); 55 | } 56 | 57 | async onOpen() { 58 | await this.showHead(); 59 | await this.showData(); 60 | } 61 | 62 | async onClose() { 63 | this.contentEl.empty(); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/modal/group-modal.ts: -------------------------------------------------------------------------------- 1 | import { App, ExtraButtonComponent, Modal, Notice, Setting } from 'obsidian'; 2 | import { ManagerSettings } from '../settings/data'; 3 | import Manager from 'main'; 4 | import { ManagerModal } from './manager-modal'; 5 | import { ManagerPlugin } from 'src/data/types'; 6 | import Commands from 'src/command'; 7 | 8 | export class GroupModal extends Modal { 9 | settings: ManagerSettings; 10 | manager: Manager; 11 | managerModal: ManagerModal; 12 | managerPlugin: ManagerPlugin; 13 | selected: string; 14 | add: boolean; 15 | 16 | constructor(app: App, manager: Manager, managerModal: ManagerModal, managerPlugin: ManagerPlugin) { 17 | super(app); 18 | this.settings = manager.settings; 19 | this.manager = manager; 20 | this.managerModal = managerModal; 21 | this.managerPlugin = managerPlugin; 22 | this.selected = ''; 23 | this.add = false; 24 | } 25 | 26 | private async showHead() { 27 | //@ts-ignore 28 | const modalEl: HTMLElement = this.contentEl.parentElement; 29 | modalEl.addClass('manager-editor__container'); 30 | modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]); 31 | this.titleEl.parentElement?.addClass('manager-container__header'); 32 | this.contentEl.addClass('manager-item-container'); 33 | 34 | // [标题行] 35 | const titleBar = new Setting(this.titleEl).setClass('manager-bar__title').setName(`[${this.managerPlugin.name}]`); 36 | // [标题行] 关闭按钮 37 | const closeButton = new ExtraButtonComponent(titleBar.controlEl) 38 | closeButton.setIcon('circle-x') 39 | closeButton.onClick(() => this.close()); 40 | } 41 | 42 | private async showData() { 43 | for (const group of this.settings.GROUPS) { 44 | const itemEl = new Setting(this.contentEl) 45 | itemEl.setClass('manager-editor__item') 46 | if (this.selected == '' || this.selected != group.id) { 47 | itemEl.addExtraButton(cb => cb 48 | .setIcon('settings') 49 | .onClick(() => { 50 | this.selected = group.id; 51 | this.reloadShowData(); 52 | }) 53 | ) 54 | itemEl.addToggle(cb => cb 55 | .setValue(group.id === this.managerPlugin.group) 56 | .onChange(() => { 57 | this.managerPlugin.group = this.managerPlugin.group === group.id ? '' : group.id; 58 | this.manager.saveSettings(); 59 | this.managerModal.reloadShowData(); 60 | this.reloadShowData(); 61 | }) 62 | ) 63 | const groupEl = createSpan({ cls: 'manager-item__name-group' }); 64 | itemEl.nameEl.appendChild(groupEl); 65 | const tag = this.manager.createTag(group.name, group.color, this.settings.GROUP_STYLE); 66 | groupEl.appendChild(tag); 67 | } 68 | if (this.selected != '' && this.selected == group.id) { 69 | itemEl.addColorPicker(cb => cb 70 | .setValue(group.color) 71 | .onChange((value) => { 72 | group.color = value; 73 | this.manager.saveSettings(); 74 | this.reloadShowData(); 75 | }) 76 | ) 77 | itemEl.addText(cb => cb 78 | .setValue(group.name) 79 | .onChange((value) => { 80 | group.name = value; 81 | this.manager.saveSettings(); 82 | }) 83 | .inputEl.addClass('manager-editor__item-input') 84 | ) 85 | itemEl.addExtraButton(cb => cb 86 | .setIcon('trash-2') 87 | .onClick(() => { 88 | const hasTestGroup = this.settings.Plugins.some(plugin => plugin.group === group.id); 89 | if (!hasTestGroup) { 90 | this.manager.settings.GROUPS = this.manager.settings.GROUPS.filter(t => t.id !== group.id); 91 | this.manager.saveSettings(); 92 | this.reloadShowData(); 93 | Commands(this.app, this.manager); 94 | new Notice(this.manager.translator.t('设置_分组设置_通知_三')); 95 | } else { 96 | new Notice(this.manager.translator.t('设置_分组设置_通知_四')); 97 | } 98 | }) 99 | ) 100 | itemEl.addExtraButton(cb => cb 101 | .setIcon('save') 102 | .onClick(() => { 103 | this.selected = ''; 104 | this.reloadShowData(); 105 | this.managerModal.reloadShowData(); 106 | }) 107 | ) 108 | const groupEl = createSpan({ cls: 'manager-item__name-group' }); 109 | itemEl.nameEl.appendChild(groupEl); 110 | const tag = this.manager.createTag(group.name, group.color, this.settings.GROUP_STYLE); 111 | groupEl.appendChild(tag); 112 | } 113 | } 114 | if (this.add) { 115 | let id = ''; 116 | let name = ''; 117 | let color = ''; 118 | const foodBar = new Setting(this.contentEl).setClass('manager-bar__title'); 119 | foodBar.infoEl.remove(); 120 | foodBar.addColorPicker(cb => cb 121 | .setValue(color) 122 | .onChange((value) => { 123 | color = value; 124 | }) 125 | ) 126 | foodBar.addText(cb => cb 127 | .setPlaceholder('ID') 128 | .onChange((value) => { id = value; this.manager.saveSettings(); }) 129 | .inputEl.addClass('manager-editor__item-input') 130 | ) 131 | foodBar.addText(cb => cb 132 | .setPlaceholder(this.manager.translator.t('通用_名称_文本')) 133 | .onChange((value) => { name = value; }) 134 | .inputEl.addClass('manager-editor__item-input') 135 | ) 136 | foodBar.addExtraButton(cb => cb 137 | .setIcon('plus') 138 | .onClick(() => { 139 | const containsId = this.manager.settings.GROUPS.some(tag => tag.id === id); 140 | if (!containsId && id !== '') { 141 | if (color === '') color = '#000000'; 142 | this.manager.settings.GROUPS.push({ id, name, color }); 143 | this.manager.saveSettings(); 144 | this.add = false; 145 | this.reloadShowData(); 146 | Commands(this.app, this.manager); 147 | new Notice(this.manager.translator.t('设置_分组设置_通知_一')); 148 | } else { 149 | new Notice(this.manager.translator.t('设置_分组设置_通知_二')); 150 | } 151 | }) 152 | ) 153 | } else { 154 | // [底部行] 新增 155 | const foodBar = new Setting(this.contentEl).setClass('manager-bar__title').setName(this.manager.translator.t('通用_新增_文本')); 156 | const addButton = new ExtraButtonComponent(foodBar.controlEl) 157 | addButton.setIcon('circle-plus') 158 | addButton.onClick(() => { 159 | this.add = true; 160 | this.reloadShowData(); 161 | }); 162 | } 163 | } 164 | 165 | private async reloadShowData() { 166 | let scrollTop = 0; 167 | const modalElement: HTMLElement = this.contentEl; 168 | scrollTop = modalElement.scrollTop; 169 | modalElement.empty(); 170 | await this.showData(); 171 | modalElement.scrollTo(0, scrollTop); 172 | } 173 | 174 | async onOpen() { 175 | await this.showHead(); 176 | await this.showData(); 177 | } 178 | 179 | async onClose() { 180 | this.contentEl.empty(); 181 | } 182 | } 183 | 184 | -------------------------------------------------------------------------------- /src/modal/hide-modal.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { 3 | App, 4 | ButtonComponent, 5 | DropdownComponent, 6 | ExtraButtonComponent, 7 | Modal, 8 | Notice, 9 | PluginManifest, 10 | SearchComponent, 11 | Setting, 12 | ToggleComponent, 13 | } from "obsidian"; 14 | 15 | import { ManagerSettings } from "../settings/data"; 16 | 17 | import Manager from "main"; 18 | import { ManagerModal } from "./manager-modal"; 19 | import { TagsModal } from "./tags-modal"; 20 | 21 | interface ExportPluginManifest { 22 | id: string; 23 | name: string; 24 | version: string; 25 | author: string; 26 | description: string; 27 | export: boolean; 28 | } 29 | 30 | interface ImportPluginManifest { 31 | id: string; 32 | name: string; 33 | version: string; 34 | author: string; 35 | description: string; 36 | } 37 | 38 | 39 | // ============================== 40 | // 侧边栏 对话框 翻译 41 | // ============================== 42 | export class HideModal extends Modal { 43 | manager: Manager; 44 | managerModal: ManagerModal; 45 | settings: ManagerSettings; 46 | // this.app.plugins 47 | appPlugins; 48 | // this.app.settings 49 | appSetting; 50 | // [本地][变量] 导出插件列表 51 | plugins: PluginManifest[] = []; 52 | 53 | // 搜索内容 54 | searchText = ""; 55 | // 搜索结果 56 | searchEl: SearchComponent; 57 | delay: string = ""; 58 | tag: string = ""; 59 | group: string = ""; 60 | filter: string = "all"; 61 | 62 | constructor(app: App, manager: Manager, managerModal: ManagerModal, plugins: PluginManifest[]) { 63 | super(app); 64 | // @ts-ignore 65 | this.appSetting = this.app.setting; 66 | // @ts-ignore 67 | this.appPlugins = this.app.plugins; 68 | this.manager = manager; 69 | this.managerModal = managerModal; 70 | this.settings = manager.settings; 71 | this.plugins = plugins; 72 | } 73 | 74 | public async showHead() { 75 | //@ts-ignore 76 | const modalEl: HTMLElement = this.contentEl.parentElement; 77 | modalEl.addClass("manager-container"); 78 | // 靠上 79 | if (!this.settings.CENTER) modalEl.addClass("manager-container__top"); 80 | modalEl.removeChild(modalEl.getElementsByClassName("modal-close-button")[0]); 81 | this.titleEl.parentElement?.addClass("manager-container__header"); 82 | this.contentEl.addClass("manager-item-container"); 83 | 84 | // [操作行] 85 | const actionBar = new Setting(this.titleEl).setClass("manager-bar__action").setName('隐藏插件'); 86 | 87 | // [操作行] 关闭 88 | const closeButton = new ButtonComponent(actionBar.controlEl); 89 | closeButton.setIcon("x"); 90 | closeButton.onClick(() => { this.close(); }); 91 | 92 | // [搜索行] 93 | const searchBar = new Setting(this.titleEl).setClass("manager-bar__search").setName(this.manager.translator.t("通用_搜索_文本")); 94 | 95 | const filterOptions = { 96 | "all": this.manager.translator.t("筛选_全部_描述"), 97 | "enabled": this.manager.translator.t("筛选_仅启用_描述"), 98 | "disabled": this.manager.translator.t("筛选_仅禁用_描述"), 99 | "grouped": this.manager.translator.t("筛选_已分组_描述"), 100 | "ungrouped": this.manager.translator.t("筛选_未分组_描述"), 101 | "tagged": this.manager.translator.t("筛选_有标签_描述"), 102 | "untagged": this.manager.translator.t("筛选_无标签_描述"), 103 | "noted": this.manager.translator.t("筛选_有笔记_描述"), 104 | }; 105 | // 过滤器 106 | const filterDropdown = new DropdownComponent(searchBar.controlEl); 107 | filterDropdown.addOptions(filterOptions); 108 | filterDropdown.setValue(this.filter); 109 | filterDropdown.onChange((value) => { this.filter = value; this.reloadShowData(); }); 110 | 111 | // [搜索行] 分组选择列表 112 | const groupCounts = this.settings.Plugins.reduce((acc: { [key: string]: number }, plugin) => { const groupId = plugin.group || ""; acc[groupId] = (acc[groupId] || 0) + 1; return acc; }, { "": 0 }); 113 | const groups = this.settings.GROUPS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} [${groupCounts[item.id] || 0}]`; return acc; }, { "": this.manager.translator.t("通用_无分组_文本") }); 114 | const groupsDropdown = new DropdownComponent(searchBar.controlEl); 115 | groupsDropdown.addOptions(groups); 116 | groupsDropdown.setValue(this.group); 117 | groupsDropdown.onChange((value) => { this.group = value; this.reloadShowData(); }); 118 | 119 | // [搜索行] 标签选择列表 120 | 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 }); 121 | const tags = this.settings.TAGS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} [${tagCounts[item.id] || 0}]`; return acc; }, { "": this.manager.translator.t("通用_无标签_文本") }); 122 | const tagsDropdown = new DropdownComponent(searchBar.controlEl); 123 | tagsDropdown.addOptions(tags); 124 | tagsDropdown.setValue(this.tag); 125 | tagsDropdown.onChange((value) => { this.tag = value; this.reloadShowData(); }); 126 | 127 | // [搜索行] 延迟选择列表 128 | if (this.settings.DELAY) { 129 | const delayCounts = this.settings.Plugins.reduce((acc: { [key: string]: number }, plugin) => { const delay = plugin.delay || ""; acc[delay] = (acc[delay] || 0) + 1; return acc; }, { "": 0 }); 130 | const delays = this.settings.DELAYS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} (${delayCounts[item.id] || 0})`; return acc; }, { "": this.manager.translator.t("通用_无延迟_文本") }); 131 | const delaysDropdown = new DropdownComponent(searchBar.controlEl); 132 | delaysDropdown.addOptions(delays); 133 | delaysDropdown.setValue(this.delay || ""); 134 | delaysDropdown.onChange((value) => { this.delay = value; this.reloadShowData(); }); 135 | } 136 | 137 | // [搜索行] 搜索框 138 | this.searchEl = new SearchComponent(searchBar.controlEl); 139 | this.searchEl.onChange((value: string) => { this.searchText = value; this.reloadShowData(); }); 140 | } 141 | 142 | public async showData() { 143 | for (const plugin of this.plugins) { 144 | const ManagerPlugin = this.manager.settings.Plugins.find((mp) => mp.id === plugin.id); 145 | // 插件是否开启 146 | const isEnabled = this.settings.DELAY ? ManagerPlugin?.enabled : this.appPlugins.enabledPlugins.has(plugin.id); 147 | if (ManagerPlugin) { 148 | // [过滤] 条件 149 | switch (this.filter) { 150 | case "enabled": 151 | if (!isEnabled) continue; // 仅显示启用插件 152 | break; 153 | case "disabled": 154 | if (isEnabled) continue; // 仅显示禁用插件 155 | break; 156 | case "grouped": 157 | if (ManagerPlugin.group === "") continue; // 仅显示有分组的插件 158 | break; 159 | case "ungrouped": 160 | if (ManagerPlugin.group !== "") continue; // 仅显示未分组插件 161 | break; 162 | case "tagged": 163 | if (ManagerPlugin.tags.length === 0) continue; // 修正为标签数组长度判断 164 | break; 165 | case "untagged": 166 | if (ManagerPlugin.tags.length > 0) continue; // 修正为标签数组长度判断 167 | break; 168 | case "noted": 169 | if (!ManagerPlugin.note || ManagerPlugin.note === "") continue; // 新增笔记判断 170 | break; 171 | default: 172 | break; // 其他情况显示所有插件 173 | } 174 | // [过滤] 分组 标签 延时 175 | if (this.group !== "" && (ManagerPlugin.group !== this.group)) continue; 176 | if (this.tag !== "" && !ManagerPlugin.tags.includes(this.tag)) continue; 177 | if (this.delay !== "" && ManagerPlugin.delay !== this.delay) continue; 178 | // [过滤] 搜索 179 | if (this.searchText !== "" && ManagerPlugin.name.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1 && ManagerPlugin.desc.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1 && plugin.author.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1) continue; 180 | // [过滤] 自身 181 | if (plugin.id === this.manager.manifest.id) continue; 182 | 183 | const itemEl = new Setting(this.contentEl); 184 | itemEl.setClass("manager-item"); 185 | itemEl.nameEl.addClass("manager-item__name-container"); 186 | itemEl.descEl.addClass("manager-item__description-container"); 187 | 188 | // [默认] 分组 189 | if (ManagerPlugin.group !== "") { 190 | const group = createSpan({ cls: "manager-item__name-group", }); 191 | itemEl.nameEl.appendChild(group); 192 | const item = this.settings.GROUPS.find((t) => t.id === ManagerPlugin.group); 193 | if (item) { const tag = this.manager.createTag(item.name, item.color, this.settings.GROUP_STYLE); group.appendChild(tag); } 194 | } 195 | 196 | // [默认] 名称 197 | const title = createSpan({ text: ManagerPlugin.name, cls: "manager-item__name-title", }); 198 | itemEl.nameEl.appendChild(title); 199 | 200 | // [默认] 版本 201 | const version = createSpan({ text: `[${plugin.version}]`, cls: ["manager-item__name-version"], }); 202 | itemEl.nameEl.appendChild(version); 203 | 204 | // [默认] 延迟 205 | if (this.settings.DELAY && ManagerPlugin.delay !== "") { 206 | const d = this.settings.DELAYS.find((item) => item.id === ManagerPlugin.delay); 207 | if (d) { 208 | const delay = createSpan({ text: `${d.time}s`, cls: ["manager-item__name-delay"], }); 209 | itemEl.nameEl.appendChild(delay); 210 | } 211 | } 212 | 213 | // [默认] 描述 214 | const desc = createDiv({ text: ManagerPlugin.desc, cls: ["manager-item__name-desc"], }); 215 | itemEl.descEl.appendChild(desc); 216 | 217 | // [默认] 标签组 218 | const tags = createDiv(); 219 | itemEl.descEl.appendChild(tags); 220 | ManagerPlugin.tags.map((id: string) => { 221 | const item = this.settings.TAGS.find((item) => item.id === id); 222 | if (item) { const tag = this.manager.createTag(item.name, item.color, this.settings.TAG_STYLE); tags.appendChild(tag); } 223 | }); 224 | 225 | const hiddenToggle = new ToggleComponent(itemEl.controlEl); 226 | // 判断当前插件是否在隐藏列表 227 | const isHidden = this.settings.HIDES.includes(plugin.id); 228 | hiddenToggle.setValue(isHidden); 229 | hiddenToggle.onChange((value) => { 230 | // 更新隐藏列表 231 | if (value) this.settings.HIDES.push(plugin.id); else this.settings.HIDES = this.settings.HIDES.filter(id => id !== plugin.id); 232 | this.manager.saveSettings(); 233 | this.managerModal.reloadShowData(); 234 | }); 235 | } 236 | } 237 | } 238 | 239 | public async reloadShowData() { 240 | let scrollTop = 0; 241 | const modalElement: HTMLElement = this.contentEl; 242 | scrollTop = modalElement.scrollTop; 243 | modalElement.empty(); 244 | this.showData(); 245 | modalElement.scrollTo(0, scrollTop); 246 | } 247 | 248 | public async onOpen() { 249 | await this.showHead(); 250 | await this.showData(); 251 | } 252 | 253 | public async onClose() { 254 | this.contentEl.empty(); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/modal/manager-modal.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { 3 | App, 4 | ButtonComponent, 5 | DropdownComponent, 6 | ExtraButtonComponent, 7 | Menu, 8 | Modal, 9 | Notice, 10 | PluginManifest, 11 | requestUrl, 12 | SearchComponent, 13 | setIcon, 14 | Setting, 15 | ToggleComponent, 16 | } from "obsidian"; 17 | 18 | import { ManagerSettings } from "../settings/data"; 19 | import { managerOpen } from "../utils"; 20 | 21 | import Manager from "main"; 22 | import { GroupModal } from "./group-modal"; 23 | import { TagsModal } from "./tags-modal"; 24 | import { DeleteModal } from "./delete-modal"; 25 | import Commands from "src/command"; 26 | import { DisableModal } from "./disable-modal"; 27 | import { NoteModal } from "./note-modal"; 28 | import { ShareModal } from "./share-modal"; 29 | import { HideModal } from "./hide-modal"; 30 | import { ShareTModal } from "./share-t-modal"; 31 | 32 | 33 | 34 | // ============================== 35 | // 侧边栏 对话框 翻译 36 | // ============================== 37 | export class ManagerModal extends Modal { 38 | manager: Manager; 39 | settings: ManagerSettings; 40 | // this.app.plugins 41 | appPlugins; 42 | // this.app.settings 43 | appSetting; 44 | // [本地][变量] 插件路径 45 | basePath: string; 46 | // [本地][变量] 展示插件列表 47 | displayPlugins: PluginManifest[] = []; 48 | 49 | allPlugins: PluginManifest[] = []; 50 | 51 | // 过滤器 52 | filter = ""; 53 | // 分组内容 54 | group = ""; 55 | // 标签内容 56 | tag = ""; 57 | // 标签内容 58 | delay = ""; 59 | // 搜索内容 60 | searchText = ""; 61 | 62 | 63 | // 编辑模式 64 | editorMode = false; 65 | // 测试模式 66 | developerMode = false; 67 | 68 | searchEl: SearchComponent; 69 | footEl: HTMLDivElement; 70 | 71 | constructor(app: App, manager: Manager) { 72 | super(app); 73 | // @ts-ignore 74 | this.appSetting = this.app.setting; 75 | // @ts-ignore 76 | this.appPlugins = this.app.plugins; 77 | this.manager = manager; 78 | this.settings = manager.settings; 79 | // @ts-ignore 80 | this.basePath = path.normalize(this.app.vault.adapter.getBasePath()); 81 | // 首次启动运行下 避免有新加入的插件 82 | manager.synchronizePlugins( 83 | Object.values(this.appPlugins.manifests).filter( 84 | (pm: PluginManifest) => pm.id !== manager.manifest.id 85 | ) as PluginManifest[] 86 | ); 87 | 88 | // this.manager.registerEvent( 89 | // this.app.workspace.on("file-menu", (menu, file) => { 90 | // const addIconMenuItem = (item: MenuItem) => { 91 | // item.setTitle("增"); 92 | // item.setIcon("hashtag"); 93 | // item.onClick(async () => { 94 | // console.log(file); 95 | // }); 96 | // }; 97 | // menu.addItem(addIconMenuItem); 98 | // const addIconMenuItem1 = (item: MenuItem) => { 99 | // item.setTitle("删"); 100 | // item.setIcon("hashtag"); 101 | // }; 102 | // menu.addItem(addIconMenuItem1); 103 | // const addIconMenuItem2 = (item: MenuItem) => { 104 | // item.setTitle("改"); 105 | // item.setIcon("hashtag"); 106 | // }; 107 | // menu.addItem(addIconMenuItem2); 108 | // }) 109 | // ); 110 | } 111 | 112 | async getActivePlugins() { 113 | // @ts-ignore 114 | const originPlugins = this.app.plugins.plugins; 115 | console.log(await this.processPlugins(originPlugins)); 116 | return await this.processPlugins(originPlugins); 117 | } 118 | 119 | async processPlugins(originPlugins: any) { 120 | let plugins: any = {}; 121 | for (let name in originPlugins) { 122 | try { 123 | let plugin = { ...originPlugins[name] }; // new an object and make it extensible 124 | plugin.manifest = { ...originPlugins[name].manifest } 125 | plugin.manifest["pluginUrl"] = `https://obsidian.md/plugins?id=${plugin.manifest.id}`; 126 | plugin.manifest["author2"] = plugin.manifest.author?.replace(/<.*?@.*?\..*?>/g, "").trim(); // remove email address 127 | plugin.manifest["installLink"] = `obsidian://BPM-install?id=${plugin.manifest.id}&enable=true`; 128 | plugins[name] = plugin; 129 | } catch (e) { 130 | console.error(name, e); 131 | console.log(originPlugins[name]); 132 | console.log(originPlugins[name].manifest); 133 | console.log(typeof originPlugins[name].manifest); 134 | } 135 | } 136 | return plugins; 137 | } 138 | 139 | public async showHead() { 140 | //@ts-ignore 141 | const modalEl: HTMLElement = this.contentEl.parentElement; 142 | modalEl.addClass("manager-container"); 143 | // 靠上 144 | if (!this.settings.CENTER) modalEl.addClass("manager-container__top"); 145 | 146 | modalEl.removeChild(modalEl.getElementsByClassName("modal-close-button")[0]); 147 | this.titleEl.parentElement?.addClass("manager-container__header"); 148 | this.contentEl.addClass("manager-item-container"); 149 | // 添加页尾 150 | this.footEl = document.createElement("div"); 151 | this.footEl.addClass("manager-food"); 152 | this.modalEl.appendChild(this.footEl); 153 | 154 | // [操作行] 155 | const actionBar = new Setting(this.titleEl).setClass("manager-bar__action").setName(this.manager.translator.t("通用_操作_文本")); 156 | 157 | // [操作行] Github 158 | const githubButton = new ButtonComponent(actionBar.controlEl); 159 | githubButton.setIcon("github"); 160 | githubButton.setTooltip(this.manager.translator.t("管理器_GITHUB_描述")); 161 | githubButton.onClick(() => { window.open(this.manager.manifest.authorUrl) }); 162 | // [操作行] Github 163 | const tutorialButton = new ButtonComponent(actionBar.controlEl); 164 | tutorialButton.setIcon("book-open"); 165 | tutorialButton.setTooltip(this.manager.translator.t("管理器_视频教程_描述")); 166 | tutorialButton.onClick(() => { window.open("https://www.bilibili.com/video/BV1WyrkYMEce/"); }); 167 | 168 | // [操作行] 检查更新 169 | const updateButton = new ButtonComponent(actionBar.controlEl); 170 | updateButton.setIcon("rss"); 171 | updateButton.setTooltip(this.manager.translator.t("管理器_检查更新_描述")); 172 | updateButton.onClick(async () => { 173 | try { 174 | const result = await this.appPlugins.checkForUpdates(); 175 | this.appSetting.open(); 176 | this.appSetting.openTabById("community-plugins"); 177 | } catch (error) { 178 | console.error("检查更新时出错:", error); // 处理可能出现的错误 179 | } 180 | }); 181 | 182 | // [操作行] 插件分享 183 | // const shareButton = new ButtonComponent(actionBar.controlEl); 184 | // shareButton.setIcon("external-link"); 185 | // // shareButton.setTooltip(this.manager.translator.t("管理器_插件分享_描述")); 186 | // shareButton.onClick(async () => { 187 | // new ShareTModal(this.app, this.manager, (type: string, url?: string) => { 188 | // if (type == 'import') { 189 | // const plugins = this.displayPlugins.map(plugin => ({ 190 | // id: plugin.id, 191 | // name: plugin.name, 192 | // version: plugin.version, 193 | // author: plugin.author, 194 | // description: plugin.description, 195 | // enabled: this.appPlugins.enabledPlugins.has(plugin.id), 196 | // export: true, 197 | // })); 198 | 199 | // // 添加管理器自身信息 200 | // plugins.push({ 201 | // id: this.manager.manifest.id, 202 | // name: this.manager.manifest.name, 203 | // version: this.manager.manifest.version, 204 | // author: this.manager.manifest.author, 205 | // description: this.manager.manifest.description, 206 | // enabled: this.appPlugins.enabledPlugins.has(this.manager.manifest.id), 207 | // export: true, 208 | // }); 209 | 210 | // console.log("当前插件详细信息:", plugins); 211 | 212 | // // new ShareModal(this.app, this.manager, plugins).open(); 213 | // } 214 | // }).open(); 215 | // // new Notice('功能未完成,敬请期待!'); 216 | // }) 217 | 218 | // [操作行] 插件隐藏 219 | const hideButton = new ButtonComponent(actionBar.controlEl); 220 | hideButton.setIcon("eye-off"); 221 | hideButton.onClick(async () => { 222 | const plugins: PluginManifest[] = Object.values(this.appPlugins.manifests); 223 | plugins.sort((item1, item2) => { return item1.name.localeCompare(item2.name); }); 224 | new HideModal(this.app, this.manager, this, plugins).open(); 225 | }) 226 | 227 | // [操作行] 重载插件 228 | const reloadButton = new ButtonComponent(actionBar.controlEl); 229 | reloadButton.setIcon("refresh-ccw"); 230 | reloadButton.setTooltip(this.manager.translator.t("管理器_重载插件_描述")); 231 | reloadButton.onClick(async () => { 232 | new Notice("重新加载第三方插件"); 233 | await this.appPlugins.loadManifests(); 234 | this.reloadShowData(); 235 | }); 236 | 237 | // [操作行] 一键禁用 238 | const disableButton = new ButtonComponent(actionBar.controlEl); 239 | disableButton.setIcon("square"); 240 | disableButton.setTooltip(this.manager.translator.t("管理器_一键禁用_描述")); 241 | disableButton.onClick(async () => { 242 | new DisableModal(this.app, this.manager, async () => { 243 | for (const plugin of this.displayPlugins) { 244 | if (this.settings.DELAY) { 245 | const ManagerPlugin = this.settings.Plugins.find((p) => p.id === plugin.id); 246 | if (ManagerPlugin && ManagerPlugin.enabled) { 247 | await this.appPlugins.disablePlugin(plugin.id); 248 | ManagerPlugin.enabled = false; 249 | this.manager.saveSettings(); 250 | this.reloadShowData(); 251 | } 252 | } else { 253 | if (this.appPlugins.enabledPlugins.has(plugin.id)) { 254 | await this.appPlugins.disablePluginAndSave(plugin.id); 255 | this.reloadShowData(); 256 | } 257 | } 258 | Commands(this.app, this.manager); 259 | } 260 | }).open(); 261 | }); 262 | 263 | // [操作行] 一键启用 264 | const enableButton = new ButtonComponent(actionBar.controlEl); 265 | enableButton.setIcon("square-check"); 266 | enableButton.setTooltip(this.manager.translator.t("管理器_一键启用_描述")); 267 | enableButton.onClick(async () => { 268 | new DisableModal(this.app, this.manager, async () => { 269 | for (const plugin of this.displayPlugins) { 270 | if (this.settings.DELAY) { 271 | const ManagerPlugin = this.manager.settings.Plugins.find((mp) => mp.id === plugin.id); 272 | if (ManagerPlugin && !ManagerPlugin.enabled) { 273 | await this.appPlugins.enablePlugin(plugin.id); 274 | ManagerPlugin.enabled = true; 275 | this.manager.saveSettings(); 276 | this.reloadShowData(); 277 | } 278 | } else { 279 | if (!this.appPlugins.enabledPlugins.has(plugin.id)) { 280 | await this.appPlugins.enablePluginAndSave(plugin.id); 281 | this.reloadShowData(); 282 | } 283 | } 284 | Commands(this.app, this.manager); 285 | } 286 | }).open(); 287 | }); 288 | 289 | // [操作行] 编辑模式 290 | const editorButton = new ButtonComponent(actionBar.controlEl); 291 | this.editorMode ? editorButton.setIcon("pen-off") : editorButton.setIcon("pen"); 292 | editorButton.setTooltip(this.manager.translator.t("管理器_编辑模式_描述")); 293 | editorButton.onClick(() => { 294 | this.editorMode = !this.editorMode; 295 | this.editorMode ? editorButton.setIcon("pen-off") : editorButton.setIcon("pen"); 296 | this.reloadShowData(); 297 | }); 298 | 299 | // [操作行] 插件设置 300 | const settingsButton = new ButtonComponent(actionBar.controlEl); 301 | settingsButton.setIcon("settings"); 302 | settingsButton.setTooltip(this.manager.translator.t("管理器_插件设置_描述")); 303 | settingsButton.onClick(() => { 304 | this.appSetting.open(); 305 | this.appSetting.openTabById(this.manager.manifest.id); 306 | // this.close(); 307 | }); 308 | 309 | 310 | // [测试行] 刷新插件 311 | if (this.developerMode) { 312 | const testButton = new ButtonComponent(actionBar.controlEl); 313 | testButton.setIcon("refresh-ccw"); 314 | testButton.setTooltip("刷新插件"); 315 | testButton.onClick(async () => { 316 | this.close(); 317 | await this.appPlugins.disablePlugin(this.manager.manifest.id); 318 | await this.appPlugins.enablePlugin(this.manager.manifest.id); 319 | }); 320 | } 321 | 322 | // [测试行] 测试插件 323 | if (this.developerMode) { 324 | const testButton = new ButtonComponent(actionBar.controlEl); 325 | testButton.setIcon("test-tube"); 326 | testButton.setTooltip("测试插件"); 327 | testButton.onClick(async () => { 328 | // 获取当前页面所有的插件ID 然后将其转换为列表 329 | }); 330 | } 331 | 332 | // [搜索行] 333 | const searchBar = new Setting(this.titleEl).setClass("manager-bar__search").setName(this.manager.translator.t("通用_搜索_文本")); 334 | 335 | const filterOptions = { 336 | "all": this.manager.translator.t("筛选_全部_描述"), 337 | "enabled": this.manager.translator.t("筛选_仅启用_描述"), 338 | "disabled": this.manager.translator.t("筛选_仅禁用_描述"), 339 | "grouped": this.manager.translator.t("筛选_已分组_描述"), 340 | "ungrouped": this.manager.translator.t("筛选_未分组_描述"), 341 | "tagged": this.manager.translator.t("筛选_有标签_描述"), 342 | "untagged": this.manager.translator.t("筛选_无标签_描述"), 343 | "noted": this.manager.translator.t("筛选_有笔记_描述"), 344 | }; 345 | // 过滤器 346 | const filterDropdown = new DropdownComponent(searchBar.controlEl); 347 | filterDropdown.addOptions(filterOptions); 348 | filterDropdown.setValue(this.filter || "all"); 349 | filterDropdown.onChange((value) => { 350 | this.filter = value; 351 | this.reloadShowData(); 352 | }); 353 | 354 | 355 | // [搜索行] 分组选择列表 356 | const groupCounts = this.settings.Plugins.reduce((acc: { [key: string]: number }, plugin) => { const groupId = plugin.group || ""; acc[groupId] = (acc[groupId] || 0) + 1; return acc; }, { "": 0 }); 357 | const groups = this.settings.GROUPS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} [${groupCounts[item.id] || 0}]`; return acc; }, { "": this.manager.translator.t("通用_无分组_文本") }); 358 | const groupsDropdown = new DropdownComponent(searchBar.controlEl); 359 | groupsDropdown.addOptions(groups); 360 | groupsDropdown.setValue(this.settings.PERSISTENCE ? this.settings.FILTER_GROUP : this.group); 361 | groupsDropdown.onChange((value) => { 362 | if (this.settings.PERSISTENCE) { 363 | this.settings.FILTER_GROUP = value; 364 | this.manager.saveSettings(); 365 | } else { 366 | this.group = value; 367 | } 368 | this.reloadShowData(); 369 | }); 370 | 371 | // [搜索行] 标签选择列表 372 | 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 }); 373 | const tags = this.settings.TAGS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} [${tagCounts[item.id] || 0}]`; return acc; }, { "": this.manager.translator.t("通用_无标签_文本") }); 374 | const tagsDropdown = new DropdownComponent(searchBar.controlEl); 375 | tagsDropdown.addOptions(tags); 376 | tagsDropdown.setValue(this.settings.PERSISTENCE ? this.settings.FILTER_TAG : this.tag); 377 | tagsDropdown.onChange((value) => { 378 | if (this.settings.PERSISTENCE) { 379 | this.settings.FILTER_TAG = value; 380 | this.manager.saveSettings(); 381 | } else { 382 | this.tag = value; 383 | } 384 | this.reloadShowData(); 385 | }); 386 | 387 | // [搜索行] 延迟选择列表 388 | if (this.settings.DELAY) { 389 | const delayCounts = this.settings.Plugins.reduce((acc: { [key: string]: number }, plugin) => { const delay = plugin.delay || ""; acc[delay] = (acc[delay] || 0) + 1; return acc; }, { "": 0 }); 390 | const delays = this.settings.DELAYS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = `${item.name} (${delayCounts[item.id] || 0})`; return acc; }, { "": this.manager.translator.t("通用_无延迟_文本") }); 391 | const delaysDropdown = new DropdownComponent(searchBar.controlEl); 392 | delaysDropdown.addOptions(delays); 393 | delaysDropdown.setValue(this.settings.PERSISTENCE ? this.settings.FILTER_DELAY : this.delay); 394 | delaysDropdown.onChange((value) => { 395 | if (this.settings.PERSISTENCE) { 396 | this.settings.FILTER_DELAY = value; 397 | this.manager.saveSettings(); 398 | } else { 399 | this.delay = value; 400 | } 401 | this.reloadShowData(); 402 | }); 403 | } 404 | 405 | // [搜索行] 搜索框 406 | this.searchEl = new SearchComponent(searchBar.controlEl); 407 | this.searchEl.onChange((value: string) => { this.searchText = value; this.reloadShowData(); }); 408 | } 409 | 410 | public async showData() { 411 | const plugins: PluginManifest[] = Object.values(this.appPlugins.manifests); 412 | plugins.sort((item1, item2) => { return item1.name.localeCompare(item2.name); }); 413 | this.displayPlugins = []; 414 | for (const plugin of plugins) { 415 | const ManagerPlugin = this.manager.settings.Plugins.find((mp) => mp.id === plugin.id); 416 | const pluginDir = path.join(this.basePath, plugin.dir ? plugin.dir : ""); 417 | // 插件是否开启 418 | const isEnabled = this.settings.DELAY ? ManagerPlugin?.enabled : this.appPlugins.enabledPlugins.has(plugin.id); 419 | if (ManagerPlugin) { 420 | // [过滤] 条件 421 | switch (this.filter) { 422 | case "enabled": 423 | if (!isEnabled) continue; // 仅显示启用插件 424 | break; 425 | case "disabled": 426 | if (isEnabled) continue; // 仅显示禁用插件 427 | break; 428 | case "grouped": 429 | if (ManagerPlugin.group === "") continue; // 仅显示有分组的插件 430 | break; 431 | case "ungrouped": 432 | if (ManagerPlugin.group !== "") continue; // 仅显示未分组插件 433 | break; 434 | case "tagged": 435 | if (ManagerPlugin.tags.length === 0) continue; // 修正为标签数组长度判断 436 | break; 437 | case "untagged": 438 | if (ManagerPlugin.tags.length > 0) continue; // 修正为标签数组长度判断 439 | break; 440 | case "noted": 441 | if (!ManagerPlugin.note || ManagerPlugin.note === "") continue; // 新增笔记判断 442 | break; 443 | default: 444 | break; // 其他情况显示所有插件 445 | } 446 | // [过滤] 筛选 447 | if (this.settings.PERSISTENCE) { 448 | // [搜索] 分组 449 | if (this.settings.FILTER_GROUP !== "" && ManagerPlugin.group !== this.settings.FILTER_GROUP) continue; 450 | // [搜索] 标签 451 | if (this.settings.FILTER_TAG !== "" && !ManagerPlugin.tags.includes(this.settings.FILTER_TAG)) continue; 452 | // [搜索] 标签 453 | if (this.settings.FILTER_DELAY !== "" && ManagerPlugin.delay !== this.settings.FILTER_DELAY) continue; 454 | } else { 455 | // [搜索] 分组 456 | if (this.group !== "" && ManagerPlugin.group !== this.group) continue; 457 | // [搜索] 标签 458 | if (this.tag !== "" && !ManagerPlugin.tags.includes(this.tag)) continue; 459 | // [搜索] 标签 460 | if (this.delay !== "" && ManagerPlugin.delay !== this.delay) continue; 461 | } 462 | // [过滤] 搜索 463 | if (this.searchText !== "" && ManagerPlugin.name.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1 && ManagerPlugin.desc.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1 && plugin.author.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1) continue; 464 | // [过滤] 隐藏 465 | if (this.settings.HIDES.includes(plugin.id)) continue; 466 | // [过滤] 自身 467 | if (plugin.id === this.manager.manifest.id) continue; 468 | 469 | const itemEl = new Setting(this.contentEl); 470 | itemEl.setClass("manager-item"); 471 | itemEl.nameEl.addClass("manager-item__name-container"); 472 | itemEl.descEl.addClass("manager-item__description-container"); 473 | 474 | // [右键操作] 475 | itemEl.settingEl.addEventListener("contextmenu", (event) => { 476 | event.preventDefault(); // 阻止默认的右键菜单 477 | const menu = new Menu(); 478 | // 第一组:插件信息类 479 | // [菜单] GITHUB 480 | menu.addItem((item) => 481 | item.setTitle(this.manager.translator.t("菜单_GitHub_标题")) 482 | .setIcon("github") 483 | .onClick(() => { window.open(`obsidian://BPM-plugin-github?id=${plugin.id}`) }) 484 | ); 485 | menu.addSeparator(); // 分隔符 486 | // 第二组:插件管理类 487 | // [菜单] 单次启动 488 | if (!this.settings.DELAY) menu.addItem((item) => 489 | item.setTitle(this.manager.translator.t("菜单_单次启动_描述")) 490 | .setIcon("repeat-1") 491 | .setDisabled(isEnabled) 492 | .onClick(async () => { 493 | new Notice("开启中,请稍等"); 494 | await this.appPlugins.enablePlugin(plugin.id); 495 | await this.reloadShowData(); 496 | 497 | }) 498 | ); 499 | // [菜单] 重启插件 500 | if (!this.settings.DELAY) menu.addItem((item) => 501 | item.setTitle(this.manager.translator.t("菜单_重启插件_描述")) 502 | .setIcon("refresh-ccw") 503 | .setDisabled(!isEnabled) 504 | .onClick(async () => { 505 | new Notice("重启中,请稍等"); 506 | await this.appPlugins.disablePluginAndSave(plugin.id); 507 | await this.appPlugins.enablePluginAndSave(plugin.id); 508 | await this.reloadShowData(); 509 | }) 510 | ); 511 | // [菜单] 隐藏插件 512 | menu.addItem((item) => 513 | item.setTitle(this.manager.translator.t("菜单_隐藏插件_标题")) 514 | .setIcon("eye-off") 515 | .onClick(() => { 516 | const isHidden = this.settings.HIDES.includes(plugin.id); 517 | if (isHidden) { 518 | this.settings.HIDES = this.settings.HIDES.filter(id => id !== plugin.id); 519 | } else { 520 | this.settings.HIDES.push(plugin.id); 521 | } 522 | this.manager.saveSettings(); 523 | this.reloadShowData(); 524 | }) 525 | ); 526 | // [菜单] 分享插件 527 | // menu.addItem((item) => 528 | // item.setTitle("分享插件_标题") 529 | // .setIcon("share-2") 530 | // .onClick(() => { 531 | // const plugins: PluginManifest[] = Object.values(this.appPlugins.manifests); 532 | // plugins.sort((item1, item2) => { return item1.name.localeCompare(item2.name); }); 533 | // }) 534 | // ); 535 | 536 | menu.addSeparator(); // 分隔符 537 | // 第三组:插件设置类 538 | // [菜单] 插件笔记 539 | menu.addItem((item) => 540 | item.setTitle(this.manager.translator.t("菜单_笔记_标题")).setIcon("notebook-pen").onClick(() => { new NoteModal(this.app, this.manager, ManagerPlugin, this).open(); }) 541 | ); 542 | // [菜单] 快捷键 543 | menu.addItem((item) => 544 | item.setTitle(this.manager.translator.t("菜单_快捷键_标题")).setIcon("circle-plus").onClick(async () => { 545 | await this.appSetting.open(); 546 | await this.appSetting.openTabById("hotkeys"); 547 | const tab = await this.appSetting.activeTab; 548 | tab.searchComponent.inputEl.value = plugin.id; 549 | tab.updateHotkeyVisibility(); 550 | tab.searchComponent.inputEl.blur(); 551 | }) 552 | ); 553 | // [菜单] 复制ID 554 | menu.addItem((item) => 555 | item.setTitle(this.manager.translator.t("菜单_复制ID_标题")) 556 | .setIcon("copy") 557 | .onClick(() => { 558 | navigator.clipboard.writeText(plugin.id); 559 | new Notice(this.manager.translator.t("通知_ID已复制")); 560 | }) 561 | ); 562 | // 第三组:测试类 563 | // menu.addSeparator(); // 分隔符 564 | 565 | // menu.addItem((item) => 566 | // item.setTitle("打开市场") 567 | // .setIcon("store") 568 | // .onClick(async () => { 569 | // // await this.app.setting.open(); 570 | // // await this.app.setting.openTabById("community-plugins"); 571 | // // // 可选:自动聚焦搜索框 572 | // // const tab = await this.app.setting.activeTab; 573 | // // tab.searchComponent.inputEl.focus(); 574 | 575 | // await this.appSetting.open(); 576 | // await this.appSetting.openTabById("community-plugins"); 577 | // console.log(this.appSetting); 578 | // setTimeout(async () => { 579 | // const tab = await this.appSetting.activeTab; 580 | // const button = tab.containerEl.querySelector('button.mod-cta'); 581 | // if (button) (button as HTMLElement).click(); 582 | 583 | // }); 584 | // }) 585 | // ); 586 | 587 | 588 | // menu.addSeparator(); 589 | // menu.addItem((item) => 590 | // item.setTitle("分组") 591 | // .setIcon("group") 592 | // .onClick(async () => { 593 | // }) 594 | // ); 595 | // menu.addItem((item) => 596 | // item.setTitle("标签") 597 | // .setIcon("tags") 598 | // .setDisabled(isEnabled) 599 | // .onClick(async () => { 600 | // }) 601 | // ); 602 | menu.showAtPosition({ x: event.clientX, y: event.clientY }); 603 | }); 604 | 605 | // [淡化插件] 606 | if (this.settings.FADE_OUT_DISABLED_PLUGINS && !isEnabled) itemEl.settingEl.addClass("inactive"); 607 | 608 | // [批量操作] 609 | this.displayPlugins.push(plugin); 610 | 611 | // [目录样式] 612 | if (!this.editorMode) { 613 | switch (this.settings.ITEM_STYLE) { 614 | case "alwaysExpand": itemEl.descEl.addClass("manager-display-block"); break; 615 | case "neverExpand": itemEl.descEl.addClass("manager-display-none"); break; 616 | case "hoverExpand": 617 | itemEl.descEl.addClass("manager-display-none"); 618 | itemEl.settingEl.addEventListener( 619 | "mouseenter", 620 | () => { 621 | itemEl.descEl.removeClass("manager-display-none"); 622 | itemEl.descEl.addClass("manager-display-block"); 623 | } 624 | ); 625 | itemEl.settingEl.addEventListener( 626 | "mouseleave", 627 | () => { 628 | itemEl.descEl.removeClass("manager-display-block"); 629 | itemEl.descEl.addClass("manager-display-none"); 630 | } 631 | ); 632 | break; 633 | case "clickExpand": 634 | itemEl.descEl.addClass("manager-display-none"); 635 | itemEl.settingEl.addEventListener( 636 | "click", 637 | function (event) { 638 | const excludedButtons = Array.from( 639 | itemEl.controlEl.querySelectorAll("div") 640 | ); 641 | if ( 642 | // @ts-ignore 643 | excludedButtons.includes(event.target) 644 | ) { 645 | event.stopPropagation(); 646 | return; 647 | } 648 | if ( 649 | itemEl.descEl.hasClass("manager-display-none") 650 | ) { 651 | itemEl.descEl.removeClass("manager-display-none"); 652 | itemEl.descEl.addClass("manager-display-block"); 653 | } else { 654 | itemEl.descEl.removeClass("manager-display-block"); 655 | itemEl.descEl.addClass("manager-display-none"); 656 | } 657 | } 658 | ); 659 | break; 660 | } 661 | } 662 | 663 | // [默认] 分组 664 | if (ManagerPlugin.group !== "") { 665 | const group = createSpan({ cls: "manager-item__name-group", }); 666 | itemEl.nameEl.appendChild(group); 667 | const item = this.settings.GROUPS.find((t) => t.id === ManagerPlugin.group); 668 | if (item) { 669 | const tag = this.manager.createTag(item.name, item.color, this.settings.GROUP_STYLE); 670 | if (this.editorMode) tag.onclick = () => { new GroupModal(this.app, this.manager, this, ManagerPlugin).open(); }; 671 | group.appendChild(tag); 672 | } 673 | } 674 | // [编辑] 分组 675 | if (ManagerPlugin.group === "" && this.editorMode) { 676 | const group = createSpan({ cls: "manager-item__name-group", }); 677 | if (this.editorMode) itemEl.nameEl.appendChild(group); 678 | const tag = this.manager.createTag("+", "", ""); 679 | if (this.editorMode) tag.onclick = () => { new GroupModal(this.app, this.manager, this, ManagerPlugin).open(); }; 680 | if (this.editorMode) group.appendChild(tag); 681 | } 682 | 683 | // [默认] 名称 684 | const title = createSpan({ text: ManagerPlugin.name, title: plugin.name, cls: "manager-item__name-title", }); 685 | // [编辑] 名称 686 | if (this.editorMode) { 687 | title.setAttribute("style", "border-width: 1px;border-style: dashed;"); 688 | title.setAttribute("contenteditable", "true"); 689 | title.addEventListener("input", () => { 690 | if (title.textContent) { 691 | ManagerPlugin.name = title.textContent; 692 | this.manager.saveSettings(); 693 | Commands(this.app, this.manager); 694 | } 695 | }); 696 | } 697 | itemEl.nameEl.appendChild(title); 698 | 699 | // [默认] 版本 700 | const version = createSpan({ text: `[${plugin.version}]`, cls: ["manager-item__name-version"], }); 701 | itemEl.nameEl.appendChild(version); 702 | 703 | // [默认] 笔记图标 704 | if (ManagerPlugin.note?.length > 0) { 705 | const note = createSpan(); 706 | note.style.cssText = "width:16px; height:16px; display:inline-flex; color: var(--text-accent);"; 707 | note.addEventListener("click", () => { new NoteModal(this.app, this.manager, ManagerPlugin, this).open(); }); 708 | itemEl.nameEl.appendChild(note); 709 | setIcon(note, "notebook-pen"); 710 | } 711 | 712 | // [默认] 延迟 713 | if (this.settings.DELAY && !this.editorMode && ManagerPlugin.delay !== "") { 714 | const d = this.settings.DELAYS.find((item) => item.id === ManagerPlugin.delay); 715 | if (d) { 716 | const delay = createSpan({ text: `${d.time}s`, cls: ["manager-item__name-delay"], }); 717 | itemEl.nameEl.appendChild(delay); 718 | } 719 | } 720 | // [默认] 描述 721 | const desc = createDiv({ text: ManagerPlugin.desc, title: plugin.description, cls: ["manager-item__name-desc"], }); 722 | 723 | // [编辑] 描述 724 | if (this.editorMode) { 725 | desc.setAttribute("style", "border-width: 1px;border-style: dashed"); 726 | desc.setAttribute("contenteditable", "true"); 727 | desc.addEventListener("input", () => { 728 | if (desc.textContent) { 729 | ManagerPlugin.desc = desc.textContent; 730 | this.manager.saveSettings(); 731 | } 732 | }); 733 | } 734 | itemEl.descEl.appendChild(desc); 735 | 736 | // [默认] 标签组 737 | const tags = createDiv(); 738 | itemEl.descEl.appendChild(tags); 739 | ManagerPlugin.tags.map((id: string) => { 740 | const item = this.settings.TAGS.find((item) => item.id === id); 741 | if (item) { 742 | const tag = this.manager.createTag(item.name, item.color, this.settings.TAG_STYLE); 743 | if (this.editorMode) tag.onclick = () => { new TagsModal(this.app, this.manager, this, ManagerPlugin).open(); }; 744 | tags.appendChild(tag); 745 | } 746 | }); 747 | 748 | // [编辑] 标签组 749 | if (this.editorMode) { 750 | const tag = this.manager.createTag("+", "", ""); 751 | tag.onclick = () => { new TagsModal(this.app, this.manager, this, ManagerPlugin).open(); }; 752 | tags.appendChild(tag); 753 | } 754 | 755 | if (!this.editorMode) { 756 | // [按钮] 打开设置 757 | if (isEnabled) { 758 | const openPluginSetting = new ExtraButtonComponent(itemEl.controlEl); 759 | openPluginSetting.setIcon("settings"); 760 | openPluginSetting.setTooltip(this.manager.translator.t("管理器_打开设置_描述")); 761 | openPluginSetting.onClick(() => { 762 | openPluginSetting.setDisabled(true); 763 | this.appSetting.open(); 764 | this.appSetting.openTabById(plugin.id); 765 | openPluginSetting.setDisabled(false); 766 | }); 767 | } 768 | 769 | // [按钮] 打开目录 770 | const openPluginDirButton = new ExtraButtonComponent(itemEl.controlEl); 771 | openPluginDirButton.setIcon("folder-open"); 772 | openPluginDirButton.setTooltip(this.manager.translator.t("管理器_打开目录_描述")); 773 | openPluginDirButton.onClick(() => { 774 | openPluginDirButton.setDisabled(true); 775 | managerOpen(pluginDir, this.manager); 776 | openPluginDirButton.setDisabled(false); 777 | }); 778 | 779 | // [按钮] 删除插件 780 | const deletePluginButton = new ExtraButtonComponent(itemEl.controlEl); 781 | deletePluginButton.setIcon("trash"); 782 | deletePluginButton.setTooltip(this.manager.translator.t("管理器_删除插件_描述")); 783 | deletePluginButton.onClick(async () => { 784 | new DeleteModal(this.app, this.manager, async () => { 785 | await this.appPlugins.uninstallPlugin(plugin.id); 786 | await this.appPlugins.loadManifests(); 787 | this.reloadShowData(); 788 | // 刷新命令行 789 | Commands(this.app, this.manager); 790 | // 删除同理 791 | this.manager.synchronizePlugins(Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manager.manifest.id) as PluginManifest[]); 792 | new Notice(this.manager.translator.t("卸载_通知_一")); 793 | }).open(); 794 | }); 795 | 796 | // [按钮] 切换状态 797 | const toggleSwitch = new ToggleComponent(itemEl.controlEl); 798 | toggleSwitch.setTooltip(this.manager.translator.t("管理器_切换状态_描述")); 799 | toggleSwitch.setValue(isEnabled); 800 | toggleSwitch.onChange(async () => { 801 | if (this.settings.DELAY) { 802 | if (toggleSwitch.getValue()) { 803 | if (this.settings.FADE_OUT_DISABLED_PLUGINS) itemEl.settingEl.removeClass("inactive"); // [淡化插件] 804 | ManagerPlugin.enabled = true; 805 | this.manager.saveSettings(); 806 | await this.appPlugins.enablePlugin(plugin.id); 807 | } else { 808 | if (this.settings.FADE_OUT_DISABLED_PLUGINS) itemEl.settingEl.addClass("inactive"); // [淡化插件] 809 | ManagerPlugin.enabled = false; 810 | this.manager.saveSettings(); 811 | await this.appPlugins.disablePlugin(plugin.id); 812 | } 813 | } else { 814 | if (toggleSwitch.getValue()) { 815 | if (this.settings.FADE_OUT_DISABLED_PLUGINS) itemEl.settingEl.removeClass("inactive"); // [淡化插件] 816 | await this.appPlugins.enablePluginAndSave(plugin.id); 817 | } else { 818 | if (this.settings.FADE_OUT_DISABLED_PLUGINS) itemEl.settingEl.addClass("inactive"); // [淡化插件] 819 | await this.appPlugins.disablePluginAndSave(plugin.id); 820 | } 821 | } 822 | Commands(this.app, this.manager); 823 | this.reloadShowData(); 824 | }); 825 | } 826 | // 827 | if (this.editorMode) { 828 | // [按钮] 还原内容 829 | const reloadButton = new ExtraButtonComponent(itemEl.controlEl); 830 | reloadButton.setIcon("refresh-ccw"); 831 | reloadButton.setTooltip(this.manager.translator.t("管理器_还原内容_描述")); 832 | reloadButton.onClick(() => { 833 | ManagerPlugin.name = plugin.name; 834 | ManagerPlugin.desc = plugin.description; 835 | ManagerPlugin.group = ""; 836 | ManagerPlugin.delay = ""; 837 | ManagerPlugin.tags = []; 838 | this.manager.saveSettings(); 839 | this.reloadShowData(); 840 | }); 841 | // [编辑] 延迟 842 | if (this.settings.DELAY) { 843 | const delays = this.settings.DELAYS.reduce((acc: { [key: string]: string }, item) => { acc[item.id] = item.name; return acc; }, { "": this.manager.translator.t("通用_无延迟_文本"), }); 844 | const delaysEl = new DropdownComponent(itemEl.controlEl); 845 | delaysEl.addOptions(delays); 846 | delaysEl.setValue(ManagerPlugin.delay); 847 | delaysEl.onChange((value) => { 848 | ManagerPlugin.delay = value; 849 | this.manager.saveSettings(); 850 | this.reloadShowData(); 851 | }); 852 | } 853 | } 854 | } 855 | } 856 | // 计算页尾 857 | this.footEl.innerHTML = this.count(); 858 | } 859 | 860 | public count(): string { 861 | let totalCount = 0; 862 | let enabledCount = 0; 863 | let disabledCount = 0; 864 | if (this.settings.DELAY) { 865 | const plugins = this.settings.Plugins; 866 | totalCount = plugins.length; 867 | plugins.forEach((plugin) => { plugin.enabled ? enabledCount++ : disabledCount++; }); 868 | } else { 869 | totalCount = Object.keys(this.manager.appPlugins.manifests).length - 1; 870 | enabledCount = this.manager.appPlugins.enabledPlugins.size - 1; 871 | disabledCount = totalCount - enabledCount; 872 | } 873 | const summary = `[${this.manager.translator.t( 874 | "通用_总计_文本" 875 | )}] ${totalCount} [${this.manager.translator.t( 876 | "通用_启用_文本" 877 | )}] ${enabledCount} [${this.manager.translator.t( 878 | "通用_禁用_文本" 879 | )}] ${disabledCount} `; 880 | return summary; 881 | } 882 | 883 | public async reloadShowData() { 884 | let scrollTop = 0; 885 | const modalElement: HTMLElement = this.contentEl; 886 | scrollTop = modalElement.scrollTop; 887 | modalElement.empty(); 888 | this.showData(); 889 | modalElement.scrollTo(0, scrollTop); 890 | } 891 | 892 | public async onOpen() { 893 | await this.showHead(); 894 | await this.showData(); 895 | this.searchEl.inputEl.focus(); 896 | // [功能] ctrl+f聚焦 897 | document.addEventListener("keydown", (event) => { 898 | if (event.ctrlKey && event.key.toLowerCase() === "f") { 899 | if (this.searchEl.inputEl) { 900 | this.searchEl.inputEl.focus(); 901 | } 902 | } 903 | }); 904 | } 905 | 906 | public async onClose() { 907 | this.contentEl.empty(); 908 | } 909 | } 910 | -------------------------------------------------------------------------------- /src/modal/note-modal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | App, 3 | ExtraButtonComponent, 4 | Modal, 5 | Setting, 6 | TextAreaComponent, 7 | } from "obsidian"; 8 | import { ManagerSettings } from "../settings/data"; 9 | import Manager from "main"; 10 | import { ManagerPlugin } from "src/data/types"; 11 | import { ManagerModal } from "./manager-modal"; 12 | 13 | export class NoteModal extends Modal { 14 | settings: ManagerSettings; 15 | manager: Manager; 16 | managerPlugin: ManagerPlugin; 17 | managerModal: ManagerModal; 18 | 19 | constructor(app: App, manager: Manager, managerPlugin: ManagerPlugin, managerModal: ManagerModal) { 20 | super(app); 21 | this.settings = manager.settings; 22 | this.manager = manager; 23 | this.managerPlugin = managerPlugin; 24 | this.managerModal = managerModal; 25 | } 26 | 27 | private async showHead() { 28 | //@ts-ignore 29 | const modalEl: HTMLElement = this.contentEl.parentElement; 30 | modalEl.addClass("manager-note__container"); 31 | modalEl.removeChild(modalEl.getElementsByClassName("modal-close-button")[0]); 32 | this.titleEl.parentElement?.addClass("manager-container__header"); 33 | this.contentEl.addClass("manager-item-container"); 34 | // [标题行] 35 | const titleBar = new Setting(this.titleEl).setClass("manager-bar__title").setName(`${this.managerPlugin.name}`); 36 | // [标题行] 关闭按钮 37 | const closeButton = new ExtraButtonComponent(titleBar.controlEl); 38 | closeButton.setIcon("circle-x"); 39 | closeButton.onClick(() => this.close()); 40 | } 41 | 42 | private async showData() { 43 | const textArea = new TextAreaComponent(this.contentEl); 44 | textArea.setValue(this.managerPlugin.note); 45 | textArea.onChange((newValue) => { 46 | this.managerPlugin.note = newValue; 47 | this.manager.saveSettings(); 48 | this.managerModal.reloadShowData(); 49 | }); 50 | } 51 | 52 | private async reloadShowData() { 53 | let scrollTop = 0; 54 | const modalElement: HTMLElement = this.contentEl; 55 | scrollTop = modalElement.scrollTop; 56 | modalElement.empty(); 57 | await this.showData(); 58 | modalElement.scrollTo(0, scrollTop); 59 | } 60 | 61 | async onOpen() { 62 | await this.showHead(); 63 | await this.showData(); 64 | } 65 | 66 | async onClose() { 67 | this.contentEl.empty(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/modal/share-modal.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { 3 | App, 4 | ButtonComponent, 5 | ExtraButtonComponent, 6 | Modal, 7 | Notice, 8 | SearchComponent, 9 | Setting, 10 | ToggleComponent, 11 | } from "obsidian"; 12 | 13 | import { ManagerSettings } from "../settings/data"; 14 | 15 | import Manager from "main"; 16 | 17 | interface ExportPluginManifest { 18 | id: string; 19 | name: string; 20 | version: string; 21 | author: string; 22 | description: string; 23 | export: boolean; 24 | } 25 | 26 | interface ImportPluginManifest { 27 | id: string; 28 | name: string; 29 | version: string; 30 | author: string; 31 | description: string; 32 | } 33 | 34 | 35 | // ============================== 36 | // 侧边栏 对话框 翻译 37 | // ============================== 38 | export class ShareModal extends Modal { 39 | manager: Manager; 40 | settings: ManagerSettings; 41 | // this.app.plugins 42 | appPlugins; 43 | // this.app.settings 44 | appSetting; 45 | // [本地][变量] 导入插件列表 46 | importPlugins: ImportPluginManifest[] = []; 47 | // [本地][变量] 导出插件列表 48 | exportPlugins: ExportPluginManifest[] = []; 49 | 50 | // 搜索内容 51 | searchText = ""; 52 | // 操作类型 53 | type = ""; 54 | // 页尾 55 | footEl: HTMLDivElement; 56 | 57 | constructor(app: App, manager: Manager, plugins: (ImportPluginManifest | ExportPluginManifest)[], type: string) { 58 | super(app); 59 | // @ts-ignore 60 | this.appSetting = this.app.setting; 61 | // @ts-ignore 62 | this.appPlugins = this.app.plugins; 63 | this.manager = manager; 64 | this.settings = manager.settings; 65 | this.type = type; 66 | if (this.type == "export") { 67 | 68 | } 69 | // 自动分类插件类型 70 | plugins.forEach(plugin => { if (this.isImportPlugin(plugin)) { this.importPlugins.push(plugin); } else { this.exportPlugins.push(plugin); } }); 71 | } 72 | 73 | public async showHead() { 74 | //@ts-ignore 75 | const modalEl: HTMLElement = this.contentEl.parentElement; 76 | modalEl.addClass("manager-container"); 77 | // 靠上 78 | if (!this.settings.CENTER) modalEl.addClass("manager-container__top"); 79 | modalEl.removeChild(modalEl.getElementsByClassName("modal-close-button")[0]); 80 | this.titleEl.parentElement?.addClass("manager-container__header"); 81 | this.contentEl.addClass("manager-item-container"); 82 | // 添加页尾 83 | this.footEl = document.createElement("div"); 84 | this.footEl.addClass("manager-food"); 85 | this.modalEl.appendChild(this.footEl); 86 | 87 | // [操作行] 88 | const actionBar = new Setting(this.titleEl).setClass("manager-bar__action").setName(this.manager.translator.t("通用_操作_文本") + '[导入]' + '[导出]'); 89 | // [操作行] 导入 90 | if (this.type == "export") { 91 | // [操作行] 导入 92 | const linkButton = new ButtonComponent(actionBar.controlEl); 93 | linkButton.setIcon("link"); 94 | linkButton.setTooltip('导入插件'); 95 | linkButton.onClick(() => { 96 | }); 97 | } 98 | // [操作行] 导入 99 | const tutorialButton = new ButtonComponent(actionBar.controlEl); 100 | tutorialButton.setIcon("file-down"); 101 | tutorialButton.setTooltip('导入插件'); 102 | tutorialButton.onClick(() => { 103 | this.type = "import"; 104 | this.reloadShowData(); 105 | }); 106 | 107 | // [操作行] 导出 108 | const githubButton = new ButtonComponent(actionBar.controlEl); 109 | githubButton.setIcon("file-up"); 110 | githubButton.setTooltip('导出插件'); 111 | githubButton.onClick(() => { 112 | actionBar.setName(this.manager.translator.t("通用_操作_文本")); 113 | this.type = "export"; 114 | this.reloadShowData(); 115 | }); 116 | 117 | // [操作行] 关闭 118 | const closeButton = new ButtonComponent(actionBar.controlEl); 119 | closeButton.setIcon("x"); 120 | closeButton.setTooltip('关闭'); 121 | closeButton.onClick(() => { 122 | actionBar.setName(this.manager.translator.t("通用_操作_文本") + '[导出]'); 123 | this.type = "export"; 124 | this.reloadShowData(); 125 | }); 126 | } 127 | 128 | public async showData() { 129 | if (this.type == "export") { 130 | for (const plugin of this.exportPlugins) { 131 | const itemEl = new Setting(this.contentEl); 132 | itemEl.setClass("manager-item"); 133 | itemEl.nameEl.addClass("manager-item__name-container"); 134 | itemEl.descEl.addClass("manager-item__description-container"); 135 | 136 | // [默认] 名称 137 | const title = createSpan({ text: plugin.name, cls: "manager-item__name-title", }); 138 | itemEl.nameEl.appendChild(title); 139 | 140 | // [默认] 版本 141 | const version = createSpan({ text: `[${plugin.version}]`, cls: ["manager-item__name-version"], }); 142 | itemEl.nameEl.appendChild(version); 143 | 144 | // [默认] 描述 145 | const desc = createDiv({ text: plugin.description, title: plugin.description, cls: ["manager-item__name-desc"], }); 146 | itemEl.descEl.appendChild(desc); 147 | 148 | const shareToggle = new ToggleComponent(itemEl.controlEl); 149 | shareToggle.setValue(plugin.export); 150 | shareToggle.onChange((value) => { 151 | plugin.export = !value; 152 | }); 153 | } 154 | // 计算页尾 155 | this.footEl.innerHTML = `[${this.manager.translator.t("通用_总计_文本")}] ${this.exportPlugins.length} `; 156 | } 157 | if (this.type == "import") { 158 | for (const plugin of this.importPlugins) { 159 | const itemEl = new Setting(this.contentEl); 160 | itemEl.setClass("manager-item"); 161 | itemEl.nameEl.addClass("manager-item__name-container"); 162 | itemEl.descEl.addClass("manager-item__description-container"); 163 | 164 | // [默认] 名称 165 | const title = createSpan({ text: plugin.name, cls: "manager-item__name-title", }); 166 | itemEl.nameEl.appendChild(title); 167 | 168 | // [默认] 版本 169 | const version = createSpan({ text: `[${plugin.version}]`, cls: ["manager-item__name-version"], }); 170 | itemEl.nameEl.appendChild(version); 171 | 172 | // [默认] 描述 173 | const desc = createDiv({ text: plugin.description, title: plugin.description, cls: ["manager-item__name-desc"], }); 174 | itemEl.descEl.appendChild(desc); 175 | 176 | // [按钮] 下载插件 177 | const openPluginDirButton = new ExtraButtonComponent(itemEl.controlEl); 178 | openPluginDirButton.setIcon("download"); 179 | openPluginDirButton.setTooltip('下载插件'); 180 | openPluginDirButton.onClick(() => { window.open(`obsidian://BPM-plugin-install?id=${plugin.id}&enable=true&version=${plugin.version}`); }); 181 | } 182 | // 计算页尾 183 | this.footEl.innerHTML = `[${this.manager.translator.t("通用_总计_文本")}] ${this.importPlugins.length} `; 184 | } 185 | } 186 | 187 | public async reloadShowData() { 188 | let scrollTop = 0; 189 | const modalElement: HTMLElement = this.contentEl; 190 | scrollTop = modalElement.scrollTop; 191 | modalElement.empty(); 192 | this.showData(); 193 | modalElement.scrollTo(0, scrollTop); 194 | } 195 | 196 | public async onOpen() { 197 | await this.showHead(); 198 | await this.showData(); 199 | } 200 | 201 | public async onClose() { 202 | this.contentEl.empty(); 203 | } 204 | 205 | // 新增类型守卫 206 | private isImportPlugin(plugin: any): plugin is ImportPluginManifest { 207 | return 'installLink' in plugin; // 根据实际特征判断 208 | } 209 | 210 | private isExportPlugin(plugin: any): plugin is ExportPluginManifest { 211 | return 'enabled' in plugin; // 根据实际特征判断 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/modal/share-t-modal.ts: -------------------------------------------------------------------------------- 1 | import { App, ExtraButtonComponent, Modal, Notice, Setting } from 'obsidian'; 2 | import { ManagerSettings } from '../settings/data'; 3 | import Manager from 'main'; 4 | 5 | export class ShareTModal extends Modal { 6 | settings: ManagerSettings; 7 | manager: Manager; 8 | url: string = ''; 9 | private deleteCallback: (type: string, url?: string) => void; 10 | 11 | constructor(app: App, manager: Manager, deleteCallback: (type: string, url?: string) => void) { 12 | super(app); 13 | this.manager = manager; 14 | this.deleteCallback = deleteCallback; 15 | } 16 | 17 | private async showHead() { 18 | //@ts-ignore 19 | const modalEl: HTMLElement = this.contentEl.parentElement; 20 | modalEl.addClass('manager-editor__container'); 21 | modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]); 22 | this.titleEl.parentElement?.addClass('manager-container__header'); 23 | this.contentEl.addClass('manager-item-container'); 24 | 25 | // [标题行] 26 | const titleBar = new Setting(this.titleEl) 27 | titleBar.setClass('manager-delete__title') 28 | titleBar.setName('共享插件'); 29 | 30 | // [标题行] 关闭按钮 31 | const closeButton = new ExtraButtonComponent(titleBar.controlEl) 32 | closeButton.setIcon('circle-x') 33 | closeButton.onClick(() => this.close()); 34 | } 35 | 36 | private async showData() { 37 | const titleBar = new Setting(this.titleEl) 38 | titleBar.setName('分享链接') 39 | titleBar.addText((cb) => { 40 | cb.setValue('') 41 | cb.setPlaceholder('请输入分享链接') 42 | cb.onChange((value) => { 43 | this.url = value; 44 | }) 45 | }) 46 | 47 | 48 | const actionBar = new Setting(this.titleEl) 49 | actionBar.setClass('manager-delete__action') 50 | actionBar.addButton(cb => cb 51 | .setButtonText('导入') 52 | .onClick(() => { 53 | if (!this.url) { new Notice('请输入分享链接'); return; } 54 | this.deleteCallback('import', this.url); 55 | this.close(); 56 | }) 57 | ); 58 | actionBar.addButton(cb => cb 59 | .setButtonText('导出') 60 | .onClick(() => { 61 | this.deleteCallback('export'); 62 | this.close(); 63 | }) 64 | ); 65 | } 66 | 67 | async onOpen() { 68 | await this.showHead(); 69 | await this.showData(); 70 | } 71 | 72 | async onClose() { 73 | this.contentEl.empty(); 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/modal/tags-modal.ts: -------------------------------------------------------------------------------- 1 | import { App, ExtraButtonComponent, Modal, Notice, Setting } from 'obsidian'; 2 | import { ManagerSettings } from '../settings/data'; 3 | import Manager from 'main'; 4 | import { ManagerModal } from './manager-modal'; 5 | import { ManagerPlugin } from 'src/data/types'; 6 | import Commands from 'src/command'; 7 | 8 | export class TagsModal extends Modal { 9 | settings: ManagerSettings; 10 | manager: Manager; 11 | managerModal: ManagerModal; 12 | managerPlugin: ManagerPlugin; 13 | selected: string; 14 | add: boolean; 15 | 16 | constructor(app: App, manager: Manager, managerModal: ManagerModal, managerPlugin: ManagerPlugin) { 17 | super(app); 18 | this.settings = manager.settings; 19 | this.manager = manager; 20 | this.managerModal = managerModal; 21 | this.managerPlugin = managerPlugin; 22 | this.selected = ''; 23 | this.add = false; 24 | } 25 | 26 | private async showHead() { 27 | //@ts-ignore 28 | const modalEl: HTMLElement = this.contentEl.parentElement; 29 | modalEl.addClass('manager-editor__container'); 30 | modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]); 31 | this.titleEl.parentElement?.addClass('manager-container__header'); 32 | this.contentEl.addClass('manager-item-container'); 33 | // [标题行] 34 | const titleBar = new Setting(this.titleEl).setClass('manager-bar__title').setName(this.managerPlugin.name); 35 | // [标题行] 关闭按钮 36 | const closeButton = new ExtraButtonComponent(titleBar.controlEl) 37 | closeButton.setIcon('circle-x') 38 | closeButton.onClick(() => this.close()); 39 | } 40 | 41 | private async showData() { 42 | for (const tag of this.settings.TAGS) { 43 | const itemEl = new Setting(this.contentEl) 44 | itemEl.setClass('manager-editor__item') 45 | if (this.selected == '' || this.selected != tag.id) { 46 | itemEl.addExtraButton(cb => cb 47 | .setIcon('settings') 48 | .onClick(() => { 49 | this.selected = tag.id; 50 | this.reloadShowData(); 51 | }) 52 | ) 53 | itemEl.addToggle(cb => cb 54 | .setValue(this.managerPlugin.tags.includes(tag.id)) 55 | .onChange((isChecked) => { 56 | if (isChecked) { 57 | // 添加开启的标签 58 | if (!this.managerPlugin.tags.includes(tag.id)) { 59 | this.managerPlugin.tags.push(tag.id); 60 | } 61 | } else { 62 | // 移除关闭的标签 63 | this.managerPlugin.tags = this.managerPlugin.tags.filter(t => t !== tag.id); 64 | } 65 | this.manager.saveSettings(); 66 | this.managerModal.reloadShowData(); 67 | }) 68 | ); 69 | const tempEl = createSpan({ cls: 'manager-item__name-group' }); 70 | itemEl.nameEl.appendChild(tempEl); 71 | const tagEl = this.manager.createTag(tag.name, tag.color, this.settings.TAG_STYLE); 72 | tempEl.appendChild(tagEl); 73 | } 74 | if (this.selected != '' && this.selected == tag.id) { 75 | itemEl.addColorPicker(cb => cb 76 | .setValue(tag.color) 77 | .onChange((value) => { 78 | tag.color = value; 79 | this.manager.saveSettings(); 80 | this.reloadShowData(); 81 | }) 82 | ) 83 | itemEl.addText(cb => cb 84 | .setValue(tag.name) 85 | .onChange((value) => { 86 | tag.name = value; 87 | this.manager.saveSettings(); 88 | }) 89 | .inputEl.addClass('manager-editor__item-input') 90 | ) 91 | itemEl.addExtraButton(cb => cb 92 | .setIcon('trash-2') 93 | .onClick(() => { 94 | const hasTestTag = this.settings.Plugins.some(plugin => plugin.tags && plugin.tags.includes(tag.id)); 95 | if (!hasTestTag) { 96 | this.manager.settings.TAGS = this.manager.settings.TAGS.filter(t => t.id !== tag.id); 97 | this.manager.saveSettings(); 98 | this.reloadShowData(); 99 | Commands(this.app, this.manager); 100 | new Notice(this.manager.translator.t('设置_标签设置_通知_三')); 101 | } else { 102 | new Notice(this.manager.translator.t('设置_标签设置_通知_四')); 103 | } 104 | }) 105 | ) 106 | 107 | itemEl.addExtraButton(cb => cb 108 | .setIcon('save') 109 | .onClick(() => { 110 | this.selected = ''; 111 | this.reloadShowData(); 112 | this.managerModal.reloadShowData(); 113 | }) 114 | ) 115 | const groupEl = createSpan({ cls: 'manager-item__name-group' }); 116 | itemEl.nameEl.appendChild(groupEl); 117 | const tagEl = this.manager.createTag(tag.name, tag.color, this.settings.TAG_STYLE); 118 | groupEl.appendChild(tagEl); 119 | } 120 | } 121 | if (this.add) { 122 | let id = ''; 123 | let name = ''; 124 | let color = ''; 125 | const foodBar = new Setting(this.contentEl).setClass('manager-bar__title'); 126 | foodBar.infoEl.remove(); 127 | foodBar.addColorPicker(cb => cb 128 | .setValue(color) 129 | .onChange((value) => { color = value; }) 130 | ) 131 | foodBar.addText(cb => cb 132 | .setPlaceholder('ID') 133 | .onChange((value) => { id = value; this.manager.saveSettings(); }) 134 | .inputEl.addClass('manager-editor__item-input') 135 | ) 136 | foodBar.addText(cb => cb 137 | .setPlaceholder(this.manager.translator.t('通用_名称_文本')) 138 | .onChange((value) => { name = value; }) 139 | .inputEl.addClass('manager-editor__item-input') 140 | ) 141 | foodBar.addExtraButton(cb => cb 142 | .setIcon('plus') 143 | .onClick(() => { 144 | const containsId = this.manager.settings.TAGS.some(tag => tag.id === id); 145 | if (!containsId && id !== '') { 146 | if (color === '') color = '#000000'; 147 | this.manager.settings.TAGS.push({ id, name, color }); 148 | this.manager.saveSettings(); 149 | this.add = false; 150 | this.reloadShowData(); 151 | Commands(this.app, this.manager); 152 | new Notice(this.manager.translator.t('设置_标签设置_通知_一')); 153 | } else { 154 | new Notice(this.manager.translator.t('设置_标签设置_通知_二')); 155 | } 156 | }) 157 | ) 158 | } else { 159 | // [底部行] 新增 160 | const foodBar = new Setting(this.contentEl).setClass('manager-bar__title').setName(this.manager.translator.t('通用_新增_文本')); 161 | const addButton = new ExtraButtonComponent(foodBar.controlEl) 162 | addButton.setIcon('circle-plus') 163 | addButton.onClick(() => { 164 | this.add = true; 165 | this.reloadShowData(); 166 | }); 167 | } 168 | } 169 | 170 | private async reloadShowData() { 171 | let scrollTop = 0; 172 | const modalElement: HTMLElement = this.contentEl; 173 | scrollTop = modalElement.scrollTop; 174 | modalElement.empty(); 175 | await this.showData(); 176 | modalElement.scrollTo(0, scrollTop); 177 | } 178 | 179 | async onOpen() { 180 | await this.showHead(); 181 | await this.showData(); 182 | } 183 | 184 | async onClose() { 185 | this.contentEl.empty(); 186 | } 187 | } 188 | 189 | -------------------------------------------------------------------------------- /src/settings/base-setting.ts: -------------------------------------------------------------------------------- 1 | import Manager from 'src/main'; 2 | import { ManagerSettingTab } from '.'; 3 | import { ManagerSettings } from './data'; 4 | import { App } from 'obsidian'; 5 | 6 | export default abstract class BaseSetting { 7 | protected settingTab: ManagerSettingTab; 8 | protected manager: Manager; 9 | protected settings: ManagerSettings; 10 | public containerEl: HTMLElement; 11 | protected app: App; 12 | 13 | constructor(obj: ManagerSettingTab) { 14 | this.settingTab = obj; 15 | this.manager = obj.manager; 16 | this.settings = obj.manager.settings; 17 | this.containerEl = obj.contentEl; 18 | this.app = obj.app; 19 | } 20 | 21 | public abstract main(): void; 22 | public display(): void { this.main() } 23 | } -------------------------------------------------------------------------------- /src/settings/data.ts: -------------------------------------------------------------------------------- 1 | import { Delay, ManagerPlugin, Tag, Type } from '../data/types'; 2 | 3 | export interface ManagerSettings { 4 | // 持久化 5 | PERSISTENCE: boolean; 6 | // 过滤标签 7 | FILTER_TAG: string; 8 | // 过滤分组 9 | FILTER_GROUP: string; 10 | // 过滤延迟 11 | FILTER_DELAY: string; 12 | 13 | // 语言 14 | LANGUAGE: string; 15 | // 居中 16 | CENTER: boolean; 17 | // 样式 18 | ITEM_STYLE: string; 19 | // 分组样式 20 | GROUP_STYLE: string; 21 | // 标签样式 22 | TAG_STYLE: string; 23 | 24 | // 延迟 25 | DELAY: boolean; 26 | // 淡出样式 27 | FADE_OUT_DISABLED_PLUGINS: boolean; 28 | // 命令项 29 | COMMAND_ITEM: boolean; 30 | // 命令组 31 | COMMAND_GROUP: boolean; 32 | 33 | GROUPS: Type[]; 34 | TAGS: Tag[]; 35 | DELAYS: Delay[]; 36 | Plugins: ManagerPlugin[]; 37 | HIDES: string[], 38 | } 39 | 40 | export const DEFAULT_SETTINGS: ManagerSettings = { 41 | PERSISTENCE: false, 42 | // 筛选 43 | FILTER_TAG: "", 44 | FILTER_GROUP: "", 45 | FILTER_DELAY: "", 46 | 47 | LANGUAGE: "zh-cn", 48 | CENTER: false, 49 | ITEM_STYLE: "alwaysExpand", 50 | GROUP_STYLE: "a", 51 | TAG_STYLE: "b", 52 | DELAY: false, 53 | FADE_OUT_DISABLED_PLUGINS: true, 54 | COMMAND_ITEM: false, 55 | COMMAND_GROUP: false, 56 | GROUPS: [ 57 | { 58 | "id": "default", 59 | "name": "默认组", 60 | "color": "#A079FF" 61 | }, 62 | ], 63 | TAGS: [ 64 | { 65 | "id": "default", 66 | "name": "默认标签", 67 | "color": "#A079FF" 68 | }, 69 | ], 70 | DELAYS: [ 71 | { 72 | "id": "default", 73 | "name": "默认延迟", 74 | "time": 10 75 | }, 76 | ], 77 | Plugins: [], 78 | HIDES: [], 79 | } 80 | -------------------------------------------------------------------------------- /src/settings/index.ts: -------------------------------------------------------------------------------- 1 | import { App, PluginSettingTab } from 'obsidian'; 2 | import Manager from "../main"; 3 | 4 | import ManagerBasis from './ui/manager-basis'; 5 | import ManagerStyle from './ui/manager-style'; 6 | import ManagerDelay from './ui/manager-delay'; 7 | import ManagerTag from './ui/manager-tag'; 8 | import ManagerGroup from './ui/manager-group'; 9 | 10 | 11 | class ManagerSettingTab extends PluginSettingTab { 12 | manager: Manager; 13 | app: App; 14 | contentEl: HTMLDivElement; 15 | 16 | constructor(app: App, manager: Manager) { 17 | super(app, manager); 18 | this.manager = manager; 19 | this.app = app; 20 | } 21 | 22 | display(): void { 23 | const { containerEl } = this; 24 | containerEl.empty(); 25 | containerEl.addClass('manager-setting__container'); 26 | const tabsEl = this.containerEl.createEl('div'); 27 | tabsEl.addClass('manager-setting__tabs'); 28 | this.contentEl = this.containerEl.createEl('div'); 29 | this.contentEl.addClass('manager-setting__content'); 30 | 31 | const tabItems = [ 32 | { text: this.manager.translator.t('设置_基础设置_前缀'), content: () => this.basisDisplay() }, 33 | { text: this.manager.translator.t('设置_样式设置_前缀'), content: () => this.styleDisplay() }, 34 | { text: this.manager.translator.t('设置_分组设置_前缀'), content: () => this.groupDisplay() }, 35 | { text: this.manager.translator.t('设置_标签设置_前缀'), content: () => this.tagDisplay() }, 36 | 37 | ]; 38 | if (this.manager.settings.DELAY) tabItems.push({ text: this.manager.translator.t('设置_延迟设置_前缀'), content: () => this.delayDisplay() }); 39 | 40 | const tabItemsEls: HTMLDivElement[] = []; 41 | 42 | tabItems.forEach((item, index) => { 43 | const itemEl = tabsEl.createEl('div'); 44 | itemEl.addClass('manager-setting__tabs-item'); 45 | itemEl.textContent = item.text; 46 | tabItemsEls.push(itemEl); 47 | if (index === 0) { itemEl.addClass('manager-setting__tabs-item_is-active'); item.content(); } 48 | itemEl.addEventListener('click', () => { 49 | tabItemsEls.forEach(tabEl => { tabEl.removeClass('manager-setting__tabs-item_is-active') }); 50 | itemEl.addClass('manager-setting__tabs-item_is-active'); 51 | item.content(); 52 | }); 53 | }); 54 | } 55 | basisDisplay() { this.contentEl.empty(); new ManagerBasis(this).display(); } 56 | styleDisplay() { this.contentEl.empty(); new ManagerStyle(this).display(); } 57 | delayDisplay() { this.contentEl.empty(); new ManagerDelay(this).display(); } 58 | groupDisplay() { this.contentEl.empty(); new ManagerGroup(this).display(); } 59 | tagDisplay() { this.contentEl.empty(); new ManagerTag(this).display(); } 60 | } 61 | 62 | export { ManagerSettingTab }; 63 | 64 | -------------------------------------------------------------------------------- /src/settings/ui/manager-basis.ts: -------------------------------------------------------------------------------- 1 | import BaseSetting from "../base-setting"; 2 | import { DropdownComponent, Setting, ToggleComponent } from "obsidian"; 3 | import Commands from "src/command"; 4 | // import { GROUP_STYLE, ITEM_STYLE, TAG_STYLE } from "src/data/data"; 5 | 6 | export default class ManagerBasis extends BaseSetting { 7 | 8 | main(): void { 9 | const languageBar = new Setting(this.containerEl) 10 | .setName(this.manager.translator.t('设置_基础设置_语言_标题')) 11 | .setDesc(this.manager.translator.t('设置_基础设置_语言_描述')); 12 | const languageDropdown = new DropdownComponent(languageBar.controlEl); 13 | languageDropdown.addOptions(this.manager.translator.language); 14 | languageDropdown.setValue(this.settings.LANGUAGE); 15 | languageDropdown.onChange((value) => { 16 | this.settings.LANGUAGE = value; 17 | this.manager.saveSettings(); 18 | this.settingTab.basisDisplay(); 19 | Commands(this.app, this.manager); 20 | this.settingTab.display(); // 重新渲染整个设置界面 21 | this.display(); // 保持当前内容区的刷新 22 | }); 23 | 24 | const DelayBar = new Setting(this.containerEl) 25 | .setName(this.manager.translator.t('设置_基础设置_延时启动_标题')) 26 | .setDesc(this.manager.translator.t('设置_基础设置_延时启动_描述')); 27 | const DelayToggle = new ToggleComponent(DelayBar.controlEl); 28 | DelayToggle.setValue(this.settings.DELAY); 29 | DelayToggle.onChange((value) => { 30 | this.settings.DELAY = value; 31 | this.manager.saveSettings(); 32 | value ? this.manager.enableDelaysForAllPlugins() : this.manager.disableDelaysForAllPlugins(); 33 | this.settingTab.display(); // 重新渲染整个设置界面 34 | this.display(); // 保持当前内容区的刷新 35 | }); 36 | 37 | const persistenceBar = new Setting(this.containerEl) 38 | .setName(this.manager.translator.t('设置_基础设置_筛选持久化_标题')) 39 | .setDesc(this.manager.translator.t('设置_基础设置_筛选持久化_描述')); 40 | const persistenceToggle = new ToggleComponent(persistenceBar.controlEl); 41 | persistenceToggle.setValue(this.settings.PERSISTENCE); 42 | persistenceToggle.onChange((value) => { 43 | this.settings.PERSISTENCE = value; 44 | this.manager.saveSettings(); 45 | }); 46 | 47 | const CommandItemBar = new Setting(this.containerEl) 48 | .setName(this.manager.translator.t('设置_基础设置_单独命令_标题')) 49 | .setDesc(this.manager.translator.t('设置_基础设置_单独命令_描述')); 50 | const CommandItemToggle = new ToggleComponent(CommandItemBar.controlEl); 51 | CommandItemToggle.setValue(this.settings.COMMAND_ITEM); 52 | CommandItemToggle.onChange((value) => { 53 | this.settings.COMMAND_ITEM = value; 54 | this.manager.saveSettings(); 55 | Commands(this.app, this.manager); 56 | }); 57 | 58 | const CommandGroupBar = new Setting(this.containerEl) 59 | .setName(this.manager.translator.t('设置_基础设置_分组命令_标题')) 60 | .setDesc(this.manager.translator.t('设置_基础设置_分组命令_描述')); 61 | const CommandGroupToggle = new ToggleComponent(CommandGroupBar.controlEl); 62 | CommandGroupToggle.setValue(this.settings.COMMAND_GROUP); 63 | CommandGroupToggle.onChange((value) => { 64 | this.settings.COMMAND_GROUP = value; 65 | this.manager.saveSettings(); 66 | Commands(this.app, this.manager); 67 | }); 68 | 69 | new Setting(this.containerEl) 70 | .setName(this.manager.translator.t('设置_提示_一_标题')) 71 | .setDesc(this.manager.translator.t('设置_提示_一_描述')); 72 | } 73 | } -------------------------------------------------------------------------------- /src/settings/ui/manager-delay.ts: -------------------------------------------------------------------------------- 1 | import BaseSetting from "../base-setting"; 2 | import { Notice, Setting } from "obsidian"; 3 | 4 | export default class ManagerDelay extends BaseSetting { 5 | main(): void { 6 | let id = ''; 7 | let name = ''; 8 | let time = 0; 9 | new Setting(this.containerEl) 10 | .setHeading() 11 | .setName(this.manager.translator.t('通用_新增_文本')) 12 | .addSlider(cb => cb 13 | .setLimits(0, 100, 1) 14 | .setValue(time) 15 | .setDynamicTooltip() 16 | .onChange((value) => { 17 | time = value; 18 | }) 19 | ) 20 | .addText(cb => cb 21 | .setPlaceholder('ID') 22 | .onChange((value) => { 23 | id = value; 24 | }) 25 | ) 26 | .addText(cb => cb 27 | .setPlaceholder(this.manager.translator.t('通用_名称_文本')) 28 | .onChange((value) => { 29 | name = value; 30 | }) 31 | ) 32 | .addExtraButton(cb => cb 33 | .setIcon('plus') 34 | .onClick(() => { 35 | const containsId = this.manager.settings.DELAYS.some(delay => delay.id === id); 36 | if (!containsId && id !== '') { 37 | this.manager.settings.DELAYS.push({ id, name, time }); 38 | this.manager.saveSettings(); 39 | this.settingTab.delayDisplay(); 40 | new Notice(this.manager.translator.t('设置_延迟设置_通知_一')); 41 | } else { 42 | new Notice(this.manager.translator.t('设置_延迟设置_通知_二')); 43 | } 44 | }) 45 | ) 46 | this.manager.settings.DELAYS.forEach((delay, index) => { 47 | const item = new Setting(this.containerEl) 48 | item.settingEl.addClass('manager-setting-group__item') 49 | item.setName(`[${delay.id}]`) 50 | item.addSlider(cb => cb 51 | .setLimits(0, 100, 1) 52 | .setValue(delay.time) 53 | .setDynamicTooltip() 54 | .onChange((value) => { 55 | delay.time = value 56 | this.manager.saveSettings(); 57 | }) 58 | ) 59 | item.addText(cb => cb 60 | .setValue(delay.name) 61 | .onChange((value) => { 62 | delay.name = value; 63 | this.manager.saveSettings(); 64 | }) 65 | ) 66 | item.addExtraButton(cb => cb 67 | .setIcon('trash-2') 68 | .onClick(() => { 69 | const hasTestGroup = this.settings.Plugins.some(plugin => plugin.delay === delay.id); 70 | if (!hasTestGroup) { 71 | this.manager.settings.DELAYS = this.manager.settings.DELAYS.filter(t => t.id !== delay.id); 72 | this.manager.saveSettings(); 73 | this.settingTab.delayDisplay(); 74 | new Notice(this.manager.translator.t('设置_延迟设置_通知_三')); 75 | } else { 76 | new Notice(this.manager.translator.t('设置_延迟设置_通知_四')); 77 | } 78 | }) 79 | ) 80 | }); 81 | } 82 | } -------------------------------------------------------------------------------- /src/settings/ui/manager-group.ts: -------------------------------------------------------------------------------- 1 | import BaseSetting from "../base-setting"; 2 | import { Notice, Setting } from "obsidian"; 3 | import Commands from "src/command"; 4 | 5 | export default class ManagerGroup extends BaseSetting { 6 | main(): void { 7 | let id = ''; 8 | let name = ''; 9 | let color = ''; 10 | new Setting(this.containerEl) 11 | .setHeading() 12 | .setName(this.manager.translator.t('通用_新增_文本')) 13 | .addColorPicker(cb => cb 14 | .setValue(color) 15 | .onChange((value) => { 16 | color = value; 17 | }) 18 | ) 19 | .addText(cb => cb 20 | .setPlaceholder('ID') 21 | .onChange((value) => { 22 | id = value; 23 | this.manager.saveSettings(); 24 | }) 25 | ) 26 | .addText(cb => cb 27 | .setPlaceholder(this.manager.translator.t('通用_名称_文本')) 28 | .onChange((value) => { 29 | name = value; 30 | }) 31 | ) 32 | .addExtraButton(cb => cb 33 | .setIcon('plus') 34 | .onClick(() => { 35 | const containsId = this.manager.settings.GROUPS.some(tag => tag.id === id); 36 | if (!containsId && id !== '') { 37 | if (color === '') color = '#000000'; 38 | this.manager.settings.GROUPS.push({ id, name, color }); 39 | this.manager.saveSettings(); 40 | this.settingTab.groupDisplay(); 41 | Commands(this.app, this.manager); 42 | new Notice(this.manager.translator.t('设置_分组设置_通知_一')); 43 | } else { 44 | new Notice(this.manager.translator.t('设置_分组设置_通知_二')); 45 | } 46 | }) 47 | ) 48 | 49 | this.manager.settings.GROUPS.forEach((group, index) => { 50 | const item = new Setting(this.containerEl) 51 | item.settingEl.addClass('manager-setting-group__item') 52 | // item.setName(`${index + 1}. `) 53 | item.addColorPicker(cb => cb 54 | .setValue(group.color) 55 | .onChange((value) => { 56 | group.color = value; 57 | this.manager.saveSettings(); 58 | this.settingTab.groupDisplay(); 59 | }) 60 | ) 61 | item.addText(cb => cb 62 | .setValue(group.name) 63 | .onChange((value) => { 64 | group.name = value; 65 | this.manager.saveSettings(); 66 | }).inputEl.addEventListener('blur', () => { 67 | this.settingTab.groupDisplay(); 68 | }) 69 | ) 70 | item.addExtraButton(cb => cb 71 | .setIcon('trash-2') 72 | .onClick(() => { 73 | const hasTestGroup = this.settings.Plugins.some(plugin => plugin.group === group.id); 74 | if (!hasTestGroup) { 75 | this.manager.settings.GROUPS = this.manager.settings.GROUPS.filter(t => t.id !== group.id); 76 | this.manager.saveSettings(); 77 | this.settingTab.groupDisplay(); 78 | Commands(this.app, this.manager); 79 | new Notice(this.manager.translator.t('设置_分组设置_通知_三')); 80 | } else { 81 | new Notice(this.manager.translator.t('设置_分组设置_通知_四')); 82 | } 83 | }) 84 | ) 85 | const tagEl = this.manager.createTag(group.name, group.color, this.settings.GROUP_STYLE); 86 | item.nameEl.appendChild(tagEl); 87 | item.nameEl.appendText(` [${group.id}]`); 88 | }); 89 | } 90 | } -------------------------------------------------------------------------------- /src/settings/ui/manager-style.ts: -------------------------------------------------------------------------------- 1 | import BaseSetting from "../base-setting"; 2 | import { DropdownComponent, Setting, ToggleComponent } from "obsidian"; 3 | import Commands from "src/command"; 4 | // import { GROUP_STYLE, ITEM_STYLE, TAG_STYLE } from "src/data/data"; 5 | 6 | export default class ManagerBasis extends BaseSetting { 7 | private ITEM_STYLE = { 8 | 'alwaysExpand': this.manager.translator.t('设置_基础设置_目录样式_选项_一'), 9 | 'neverExpand': this.manager.translator.t('设置_基础设置_目录样式_选项_二'), 10 | 'hoverExpand': this.manager.translator.t('设置_基础设置_目录样式_选项_三'), 11 | 'clickExpand': this.manager.translator.t('设置_基础设置_目录样式_选项_四'), 12 | } 13 | private GROUP_STYLE = { 14 | 'a': this.manager.translator.t('设置_基础设置_分组样式_选项_一'), 15 | 'b': this.manager.translator.t('设置_基础设置_分组样式_选项_二'), 16 | 'c': this.manager.translator.t('设置_基础设置_分组样式_选项_三'), 17 | 'd': this.manager.translator.t('设置_基础设置_分组样式_选项_四') 18 | } 19 | private TAG_STYLE = { 20 | 'a': this.manager.translator.t('设置_基础设置_标签样式_选项_一'), 21 | 'b': this.manager.translator.t('设置_基础设置_标签样式_选项_二'), 22 | 'c': this.manager.translator.t('设置_基础设置_标签样式_选项_三'), 23 | 'd': this.manager.translator.t('设置_基础设置_标签样式_选项_四') 24 | } 25 | 26 | 27 | main(): void { 28 | 29 | const itemStyleBar = new Setting(this.containerEl) 30 | .setName(this.manager.translator.t('设置_基础设置_目录样式_标题')) 31 | .setDesc(this.manager.translator.t('设置_基础设置_目录样式_描述')); 32 | const itemStyleDropdown = new DropdownComponent(itemStyleBar.controlEl); 33 | itemStyleDropdown.addOptions(this.ITEM_STYLE); 34 | itemStyleDropdown.setValue(this.settings.ITEM_STYLE); 35 | itemStyleDropdown.onChange((value) => { 36 | this.settings.ITEM_STYLE = value; 37 | this.manager.saveSettings(); 38 | }); 39 | 40 | const groupStyleBar = new Setting(this.containerEl) 41 | .setName(this.manager.translator.t('设置_基础设置_分组样式_标题')) 42 | .setDesc(this.manager.translator.t('设置_基础设置_分组样式_描述')); 43 | const groupStyleDropdown = new DropdownComponent(groupStyleBar.controlEl); 44 | groupStyleDropdown.addOptions(this.GROUP_STYLE); 45 | groupStyleDropdown.setValue(this.settings.GROUP_STYLE); 46 | groupStyleDropdown.onChange((value) => { 47 | this.settings.GROUP_STYLE = value; 48 | this.manager.saveSettings(); 49 | }); 50 | 51 | const tagStyleBar = new Setting(this.containerEl) 52 | .setName(this.manager.translator.t('设置_基础设置_标签样式_标题')) 53 | .setDesc(this.manager.translator.t('设置_基础设置_标签样式_描述')); 54 | const tagStyleDropdown = new DropdownComponent(tagStyleBar.controlEl); 55 | tagStyleDropdown.addOptions(this.TAG_STYLE); 56 | tagStyleDropdown.setValue(this.settings.TAG_STYLE); 57 | tagStyleDropdown.onChange((value) => { 58 | this.settings.TAG_STYLE = value; 59 | this.manager.saveSettings(); 60 | }); 61 | 62 | const topBar = new Setting(this.containerEl) 63 | .setName(this.manager.translator.t('设置_基础设置_界面居中_标题')) 64 | .setDesc(this.manager.translator.t('设置_基础设置_界面居中_描述')); 65 | const topToggle = new ToggleComponent(topBar.controlEl); 66 | topToggle.setValue(this.settings.CENTER); 67 | topToggle.onChange((value) => { 68 | this.settings.CENTER = value; 69 | this.manager.saveSettings(); 70 | }); 71 | 72 | const fadeOutDisabledPluginsBar = new Setting(this.containerEl) 73 | .setName(this.manager.translator.t('设置_基础设置_淡化插件_标题')) 74 | .setDesc(this.manager.translator.t('设置_基础设置_淡化插件_描述')); 75 | const fadeOutDisabledPluginsToggle = new ToggleComponent(fadeOutDisabledPluginsBar.controlEl); 76 | fadeOutDisabledPluginsToggle.setValue(this.settings.FADE_OUT_DISABLED_PLUGINS); 77 | fadeOutDisabledPluginsToggle.onChange((value) => { 78 | this.settings.FADE_OUT_DISABLED_PLUGINS = value; 79 | this.manager.saveSettings(); 80 | }); 81 | 82 | } 83 | } -------------------------------------------------------------------------------- /src/settings/ui/manager-tag.ts: -------------------------------------------------------------------------------- 1 | import BaseSetting from "../base-setting"; 2 | import { Notice, Setting } from "obsidian"; 3 | 4 | export default class ManagerTag extends BaseSetting { 5 | main(): void { 6 | let id = ''; 7 | let name = ''; 8 | let color = ''; 9 | new Setting(this.containerEl) 10 | .setHeading() 11 | .setName(this.manager.translator.t('通用_新增_文本')) 12 | .addColorPicker(cb => cb 13 | .setValue(color) 14 | .onChange((value) => { 15 | color = value; 16 | }) 17 | ) 18 | .addText(cb => cb 19 | .setPlaceholder('ID') 20 | .onChange((value) => { 21 | id = value; 22 | this.manager.saveSettings(); 23 | }) 24 | ) 25 | .addText(cb => cb 26 | .setPlaceholder(this.manager.translator.t('通用_名称_文本')) 27 | .onChange((value) => { 28 | name = value; 29 | }) 30 | ) 31 | .addExtraButton(cb => cb 32 | .setIcon('plus') 33 | .onClick(() => { 34 | const containsId = this.manager.settings.TAGS.some(tag => tag.id === id); 35 | if (!containsId && id !== '') { 36 | if (color === '') color = '#000000'; 37 | this.manager.settings.TAGS.push({ id, name, color }); 38 | this.manager.saveSettings(); 39 | this.settingTab.tagDisplay(); 40 | new Notice(this.manager.translator.t('设置_标签设置_通知_一')); 41 | } else { 42 | new Notice(this.manager.translator.t('设置_标签设置_通知_二')); 43 | } 44 | }) 45 | ) 46 | this.manager.settings.TAGS.forEach((tag, index) => { 47 | const item = new Setting(this.containerEl) 48 | item.setClass('manager-setting-tag__item') 49 | // item.setName(`${index + 1}. `) 50 | item.addColorPicker(cb => cb 51 | .setValue(tag.color) 52 | .onChange((value) => { 53 | tag.color = value; 54 | this.manager.saveSettings(); 55 | this.settingTab.tagDisplay(); 56 | }) 57 | ) 58 | item.addText(cb => cb 59 | .setValue(tag.name) 60 | .onChange((value) => { 61 | tag.name = value; 62 | this.manager.saveSettings(); 63 | }).inputEl.addEventListener('blur', () => { 64 | this.settingTab.tagDisplay(); 65 | }) 66 | ) 67 | item.addExtraButton(cb => cb 68 | .setIcon('trash-2') 69 | .onClick(() => { 70 | const hasTestTag = this.settings.Plugins.some(plugin => plugin.tags && plugin.tags.includes(tag.id)); 71 | if (!hasTestTag) { 72 | this.manager.settings.TAGS = this.manager.settings.TAGS.filter(t => t.id !== tag.id); 73 | this.manager.saveSettings(); 74 | this.settingTab.tagDisplay(); 75 | new Notice(this.manager.translator.t('设置_标签设置_通知_三')); 76 | } else { 77 | new Notice(this.manager.translator.t('设置_标签设置_通知_四')); 78 | } 79 | }) 80 | ) 81 | const tagEl = this.manager.createTag(tag.name, tag.color, this.settings.TAG_STYLE); 82 | item.nameEl.appendChild(tagEl); 83 | item.nameEl.appendText(` [${tag.id}]`); 84 | }); 85 | 86 | } 87 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { Notice, Platform } from 'obsidian'; 2 | import { exec } from 'child_process'; 3 | import Manager from 'main'; 4 | import { existsSync } from 'fs'; 5 | import * as path from 'path'; 6 | 7 | /** 8 | * 打开文件或文件夹的操作系统命令。 9 | * @param i18n - 国际化对象,用于显示操作结果的通知。 10 | * @param dir - 要打开的文件夹路径。 11 | * @description 根据操作系统执行相应的命令来打开文件夹。在Windows上使用'start'命令,在Mac上使用'open'命令。 12 | * 如果操作成功,显示成功通知;如果失败,显示错误通知。 13 | */ 14 | export const managerOpen = (dir: string, manager: Manager) => { 15 | if (Platform.isDesktop) { 16 | exec(`start "" "${dir}"`, (error) => { 17 | if (error) { new Notice(manager.translator.t('通用_失败_文本')); } else { new Notice(manager.translator.t('通用_成功_文本')); } 18 | }); 19 | } 20 | if (Platform.isMacOS) { 21 | exec(`open ${dir}`, (error) => { 22 | if (error) { new Notice(manager.translator.t('通用_失败_文本')); } else { new Notice(manager.translator.t('通用_成功_文本')); } 23 | }); 24 | } 25 | } 26 | 27 | 28 | 29 | export async function updatePlugin(modal: QPSModal, matchingItem: PluginInstalled, commPlugins: Record) { 30 | // 从 matchingItem 中解构出插件的 ID 和版本号 31 | const { id, version } = matchingItem; 32 | // 检查插件是否有目录信息,如果没有则显示提示信息并返回 33 | if (!matchingItem.dir) { new Notice(`Not a published plugin`, 2500); return } 34 | // 获取插件目录的完整路径 35 | const filePath = modal.app.vault.adapter.getFullPath(matchingItem.dir); 36 | // 如果无法获取完整路径则返回 37 | if (!filePath) return 38 | 39 | // 如果是桌面平台 40 | if (Platform.isDesktop) { 41 | // 构建插件开发路径下的 package.json 文件路径 42 | const isDevPath = path.join(filePath, "package.json"); 43 | // 检查该文件是否存在,如果存在则返回,不进行更新操作 44 | if (existsSync(isDevPath)) { return; } 45 | } 46 | 47 | // 异步获取插件的清单文件信息 48 | const manifest = await getManifest(modal, id); 49 | // 如果无法获取清单文件则返回 50 | if (!manifest) return 51 | // 异步检查插件是否有可用的发布版本 52 | const hasRelease = await getReleaseVersion(modal, id, manifest) 53 | // 获取清单文件中的插件版本号 54 | const lastVersion = manifest.version 55 | 56 | // 如果插件 ID 不在 commPlugins 对象中,说明不是已发布插件,显示提示信息 57 | if (!(id in commPlugins)) { new Notice(`Not a published plugin`, 2500); } 58 | // 如果没有获取到清单文件,显示提示信息 59 | else if (!manifest) { new Notice(`No manifest in ${commPlugins[id].repo}`, 3500) } 60 | // 如果插件没有可用的发布版本,显示提示信息 61 | else if (!hasRelease) { new Notice(`can't update, version ${manifest.version} in repo has not been released!`) } 62 | // 如果清单文件中的版本号小于等于当前已安装的版本号,说明已经是最新版本,显示提示信息 63 | else if (lastVersion <= version) { new Notice(`Already last version ${lastVersion}`, 2500) } 64 | // 满足更新条件,进行插件更新操作 65 | else { 66 | try { 67 | // 调用 app.plugins.installPlugin 方法安装插件的新版本 68 | await modal.app.plugins.installPlugin(commPlugins[id!].repo, lastVersion, manifest); 69 | // 显示更新成功的提示信息 70 | new Notice(`version ${version} updated to ${lastVersion}`, 2500); 71 | // 更新 matchingItem 中的插件版本号 72 | matchingItem.version = lastVersion 73 | // 调用插件的 installedUpdate 方法进行更新操作 74 | await modal.plugin.installedUpdate(); 75 | } catch { 76 | // 安装过程中出现错误,打印错误信息 77 | console.error("install failed"); 78 | } 79 | } 80 | // 将插件的 toUpdate 标志设置为 false,表示不需要更新 81 | matchingItem.toUpdate = false 82 | // 重新打开模态框 83 | await reOpenModal(modal); 84 | } -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --i18n-border-radius: 4px; 3 | --el-font-size-base: 14px; 4 | --i18n-button-font-weight: 500px; 5 | --i18n-tag-font-size: 10px; 6 | --i18n-tag-border-radius: 3px; 7 | --i18n-tag-border-radius-rounded: 9999px; 8 | } 9 | 10 | body { 11 | --item-inactive: 0.75; 12 | } 13 | 14 | body.theme-dark { 15 | --item-inactive: 0.5; 16 | } 17 | 18 | .manager-display-none { 19 | display: none; 20 | } 21 | 22 | .manager-display-block { 23 | display: block; 24 | } 25 | 26 | /* [通用] Tag (标签) 27 | ---------------------------------------------------------------- */ 28 | .manager-tag { 29 | display: inline-flex; 30 | justify-content: center; 31 | align-items: center; 32 | vertical-align: middle; 33 | height: 20px; 34 | padding: 0 6px; 35 | margin-left: 5px; 36 | font-size: var(--i18n-tag-font-size); 37 | line-height: 1; 38 | border-width: 1px; 39 | border-style: solid; 40 | border-radius: var(--i18n-tag-border-radius); 41 | box-sizing: border-box; 42 | white-space: nowrap; 43 | } 44 | 45 | .manager-tag:first-child { 46 | margin-left: 0px; 47 | } 48 | 49 | 50 | /* 插件管理器 模块 51 | ---------------------------------------------------------------- */ 52 | .manager-container { 53 | display: flex; 54 | flex-direction: column; 55 | z-index: 100; 56 | width: 800px; 57 | min-width: 550px; 58 | border-radius: var(--i18n-border-radius); 59 | background-color: var(--background-primary); 60 | } 61 | 62 | .manager-container::-webkit-scrollbar { 63 | display: none; 64 | } 65 | 66 | .manager-container__top { 67 | position: absolute; 68 | top: 100px; 69 | } 70 | 71 | .manager-container__header { 72 | margin-bottom: 0px; 73 | } 74 | 75 | .manager-bar__action { 76 | margin: 0; 77 | padding-top: 10px; 78 | padding-bottom: 10px; 79 | border: #fff solid 0px; 80 | } 81 | 82 | .manager-bar__action:first-child { 83 | padding-top: 0px; 84 | } 85 | 86 | .manager-bar__search { 87 | margin: 0; 88 | padding-top: 0px; 89 | padding-bottom: 10px; 90 | border: #fff solid 0px; 91 | } 92 | 93 | /* 目录容器 */ 94 | .manager-item-container { 95 | overflow: auto; 96 | } 97 | 98 | .manager-item-container::-webkit-scrollbar { 99 | display: none; 100 | } 101 | 102 | /* 目录项 */ 103 | .manager-item { 104 | display: flex; 105 | align-items: center; 106 | height: 100%; 107 | padding: 6px 10px; 108 | border: #fff solid 0px; 109 | border-radius: var(--i18n-border-radius); 110 | background-color: var(--background-modifier-hover); 111 | margin-bottom: 6px; 112 | } 113 | 114 | .manager-item:first-child { 115 | padding-top: 6px; 116 | } 117 | 118 | .manager-item:hover { 119 | background-color: var(--background-modifier-hover); 120 | } 121 | 122 | .manager-item.inactive { 123 | filter: brightness(var(--item-inactive)); 124 | } 125 | 126 | /* 目录项 名称行*/ 127 | .manager-item__name-container { 128 | display: flex; 129 | align-items: center; 130 | height: auto; 131 | line-height: 20px; 132 | font-weight: bold; 133 | font-size: 15px; 134 | } 135 | 136 | .manager-item__name-group { 137 | display: flex; 138 | margin-right: 5px; 139 | } 140 | 141 | .manager-item__name-title { 142 | display: flex; 143 | margin-right: 5px; 144 | } 145 | 146 | .manager-item__name-version { 147 | display: flex; 148 | font-size: 9px; 149 | margin-right: 5px; 150 | } 151 | 152 | .manager-item__name-delay { 153 | font-weight: bold; 154 | color: var(--interactive-accent); 155 | } 156 | 157 | .manager-item__name-desc { 158 | margin-bottom: 4px; 159 | } 160 | 161 | /* 目录项 描述行*/ 162 | .manager-item__description-container { 163 | margin-left: 0px; 164 | transition: opacity 3s ease-out; 165 | } 166 | 167 | 168 | 169 | .manager-editor__container { 170 | display: flex; 171 | flex-direction: column; 172 | z-index: 200; 173 | width: 350px; 174 | border-radius: var(--i18n-border-radius); 175 | background-color: var(--background-primary); 176 | } 177 | 178 | .manager-editor__container::-webkit-scrollbar { 179 | display: none; 180 | } 181 | 182 | .manager-bar__title { 183 | margin: 0; 184 | padding-top: 0px; 185 | padding-bottom: 6px; 186 | border: #fff solid 0px; 187 | } 188 | 189 | .manager-editor__item { 190 | display: flex; 191 | align-items: center; 192 | height: 100%; 193 | padding: 6px 3px; 194 | border: #fff solid 0px; 195 | border-radius: var(--i18n-border-radius); 196 | } 197 | 198 | .manager-editor__item:first-child { 199 | padding-top: 6px; 200 | } 201 | 202 | .manager-editor__item:hover { 203 | background-color: var(--i18n-background-modifier-hover); 204 | } 205 | 206 | .manager-editor__item-input { 207 | width: 100px; 208 | } 209 | 210 | /* 设置界面CSS */ 211 | .manager-setting__container { 212 | display: flex; 213 | flex-direction: column; 214 | width: 100%; 215 | height: 100%; 216 | } 217 | 218 | .manager-setting__tabs { 219 | display: flex; 220 | flex-direction: row; 221 | height: 40px; 222 | border-bottom: 1px solid var(--interactive-accent); 223 | } 224 | 225 | .manager-setting__content { 226 | display: flex; 227 | flex-direction: column; 228 | flex-grow: 1; 229 | margin-top: 25px; 230 | overflow-y: auto; 231 | overflow-x: hidden; 232 | } 233 | 234 | .manager-setting__content::-webkit-scrollbar { 235 | display: none; 236 | } 237 | 238 | .manager-setting__tabs-item { 239 | display: flex; 240 | align-items: center; 241 | justify-content: center; 242 | padding: 0 15px; 243 | height: 40px; 244 | font-weight: bold; 245 | font-size: 14px; 246 | border-top: 1px solid var(--interactive-accent); 247 | border-right: 1px solid var(--interactive-accent); 248 | } 249 | 250 | .manager-setting__tabs-item:first-child { 251 | border-top-left-radius: 4px; 252 | border-left: 1px solid var(--interactive-accent); 253 | } 254 | 255 | .manager-setting__tabs-item:last-child { 256 | border-top-right-radius: 4px; 257 | } 258 | 259 | .manager-setting__tabs-item_is-active { 260 | color: var(--interactive-accent); 261 | } 262 | 263 | /* 标签设置页面 */ 264 | .manager-setting-tag__container { 265 | display: flex; 266 | flex-direction: row; 267 | flex-wrap: wrap; 268 | margin-top: 10px; 269 | } 270 | 271 | .manager-setting-tag__item { 272 | padding: 3px 0px; 273 | border-top: 0px solid #fff; 274 | } 275 | 276 | .manager-setting-tag__item:hover { 277 | background-color: var(--background-modifier-hover); 278 | } 279 | 280 | /* 分组设置页面 */ 281 | .manager-setting-group__container { 282 | padding: 3px; 283 | border-top: 0px solid #fff; 284 | border-radius: var(--i18n-border-radius); 285 | } 286 | 287 | .manager-setting-group__item { 288 | padding: 3px 0px; 289 | border-top: 0px solid #fff; 290 | border-radius: var(--i18n-border-radius); 291 | } 292 | 293 | .manager-setting-group__item:hover { 294 | background-color: var(--background-modifier-hover); 295 | } 296 | 297 | /* 卸载 */ 298 | .manager-delete__title { 299 | margin: 0; 300 | padding-top: 0px; 301 | padding-bottom: 6px; 302 | border: #fff solid 0px; 303 | } 304 | 305 | .manager-delete__action { 306 | padding-bottom: 0px; 307 | } 308 | 309 | .manager-food { 310 | margin-top: 10px; 311 | } 312 | 313 | /* 笔记模块 CSS 314 | ---------------------------------------------------------------- */ 315 | .manager-note__container { 316 | display: flex; 317 | flex-direction: column; 318 | z-index: 200; 319 | width: 700px; 320 | height: 700px; 321 | border-radius: var(--i18n-border-radius); 322 | background-color: var(--background-primary); 323 | } 324 | 325 | .manager-note__container textarea{ 326 | width: 100%; 327 | height: 100%; 328 | resize: none; 329 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "isolatedModules": true, 13 | "strictNullChecks": true, 14 | "lib": [ 15 | "DOM", 16 | "ES5", 17 | "ES6", 18 | "ES7", 19 | "ES2021" 20 | ] 21 | }, 22 | "include": [ 23 | "**/*.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /version-bump.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "fs"; 2 | 3 | const targetVersion = process.env.npm_package_version; 4 | 5 | let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); 6 | const { minAppVersion } = manifest; 7 | manifest.version = targetVersion; 8 | writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); 9 | 10 | let versions = JSON.parse(readFileSync("versions.json", "utf8")); 11 | versions[targetVersion] = minAppVersion; 12 | writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); 13 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0.0": "0.15.0" 3 | } 4 | --------------------------------------------------------------------------------