Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57fccedcdd | ||
|
|
5932bbfa9d | ||
|
|
3ac82877f7 | ||
|
|
d3fb5b4383 | ||
|
|
92c7a27dc2 | ||
|
|
0983970abf | ||
|
|
14cfe46c26 | ||
|
|
697966deff | ||
|
|
4de8def0bb | ||
|
|
5f564b5f18 | ||
|
|
c3c1a98574 | ||
|
|
ef35f26937 | ||
|
|
eb3e462603 | ||
|
|
3190d176f2 |
@@ -0,0 +1,12 @@
|
|||||||
|
## Releasing new releases
|
||||||
|
|
||||||
|
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
|
||||||
|
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
|
||||||
|
- Create new GitHub release using your new version number as the "Tag version".
|
||||||
|
Use the exact version number, don't include a prefix `v`.
|
||||||
|
See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
|
||||||
|
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments.
|
||||||
|
Note: The `manifest.json` file must be in two places, first the root path of your repository and also in the release.
|
||||||
|
- Publish the release.
|
||||||
|
|
||||||
|
From <https://github.com/obsidianmd/obsidian-sample-plugin/blob/master/README.md#releasing-new-releases>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
syntax-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: node --check main.js
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
REPO_NAME: ${{ github.event.repository.name}}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Create zip archive
|
||||||
|
run: |
|
||||||
|
zip -j "${REPO_NAME}.zip" main.js manifest.json
|
||||||
|
- name: Upload release assets
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: | #shell
|
||||||
|
gh release upload "${{ github.event.release.tag_name }}" \
|
||||||
|
main.js manifest.json "${REPO_NAME}.zip"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Ribbon Order Lock
|
# Obsidian Ribbon Clasp
|
||||||
|
|
||||||
Obsidian plugin that stops the left ribbon icons from reshuffling themselves.
|
An [Obsidian][^1] plugin that stops the left ribbon icons from reshuffling themselves.
|
||||||
|
|
||||||
The plugin should work completely transparently, but it relies on undocumented
|
The plugin should work completely transparently, but it relies on undocumented
|
||||||
internals within Obsidian's `workspace.leftRibbon` object, so some future app
|
internals within Obsidian's `workspace.leftRibbon` object, so some future app
|
||||||
@@ -8,9 +8,11 @@ update could break it. Please open an issue if the ribbon ever stops behaving.
|
|||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
**BRAT**: `bdeshi/obsidian-ribbon-order-lock`
|
**BRAT**: `bdeshi/obsidian-ribbon-clasp`
|
||||||
|
|
||||||
**Manual**: extract the latest release archive inside
|
**Manual**: extract the latest release archive inside
|
||||||
`.obsidian/plugins/ribbon-order-lock/`, and enable it.
|
`.obsidian/plugins/ribbon-clasp/`, and enable it.
|
||||||
|
|
||||||
If you're using lazy-plugins, set this plugin's startup type to "Instant".
|
If you're using lazy-plugins, set this plugin's startup type to "Instant".
|
||||||
|
|
||||||
|
[^1]: https://obsidian.md/
|
||||||
|
|||||||
@@ -1,36 +1,26 @@
|
|||||||
const { Plugin } = require('obsidian');
|
const { Plugin, debounce } = require('obsidian');
|
||||||
|
|
||||||
// a bunch of ribbon icons can attach within milliseconds of each other on
|
module.exports = class RibbonClasp extends Plugin {
|
||||||
// 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() {
|
async onload() {
|
||||||
this.settings = Object.assign({ order: [] }, await this.loadData());
|
this.settings = Object.assign({ order: [] }, await this.loadData());
|
||||||
this.enforcing = false;
|
this.enforcing = false;
|
||||||
this.debouncedEnforce = debounce(() => this.enforceOrder(), 200);
|
this.alive = true;
|
||||||
|
this.register(() => { this.alive = false; });
|
||||||
|
// icons can attach within milliseconds of each other on startup
|
||||||
|
// (resetTimer, or it fires 200ms after the first one instead of the last)
|
||||||
|
this.debouncedEnforce = debounce(() => this.enforceOrder(), 200, true);
|
||||||
|
// disconnecting the observer doesn't stop an already-scheduled call
|
||||||
|
this.register(() => this.debouncedEnforce.cancel());
|
||||||
|
|
||||||
this.app.workspace.onLayoutReady(() => {
|
this.app.workspace.onLayoutReady(() => {
|
||||||
|
// not tied to our lifecycle; still fires if we were disabled during boot
|
||||||
|
if (!this.alive) return;
|
||||||
this.patchRibbon();
|
this.patchRibbon();
|
||||||
this.enforceOrder();
|
this.enforceOrder();
|
||||||
this.observeRibbon();
|
this.observeRibbon();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
|
||||||
this.observer?.disconnect();
|
|
||||||
const ribbon = this.getRibbon();
|
|
||||||
if (ribbon && this.originalOnChange) {
|
|
||||||
ribbon.onChange = this.originalOnChange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getRibbon() {
|
getRibbon() {
|
||||||
return this.app.workspace.leftRibbon ?? null;
|
return this.app.workspace.leftRibbon ?? null;
|
||||||
}
|
}
|
||||||
@@ -45,8 +35,9 @@ module.exports = class RibbonOrderLock extends Plugin {
|
|||||||
const containerEl = this.getRibbonContainerEl(ribbon);
|
const containerEl = this.getRibbonContainerEl(ribbon);
|
||||||
if (!containerEl) return;
|
if (!containerEl) return;
|
||||||
|
|
||||||
this.observer = new MutationObserver(() => this.debouncedEnforce());
|
const observer = new MutationObserver(() => this.debouncedEnforce());
|
||||||
this.observer.observe(containerEl, { childList: true });
|
observer.observe(containerEl, { childList: true });
|
||||||
|
this.register(() => observer.disconnect());
|
||||||
}
|
}
|
||||||
|
|
||||||
// hook the ribbon's own onChange so drags and hide/show toggles update
|
// hook the ribbon's own onChange so drags and hide/show toggles update
|
||||||
@@ -58,12 +49,18 @@ module.exports = class RibbonOrderLock extends Plugin {
|
|||||||
const original = ribbon.onChange.bind(ribbon);
|
const original = ribbon.onChange.bind(ribbon);
|
||||||
this.originalOnChange = original;
|
this.originalOnChange = original;
|
||||||
|
|
||||||
ribbon.onChange = (save) => {
|
const wrapper = (save) => {
|
||||||
original(save);
|
original(save);
|
||||||
if (save && !this.enforcing) {
|
if (save && !this.enforcing && this.alive) {
|
||||||
this.recordOrder(ribbon);
|
this.recordOrder(ribbon);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ribbon.onChange = wrapper;
|
||||||
|
|
||||||
|
this.register(() => {
|
||||||
|
// don't restore over someone else's patch, just go inert instead
|
||||||
|
if (ribbon.onChange === wrapper) ribbon.onChange = original;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceOrder() {
|
enforceOrder() {
|
||||||
@@ -76,13 +73,12 @@ module.exports = class RibbonOrderLock extends Plugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// anything we haven't seen before sorts to the bottom, in the order it came in
|
||||||
|
const rank = new Map(order.map((id, i) => [id, i]));
|
||||||
const sorted = [...ribbon.items].sort((a, b) => {
|
const sorted = [...ribbon.items].sort((a, b) => {
|
||||||
const ia = order.indexOf(a.id);
|
const ia = rank.get(a.id) ?? Infinity;
|
||||||
const ib = order.indexOf(b.id);
|
const ib = rank.get(b.id) ?? Infinity;
|
||||||
if (ia === -1 && ib === -1) return 0;
|
return ia === ib ? 0 : ia - ib;
|
||||||
if (ia === -1) return 1;
|
|
||||||
if (ib === -1) return -1;
|
|
||||||
return ia - ib;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const changed = sorted.some((item, i) => ribbon.items[i] !== item);
|
const changed = sorted.some((item, i) => ribbon.items[i] !== item);
|
||||||
@@ -100,13 +96,25 @@ module.exports = class RibbonOrderLock extends Plugin {
|
|||||||
this.mergeNewItems(ribbon);
|
this.mergeNewItems(ribbon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a drag saves, and the dom change it makes wakes the observer up, which can
|
||||||
|
// save again before the first one has finished writing. queue them instead.
|
||||||
|
save() {
|
||||||
|
this.writing = Promise.resolve(this.writing)
|
||||||
|
.catch(() => {})
|
||||||
|
.then(() => this.saveData(this.settings));
|
||||||
|
return this.writing;
|
||||||
|
}
|
||||||
|
|
||||||
recordOrder(ribbon) {
|
recordOrder(ribbon) {
|
||||||
const ids = ribbon.items.map((item) => item.id);
|
const ids = ribbon.items.map((item) => item.id);
|
||||||
if (this.sameOrder(ids, this.settings.order)) return;
|
if (this.sameOrder(ids, this.settings.order)) return;
|
||||||
this.settings.order = ids;
|
this.settings.order = ids;
|
||||||
this.saveData(this.settings);
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ids of plugins that are gone stay in the list on purpose -- nothing prunes
|
||||||
|
// them, so reinstalling one puts its icon back where it used to be
|
||||||
|
//
|
||||||
// only append icons we haven't seen before instead of overwriting the
|
// only append icons we haven't seen before instead of overwriting the
|
||||||
// whole order -- enforceOrder() runs during boot too, before every
|
// whole order -- enforceOrder() runs during boot too, before every
|
||||||
// plugin has attached its icon yet, and a full overwrite there was
|
// plugin has attached its icon yet, and a full overwrite there was
|
||||||
@@ -116,7 +124,7 @@ module.exports = class RibbonOrderLock extends Plugin {
|
|||||||
const newIds = ribbon.items.map((item) => item.id).filter((id) => !known.has(id));
|
const newIds = ribbon.items.map((item) => item.id).filter((id) => !known.has(id));
|
||||||
if (newIds.length === 0) return;
|
if (newIds.length === 0) return;
|
||||||
this.settings.order = [...this.settings.order, ...newIds];
|
this.settings.order = [...this.settings.order, ...newIds];
|
||||||
this.saveData(this.settings);
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
sameOrder(a, b) {
|
sameOrder(a, b) {
|
||||||
|
|||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"id": "ribbon-order-lock",
|
"id": "ribbon-clasp",
|
||||||
"name": "Ribbon Order Lock",
|
"name": "Ribbon Clasp",
|
||||||
"version": "0.9.0",
|
"version": "1.1.0",
|
||||||
"minAppVersion": "1.1.0",
|
"minAppVersion": "1.1.0",
|
||||||
"description": "Keeps left-ribbon icon order stable across restarts",
|
"description": "Stops the left ribbon icons from reshuffling themselves.",
|
||||||
"author": "bdeshi",
|
"author": "bdeshi",
|
||||||
"authorUrl": "https://github.com/bdeshi",
|
"authorUrl": "https://github.com/bdeshi",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"1.0.0": "1.1.0",
|
||||||
|
"1.1.0": "1.1.0"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user