Files
obsidian-ribbon-clasp/main.js
T

100 lines
2.9 KiB
JavaScript

const { Plugin } = require('obsidian');
// a bunch of ribbon icons can attach within milliseconds of each other on
// startup, so debounce instead of re-checking the order on every single one
function debounce(fn, wait) {
let timer = null;
return () => {
clearTimeout(timer);
timer = setTimeout(fn, wait);
};
}
module.exports = class RibbonOrderLock extends Plugin {
async onload() {
this.settings = Object.assign({ order: [] }, await this.loadData());
this.debouncedEnforce = debounce(() => this.enforceOrder(), 200);
this.app.workspace.onLayoutReady(() => {
this.patchRibbon();
this.enforceOrder();
this.observeRibbon();
});
}
observeRibbon() {
const ribbon = this.app.workspace.leftRibbon;
const containerEl = ribbon.ribbonItemsEl;
this.observer = new MutationObserver(() => this.debouncedEnforce());
this.observer.observe(containerEl, { childList: true });
}
// hook the ribbon's own onChange so drags and hide/show toggles update
// our saved order automatically, same as Obsidian's native reorder does
patchRibbon() {
const ribbon = this.app.workspace.leftRibbon;
const original = ribbon.onChange.bind(ribbon);
this.originalOnChange = original;
ribbon.onChange = (save) => {
original(save);
if (save) {
this.recordOrder(ribbon);
}
};
}
enforceOrder() {
const ribbon = this.app.workspace.leftRibbon;
const order = this.settings.order;
if (order.length === 0) {
this.recordOrder(ribbon);
return;
}
const sorted = [...ribbon.items].sort((a, b) => {
const ia = order.indexOf(a.id);
const ib = order.indexOf(b.id);
if (ia === -1 && ib === -1) return 0;
if (ia === -1) return 1;
if (ib === -1) return -1;
return ia - ib;
});
const changed = sorted.some((item, i) => ribbon.items[i] !== item);
if (changed) {
ribbon.items.length = 0;
ribbon.items.push(...sorted);
ribbon.onChange(true);
}
this.mergeNewItems(ribbon);
}
recordOrder(ribbon) {
const ids = ribbon.items.map((item) => item.id);
if (this.sameOrder(ids, this.settings.order)) return;
this.settings.order = ids;
this.saveData(this.settings);
}
// only append icons we haven't seen before instead of overwriting the
// whole order -- enforceOrder() runs during boot too, before every
// plugin has attached its icon yet, and a full overwrite there was
// wiping out the remembered position for anything still loading
mergeNewItems(ribbon) {
const known = new Set(this.settings.order);
const newIds = ribbon.items.map((item) => item.id).filter((id) => !known.has(id));
if (newIds.length === 0) return;
this.settings.order = [...this.settings.order, ...newIds];
this.saveData(this.settings);
}
sameOrder(a, b) {
return a.length === b.length && a.every((id, i) => id === b[i]);
}
};