This commit is contained in:
zero 2024-12-12 14:49:05 +08:00
commit 4b94eeaa81
32 changed files with 3845 additions and 0 deletions

10
.editorconfig Normal file
View file

@ -0,0 +1,10 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
tab_width = 4

3
.eslintignore Normal file
View file

@ -0,0 +1,3 @@
node_modules/
main.js

23
.eslintrc Normal file
View file

@ -0,0 +1,23 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}

27
.gitignore vendored Normal file
View file

@ -0,0 +1,27 @@
# vscode
.vscode
# Intellij
*.iml
.idea
# npm
node_modules
# 不要将编译后的 `main.js` 文件放入代码库中。应该将其上传到 GitHub 发布版中。
main.js
# Exclude sourcemaps
*.map
# obsidian
data.json
# 排除macOS Finder System Explorer视图状态
.DS_Store
# 无
笔记.md
repealed.ts
README.md
i18n

1
.npmrc Normal file
View file

@ -0,0 +1 @@
tag-version-prefix=""

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 zero
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

44
esbuild.config.mjs Normal file
View file

@ -0,0 +1,44 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner = ``;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
// 修改启动路径
entryPoints: ["main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

3
main.ts Normal file
View file

@ -0,0 +1,3 @@
import I18n from './src/main'
export default I18n

10
manifest.json Normal file
View file

@ -0,0 +1,10 @@
{
"id": "manager",
"name": "Manager",
"version": "0.0.1",
"minAppVersion": "1.5.8",
"description": "打造您的极致插件管理体验,让插件管理变得更加直观、高效,同时提升您的工作流程和个性化设置。",
"author": "zero",
"authorUrl": "https://github.com/0011000000110010/",
"isDesktopOnly": true
}

2089
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

24
package.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "obsidian-sample-plugin",
"version": "1.0.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
}
}

14
src/command.ts Normal file
View file

@ -0,0 +1,14 @@
import { App } from "obsidian";
import I18N from "./main";
import { ManagerModal } from "./modal/manager-modal";
import { t } from "./lang/inxdex";
const Commands = (app: App, i18n: I18N) => {
i18n.addCommand({
id: 'i18n-translate',
name: t('命令_管理面板_描述'),
callback: () => { new ManagerModal(app, i18n).open() }
});
}
export default Commands

20
src/data/data.ts Normal file
View file

@ -0,0 +1,20 @@
export const ITEM_STYLE = {
'alwaysExpand': '始终展开',
'neverExpand': '永不展开',
'hoverExpand': '悬浮展开',
'clickExpand': '单击展开'
}
export const GROUP_STYLE = {
'a': '样式一',
'b': '样式二',
'c': '样式三',
'd': '样式四'
}
export const TAG_STYLE = {
'a': '样式一',
'b': '样式二',
'c': '样式三',
'd': '样式四'
}

22
src/data/types.ts Normal file
View file

@ -0,0 +1,22 @@
export interface ManagerPlugin {
id: string;
name: string;
desc: string;
group: string;
tags: string[];
enabled: boolean;
delay: number;
}
export interface Type {
id: string;
name: string;
color: string;
}
export interface Tag {
id: string;
name: string;
color: string;
}

12
src/lang/inxdex.ts Normal file
View file

@ -0,0 +1,12 @@
import { moment } from "obsidian";
import zh_cn from './locale/zh_cn';
const localeMap: { [k: string]: Partial<typeof zh_cn> } = {
'zh-cn': zh_cn,
};
const locale = localeMap[moment.locale()];
export function t(str: keyof typeof zh_cn): string {
return (locale && locale[str]) || zh_cn[str];
}

40
src/lang/locale/zh_cn.ts Normal file
View file

@ -0,0 +1,40 @@
export default {
_管理器_文本: '插件管理器',
_成功_文本: '成功',
_失败_文本: '失败',
_新增_文本: '新增',
_操作_文本: '操作',
_搜索_文本: '搜索',
_GITHUB_描述: '访问作者的GitHub页面查看项目详情、更新日志、参与讨论和贡献代码。',
_编辑模式_描述: '启用编辑模式,允许对插件的配置进行更深入的自定义和调整。',
_重载插件_描述: '重新加载所有插件使更改立即生效而无需重启Obsidian。',
_检查更新_描述: '检查插件是否有可用的更新,保持插件最新以获得最佳体验。',
_一键禁用_描述: '一键禁用插件,帮助你快速重置插件设置或解决性能问题。',
_一键启用_描述: '一键启用插件,快速恢复到默认的插件启用状态。',
_插件设置_描述: '管理插件的设置,调整功能和行为以满足个人需求。',
_仅启用_描述: '仅显示当前已启用的插件,快速管理和调整启用状态。',
_打开设置_描述: '打开插件的设置界面,在这里你可以调整插件管理的相关选项和偏好设置。',
_还原内容_描述: '将插件展示内容还原到初始状态,撤销所有之前的更改,恢复到默认配置。',
_打开目录_描述: '打开包含所有插件文件的目录,方便你进行手动检查、编辑或备份插件文件。',
_删除插件_描述: '从你的系统中彻底删除选定的插件,包括其所有文件和设置。请谨慎操作,因为此操作不可撤销。',
_切换状态_描述: '“切换插件的启用或禁用状态。点击启用以运行插件,点击禁用以停止插件功能,而不删除插件。',
_基础设置_前缀: '基础',
_分组设置_前缀: '分组',
_标签设置_前缀: '标签',
_基础设置_样式_标题: '样式',
_基础设置_目录样式_标题: '目录样式',
_基础设置_目录样式_描述: '选择分组的样式,以提升浏览体验。',
_基础设置_分组样式_标题: '分组样式',
_基础设置_分组样式_描述: '选择分组的样式,使分组更加明显,便于识别。',
_基础设置_标签样式_标题: '标签样式',
_基础设置_标签样式_描述: '选择标签的样式,使标签更加明显,便于识别。',
_基础设置_功能_标题: '功能',
_基础设置_淡化插件_标题: '淡化插件',
_基础设置_淡化插件_描述: '为未启用的插件提供视觉淡化效果,以便清晰地区分启用和未启用的插件。',
_管理面板_描述: '开启插件管理器',
}

85
src/main.ts Normal file
View file

