chore: 1.0.6

This commit is contained in:
eondrcode 2026-06-08 23:46:07 +08:00
parent 450bad9c65
commit e9b11020da
15 changed files with 1011 additions and 346 deletions

View file

@ -1,7 +1,7 @@
{
"id": "better-plugins-manager",
"name": "Better Plugins Manager",
"version": "1.0.5",
"version": "1.0.6",
"minAppVersion": "1.5.8",
"description": "Plugin Manager: Simplify, Enhance, Personalize | 插件管理器:简化操作、增强功能、个性化设置",
"author": "zero",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-sample-plugin",
"version": "1.0.5",
"version": "1.0.6",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
@ -21,4 +21,4 @@
"tslib": "2.4.0",
"typescript": "4.7.4"
}
}
}

View file

@ -24,6 +24,7 @@ export interface Tag {
// 已知的特殊 Tag
export const BPM_TAG_ID = "bpm-install"; // BPM 安装标识
export const BPM_IGNORE_TAG = "bpm-ignore"; // BPM 忽略标识
export const EONDR_PLUGIN_TAG_ID = "bpm-eondr-plugin"; // Eondr 自有插件标识
export interface Delay {

View file

@ -1,6 +1,7 @@
import { Notice, normalizePath, requestUrl } from "obsidian";
import type { PluginManifest, RequestUrlResponse } from "obsidian";
import Manager from "main";
import type { ManagerPlugin } from "./data/types";
import { BPM_TAG_ID } from "./repo-resolver";
/**
@ -260,10 +261,11 @@ const upsertInstalledPluginRecord = (
plugin.desc = nextDesc || plugin.desc;
plugin.enabled = true;
if (shouldHaveBpmTag && !plugin.tags.includes(BPM_TAG_ID)) plugin.tags.push(BPM_TAG_ID);
manager.applySpecialPluginTags(plugin);
return;
}
manager.settings.Plugins.push({
const record: ManagerPlugin = {
id: pluginId,
name: nextName,
desc: nextDesc,
@ -272,7 +274,9 @@ const upsertInstalledPluginRecord = (
enabled: true,
delay: "",
note: "",
});
};
manager.applySpecialPluginTags(record);
manager.settings.Plugins.push(record);
};
/**

View file

@ -216,6 +216,8 @@ export default {
_基础设置_淡化插件_描述: 'Provide a visual fade effect for disabled plugins to clearly distinguish between enabled and disabled plugins.',
_基础设置_隐藏BPM标签_标题: 'Hide Preset Tags (Install/Ignore)',
_基础设置_隐藏BPM标签_描述: 'Hides automatically added bpm-install and bpm-ignore tags from the list.',
_基础设置_边栏编排_标题: 'Enable ribbon order',
_基础设置_边栏编排_描述: 'When enabled, BPM controls Ribbon icon order and visibility. When disabled, BPM removes its overrides and uses Obsidian native order.',
_基础设置_启动检查更新_标题: 'Check updates on startup',
_基础设置_启动检查更新_描述: 'When BPM opens, automatically check for plugin updates and briefly show the count.',
_基础设置_来源启动检查更新_标题: 'Check source updates on startup',
@ -274,6 +276,7 @@ export default {
_管理面板_描述: 'Open the plugin manager',
_BPM安装_名称: 'bpm install',
_Eondr插件_名称: 'Eondr plugin',
_下载更新_描述: 'Download an update (choose version, incl. pre-release)',
_成功_提示: 'Installed/updated: {name}',
_错误_限速: 'GitHub rate-limited (403). Please set a GitHub token and retry.',

View file

@ -64,6 +64,8 @@ export default {
_基础设置_隐藏BPM标签_标题: '隐藏 BPM 预设标签(安装/忽略)',
_基础设置_隐藏BPM标签_描述: '开启后列表不显示自动添加的 BPM 安装和 BPM 忽略标签。',
_基础设置_边栏编排_标题: '启用边栏编排',
_基础设置_边栏编排_描述: '开启后 BPM 接管 Ribbon 图标的排序和显隐;关闭后移除接管,使用 Obsidian 原生边栏顺序。',
_基础设置_启动检查更新_标题: '启动时检测插件更新',
_基础设置_启动检查更新_描述: '打开 BPM 时自动检测可更新插件,并在右上角提示数量。',
_基础设置_来源启动检查更新_标题: '启动时检测来源更新',
@ -233,6 +235,7 @@ export default {
_基础设置_分组命令_标题: '分组控制插件命令',
_基础设置_分组命令_描述: '启用此选项可以一键启用或禁用指定分组中的所有插件。(重启Obsidian生效)',
_BPM安装_名称: 'BPM 安装',
_Eondr插件_名称: 'Eondr 出品',
_主页面功能_标题: '主页面功能',
_主页面功能_描述: '选择插件管理页中每个插件功能是直接展示在 item 上,还是收纳在右键菜单里。',

View file

@ -7,7 +7,7 @@ import Commands from './command';
import Agreement from 'src/agreement';
import { RepoResolver, ensureBpmTagExists, BPM_TAG_ID } from './repo-resolver';
import { Notice, Platform, requestUrl } from 'obsidian';
import { BetaSource, ManagerPlugin, BPM_IGNORE_TAG } from './data/types';
import { BetaSource, ManagerPlugin, BPM_IGNORE_TAG, EONDR_PLUGIN_TAG_ID } from './data/types';
import { runMigrations } from './migrations';
import { fetchReleaseVersions, installPluginFromGithub, installThemeFromGithub, ReleaseVersion, sanitizeRepo } from './github-install';
import { performSelfCheck } from './self-check';
@ -15,7 +15,7 @@ import { SystemRibbonManager } from './manager/system-ribbon-manager';
import { RibbonItem } from './data/types';
type UpdateSource = 'official' | 'github' | 'unknown';
interface UpdateStatus {
interface UpdateStatus {
source: UpdateSource;
localVersion?: string;
remoteVersion?: string | null;
@ -25,7 +25,12 @@ interface UpdateStatus {
checkedAt?: number;
repo?: string | null;
versions?: ReleaseVersion[];
}
}
const EONDR_PLUGIN_RULES = [
{ id: "i18n", repo: "eondrcode/obsidian-i18n" },
];
const EONDR_REPO_OWNER = "eondrcode";
export default class Manager extends Plugin {
public settings: ManagerSettings;
@ -38,7 +43,7 @@ export default class Manager extends Plugin {
public agreement: Agreement;
public repoResolver: RepoResolver;
public systemRibbonManager: SystemRibbonManager;
public systemRibbonManager?: SystemRibbonManager;
public updateStatus: Record<string, UpdateStatus> = {};
private updateProgressNotice: Notice | null = null;
@ -62,36 +67,41 @@ export default class Manager extends Plugin {
this.settings.LANGUAGE_INITIALIZED = true;
await this.saveSettings();
}
// 初始化语言系统
this.translator = new Translator(this);
let builtinTagsChanged = false;
const tagCountBeforeBpmEnsure = this.settings.TAGS.length;
ensureBpmTagExists(this);
builtinTagsChanged = this.settings.TAGS.length !== tagCountBeforeBpmEnsure;
this.ensureBpmTagAndRecords();
this.ensureSelfPluginRecord();
// 确保 BPM Ignore 标签存在
if (!this.settings.TAGS.some(t => t.id === BPM_IGNORE_TAG)) {
this.settings.TAGS.push({
// 初始化语言系统
this.translator = new Translator(this);
let builtinTagsChanged = false;
const tagCountBeforeBpmEnsure = this.settings.TAGS.length;
ensureBpmTagExists(this);
builtinTagsChanged = this.settings.TAGS.length !== tagCountBeforeBpmEnsure;
// 确保 BPM Ignore 标签存在
if (!this.settings.TAGS.some(t => t.id === BPM_IGNORE_TAG)) {
this.settings.TAGS.push({
id: BPM_IGNORE_TAG,
name: this.translator.t("标签_BPM忽略_名称") || "BPM Ignored",
color: "#6c757d" // 灰色
});
builtinTagsChanged = true;
}
if (this.normalizeBuiltinTagNames() || builtinTagsChanged) await this.saveSettings();
});
builtinTagsChanged = true;
}
if (!this.settings.TAGS.some(t => t.id === EONDR_PLUGIN_TAG_ID)) {
this.settings.TAGS.push({
id: EONDR_PLUGIN_TAG_ID,
name: this.translator.t("标签_Eondr插件_名称") || "Eondr Plugin",
color: "#B36BFF",
});
builtinTagsChanged = true;
}
this.ensureBpmTagAndRecords();
this.ensureSelfPluginRecord();
if (this.normalizeBuiltinTagNames() || builtinTagsChanged) await this.saveSettings();
this.repoResolver = new RepoResolver(this);
// 初始化 Ribbon 管理器。功能编排不写入 Obsidian workspace 配置,只用 BPM 数据驱动运行时样式。
this.systemRibbonManager = new SystemRibbonManager(this.app, this);
const savedRibbonItems = [...(this.settings.RIBBON_SETTINGS || [])]
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
const orderedRibbonIds = savedRibbonItems.map((item) => item.id);
const hiddenRibbonStatus: Record<string, boolean> = {};
savedRibbonItems.forEach((item) => hiddenRibbonStatus[item.id] = !item.visible);
await this.syncRibbonConfig(orderedRibbonIds, hiddenRibbonStatus);
if (this.isRibbonManagerEnabled()) {
await this.syncStoredRibbonConfig();
} else {
this.clearRibbonStyleOverrides();
}
// 初始化侧边栏图标
this.addRibbonIcon('folder-cog', this.translator.t('通用_管理器_文本'), () => { this.managerModal = new ManagerModal(this.app, this); this.managerModal.open(); });
@ -111,53 +121,50 @@ export default class Manager extends Plugin {
await this.agreement.parsePluginGithub(params);
});
this.app.workspace.onLayoutReady(() => {
this.updateRibbonStyles();
if (Platform.isMobile) {
this.setupMenuObserver();
} else {
// 仅桌面端启用“拖出即隐藏”功能
this.setupDragToHideObserver();
}
// 延迟启动自检,确保 Obsidian 初始化完成,避免自动接管被覆盖
setTimeout(() => {
this.cleanRibbonItems(); // 启动后清理一次
if (this.settings.DELAY) performSelfCheck(this);
}, 2000);
});
}
public async onunload() {
if (this.dragObserverCleanup) {
this.dragObserverCleanup();
this.dragObserverCleanup = null;
}
if (this.settings.DELAY) this.disableDelaysForAllPlugins();
if (this.menuObserver) {
this.menuObserver.disconnect();
}
// 临走前再清理一次
this.cleanRibbonItems();
this.systemRibbonManager?.stopWatch();
}
private setupDragToHideObserver() {
const handlePointerDown = (e: PointerEvent) => {
const target = e.target as HTMLElement;
// 检查是否是 Ribbon Icon
if (target && target.closest && target.closest('.side-dock-ribbon-action')) {
this.app.workspace.onLayoutReady(() => {
this.startRibbonRuntimeFeatures();
// 延迟启动自检,确保 Obsidian 初始化完成,避免自动接管被覆盖
setTimeout(() => {
if (this.isRibbonManagerEnabled()) this.cleanRibbonItems(); // 启动后清理一次
if (this.settings.DELAY) performSelfCheck(this);
}, 2000);
});
}
public async onunload() {
this.stopRibbonRuntimeFeatures();
if (this.settings.DELAY) this.disableDelaysForAllPlugins();
// 临走前再清理一次
if (this.isRibbonManagerEnabled()) this.cleanRibbonItems();
this.systemRibbonManager?.stopWatch();
this.clearRibbonStyleOverrides();
}
private setupDragToHideObserver() {
if (!this.isRibbonManagerEnabled() || this.dragObserverCleanup) return;
const handlePointerDown = (e: PointerEvent) => {
if (!this.isRibbonManagerEnabled()) return;
const target = e.target as HTMLElement;
// 检查是否是 Ribbon Icon
if (target && target.closest && target.closest('.side-dock-ribbon-action')) {
this.isRibbonDragging = true;
this.draggedRibbonItem = target.closest('.side-dock-ribbon-action') as HTMLElement;
}
};
const handlePointerUp = async (e: PointerEvent) => {
if (!this.isRibbonDragging || !this.draggedRibbonItem) {
this.isRibbonDragging = false;
this.draggedRibbonItem = null;
const handlePointerUp = async (e: PointerEvent) => {
if (!this.isRibbonManagerEnabled()) {
this.isRibbonDragging = false;
this.draggedRibbonItem = null;
return;
}
if (!this.isRibbonDragging || !this.draggedRibbonItem) {
this.isRibbonDragging = false;
this.draggedRibbonItem = null;
return;
}
@ -201,11 +208,13 @@ export default class Manager extends Plugin {
document.removeEventListener('pointerdown', handlePointerDown, true);
document.removeEventListener('pointerup', handlePointerUp, true);
};
}
private async hideRibbonItemByLabel(label: string) {
// 查找对应的 Item ID
const items = this.settings.RIBBON_SETTINGS;
}
private async hideRibbonItemByLabel(label: string) {
if (!this.isRibbonManagerEnabled()) return;
// 查找对应的 Item ID
const items = this.settings.RIBBON_SETTINGS;
const targetItem = items.find(i => i.name === label); // name 通常就是 label
let targetId = targetItem?.id;
@ -250,10 +259,88 @@ export default class Manager extends Plugin {
this.updateRibbonStyles();
new Notice(this.translator.t("Ribbon_已隐藏_通知", { name: label }));
}
}
public async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); }
}
}
public isRibbonManagerEnabled(): boolean {
return this.settings?.RIBBON_MANAGER_ENABLED !== false;
}
private ensureSystemRibbonManager() {
if (!this.systemRibbonManager) this.systemRibbonManager = new SystemRibbonManager(this.app, this);
}
private clearRibbonStyleOverrides() {
document.getElementById("bpm-ribbon-manager-style")?.remove();
}
private stopRibbonRuntimeFeatures() {
if (this.dragObserverCleanup) {
this.dragObserverCleanup();
this.dragObserverCleanup = null;
}
this.isRibbonDragging = false;
this.draggedRibbonItem = null;
if (this.menuObserver) {
this.menuObserver.disconnect();
this.menuObserver = null;
}
}
private async syncStoredRibbonConfig() {
if (!this.isRibbonManagerEnabled()) return;
this.ensureSystemRibbonManager();
const savedRibbonItems = [...(this.settings.RIBBON_SETTINGS || [])]
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
const orderedRibbonIds = savedRibbonItems.map((item) => item.id);
const hiddenRibbonStatus: Record<string, boolean> = {};
savedRibbonItems.forEach((item) => hiddenRibbonStatus[item.id] = !item.visible);
await this.syncRibbonConfig(orderedRibbonIds, hiddenRibbonStatus);
}
private startRibbonRuntimeFeatures() {
if (!this.isRibbonManagerEnabled()) {
this.stopRibbonRuntimeFeatures();
this.clearRibbonStyleOverrides();
return;
}
this.ensureSystemRibbonManager();
this.updateRibbonStyles();
if (Platform.isMobile) {
this.setupMenuObserver();
} else {
// 仅桌面端启用“拖出即隐藏”功能
this.setupDragToHideObserver();
}
}
public async refreshRibbonManagerFeature() {
if (this.isRibbonManagerEnabled()) {
await this.syncStoredRibbonConfig();
this.startRibbonRuntimeFeatures();
} else {
this.stopRibbonRuntimeFeatures();
this.systemRibbonManager?.stopWatch();
this.systemRibbonManager = undefined;
this.clearRibbonStyleOverrides();
try {
this.ribbonModal?.close?.();
} catch {
// ignore
}
this.ribbonModal = null;
}
try {
await this.managerModal?.refreshRibbonFeatureAvailability?.();
} catch {
// ignore
}
}
public async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); }
public async saveSettings() { await this.saveData(this.settings); }
// 保存单个插件配置。保留方法名以兼容旧调用点。
@ -434,14 +521,32 @@ export default class Manager extends Plugin {
await this.saveSettings();
}
public ensureBpmTagAndRecords() {
ensureBpmTagExists(this);
// 确保 BPM 安装的插件拥有标签
this.settings.BPM_INSTALLED.forEach((id) => {
const mp = this.settings.Plugins.find(p => p.id === id);
if (mp && !mp.tags.includes(BPM_TAG_ID)) mp.tags.push(BPM_TAG_ID);
});
}
public ensureBpmTagAndRecords() {
ensureBpmTagExists(this);
// 确保 BPM 安装的插件拥有标签
this.settings.BPM_INSTALLED.forEach((id) => {
const mp = this.settings.Plugins.find(p => p.id === id);
if (mp && !mp.tags.includes(BPM_TAG_ID)) mp.tags.push(BPM_TAG_ID);
});
this.settings.Plugins.forEach((plugin) => this.applySpecialPluginTags(plugin));
}
private isEondrPlugin(pluginId: string): boolean {
const normalizedId = pluginId.trim().toLowerCase();
if (EONDR_PLUGIN_RULES.some((rule) => rule.id.toLowerCase() === normalizedId)) return true;
const mappedRepo = sanitizeRepo(this.settings.REPO_MAP?.[pluginId] || "").toLowerCase();
return Boolean(mappedRepo && (
mappedRepo.startsWith(`${EONDR_REPO_OWNER}/`)
|| EONDR_PLUGIN_RULES.some((rule) => rule.repo.toLowerCase() === mappedRepo)
));
}
public applySpecialPluginTags(plugin: ManagerPlugin) {
if (this.isEondrPlugin(plugin.id) && !plugin.tags.includes(EONDR_PLUGIN_TAG_ID)) {
plugin.tags.push(EONDR_PLUGIN_TAG_ID);
}
}
private normalizeBuiltinTagNames(): boolean {
let changed = false;
@ -464,12 +569,17 @@ export default class Manager extends Plugin {
"BPM Install",
"BPM Installed",
], "#409EFF");
normalize(BPM_IGNORE_TAG, this.translator.t("标签_BPM忽略_名称") || "BPM 忽略", [
"BPM Ignore",
"BPM Ignored",
], "#6c757d");
return changed;
}
normalize(BPM_IGNORE_TAG, this.translator.t("标签_BPM忽略_名称") || "BPM 忽略", [
"BPM Ignore",
"BPM Ignored",
], "#6c757d");
normalize(EONDR_PLUGIN_TAG_ID, this.translator.t("标签_Eondr插件_名称") || "Eondr 出品", [
"Eondr Plugin",
"Eondr Plugins",
"Eondr",
], "#B36BFF");
return changed;
}
// 确保 BPM 自身也存在于插件记录中(用于面板显示)
public ensureSelfPluginRecord() {
@ -632,10 +742,11 @@ export default class Manager extends Plugin {
nextPlugins.push(mp);
pluginSettingsById.set(p1Item.id, mp);
}
if (bpmInstalledIds.has(p1Item.id) && !mp.tags.includes(BPM_TAG_ID)) {
mp.tags.push(BPM_TAG_ID);
}
});
if (bpmInstalledIds.has(p1Item.id) && !mp.tags.includes(BPM_TAG_ID)) {
mp.tags.push(BPM_TAG_ID);
}
this.applySpecialPluginTags(mp);
});
this.settings.Plugins = nextPlugins;
// BPM 自身保持启用且不允许延迟
this.ensureSelfPluginRecord();
@ -980,11 +1091,15 @@ export default class Manager extends Plugin {
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
}
public updateRibbonStyles() {
if (!this.settings) return;
let styleEl = document.getElementById("bpm-ribbon-manager-style");
if (!styleEl) {
public updateRibbonStyles() {
if (!this.settings) return;
if (!this.isRibbonManagerEnabled()) {
this.clearRibbonStyleOverrides();
return;
}
let styleEl = document.getElementById("bpm-ribbon-manager-style");
if (!styleEl) {
styleEl = document.createElement("style");
styleEl.id = "bpm-ribbon-manager-style";
document.head.appendChild(styleEl);
@ -1035,12 +1150,15 @@ export default class Manager extends Plugin {
}
}
private menuObserver: MutationObserver | null = null;
setupMenuObserver() {
this.menuObserver = new MutationObserver((mutations) => {
let shouldProcess = false;
let targetNode: HTMLElement | null = null;
private menuObserver: MutationObserver | null = null;
setupMenuObserver() {
if (!this.isRibbonManagerEnabled() || this.menuObserver) return;
this.menuObserver = new MutationObserver((mutations) => {
if (!this.isRibbonManagerEnabled()) return;
let shouldProcess = false;
let targetNode: HTMLElement | null = null;
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
@ -1072,11 +1190,13 @@ export default class Manager extends Plugin {
childList: true,
subtree: true,
});
}
processMenuItems(menuScrollElement: HTMLElement) {
// [修复] 移动端 Obsidian 菜单项包裹在 .menu-group 中
// 必须在 .menu-group 内排序,否则会破坏样式布局
}
processMenuItems(menuScrollElement: HTMLElement) {
if (!this.isRibbonManagerEnabled()) return;
// [修复] 移动端 Obsidian 菜单项包裹在 .menu-group 中
// 必须在 .menu-group 内排序,否则会破坏样式布局
let containerElement: HTMLElement = menuScrollElement;
const menuGroup = menuScrollElement.querySelector(".menu-group");
if (menuGroup) {
@ -1157,14 +1277,23 @@ export default class Manager extends Plugin {
// 功能编排只应用运行时样式,不写入 Obsidian workspace 配置或 Ribbon 内存状态。
applyRibbonConfigToMemory(orderedIds: string[], hiddenStatus: Record<string, boolean>) {
this.updateRibbonStyles();
}
public async syncRibbonConfig(orderedIds: string[], hiddenStatus: Record<string, boolean>) {
// 更新本地设置以匹配原生配置
const currentItems = this.settings.RIBBON_SETTINGS || [];
// 功能编排只应用运行时样式,不写入 Obsidian workspace 配置或 Ribbon 内存状态。
applyRibbonConfigToMemory(orderedIds: string[], hiddenStatus: Record<string, boolean>) {
if (!this.isRibbonManagerEnabled()) {
this.clearRibbonStyleOverrides();
return;
}
this.updateRibbonStyles();
}
public async syncRibbonConfig(orderedIds: string[], hiddenStatus: Record<string, boolean>) {
if (!this.isRibbonManagerEnabled()) {
this.clearRibbonStyleOverrides();
return;
}
// 更新本地设置以匹配原生配置
const currentItems = this.settings.RIBBON_SETTINGS || [];
const itemMap = new Map(currentItems.map(i => [i.id, i]));
const newItems: RibbonItem[] = [];

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,11 @@ export class RibbonModal extends Modal {
}
async onOpen() {
if (!this.manager.isRibbonManagerEnabled()) {
this.close();
return;
}
this.manager.ribbonModal = this;
this.modalEl.addClass("ribbon-manager-modal");
this.titleEl.setText(this.manager.translator.t("Ribbon_标题"));
@ -33,6 +38,8 @@ export class RibbonModal extends Modal {
// 同步 Ribbon 项:读取当前工作区的 Ribbon合并到设置中
async syncRibbonItems() {
if (!this.manager.isRibbonManagerEnabled()) return;
// 以 BPM 自己的 data.json 为源头,只从运行时内存补齐新出现的 Ribbon 项。
const savedItems = [...(this.manager.settings.RIBBON_SETTINGS || [])]
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
@ -76,6 +83,7 @@ export class RibbonModal extends Modal {
this.renderRootEl = contentEl;
this.renderToolbarInRoot = showToolbar;
contentEl.empty();
if (!this.manager.isRibbonManagerEnabled()) return;
if (showToolbar) this.renderToolbar(contentEl);
this.renderDraggableList(contentEl);
@ -283,6 +291,8 @@ export class RibbonModal extends Modal {
}
async moveItem(oldIndex: number, newIndex: number) {
if (!this.manager.isRibbonManagerEnabled()) return;
const items = this.manager.settings.RIBBON_SETTINGS;
if (oldIndex < 0 || oldIndex >= items.length || newIndex < 0 || newIndex > items.length) {
this.display();
@ -297,6 +307,8 @@ export class RibbonModal extends Modal {
}
private async persistRibbonConfig() {
if (!this.manager.isRibbonManagerEnabled()) return;
const items = this.manager.settings.RIBBON_SETTINGS;
items.forEach((item, idx) => item.order = idx);
await this.manager.saveSettings();
@ -311,6 +323,8 @@ export class RibbonModal extends Modal {
}
async resetRibbonLayout() {
if (!this.manager.isRibbonManagerEnabled()) return;
const items = this.manager.settings.RIBBON_SETTINGS;
items.sort((a, b) => (a.name || a.id).localeCompare(b.name || b.id));
items.forEach((item, idx) => {

View file

@ -58,6 +58,8 @@ export interface ManagerSettings {
DELAY: boolean;
/** 是否在插件卡片中隐藏 BPM 内置标签,例如 BPM 管理标识和 BPM 忽略。 */
HIDE_BPM_TAG: boolean;
/** 是否启用边栏编排接管;关闭时 BPM 不再排序或隐藏 Ribbon 图标。 */
RIBBON_MANAGER_ENABLED: boolean;
/** 管理页插件功能显示位置item 表示直接展示在插件卡片上menu 表示收纳到右键菜单。 */
MAIN_PAGE_ACTION_PLACEMENT: MainPageActionPlacementSettings;
/** 自检发现非 BPM 管理插件时,是否自动接管 community-plugins.json。 */
@ -81,15 +83,19 @@ export interface ManagerSettings {
/** 持久化的搜索关键词,仅在 PERSISTENCE 开启时作为管理页默认搜索值。 */
FILTER_SEARCH: string;
FILTER_STATUS: string;
FILTER_STATUS_VALUES?: string[];
FILTER_STATUS_OPERATOR: FilterOperator;
/** 持久化的分组筛选 id仅在 PERSISTENCE 开启时生效。 */
FILTER_GROUP: string;
FILTER_GROUP_VALUES?: string[];
FILTER_GROUP_OPERATOR: FilterOperator;
/** 持久化的标签筛选 id仅在 PERSISTENCE 开启时生效。 */
FILTER_TAG: string;
FILTER_TAG_VALUES?: string[];
FILTER_TAG_OPERATOR: FilterOperator;
/** 持久化的延迟配置筛选 id仅在 PERSISTENCE 与 DELAY 开启时生效。 */
FILTER_DELAY: string;
FILTER_DELAY_VALUES?: string[];
FILTER_DELAY_OPERATOR: FilterOperator;
// 样式设置页
@ -161,6 +167,7 @@ export const DEFAULT_SETTINGS: ManagerSettings = {
CENTER: false,
DELAY: false,
HIDE_BPM_TAG: false,
RIBBON_MANAGER_ENABLED: false,
MAIN_PAGE_ACTION_PLACEMENT: { ...DEFAULT_MAIN_PAGE_ACTION_PLACEMENT },
AUTO_TAKEOVER: false,
SELF_CHECK_IGNORED: false,
@ -174,12 +181,16 @@ export const DEFAULT_SETTINGS: ManagerSettings = {
// 管理页筛选状态
FILTER_SEARCH: "",
FILTER_STATUS: "all",
FILTER_STATUS_VALUES: [],
FILTER_STATUS_OPERATOR: "contains",
FILTER_GROUP: "",
FILTER_GROUP_VALUES: [],
FILTER_GROUP_OPERATOR: "contains",
FILTER_TAG: "",
FILTER_TAG_VALUES: [],
FILTER_TAG_OPERATOR: "contains",
FILTER_DELAY: "",
FILTER_DELAY_VALUES: [],
FILTER_DELAY_OPERATOR: "contains",
// 样式设置页

View file

@ -35,7 +35,14 @@ export default class ManagerBasis extends BaseSetting {
const persistenceToggle = new ToggleComponent(persistenceBar.controlEl);
persistenceToggle.setValue(this.settings.PERSISTENCE);
persistenceToggle.onChange((value) => {
const managerModal = this.manager.managerModal;
if (value) {
managerModal?.persistCurrentFilters();
}
this.settings.PERSISTENCE = value;
if (!value && managerModal) {
managerModal.usePersistedFiltersAsSessionFilters();
}
this.manager.saveSettings();
});
@ -111,6 +118,17 @@ export default class ManagerBasis extends BaseSetting {
this.manager.managerModal?.reloadShowData();
});
const ribbonManagerBar = new Setting(this.containerEl)
.setName(this.manager.translator.t('设置_基础设置_边栏编排_标题'))
.setDesc(this.manager.translator.t('设置_基础设置_边栏编排_描述'));
const ribbonManagerToggle = new ToggleComponent(ribbonManagerBar.controlEl);
ribbonManagerToggle.setValue(this.settings.RIBBON_MANAGER_ENABLED !== false);
ribbonManagerToggle.onChange(async (value) => {
this.settings.RIBBON_MANAGER_ENABLED = value;
await this.manager.saveSettings();
await this.manager.refreshRibbonManagerFeature();
});
heading('设置_基础设置_分组_命令');
const CommandItemBar = new Setting(this.containerEl)

View file

@ -1,7 +1,7 @@
import BaseSetting from "../base-setting";
import { Notice, setIcon, Setting } from "obsidian";
import { BPM_TAG_ID } from "src/repo-resolver";
import { BPM_IGNORE_TAG } from "src/data/types";
import { BPM_IGNORE_TAG, EONDR_PLUGIN_TAG_ID } from "src/data/types";
import Commands from "src/command";
export default class ManagerTag extends BaseSetting {
@ -10,7 +10,7 @@ export default class ManagerTag extends BaseSetting {
}
private isPresetTag(tagId: string): boolean {
return tagId === BPM_TAG_ID || tagId === BPM_IGNORE_TAG;
return tagId === BPM_TAG_ID || tagId === BPM_IGNORE_TAG || tagId === EONDR_PLUGIN_TAG_ID;
}
main(): void {
@ -77,7 +77,7 @@ export default class ManagerTag extends BaseSetting {
const nextId = id.trim();
const nextName = name.trim() || nextId;
const containsId = this.manager.settings.TAGS.some(tag => tag.id === nextId);
if (!containsId && nextId !== '' && nextId !== BPM_TAG_ID && nextId !== BPM_IGNORE_TAG) {
if (!containsId && nextId !== '' && !this.isPresetTag(nextId)) {
if (color === '') color = this.manager.generateAutoColor(this.manager.settings.TAGS.map(t => t.color));
this.manager.settings.TAGS.push({ id: nextId, name: nextName, color });
this.manager.saveSettings();

View file

@ -110,7 +110,7 @@ export class TroubleshootPanel {
startBtn.setButtonText(this.t("排查_开始_按钮"));
startBtn.setCta();
startBtn.setDisabled(enabledPlugins.length === 0);
startBtn.onClick(async () => {
startBtn.onClick(async () => {
await this.startTroubleshoot();
});

View file

@ -1894,15 +1894,16 @@ body.theme-dark {
}
/* 筛选面板展开动画 */
.manager-container--mobile .bpm-mobile-header__filters {
transition: max-height 0.25s ease, opacity 0.2s ease, padding 0.25s ease, margin 0.25s ease;
overflow: hidden;
max-height: 500px;
opacity: 1;
}
.manager-container--mobile .bpm-mobile-header__filters.is-collapsed {
max-height: 0 !important;
.manager-container--mobile .bpm-mobile-header__filters {
transition: max-height 0.25s ease, opacity 0.2s ease, padding 0.25s ease, margin 0.25s ease;
overflow: visible;
max-height: 500px;
opacity: 1;
}
.manager-container--mobile .bpm-mobile-header__filters.is-collapsed {
overflow: hidden;
max-height: 0 !important;
opacity: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
@ -2602,17 +2603,25 @@ body.theme-dark {
}
.manager-filter-field {
display: flex;
align-items: center;
flex: 1 1 0;
min-width: 0;
display: flex;
align-items: center;
flex: 1 1 0;
min-width: 0;
max-width: 100%;
height: 34px;
border: 1px solid var(--bpm-border);
border-radius: var(--bpm-radius-sm);
background: var(--bpm-surface);
overflow: hidden;
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
.manager-filter-field:has(.manager-multiselect-filter) {
overflow: visible;
}
.manager-filter-field:has(.manager-multiselect-filter.is-open) {
z-index: 30;
}
.manager-filter-field--select {
@ -2623,6 +2632,7 @@ body.theme-dark {
.manager-filter-field--compound {
flex: 1 1 220px;
max-width: none;
overflow: visible;
}
.manager-filter-field--search {
@ -2692,6 +2702,11 @@ body.theme-dark {
width: auto;
}
.manager-filter-select-group .manager-multiselect-filter {
flex: 1 1 auto;
min-width: 0;
}
.manager-filter-select-group .manager-filter-operator {
font-size: 12px;
color: var(--text-muted);
@ -2738,20 +2753,183 @@ body.theme-dark {
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
.manager-container .manager-bar__search .dropdown select:focus,
.manager-container .manager-bar__search .search-input-container input:focus,
.manager-container .bpm-mobile-header__filters .dropdown select:focus,
.manager-container .bpm-mobile-header__search .search-input-container input:focus {
border-color: var(--interactive-accent);
box-shadow: none;
}
.manager-container--mobile .bpm-mobile-header__filters .dropdown select {
border: 1px solid var(--bpm-border);
border-radius: var(--bpm-radius-sm);
background: var(--bpm-surface);
}
.manager-container .manager-bar__search .dropdown select:focus,
.manager-container .manager-bar__search .search-input-container input:focus,
.manager-container .bpm-mobile-header__filters .dropdown select:focus,
.manager-container .bpm-mobile-header__search .search-input-container input:focus {
border-color: var(--interactive-accent);
box-shadow: none;
}
.manager-multiselect-filter {
position: relative;
width: 100%;
min-width: 0;
height: 100%;
}
.manager-multiselect-filter__trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
width: 100%;
min-width: 0;
min-height: 34px;
height: 100%;
padding: 0 8px;
border: 0;
border-radius: 0;
background: transparent;
color: var(--text-normal);
font-size: 13px;
text-align: left;
cursor: pointer;
}
.manager-multiselect-filter__trigger:hover,
.manager-multiselect-filter__trigger:focus-visible {
background: var(--background-modifier-hover);
}
.manager-multiselect-filter__summary {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.manager-multiselect-filter__count {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 9px;
background: color-mix(in srgb, var(--interactive-accent) 16%, transparent);
color: var(--text-accent);
font-size: 11px;
font-weight: 700;
line-height: 1;
}
.manager-multiselect-filter__count.is-empty {
display: none;
}
.manager-multiselect-filter__chevron {
display: inline-flex;
flex: 0 0 auto;
width: 14px;
height: 14px;
color: var(--text-muted);
transition: transform 0.15s ease;
}
.manager-multiselect-filter__chevron svg {
width: 14px;
height: 14px;
}
.manager-multiselect-filter.is-open .manager-multiselect-filter__chevron {
transform: rotate(180deg);
}
.manager-multiselect-filter__menu {
position: absolute;
z-index: 1000;
top: calc(100% + 4px);
left: 0;
right: 0;
display: none;
max-height: min(280px, 48vh);
overflow-y: auto;
padding: 4px;
border: 1px solid var(--bpm-border);
border-radius: var(--bpm-radius-sm);
background: var(--background-primary);
box-shadow: var(--shadow-s);
}
.manager-multiselect-filter.is-open .manager-multiselect-filter__menu {
display: flex;
flex-direction: column;
}
.manager-multiselect-filter__option {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-height: 32px;
padding: 6px 8px;
border: 0;
border-radius: var(--bpm-radius-xs);
background: transparent;
color: var(--text-normal);
text-align: left;
cursor: pointer;
}
.manager-multiselect-filter__option:hover,
.manager-multiselect-filter__option:focus-visible {
background: var(--background-modifier-hover);
}
.manager-multiselect-filter__option.is-selected {
background: color-mix(in srgb, var(--interactive-accent) 12%, transparent);
color: var(--text-accent);
}
.manager-multiselect-filter__check {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 16px;
height: 16px;
}
.manager-multiselect-filter__check svg {
width: 16px;
height: 16px;
}
.manager-multiselect-filter__option-label {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.manager-container--mobile .bpm-mobile-header__filters .dropdown select {
border: 1px solid var(--bpm-border);
border-radius: var(--bpm-radius-sm);
background: var(--bpm-surface);
}
.manager-container--mobile .bpm-mobile-header__filters .manager-multiselect-filter {
flex: 1 1 auto;
max-width: 220px;
height: 34px;
}
.manager-container--mobile .bpm-mobile-header__filters .manager-multiselect-filter__trigger {
border: 1px solid var(--bpm-border);
border-radius: var(--bpm-radius-sm);
background: var(--bpm-surface);
}
.manager-container--mobile .bpm-mobile-header__filters .manager-multiselect-filter__menu {
right: 0;
left: auto;
width: min(260px, 70vw);
}
.manager-container--mobile .bpm-mobile-header__filters .manager-filter-operator {
color: var(--text-muted);
}

View file

@ -4,5 +4,6 @@
"1.0.2": "1.5.8",
"1.0.3": "1.5.8",
"1.0.4": "1.5.8",
"1.0.5": "1.5.8"
"1.0.5": "1.5.8",
"1.0.6": "1.5.8"
}