@ -0,0 +1,85 @@
import { Plugin, PluginManifest } from 'obsidian';
import { DEFAULT_SETTINGS, ManagerSettings } from './settings/data';
import { ManagerSettingTab } from './settings';
import { t } from './lang/inxdex';
import { ManagerModal } from './modal/manager-modal';
import Commands from './command';
import { ManagerPlugin } from './data/types';
export default class Manager extends Plugin {
settings: ManagerSettings;
managerModal: ManagerModal;
appPlugins: any;
async onload() {
// @ts-ignore
this.appPlugins = this.app.plugins;
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;`);
Commands(this.app, this);
await this.loadSettings();
this.addRibbonIcon('folder-cog', t('通用_管理器_文本'), () => {
this.managerModal = new ManagerModal(this.app, this);
this.managerModal.open();
});
this.addSettingTab(new ManagerSettingTab(this.app, this));
const plugins = Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manifest.id);
plugins.forEach((plugin: PluginManifest) => this.initPlugin(plugin));
plugins.forEach((plugin: PluginManifest) => this.startPlugin(plugin.id));
}
async onunload() {
const plugins = Object.values(this.appPlugins.manifests).filter((pm: PluginManifest) => pm.id !== this.manifest.id);
plugins.forEach(async (pm: PluginManifest) => {
const plugin = this.settings.Plugins.find(p => p.id === pm.id)
if (plugin) {
if (plugin.enabled) {
await this.appPlugins.disablePlugin(pm.id);
await this.appPlugins.enablePluginAndSave(pm.id);
}
}
});
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
private async initPlugin(plugin: PluginManifest) {
const isEnabled = this.appPlugins.enabledPlugins.has(plugin.id);
if (isEnabled) await this.appPlugins.disablePluginAndSave(plugin.id);
if (!(this.settings.Plugins.some(p => p.id === plugin.id))) {
const mp: ManagerPlugin = {
'id': plugin.id,
'name': plugin.name,
'desc': plugin.description,
'group': '',
'tags': [],
'enabled': isEnabled,
'delay': 0,
}
this.settings.Plugins.push(mp);
this.saveSettings();
}
}
private async startPlugin(id: string) {
const plugin = this.settings.Plugins.find(p => p.id === id);
if (plugin && plugin.enabled)
setTimeout(async () => {
await this.appPlugins.enablePlugin(id);
}, plugin.delay * 1000);
}
}

63
src/modal/delete-modal.ts Normal file
View file

@ -0,0 +1,63 @@
import { App, ExtraButtonComponent, Modal, Setting } from 'obsidian';
import { ManagerSettings } from '../settings/data';
export class DeleteModal extends Modal {
settings: ManagerSettings;
private deleteCallback: () => void;
constructor(app: App, deleteCallback: () => void) {
super(app);
this.deleteCallback = deleteCallback;
}
private async showHead() {
//@ts-ignore
const modalEl: HTMLElement = this.contentEl.parentElement;
modalEl.addClass('manager-editor__container');
modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]);
this.titleEl.parentElement?.addClass('manager-container__header');
this.contentEl.addClass('manager-item-container');
// [标题行]
const titleBar = new Setting(this.titleEl)
titleBar.setClass('manager-delete__title')
titleBar.setName('卸载插件');
// [标题行] 关闭按钮
const closeButton = new ExtraButtonComponent(titleBar.controlEl)
closeButton.setIcon('circle-x')
closeButton.onClick(() => this.close());
}
private async showData() {
const titleBar = new Setting(this.titleEl)
titleBar.setName('你确定要卸载此插件吗?这将删除插件的文件夹。');
const actionBar = new Setting(this.titleEl)
actionBar.setClass('manager-delete__action')
actionBar.addButton(cb => cb
.setWarning()
.setButtonText('卸载')
.onClick(() => {
this.deleteCallback();
this.close();
})
);
actionBar.addButton(cb => cb
.setButtonText('取消')
.onClick(() => {
this.close();
})
);
}
async onOpen() {
await this.showHead();
await this.showData();
}
async onClose() {
this.contentEl.empty();
}
}

75
src/modal/group-modal.ts Normal file
View file

@ -0,0 +1,75 @@
import { App, ExtraButtonComponent, Modal, Setting } from 'obsidian';
import { ManagerSettings } from '../settings/data';
import Manager from 'main';
import { ManagerModal } from './manager-modal';
import { ManagerPlugin } from 'src/data/types';
export class GroupModal extends Modal {
settings: ManagerSettings;
manager: Manager;
managerModal: ManagerModal;
managerPlugin: ManagerPlugin;
constructor(app: App, manager: Manager, managerModal: ManagerModal, managerPlugin: ManagerPlugin) {
super(app);
this.settings = manager.settings;
this.manager = manager;
this.managerModal = managerModal;
this.managerPlugin = managerPlugin;
}
private async showHead() {
//@ts-ignore
const modalEl: HTMLElement = this.contentEl.parentElement;
modalEl.addClass('manager-editor__container');
modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]);
this.titleEl.parentElement?.addClass('manager-container__header');
this.contentEl.addClass('manager-item-container');
// [标题行]
const titleBar = new Setting(this.titleEl).setClass('manager-bar__title').setName(this.managerPlugin.name);
// [标题行] 关闭按钮
const closeButton = new ExtraButtonComponent(titleBar.controlEl)
closeButton.setIcon('circle-x')
closeButton.onClick(() => this.close());
}
private async showData() {
// @ts-ignore
const allGroups: Record<string, string> = this.settings.GROUPS.reduce((acc, item) => { acc[item.id] = item.name; return acc; }, {});
for (const group in allGroups) {
new Setting(this.contentEl)
.setClass('manager-editor__item')
.setName(allGroups[group])
.addToggle(cb => cb
.setValue(group === this.managerPlugin.group)
.onChange(() => {
this.managerPlugin.group = group;
this.manager.saveSettings();
this.managerModal.reloadShowData();
this.reloadShowData();
})
);
}
}
private async reloadShowData() {
let scrollTop = 0;
const modalElement: HTMLElement = this.contentEl;
scrollTop = modalElement.scrollTop;
modalElement.empty();
await this.showData();
modalElement.scrollTo(0, scrollTop);
}
async onOpen() {
await this.showHead();
await this.showData();
}
async onClose() {
this.contentEl.empty();
}
}

513
src/modal/manager-modal.ts Normal file
View file

@ -0,0 +1,513 @@
import * as path from 'path';
import { App, ButtonComponent, DropdownComponent, ExtraButtonComponent, Modal, Notice, PluginManifest, SearchComponent, Setting, SliderComponent, ToggleComponent } from 'obsidian';
import { ManagerSettings } from '../settings/data';
import { managerOpen } from '../utils';
import { t } from '../lang/inxdex';
import Manager from 'main';
import { GroupModal } from './group-modal';
import { TagsModal } from './tags-modal';
import { DeleteModal } from './delete-modal';
// ==============================
// 侧边栏 对话框 翻译
// ==============================
export class ManagerModal extends Modal {
manager: Manager;
settings: ManagerSettings;
// this.app.plugins
appPlugins;
// this.app.settings
appSetting;
// [本地][变量] 插件路径
basePath: string;
// [本地][变量] 展示插件列表
displayPlugins: PluginManifest[] = [];
// 分组内容
group = '';
// 标签内容
tag = '';
// 搜索内容
searchText = '';
// 编辑模式
editorMode = false;
// 仅启用
onlyEnabled = false;
// 测试模式
developerMode = false;
constructor(app: App, manager: Manager) {
super(app);
// @ts-ignore
this.appSetting = this.app.setting;
// @ts-ignore
this.appPlugins = this.app.plugins;
this.manager = manager;
this.settings = manager.settings;
// @ts-ignore
this.basePath = path.normalize(this.app.vault.adapter.getBasePath());
}
public async showHead() {
//@ts-ignore
const modalEl: HTMLElement = this.contentEl.parentElement;
modalEl.addClass('manager-container');
modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]);
this.titleEl.parentElement?.addClass('manager-container__header');
this.contentEl.addClass('manager-item-container');
// [操作行]
const actionBar = new Setting(this.titleEl).setClass('manager-bar__action').setName(t('通用_操作_文本'));
// [操作行] Github
const githubButton = new ButtonComponent(actionBar.controlEl)
githubButton.setIcon('github')
githubButton.setTooltip(t('管理器_GITHUB_描述'))
githubButton.onClick(() => {
window.open(this.manager.manifest.authorUrl)
});
// [操作行] 编辑模式
const editorButton = new ButtonComponent(actionBar.controlEl)
this.editorMode ? editorButton.setIcon('pen') : editorButton.setIcon('pen-off');
if (this.editorMode) editorButton.setCta();
editorButton.setTooltip(t('管理器_编辑模式_描述'))
editorButton.onClick(() => {
this.editorMode = !this.editorMode;
this.editorMode ? editorButton.setIcon('pen') : editorButton.setIcon('pen-off');
this.reloadShowData();
});
// [操作行] 重载插件
const reloadButton = new ButtonComponent(actionBar.controlEl)
reloadButton.setIcon('refresh-ccw')
reloadButton.setTooltip(t('管理器_重载插件_描述'))
reloadButton.onClick(async () => {
new Notice('重新加载第三方插件');
await this.appPlugins.loadManifests();
this.reloadShowData();
});
// [操作行] 检查更新
const updateButton = new ButtonComponent(actionBar.controlEl)
updateButton.setIcon('rss')
updateButton.setTooltip(t('管理器_检查更新_描述'))
updateButton.onClick(() => {
console.log(this.appPlugins.checkForUpdates());
});
// [操作行] 一键禁用
const disableButton = new ButtonComponent(actionBar.controlEl)
disableButton.setIcon('square')
disableButton.setTooltip(t('管理器_一键禁用_描述'))
disableButton.onClick(async () => {
for (const plugin of this.displayPlugins) {
await this.appPlugins.disablePlugin(plugin.id);
const ManagerPlugin = this.manager.settings.Plugins.find(mp => mp.id === plugin.id);
if (ManagerPlugin) ManagerPlugin.enabled = false;
this.manager.saveSettings();
}
this.reloadShowData();
});
// [操作行] 一键启用
const enableButton = new ButtonComponent(actionBar.controlEl)
enableButton.setIcon('square-check')
enableButton.setTooltip(t('管理器_一键启用_描述'))
enableButton.onClick(async () => {
for (const plugin of this.displayPlugins) {
await this.appPlugins.enablePlugin(plugin.id);
const ManagerPlugin = this.manager.settings.Plugins.find(mp => mp.id === plugin.id);
if (ManagerPlugin) ManagerPlugin.enabled = true;
this.manager.saveSettings();
}
this.reloadShowData();
});
// [操作行] 插件设置
const settingsButton = new ButtonComponent(actionBar.controlEl)
settingsButton.setIcon('settings')
settingsButton.setTooltip(t('管理器_插件设置_描述'))
settingsButton.onClick(() => {
this.appSetting.open();
this.appSetting.openTabById(this.manager.manifest.id);
this.close();
});
// [测试行] 刷新插件
if (this.developerMode) {
const testButton = new ButtonComponent(actionBar.controlEl)
testButton.setIcon('refresh-ccw')
testButton.setTooltip('刷新插件')
testButton.onClick(async () => {
this.close();
await this.appPlugins.disablePlugin(this.manager.manifest.id);
await this.appPlugins.enablePlugin(this.manager.manifest.id);
});
}
// [搜索行]
const searchBar = new Setting(this.titleEl).setClass('manager-bar__search').setName(t('通用_搜索_文本'));
// [搜索行] 仅启用
const onlyEnabled = new ButtonComponent(searchBar.controlEl)
this.onlyEnabled ? onlyEnabled.setIcon('toggle-right') : onlyEnabled.setIcon('toggle-left');
onlyEnabled.setTooltip(t('管理器_仅启用_描述'))
onlyEnabled.onClick(() => {
this.onlyEnabled = !this.onlyEnabled;
this.onlyEnabled ? onlyEnabled.setIcon('toggle-right') : onlyEnabled.setIcon('toggle-left');
this.reloadShowData();
});
// [搜索行] 分组选择列表
// @ts-ignore
const groups = this.settings.GROUPS.reduce((acc, item) => { acc[item.id] = item.name; return acc; }, { '': '无分组' });
const groupsDropdown = new DropdownComponent(searchBar.controlEl)
groupsDropdown.addOptions(groups)
groupsDropdown.setValue(this.group !== '' ? this.group : '')
groupsDropdown.onChange((value) => {
this.group = value;
this.reloadShowData();
});
// [搜索行] 标签选择列表
// @ts-ignore
const tags = this.settings.TAGS.reduce((acc, item) => { acc[item.id] = item.name; return acc; }, { '': '无标签' });
const tagsDropdown = new DropdownComponent(searchBar.controlEl)
tagsDropdown.addOptions(tags)
tagsDropdown.setValue(this.tag)
tagsDropdown.onChange((value) => {
this.tag = value;
this.reloadShowData();
});
// [搜索行] 搜索框
const search = new SearchComponent(searchBar.controlEl)
search.onChange((value) => {
this.searchText = value;
this.reloadShowData();
})
}
public async showData() {
const plugins: PluginManifest[] = Object.values(this.appPlugins.manifests);
plugins.sort((item1, item2) => { return item1.name.localeCompare(item2.name) });
this.displayPlugins = [];
for (const plugin of plugins) {
const ManagerPlugin = this.manager.settings.Plugins.find(mp => mp.id === plugin.id);
const pluginDir = path.join(this.basePath, plugin.dir ? plugin.dir : '');
if (ManagerPlugin) {
// [搜索] 仅启用
if (this.onlyEnabled && !ManagerPlugin.enabled) continue;
// [搜索] 分组
if (this.group !== '' && ManagerPlugin.group !== this.group) continue;
// [搜索] 标签
if (this.tag !== '' && !(ManagerPlugin.tags.includes(this.tag))) continue;
// [搜索] 标题
if (this.searchText !== '' && ManagerPlugin.name.toLowerCase().indexOf(this.searchText.toLowerCase()) == -1) continue;
// [禁用] 自己
if (plugin.id === this.manager.manifest.id) continue;
const itemEl = new Setting(this.contentEl);
itemEl.setClass('manager-item');
itemEl.nameEl.addClass('manager-item__name-container');
itemEl.descEl.addClass('manager-item__description-container');
// [淡化插件]
if (this.settings.FADE_OUT_DISABLED_PLUGINS && !ManagerPlugin.enabled) itemEl.settingEl.addClass('inactive');
// [批量操作]
this.displayPlugins.push(plugin);
// [目录样式]
if (!this.editorMode) {
switch (this.settings.ITEM_STYLE) {
case 'alwaysExpand':
itemEl.descEl.style.display = 'block';
break;
case 'neverExpand':
itemEl.descEl.style.display = 'none';
break;
case 'hoverExpand':
itemEl.descEl.style.display = 'none';
itemEl.settingEl.addEventListener('mouseenter', function () {
itemEl.descEl.style.display = 'block';
});
itemEl.settingEl.addEventListener('mouseleave', function () {
itemEl.descEl.style.display = 'none';
});
break;
case 'clickExpand':
itemEl.descEl.style.display = 'none';
itemEl.settingEl.addEventListener('click', function (event) {
const excludedButtons = Array.from(itemEl.controlEl.querySelectorAll('div'));
// @ts-ignore
if (excludedButtons.includes(event.target)) {
event.stopPropagation();
return;
}
if (itemEl.descEl.style.display === 'none') {
itemEl.descEl.style.display = 'block';
} else {
itemEl.descEl.style.display = 'none';
}
});
break;
}
}
// [默认] 分组
if (ManagerPlugin.group !== '') {
const group = createSpan({
cls: 'manager-item__name-group'
})
itemEl.nameEl.appendChild(group);
const item = this.settings.GROUPS.find(t => t.id === ManagerPlugin.group);
if (item) {
const tag = this.createTag(item.name, item.color, this.settings.GROUP_STYLE);
if (this.editorMode) tag.onclick = () => { new GroupModal(this.app, this.manager, this, ManagerPlugin).open(); }
group.appendChild(tag);
}
}
// [编辑] 分组
if (ManagerPlugin.group === '' && this.editorMode) {
const group = createSpan({ cls: 'manager-item__name-group' })
if (this.editorMode) itemEl.nameEl.appendChild(group);
const tag = this.createTag('+', '', '');
if (this.editorMode) tag.onclick = () => { new GroupModal(this.app, this.manager, this, ManagerPlugin).open(); }
if (this.editorMode) group.appendChild(tag);
}
// [默认] 名称
const title = createSpan({
text: ManagerPlugin.name,
title: plugin.name,
cls: 'manager-item__name-title'
})
itemEl.nameEl.appendChild(title);
// [编辑] 名称
if (this.editorMode) {
title.setAttribute('style', 'border-width: 1px;border-style: dashed;')
title.setAttribute('contenteditable', 'true');
title.addEventListener('input', () => {
if (title.textContent) {
ManagerPlugin.name = title.textContent;
this.manager.saveSettings();
}
});
}
// [默认] 版本
const version = createSpan({
text: `[${plugin.version}]`,
cls: ['manager-item__name-version'],
title: plugin.author
})
itemEl.nameEl.appendChild(version);
// [默认] 延迟
if (!this.editorMode && ManagerPlugin.delay !== 0) {
const delay = createSpan({
text: `${ManagerPlugin.delay}s`,
cls: ['manager-item__name-delay'],
title: plugin.author
})
itemEl.nameEl.appendChild(delay);
}
// [编辑] 延迟
if (this.editorMode) {
const delay = new SliderComponent(itemEl.nameEl);
delay.setLimits(0, 50, 1)
delay.setValue(ManagerPlugin.delay)
delay.setDynamicTooltip()
delay.onChange((value) => {
ManagerPlugin.delay = value;
this.manager.saveSettings();
})
}
// [默认] 描述
const desc = createDiv({
text: ManagerPlugin.desc,
title: plugin.description,
cls: ['manager-item__name-desc']
})
itemEl.descEl.appendChild(desc);
// [编辑] 描述
if (this.editorMode) {
desc.setAttribute('style', 'border-width: 1px;border-style: dashed')
desc.setAttribute('contenteditable', 'true');
desc.addEventListener('input', () => {
if (desc.textContent) {
ManagerPlugin.desc = desc.textContent;
this.manager.saveSettings();
}
});
}
// [默认] 标签组
const tags = createDiv();
itemEl.descEl.appendChild(tags);
ManagerPlugin.tags.map((id: string) => {
const item = this.settings.TAGS.find(item => item.id === id);
if (item) {
const tag = this.createTag(item.name, item.color, this.settings.TAG_STYLE);
tags.appendChild(tag);
}
});
// [编辑] 标签组
if (this.editorMode) {
const tag = this.createTag('+', '', '')
tag.onclick = () => { new TagsModal(this.app, this.manager, this, ManagerPlugin).open(); }
tags.appendChild(tag);
}
// [按钮] 打开设置
if (ManagerPlugin.enabled) {
const openPluginSetting = new ExtraButtonComponent(itemEl.controlEl)
openPluginSetting.setIcon('settings')
openPluginSetting.setTooltip(t('管理器_打开设置_描述'))
openPluginSetting.onClick(() => {
openPluginSetting.setDisabled(true);
this.appSetting.open();
this.appSetting.openTabById(plugin.id);
openPluginSetting.setDisabled(false);
});
}
// [按钮] 还原内容
if (this.editorMode) {
const reloadButton = new ExtraButtonComponent(itemEl.controlEl)
reloadButton.setIcon('refresh-ccw')
reloadButton.setTooltip(t('管理器_还原内容_描述'))
reloadButton.onClick(() => {
ManagerPlugin.name = plugin.name;
ManagerPlugin.desc = plugin.description;
ManagerPlugin.group = '';
ManagerPlugin.tags = [];
this.manager.saveSettings();
this.reloadShowData();
});
}
// [按钮] 打开目录
const openPluginDirButton = new ExtraButtonComponent(itemEl.controlEl)
openPluginDirButton.setIcon('folder-open')
openPluginDirButton.setTooltip(t('管理器_打开目录_描述'))
openPluginDirButton.onClick(() => {
openPluginDirButton.setDisabled(true);
managerOpen(pluginDir);
openPluginDirButton.setDisabled(false);
});
// [按钮] 删除插件
const deletePluginButton = new ExtraButtonComponent(itemEl.controlEl)
deletePluginButton.setIcon('trash')
deletePluginButton.setTooltip(t('管理器_删除插件_描述'))
deletePluginButton.onClick(async () => {
new DeleteModal(this.app, async () => {
await this.appPlugins.uninstallPlugin(plugin.id);
await this.appPlugins.loadManifests();
this.reloadShowData();
new Notice('卸载成功');
}).open();
});
// [按钮] 切换状态
const toggleSwitch = new ToggleComponent(itemEl.controlEl)
toggleSwitch.setTooltip(t('管理器_切换状态_描述'))
toggleSwitch.setValue(ManagerPlugin.enabled)
toggleSwitch.onChange(async () => {
if (toggleSwitch.getValue()) {
// await this.appPlugins.enablePluginAndSave(plugin.id);
if (this.settings.FADE_OUT_DISABLED_PLUGINS) itemEl.settingEl.removeClass('inactive'); // [淡化插件]
ManagerPlugin.enabled = true;
this.manager.saveSettings();
await this.appPlugins.enablePlugin(plugin.id);
} else {
// await this.appPlugins.disablePluginAndSave(plugin.id);
if (this.settings.FADE_OUT_DISABLED_PLUGINS) itemEl.settingEl.addClass('inactive'); // [淡化插件]
ManagerPlugin.enabled = false;
this.manager.saveSettings();
await this.appPlugins.disablePlugin(plugin.id);
}
this.reloadShowData();
})
}
}
}
public async reloadShowData() {
let scrollTop = 0;
const modalElement: HTMLElement = this.contentEl;
scrollTop = modalElement.scrollTop;
modalElement.empty();
await this.showData();
modalElement.scrollTo(0, scrollTop);
}
public async onOpen() {
await this.showHead();
await this.showData();
}
public async onClose() {
this.contentEl.empty();
}
private createTag(text: string, color: string, type: string) {
const style = this.generateTagStyle(color, type);
const tag = createEl('span', {
text: text,
cls: 'manager-tag',
attr: { 'style': style }
})
return tag;
}
private generateTagStyle(color: string, type: string) {
let style;
const [r, g, b] = this.hexToRgbArray(color);
switch (type) {
case 'a':
style = `color: #fff; background-color: ${color}; border-color: ${color};`;
break;
case 'b':
style = `color: ${color}; background-color: transparent; border-color: ${color};`;
break;
case 'c':
style = `color: ${color}; background-color: rgba(${r}, ${g}, ${b}, 0.3); border-color: ${color};`;
break;
case 'd':
style = `color: ${color}; background-color: ${this.adjustColorBrightness(color, 50)}; border-color: ${this.adjustColorBrightness(color, 50)};`;
break;
default:
style = `background-color: transparent;border-style: dashed;`;
}
return style;
}
private hexToRgbArray(hex: string) {
const rgb = parseInt(hex.slice(1), 16);
const r = (rgb >> 16);
const g = ((rgb >> 8) & 0x00FF);
const b = (rgb & 0x0000FF);
return [r, g, b];
}
private adjustColorBrightness(hex: string, amount: number) {
const rgb = parseInt(hex.slice(1), 16);
const r = Math.min(255, Math.max(0, ((rgb >> 16) & 0xFF) + amount));
const g = Math.min(255, Math.max(0, ((rgb >> 8) & 0xFF) + amount));
const b = Math.min(255, Math.max(0, (rgb & 0xFF) + amount));
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
}
}

78
src/modal/tags-modal.ts Normal file
View file

@ -0,0 +1,78 @@
import { App, ExtraButtonComponent, Modal, Setting } from 'obsidian';
import { ManagerSettings } from '../settings/data';
import Manager from 'main';
import { ManagerModal } from './manager-modal';
import { ManagerPlugin } from 'src/data/types';
export class TagsModal extends Modal {
settings: ManagerSettings;
manager: Manager;
managerModal: ManagerModal;
managerPlugin: ManagerPlugin;
constructor(app: App, manager: Manager, managerModal: ManagerModal, managerPlugin: ManagerPlugin) {
super(app);
this.settings = manager.settings;
this.manager = manager;
this.managerModal = managerModal;
this.managerPlugin = managerPlugin;
}
private async showHead() {
//@ts-ignore
const modalEl: HTMLElement = this.contentEl.parentElement;
modalEl.addClass('manager-editor__container');
modalEl.removeChild(modalEl.getElementsByClassName('modal-close-button')[0]);
this.titleEl.parentElement?.addClass('manager-container__header');
this.contentEl.addClass('manager-item-container');
// [标题行]
const titleBar = new Setting(this.titleEl).setClass('manager-bar__title').setName(this.managerPlugin.name);
// [标题行] 关闭按钮
const closeButton = new ExtraButtonComponent(titleBar.controlEl)
closeButton.setIcon('circle-x')
closeButton.onClick(() => this.close());
}
private async showData() {
// @ts-ignore
const allTags: Record<string, string> = this.settings.TAGS.reduce((acc, item) => { acc[item.id] = item.name; return acc; }, {});
for (const tag in allTags) {
const item = new Setting(this.contentEl)
item.setClass('manager-editor__item')
item.setName(allTags[tag])
item.addToggle(cb => cb
.setValue(this.managerPlugin.tags.includes(tag))
.onChange((isChecked) => {
if (isChecked) {
// 添加开启的标签
if (!this.managerPlugin.tags.includes(tag)) this.managerPlugin.tags.push(tag);
} else {
// 移除关闭的标签
this.managerPlugin.tags = this.managerPlugin.tags.filter(t => t !== tag);
}
this.manager.saveSettings();
this.managerModal.reloadShowData();
})
);
}
}
private async reloadShowData() {
let scrollTop = 0;
const modalElement: HTMLElement = this.contentEl;
scrollTop = modalElement.scrollTop;
modalElement.empty();
await this.showData();
modalElement.scrollTo(0, scrollTop);
}
async onOpen() {
await this.showHead();
await this.showData();
}
async onClose() {
this.contentEl.empty();
}
}

View file

@ -0,0 +1,23 @@
import Manager from 'src/main';
import { ManagerSettingTab } from '.';
import { ManagerSettings } from './data';
import { App } from 'obsidian';
export default abstract class BaseSetting {
protected settingTab: ManagerSettingTab;
protected manager: Manager;
protected settings: ManagerSettings;
public containerEl: HTMLElement;
protected app: App;
constructor(obj: ManagerSettingTab) {
this.settingTab = obj;
this.manager = obj.i18n;
this.settings = obj.i18n.settings;
this.containerEl = obj.contentEl;
this.app = obj.app;
}
public abstract main(): void;
public display(): void { this.main() }
}

33
src/settings/data.ts Normal file
View file

@ -0,0 +1,33 @@
import { ManagerPlugin, Tag, Type } from '../data/types';
export interface ManagerSettings {
ITEM_STYLE: string;
GROUP_STYLE: string;
TAG_STYLE: string;
FADE_OUT_DISABLED_PLUGINS: boolean,
GROUPS: Type[];
TAGS: Tag[];
Plugins: ManagerPlugin[];
}
export const DEFAULT_SETTINGS: ManagerSettings = {
ITEM_STYLE: "alwaysExpand",
GROUP_STYLE: "a",
TAG_STYLE: "b",
FADE_OUT_DISABLED_PLUGINS: true,
GROUPS: [
{
"id": "default",
"name": "默认组",
"color": "#A079FF"
},
],
TAGS: [
{
"id": "default",
"name": "默认标签",
"color": "#A079FF"
},
],
Plugins: [],
}

55
src/settings/index.ts Normal file
View file

@ -0,0 +1,55 @@
import { App, PluginSettingTab } from 'obsidian';
import I18N from "../main";
import ManagerBasis from './ui/manager-basis';
import ManagerGroup from './ui/manager-group';
import ManagerTag from './ui/manager-tag';
import { t } from 'src/lang/inxdex';
class ManagerSettingTab extends PluginSettingTab {
i18n: I18N;
app: App;
contentEl: HTMLDivElement;
constructor(app: App, i18n: I18N) {
super(app, i18n);
this.i18n = i18n;
this.app = app;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.addClass('manager-setting__container');
const tabsEl = this.containerEl.createEl('div');
tabsEl.addClass('manager-setting__tabs');
this.contentEl = this.containerEl.createEl('div');
this.contentEl.addClass('manager-setting__content');
const tabItems = [
{ text: t('设置_基础设置_前缀'), content: () => this.basisDisplay() },
{ text: t('设置_分组设置_前缀'), content: () => this.groupDisplay() },
{ text: t('设置_标签设置_前缀'), content: () => this.tagDisplay() },
];
const tabItemsEls: HTMLDivElement[] = [];
tabItems.forEach((item, index) => {
const itemEl = tabsEl.createEl('div');
itemEl.addClass('manager-setting__tabs-item');
itemEl.textContent = item.text;
tabItemsEls.push(itemEl);
if (index === 0) { itemEl.addClass('manager-setting__tabs-item_is-active'); item.content(); }
itemEl.addEventListener('click', () => {
tabItemsEls.forEach(tabEl => { tabEl.removeClass('manager-setting__tabs-item_is-active') });
itemEl.addClass('manager-setting__tabs-item_is-active');
item.content();
});
});
}
basisDisplay() { this.contentEl.empty(); new ManagerBasis(this).display(); }
groupDisplay() { this.contentEl.empty(); new ManagerGroup(this).display(); }
tagDisplay() { this.contentEl.empty(); new ManagerTag(this).display(); }
}
export { ManagerSettingTab };

View file

@ -0,0 +1,47 @@
import BaseSetting from "../base-setting";
import { DropdownComponent, Setting, ToggleComponent } from "obsidian";
import { GROUP_STYLE, ITEM_STYLE, TAG_STYLE } from "src/data/data";
import { t } from "src/lang/inxdex";
export default class ManagerBasis extends BaseSetting {
main(): void {
new Setting(this.containerEl).setName(t('设置_基础设置_样式_标题')).setHeading();
const itemStyleBar = new Setting(this.containerEl).setName(t('设置_基础设置_目录样式_标题')).setDesc(t('设置_基础设置_目录样式_描述'));
const itemStyleDropdown = new DropdownComponent(itemStyleBar.controlEl);
itemStyleDropdown.addOptions(ITEM_STYLE);
itemStyleDropdown.setValue(this.settings.ITEM_STYLE);
itemStyleDropdown.onChange((value) => {
this.settings.ITEM_STYLE = value;
this.manager.saveSettings();
});
const groupStyleBar = new Setting(this.containerEl).setName(t('设置_基础设置_分组样式_标题')).setDesc(t('设置_基础设置_分组样式_描述'));
const groupStyleDropdown = new DropdownComponent(groupStyleBar.controlEl);
groupStyleDropdown.addOptions(GROUP_STYLE);
groupStyleDropdown.setValue(this.settings.GROUP_STYLE);
groupStyleDropdown.onChange((value) => {
this.settings.GROUP_STYLE = value;
this.manager.saveSettings();
});
const tagStyleBar = new Setting(this.containerEl).setName(t('设置_基础设置_标签样式_标题')).setDesc(t('设置_基础设置_标签样式_描述'));
const tagStyleDropdown = new DropdownComponent(tagStyleBar.controlEl);
tagStyleDropdown.addOptions(TAG_STYLE);
tagStyleDropdown.setValue(this.settings.TAG_STYLE);
tagStyleDropdown.onChange((value) => {
this.settings.TAG_STYLE = value;
this.manager.saveSettings();
});
new Setting(this.containerEl).setName(t('设置_基础设置_功能_标题')).setHeading();
const fadeOutDisabledPluginsBar = new Setting(this.containerEl).setName(t('设置_基础设置_淡化插件_标题')).setDesc(t('设置_基础设置_淡化插件_描述'));
const fadeOutDisabledPluginsToggle = new ToggleComponent(fadeOutDisabledPluginsBar.controlEl);
fadeOutDisabledPluginsToggle.setValue(this.settings.FADE_OUT_DISABLED_PLUGINS);
fadeOutDisabledPluginsToggle.onChange((value) => {
this.settings.FADE_OUT_DISABLED_PLUGINS = value;
this.manager.saveSettings();
});
}
}

View file

@ -0,0 +1,82 @@
import { t } from "src/lang/inxdex";
import BaseSetting from "../base-setting";
import { Notice, Setting } from "obsidian";
export default class ManagerGroup extends BaseSetting {
main(): void {
let id = '';
let name = '';
let color = '';
new Setting(this.containerEl)
.setHeading()
.setName(t('通用_新增_文本'))
.addColorPicker(cb => cb
.setValue(color)
.onChange((value) => {
color = value;
})
)
.addText(cb => cb
.setPlaceholder('ID')
.onChange((value) => {
id = value;
this.manager.saveSettings();
})
)
.addText(cb => cb
.setPlaceholder('名称')
.onChange((value) => {
name = value;
})
)
.addExtraButton(cb => cb
.setIcon('plus')
.onClick(() => {
const containsId = this.manager.settings.GROUPS.some(tag => tag.id === id);
if (!containsId && id !== '') {
if (color === '') color = '#000000';
this.manager.settings.GROUPS.push({ id, name, color });
this.manager.saveSettings();
this.settingTab.groupDisplay();
new Notice('[分组] 分组已添加');
} else {
new Notice('[分组] ID已存在或为空');
}
})
)
this.manager.settings.GROUPS.forEach((tag, index) => {
const item = new Setting(this.containerEl)
item.settingEl.addClass('manager-setting-group__item')
item.setName(`${index + 1}项: ${tag.id}`)
item.addColorPicker(cb => cb
.setValue(tag.color)
.onChange((value) => {
tag.color = value;
this.manager.saveSettings();
})
)
item.addText(cb => cb
.setValue(tag.name)
.onChange((value) => {
tag.name = value;
this.manager.saveSettings();
})
)
item.addExtraButton(cb => cb
.setIcon('trash-2')
.onClick(() => {
const hasTestGroup = this.settings.Plugins.some(plugin => plugin.group === tag.id);
if (!hasTestGroup) {
this.manager.settings.GROUPS = this.manager.settings.GROUPS.filter(t => t.id !== tag.id);
this.manager.saveSettings();
this.settingTab.groupDisplay();
new Notice('[分组] 分组删除成功');
} else {
new Notice('[分组] 无法删除此分组,此分组下存在插件');
}
})
)
});
}
}

View file

@ -0,0 +1,82 @@
import { t } from "src/lang/inxdex";
import BaseSetting from "../base-setting";
import { Notice, Setting } from "obsidian";
export default class ManagerTag extends BaseSetting {
main(): void {
let id = '';
let name = '';
let color = '';
new Setting(this.containerEl)
.setHeading()
.setName(t('通用_新增_文本'))
.addColorPicker(cb => cb
.setValue(color)
.onChange((value) => {
color = value;
})
)
.addText(cb => cb
.setPlaceholder('ID')
.onChange((value) => {
id = value;
this.manager.saveSettings();
})
)
.addText(cb => cb
.setPlaceholder('名称')
.onChange((value) => {
name = value;
})
)
.addExtraButton(cb => cb
.setIcon('plus')
.onClick(() => {
const containsId = this.manager.settings.TAGS.some(tag => tag.id === id);
if (!containsId && id !== '') {
if (color === '') color = '#000000';
this.manager.settings.TAGS.push({ id, name, color });
this.manager.saveSettings();
this.settingTab.tagDisplay();
new Notice('[标签] 标签已添加');
} else {
new Notice('[标签] ID已存在或为空');
}
})
)
this.manager.settings.TAGS.forEach((tag, index) => {
const item = new Setting(this.containerEl)
item.setClass('manager-setting-tag__item')
item.setName(`${index + 1}项: ${tag.id}`)
item.addColorPicker(cb => cb
.setValue(tag.color)
.onChange((value) => {
tag.color = value;
this.manager.saveSettings();
})
)
item.addText(cb => cb
.setValue(tag.name)
.onChange((value) => {
tag.name = value;
this.manager.saveSettings();
})
)
item.addExtraButton(cb => cb
.setIcon('trash-2')
.onClick(() => {
const hasTestTag = this.settings.Plugins.some(plugin => plugin.tags && plugin.tags.includes(tag.id));
if (!hasTestTag) {
this.manager.settings.TAGS = this.manager.settings.TAGS.filter(t => t.id !== tag.id);
this.manager.saveSettings();
this.settingTab.tagDisplay();
new Notice('[标签] 标签删除成功');
} else {
new Notice('[标签] 无法删除此标签,此标签下存在插件');
}
})
)
});
}
}

23
src/utils.ts Normal file
View file

@ -0,0 +1,23 @@
import { Notice } from 'obsidian';
import { exec } from 'child_process';
import { t } from './lang/inxdex';
/**
*
* @param i18n -
* @param dir -
* @description Windows上使用'start'Mac上使用'open'
*
*/
export const managerOpen = (dir: string) => {
if (navigator.userAgent.match(/Win/i)) {
exec(`start "" "${dir}"`, (error) => {
if (error) { new Notice(t('通用_失败_文本')); } else { new Notice(t('通用_成功_文本')); }
});
}
if (navigator.userAgent.match(/Mac/i)) {
exec(`open ${dir}`, (error) => {
if (error) { new Notice(t('通用_失败_文本')); } else { new Notice(t('通用_成功_文本')); }
});
}
}

283
styles.css Normal file
View file

@ -0,0 +1,283 @@
:root {
--i18n-border-radius: 4px;
--el-font-size-base: 14px;
--i18n-button-font-weight: 500px;
--i18n-tag-font-size: 10px;
--i18n-tag-border-radius: 3px;
--i18n-tag-border-radius-rounded: 9999px;
}
body {
--item-inactive: 0.75;
}
body.theme-dark {
--item-inactive: 0.5;
}
/* [通用] Tag (标签)
---------------------------------------------------------------- */
.manager-tag {
display: inline-flex;
justify-content: center;
align-items: center;
vertical-align: middle;
height: 20px;
padding: 0 6px;
margin-left: 5px;
font-size: var(--i18n-tag-font-size);
line-height: 1;
border-width: 1px;
border-style: solid;
border-radius: var(--i18n-tag-border-radius);
box-sizing: border-box;
white-space: nowrap;
}
.manager-tag:first-child {
margin-left: 0px;
}
/* 插件管理器 模块
---------------------------------------------------------------- */
.manager-container {
display: flex;
flex-direction: column;
z-index: 100;
width: 800px;
min-width: 550px;
border-radius: var(--i18n-border-radius);
background-color: var(--background-primary);
}
.manager-container::-webkit-scrollbar {
display: none;
}
.manager-container__header {
margin-bottom: 0px;
}
.manager-bar__action {
margin: 0;
padding-top: 10px;
padding-bottom: 10px;
border: #fff solid 0px;
}
.manager-bar__action:first-child {
padding-top: 0px;
}
.manager-bar__search {
margin: 0;
padding-top: 0px;
padding-bottom: 10px;
border: #fff solid 0px;
}
/* 目录容器 */
.manager-item-container {
overflow: auto;
}
.manager-item-container::-webkit-scrollbar {
display: none;
}
/* 目录项 */
.manager-item {
display: flex;
align-items: center;
height: 100%;
padding: 6px 10px;
border: #fff solid 0px;
border-radius: var(--i18n-border-radius);
background-color: var(--background-modifier-hover);
margin-bottom: 6px;
}
.manager-item:first-child {
padding-top: 6px;
}
.manager-item:hover {
background-color: var(--background-modifier-hover);
}
.manager-item.inactive {
filter: brightness(var(--item-inactive));
}
/* 目录项 名称行*/
.manager-item__name-container {
display: flex;
align-items: center;
height: auto;
line-height: 20px;
font-weight: bold;
font-size: 15px;
}
.manager-item__name-group {
display: flex;
margin-right: 5px;
}
.manager-item__name-title {
display: flex;
margin-right: 5px;
}
.manager-item__name-version {
display: flex;
font-size: 9px;
}
.manager-item__name-desc {
margin-bottom: 4px;
}
/* 目录项 描述行*/
.manager-item__description-container {
margin-left: 0px;
transition: opacity 3s ease-out;
}
.manager-editor__container {
display: flex;
flex-direction: column;
z-index: 200;
width: 350px;
border-radius: var(--i18n-border-radius);
background-color: var(--background-primary);
}
.manager-editor__container::-webkit-scrollbar {
display: none;
}
.manager-bar__title {
margin: 0;
padding-top: 0px;
padding-bottom: 6px;
border: #fff solid 0px;
}
.manager-editor__item {
display: flex;
align-items: center;
height: 100%;
padding: 6px 0px;
border: #fff solid 0px;
border-radius: var(--i18n-border-radius);
}
.manager-editor__item:first-child {
padding-top: 6px;
}
.manager-editor__item:hover {
background-color: var(--i18n-background-modifier-hover);
}
/* 设置界面CSS */
.manager-setting__container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.manager-setting__tabs {
display: flex;
flex-direction: row;
height: 40px;
border-bottom: 1px solid var(--interactive-accent);
}
.manager-setting__content {
display: flex;
flex-direction: column;
flex-grow: 1;
margin-top: 25px;
overflow-y: auto;
overflow-x: hidden;
}
.manager-setting__content::-webkit-scrollbar {
display: none;
}
.manager-setting__tabs-item {
display: flex;
align-items: center;
justify-content: center;
padding: 0 15px;
height: 40px;
font-weight: bold;
font-size: 14px;
border-top: 1px solid var(--interactive-accent);
border-right: 1px solid var(--interactive-accent);
}
.manager-setting__tabs-item:first-child {
border-top-left-radius: 4px;
border-left: 1px solid var(--interactive-accent);
}
.manager-setting__tabs-item:last-child {
border-top-right-radius: 4px;
}
.manager-setting__tabs-item_is-active {
color: var(--interactive-accent);
}
/* 标签设置页面 */
.manager-setting-tag__container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-top: 10px;
}
.manager-setting-tag__item {
padding: 3px 0px;
border-top: 0px solid #fff;
}
.manager-setting-tag__item:hover {
background-color: var(--background-modifier-hover);
}
/* 分组设置页面 */
.manager-setting-group__container {
padding: 3px;
border-top: 0px solid #fff;
border-radius: var(--i18n-border-radius);
}
.manager-setting-group__item {
padding: 3px 0px;
border-top: 0px solid #fff;
border-radius: var(--i18n-border-radius);
}
.manager-setting-group__item:hover {
background-color: var(--background-modifier-hover);
}
/* 卸载 */
.manager-delete__title {
margin: 0;
padding-top: 0px;
padding-bottom: 6px;
border: #fff solid 0px;
}
.manager-delete__action{
padding-bottom: 0px;
}

25
tsconfig.json Normal file
View file

@ -0,0 +1,25 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7",
"ES2021"
]
},
"include": [
"**/*.ts"
]
}

12
version-bump.mjs Normal file
View file

@ -0,0 +1,12 @@
import { readFileSync, writeFileSync } from "fs";
const targetVersion = process.env.npm_package_version;
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
versions[targetVersion] = minAppVersion;
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));

3
versions.json Normal file
View file

@ -0,0 +1,3 @@
{
"1.0.0": "0.15.0"
}