┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/.obsidian
diff options
context:
space:
mode:
Diffstat (limited to '.obsidian')
-rw-r--r--.obsidian/plugins/abacus-sorter/main.js334
-rw-r--r--.obsidian/plugins/abacus-sorter/manifest.json2
-rw-r--r--.obsidian/plugins/abacus-sorter/styles.css34
-rw-r--r--.obsidian/plugins/table-trimmer/main.js98
4 files changed, 65 insertions, 403 deletions
diff --git a/.obsidian/plugins/abacus-sorter/main.js b/.obsidian/plugins/abacus-sorter/main.js
index 53bdb19..f49d93c 100644
--- a/.obsidian/plugins/abacus-sorter/main.js
+++ b/.obsidian/plugins/abacus-sorter/main.js
@@ -1,15 +1,12 @@
-/* Abacus Custom Sorter. Config-driven custom sorting for Obsidian's File Explorer. Manual ordering is stored internally (no bookmarks dependency). */
+/* Abacus Sorter. Config-driven custom sorting for Obsidian's File Explorer. Manual ordering is stored internally (no bookmarks dependency). */
var obsidian = require('obsidian');
-// Default Settings
-
const DEFAULT_SETTINGS = {
suspended: false,
sortSpecFile: '',
statusBarEnabled: true,
notificationsEnabled: true,
- contextMenuEnabled: true,
delayForInitialApplication: 0,
manualOrder: {} // { folderPath: [itemName, itemName, ...] }
};
@@ -69,15 +66,12 @@ function parseSortSpec(text) {
const line = rawLine.trim();
if (!line || line.startsWith('//') || line.startsWith('#')) continue;
- // Folder target
if (line.startsWith('target-folder:')) {
finishFolder();
currentFolder = line.slice('target-folder:'.length).trim();
- if (currentFolder === '/') currentFolder = '/';
continue;
}
- // Wildcard folder target
if (line.startsWith('target-folder-wildcard:')) {
finishFolder();
currentFolder = line.slice('target-folder-wildcard:'.length).trim();
@@ -157,7 +151,7 @@ function getItemName(item) {
function itemMatchesGroup(item, group, app) {
const f = getItemFile(item);
- const name = f ? (f.name || '') : '';
+ const name = getItemName(item);
const basename = f ? (f.basename || name.replace(/\.[^.]+$/, '')) : '';
switch (group.matchType) {
@@ -212,9 +206,7 @@ function makeComparator(order, app, manualOrderList) {
case 'natural':
return (a, b) => {
- const an = getItemName(a);
- const bn = getItemName(b);
- const cmp = naturalCompare(an, bn);
+ const cmp = naturalCompare(getItemName(a), getItemName(b));
return order.reverse ? -cmp : cmp;
};
@@ -266,10 +258,8 @@ function makeComparator(order, app, manualOrderList) {
case 'manual':
return (a, b) => {
if (!manualOrderList) return 0;
- const an = getItemName(a);
- const bn = getItemName(b);
- const ai = manualOrderList.indexOf(an);
- const bi = manualOrderList.indexOf(bn);
+ const ai = manualOrderList.indexOf(getItemName(a));
+ const bi = manualOrderList.indexOf(getItemName(b));
if (ai === -1 && bi === -1) return 0;
if (ai === -1) return 1;
if (bi === -1) return -1;
@@ -283,7 +273,6 @@ function makeComparator(order, app, manualOrderList) {
function sortItems(items, spec, app, manualOrderList) {
if (!spec || !spec.groups || spec.groups.length === 0) {
- // No groups, just apply default order if present
if (spec && spec.defaultOrder) {
const cmp = makeComparator(spec.defaultOrder, app, manualOrderList);
if (cmp) items.sort(cmp);
@@ -316,7 +305,6 @@ function sortItems(items, spec, app, manualOrderList) {
}
if (defaultCmp) unmatched.sort(defaultCmp);
- // Reassemble
const result = [];
for (const bucket of buckets) result.push(...bucket);
result.push(...unmatched);
@@ -332,8 +320,7 @@ function applyManualOrder(items, manualOrderList) {
const unordered = [];
for (const item of items) {
- const name = getItemName(item);
- const idx = manualOrderList.indexOf(name);
+ const idx = manualOrderList.indexOf(getItemName(item));
if (idx !== -1) {
ordered[idx] = item;
} else {
@@ -347,14 +334,9 @@ function applyManualOrder(items, manualOrderList) {
// File Explorer Patcher
-function getFileExplorerLeaf(app) {
- const leaves = app.workspace.getLeavesOfType('file-explorer');
- return leaves.length > 0 ? leaves[0] : null;
-}
-
function getFileExplorer(app) {
- const leaf = getFileExplorerLeaf(app);
- return leaf ? leaf.view : null;
+ const leaves = app.workspace.getLeavesOfType('file-explorer');
+ return leaves.length > 0 ? leaves[0].view : null;
}
/**
@@ -370,7 +352,6 @@ function monkeyPatch(obj, methodName, wrapper) {
return wrapper.call(this, original.bind(this), ...args);
};
- // Return uninstaller
return function uninstall() {
if (hasOwn) {
obj[methodName] = original;
@@ -387,40 +368,29 @@ function patchFileExplorer(plugin) {
return false;
}
- plugin.fileExplorer = explorer;
-
- // Verify the view has getSortedFolderItems and requestSort
if (typeof explorer.getSortedFolderItems !== 'function') {
console.warn('Abacus: getSortedFolderItems not found on file explorer view');
return false;
}
- if (typeof explorer.requestSort !== 'function') {
- console.warn('Abacus: requestSort not found on file explorer view');
- }
if (plugin.unpatchFn) return true; // already patched
- // Determine where to patch: prefer prototype, fall back to instance
+ // Prefer patching the prototype, fall back to the instance
const proto = explorer.constructor.prototype;
const target = (typeof proto.getSortedFolderItems === 'function') ? proto : explorer;
- console.log('Abacus: Patching getSortedFolderItems on', target === proto ? 'prototype' : 'instance');
-
- // Patch getSortedFolderItems - this is called by Obsidian to get the ordered
- // list of children (FileExplorerItem[]) for each folder in the file explorer
+ // getSortedFolderItems is called by Obsidian to get the ordered list of
+ // children (FileExplorerItem[]) for each folder in the file explorer
plugin.unpatchFn = monkeyPatch(
target,
'getSortedFolderItems',
function(originalFn, folder) {
- // Always call original first to get Obsidian's default sorted items
const defaultResult = originalFn(folder);
if (plugin.settings.suspended) return defaultResult;
-
- const folderPath = folder ? folder.path : '';
if (!defaultResult || defaultResult.length === 0) return defaultResult;
- // Check for sort spec
+ const folderPath = folder ? folder.path : '';
const spec = plugin.getSpecForFolder(folderPath);
const manualOrderList = plugin.settings.manualOrder[folderPath] || null;
@@ -434,7 +404,6 @@ function patchFileExplorer(plugin) {
}
);
- console.log('Abacus: File explorer patched successfully');
return true;
}
@@ -457,19 +426,7 @@ class AbacusSettingTab extends obsidian.PluginSettingTab {
const { containerEl } = this;
containerEl.empty();
- containerEl.createEl('h2', { text: 'Abacus Custom Sorter' });
-
- new obsidian.Setting(containerEl)
- .setName('Suspend sorting')
- .setDesc('Temporarily disable custom sorting and revert to Obsidian defaults.')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.suspended)
- .onChange(async (value) => {
- this.plugin.settings.suspended = value;
- await this.plugin.saveSettings();
- this.plugin.refreshExplorer();
- this.plugin.updateStatusBar();
- }));
+ containerEl.createEl('h2', { text: 'Abacus' });
new obsidian.Setting(containerEl)
.setName('Sort specification file')
@@ -506,16 +463,6 @@ class AbacusSettingTab extends obsidian.PluginSettingTab {
}));
new obsidian.Setting(containerEl)
- .setName('Context menu')
- .setDesc('Show sorting options in file explorer context menu.')
- .addToggle(toggle => toggle
- .setValue(this.plugin.settings.contextMenuEnabled)
- .onChange(async (value) => {
- this.plugin.settings.contextMenuEnabled = value;
- await this.plugin.saveSettings();
- }));
-
- new obsidian.Setting(containerEl)
.setName('Initial delay (ms)')
.setDesc('Delay before applying sort on startup. Increase if sorting fails to apply.')
.addText(text => text
@@ -550,7 +497,7 @@ class AbacusSettingTab extends obsidian.PluginSettingTab {
delete this.plugin.settings.manualOrder[folder];
await this.plugin.saveSettings();
this.plugin.refreshExplorer();
- this.display(); // refresh settings view
+ this.display();
}));
}
}
@@ -587,14 +534,8 @@ class ManualOrderModal extends obsidian.Modal {
// If there's an existing manual order, apply it
const existing = plugin.settings.manualOrder[folderPath];
if (existing && existing.length > 0) {
- const ordered = [];
- const rest = [];
- for (const name of existing) {
- if (this.items.includes(name)) ordered.push(name);
- }
- for (const name of this.items) {
- if (!ordered.includes(name)) rest.push(name);
- }
+ const ordered = existing.filter(name => this.items.includes(name));
+ const rest = this.items.filter(name => !ordered.includes(name));
this.orderList = ordered.concat(rest);
}
}
@@ -624,9 +565,7 @@ class ManualOrderModal extends obsidian.Modal {
this.plugin.settings.manualOrder[this.folderPath] = [...this.orderList];
await this.plugin.saveSettings();
this.plugin.refreshExplorer();
- if (this.plugin.settings.notificationsEnabled) {
- new obsidian.Notice(`Abacus: Manual order saved for ${this.folderPath || '/'}`);
- }
+ this.plugin.notify(`Abacus: Manual order saved for ${this.folderPath || '/'}`);
this.close();
});
@@ -635,9 +574,7 @@ class ManualOrderModal extends obsidian.Modal {
delete this.plugin.settings.manualOrder[this.folderPath];
await this.plugin.saveSettings();
this.plugin.refreshExplorer();
- if (this.plugin.settings.notificationsEnabled) {
- new obsidian.Notice(`Abacus: Manual order cleared for ${this.folderPath || '/'}`);
- }
+ this.plugin.notify(`Abacus: Manual order cleared for ${this.folderPath || '/'}`);
this.close();
});
@@ -652,28 +589,21 @@ class ManualOrderModal extends obsidian.Modal {
row.setAttribute('draggable', 'true');
row.dataset.index = String(i);
- // Drag handle
- const handle = row.createEl('span', { cls: 'abacus-drag-handle', text: '⠿' });
-
- // Index number
+ row.createEl('span', { cls: 'abacus-drag-handle', text: '⠿' });
row.createEl('span', { cls: 'abacus-order-index', text: String(i + 1) });
-
- // Item name
row.createEl('span', { cls: 'abacus-order-name', text: this.orderList[i] });
- // Up/down buttons
const btnContainer = row.createEl('span', { cls: 'abacus-order-buttons' });
if (i > 0) {
- const upBtn = btnContainer.createEl('button', { cls: 'abacus-order-btn', text: '\u25B2' });
+ const upBtn = btnContainer.createEl('button', { cls: 'abacus-order-btn', text: '▲' });
upBtn.addEventListener('click', () => this.moveItem(i, i - 1));
}
if (i < this.orderList.length - 1) {
- const downBtn = btnContainer.createEl('button', { cls: 'abacus-order-btn', text: '\u25BC' });
+ const downBtn = btnContainer.createEl('button', { cls: 'abacus-order-btn', text: '▼' });
downBtn.addEventListener('click', () => this.moveItem(i, i + 1));
}
- // Drag events
row.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', String(i));
row.addClass('abacus-dragging');
@@ -710,74 +640,6 @@ class ManualOrderModal extends obsidian.Modal {
}
}
-// Quick Sort Modal (apply a preset sort to a folder)
-
-class QuickSortModal extends obsidian.Modal {
- constructor(app, plugin, folderPath, items) {
- super(app);
- this.plugin = plugin;
- this.folderPath = folderPath;
- this.items = items;
- }
-
- onOpen() {
- const { contentEl } = this;
- contentEl.empty();
- contentEl.addClass('abacus-quick-sort-modal');
-
- contentEl.createEl('h2', { text: 'Quick Sort' });
- contentEl.createEl('p', {
- text: `Apply a sort order to: ${this.folderPath || '/ (vault root)'}`,
- cls: 'abacus-modal-subtitle'
- });
-
- const options = [
- ['A \u2192 Z (natural)', 'natural-a-z'],
- ['Z \u2192 A (natural)', 'natural-z-a'],
- ['A \u2192 Z (strict)', 'a-z'],
- ['Z \u2192 A (strict)', 'z-a'],
- ['Newest modified first', 'modified-new'],
- ['Oldest modified first', 'modified-old'],
- ['Newest created first', 'created-new'],
- ['Oldest created first', 'created-old'],
- ['Folders first', 'folders-first'],
- ['Files first', 'files-first'],
- ];
-
- const list = contentEl.createEl('div', { cls: 'abacus-quick-sort-list' });
-
- for (const [label, orderKey] of options) {
- const btn = list.createEl('button', { text: label, cls: 'abacus-quick-sort-option' });
- btn.addEventListener('click', async () => {
- const order = SORT_ORDERS[orderKey];
- if (order) {
- const cmp = makeComparator(order, this.app, null);
- if (cmp) {
- const sorted = [...this.items].sort((a, b) => {
- return cmp(a, b);
- });
- const names = sorted.map(i => getItemName(i));
- this.plugin.settings.manualOrder[this.folderPath] = names;
- await this.plugin.saveSettings();
- this.plugin.refreshExplorer();
- if (this.plugin.settings.notificationsEnabled) {
- new obsidian.Notice(`Abacus: Sorted ${this.folderPath || '/'} by ${label}`);
- }
- }
- }
- this.close();
- });
- }
-
- const cancelBtn = contentEl.createEl('button', { text: 'Cancel', cls: 'abacus-quick-sort-cancel' });
- cancelBtn.addEventListener('click', () => this.close());
- }
-
- onClose() {
- this.contentEl.empty();
- }
-}
-
// Main Plugin Class
class AbacusSorterPlugin extends obsidian.Plugin {
@@ -785,80 +647,26 @@ class AbacusSorterPlugin extends obsidian.Plugin {
await this.loadSettings();
this.sortSpecs = null;
- this.originalSort = null;
- this.fileExplorer = null;
- // Add settings tab
this.addSettingTab(new AbacusSettingTab(this.app, this));
- // Ribbon icon
- this.ribbonIconEl = this.addRibbonIcon('arrow-up-down', 'Abacus Custom Sorter', () => {
- this.settings.suspended = !this.settings.suspended;
- this.saveSettings();
- this.refreshExplorer();
- this.updateStatusBar();
- this.updateRibbonIcon();
- if (this.settings.notificationsEnabled) {
- new obsidian.Notice(`Abacus: Sorting ${this.settings.suspended ? 'suspended' : 'active'}`);
- }
- });
+ this.ribbonIconEl = this.addRibbonIcon('arrow-up-down', 'Abacus', () => this.toggleSuspended());
this.updateRibbonIcon();
- // Status bar
this.statusBarEl = this.addStatusBarItem();
this.updateStatusBar();
- // Context menu
this.registerEvent(
this.app.workspace.on('file-menu', (menu, file, source) => {
- if (!this.settings.contextMenuEnabled) return;
if (source !== 'file-explorer-context-menu') return;
const folderPath = file.children !== undefined ? file.path : (file.parent ? file.parent.path : '');
menu.addSeparator();
-
menu.addItem((item) => {
item.setTitle('Abacus: Set manual order...')
.setIcon('arrow-up-down')
- .onClick(() => {
- this.openManualOrderModal(folderPath);
- });
- });
-
- menu.addItem((item) => {
- item.setTitle('Abacus: Quick sort...')
- .setIcon('sort-asc')
- .onClick(() => {
- this.openQuickSortModal(folderPath);
- });
- });
-
- if (this.settings.manualOrder[folderPath]) {
- menu.addItem((item) => {
- item.setTitle('Abacus: Clear manual order')
- .setIcon('x')
- .onClick(async () => {
- delete this.settings.manualOrder[folderPath];
- await this.saveSettings();
- this.refreshExplorer();
- if (this.settings.notificationsEnabled) {
- new obsidian.Notice(`Abacus: Manual order cleared for ${folderPath || '/'}`);
- }
- });
- });
- }
-
- menu.addItem((item) => {
- item.setTitle(this.settings.suspended ? 'Abacus: Resume sorting' : 'Abacus: Suspend sorting')
- .setIcon(this.settings.suspended ? 'play' : 'pause')
- .onClick(async () => {
- this.settings.suspended = !this.settings.suspended;
- await this.saveSettings();
- this.refreshExplorer();
- this.updateStatusBar();
- this.updateRibbonIcon();
- });
+ .onClick(() => this.openManualOrderModal(folderPath));
});
})
);
@@ -878,20 +686,10 @@ class AbacusSorterPlugin extends obsidian.Plugin {
this.registerEvent(this.app.vault.on('create', () => this.debouncedRefresh()));
this.registerEvent(this.app.vault.on('delete', () => this.debouncedRefresh()));
- // Commands
this.addCommand({
id: 'toggle-sorting',
name: 'Toggle custom sorting',
- callback: () => {
- this.settings.suspended = !this.settings.suspended;
- this.saveSettings();
- this.refreshExplorer();
- this.updateStatusBar();
- this.updateRibbonIcon();
- if (this.settings.notificationsEnabled) {
- new obsidian.Notice(`Abacus: Sorting ${this.settings.suspended ? 'suspended' : 'active'}`);
- }
- }
+ callback: () => this.toggleSuspended()
});
this.addCommand({
@@ -905,18 +703,14 @@ class AbacusSorterPlugin extends obsidian.Plugin {
});
}
- async onunload() {
+ onunload() {
unpatchFileExplorer(this);
// Trigger a re-sort so Obsidian reverts to its default order
- const explorer = getFileExplorer(this.app);
- if (explorer && typeof explorer.requestSort === 'function') {
- explorer.requestSort();
- }
+ this.refreshExplorer();
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
- // Ensure manualOrder exists
if (!this.settings.manualOrder) this.settings.manualOrder = {};
}
@@ -924,26 +718,33 @@ class AbacusSorterPlugin extends obsidian.Plugin {
await this.saveData(this.settings);
}
+ notify(message) {
+ if (this.settings.notificationsEnabled) new obsidian.Notice(message);
+ }
+
+ toggleSuspended() {
+ this.settings.suspended = !this.settings.suspended;
+ this.saveSettings();
+ this.refreshExplorer();
+ this.updateStatusBar();
+ this.updateRibbonIcon();
+ this.notify(`Abacus: Sorting ${this.settings.suspended ? 'suspended' : 'active'}`);
+ }
+
async initializeSorting() {
await this.loadSortSpec();
- const patched = patchFileExplorer(this);
- if (patched) {
+ if (patchFileExplorer(this)) {
this.refreshExplorer();
- if (this.settings.notificationsEnabled) {
- new obsidian.Notice('Abacus Custom Sorter: Active');
- }
- } else {
- // Retry once after a short delay
- setTimeout(() => {
- const retry = patchFileExplorer(this);
- if (retry) {
- this.refreshExplorer();
- if (this.settings.notificationsEnabled) {
- new obsidian.Notice('Abacus Custom Sorter: Active');
- }
- }
- }, 1000);
+ this.notify('Abacus: Active');
+ return;
}
+ // Retry once after a short delay
+ setTimeout(() => {
+ if (patchFileExplorer(this)) {
+ this.refreshExplorer();
+ this.notify('Abacus: Active');
+ }
+ }, 1000);
}
async loadSortSpec() {
@@ -965,17 +766,15 @@ class AbacusSorterPlugin extends obsidian.Plugin {
getSpecForFolder(folderPath) {
if (!this.sortSpecs) return null;
- // Exact match
if (this.sortSpecs[folderPath]) return this.sortSpecs[folderPath];
- // Check wildcards: try parent paths with /*
+ // Wildcards: try parent paths with /*
const parts = folderPath.split('/');
for (let i = parts.length - 1; i >= 0; i--) {
const partial = parts.slice(0, i).join('/') + '/*';
if (this.sortSpecs[partial]) return this.sortSpecs[partial];
}
- // Global wildcard
if (this.sortSpecs['/*']) return this.sortSpecs['/*'];
if (this.sortSpecs['*']) return this.sortSpecs['*'];
@@ -983,17 +782,14 @@ class AbacusSorterPlugin extends obsidian.Plugin {
}
openManualOrderModal(folderPath) {
- // Get the TFolder and its children directly from the vault
const folder = this.app.vault.getAbstractFileByPath(folderPath);
if (!folder || !folder.children) {
new obsidian.Notice('Abacus: Folder not found');
return;
}
- // Build item wrappers that match what getSortedFolderItems returns
- // Each needs a .file property pointing to the TAbstractFile
+ // Wrap children the way getSortedFolderItems returns them: { file }
const items = folder.children.map(child => ({ file: child }));
-
if (items.length === 0) {
new obsidian.Notice('Abacus: No items found in folder');
return;
@@ -1005,23 +801,6 @@ class AbacusSorterPlugin extends obsidian.Plugin {
new ManualOrderModal(this.app, this, folderPath, items).open();
}
- openQuickSortModal(folderPath) {
- const folder = this.app.vault.getAbstractFileByPath(folderPath);
- if (!folder || !folder.children) {
- new obsidian.Notice('Abacus: Folder not found');
- return;
- }
-
- const items = folder.children.map(child => ({ file: child }));
-
- if (items.length === 0) {
- new obsidian.Notice('Abacus: No items found in folder');
- return;
- }
-
- new QuickSortModal(this.app, this, folderPath, items).open();
- }
-
refreshExplorer() {
// requestSort() tells the file explorer to re-call getSortedFolderItems
// for all visible folders, which triggers our patched version
@@ -1034,9 +813,7 @@ class AbacusSorterPlugin extends obsidian.Plugin {
_refreshTimeout = null;
debouncedRefresh() {
if (this._refreshTimeout) clearTimeout(this._refreshTimeout);
- this._refreshTimeout = setTimeout(() => {
- this.refreshExplorer();
- }, 300);
+ this._refreshTimeout = setTimeout(() => this.refreshExplorer(), 300);
}
updateStatusBar() {
@@ -1047,8 +824,7 @@ class AbacusSorterPlugin extends obsidian.Plugin {
}
const manualCount = Object.keys(this.settings.manualOrder).length;
const specCount = this.sortSpecs ? Object.keys(this.sortSpecs).length : 0;
- const state = this.settings.suspended ? 'OFF' : 'ON';
- let text = `Abacus: ${state}`;
+ let text = `Abacus: ${this.settings.suspended ? 'OFF' : 'ON'}`;
if (!this.settings.suspended && (manualCount > 0 || specCount > 0)) {
const parts = [];
if (specCount > 0) parts.push(`${specCount} spec`);
@@ -1062,10 +838,10 @@ class AbacusSorterPlugin extends obsidian.Plugin {
if (!this.ribbonIconEl) return;
if (this.settings.suspended) {
this.ribbonIconEl.addClass('abacus-ribbon-suspended');
- this.ribbonIconEl.setAttribute('aria-label', 'Abacus Custom Sorter (suspended)');
+ this.ribbonIconEl.setAttribute('aria-label', 'Abacus (suspended)');
} else {
this.ribbonIconEl.removeClass('abacus-ribbon-suspended');
- this.ribbonIconEl.setAttribute('aria-label', 'Abacus Custom Sorter (active)');
+ this.ribbonIconEl.setAttribute('aria-label', 'Abacus (active)');
}
}
}
diff --git a/.obsidian/plugins/abacus-sorter/manifest.json b/.obsidian/plugins/abacus-sorter/manifest.json
index 1d35c98..c49ccd1 100644
--- a/.obsidian/plugins/abacus-sorter/manifest.json
+++ b/.obsidian/plugins/abacus-sorter/manifest.json
@@ -1,6 +1,6 @@
{
"id": "abacus-sorter",
- "name": "Abacus Custom Sorter",
+ "name": "Abacus Sorter",
"version": "1.0.0",
"minAppVersion": "1.7.2",
"description": "Config-driven custom sorting of files and folders in File Explorer, with manual drag-style reordering and sort specification support.",
diff --git a/.obsidian/plugins/abacus-sorter/styles.css b/.obsidian/plugins/abacus-sorter/styles.css
index 599055a..44437ce 100644
--- a/.obsidian/plugins/abacus-sorter/styles.css
+++ b/.obsidian/plugins/abacus-sorter/styles.css
@@ -1,4 +1,4 @@
-/* Abacus Custom Sorter */
+/* Abacus */
/* Ribbon icon when suspended */
.abacus-ribbon-suspended {
@@ -105,35 +105,3 @@
gap: 8px;
justify-content: flex-end;
}
-
-/* Quick Sort Modal */
-.abacus-quick-sort-modal {
- max-width: 350px;
-}
-
-.abacus-quick-sort-list {
- display: flex;
- flex-direction: column;
- gap: 4px;
- margin-bottom: 12px;
-}
-
-.abacus-quick-sort-option {
- text-align: left;
- padding: 8px 12px;
- border: 1px solid var(--background-modifier-border);
- border-radius: 6px;
- background: var(--background-secondary);
- cursor: pointer;
- transition: background-color 0.1s;
-}
-
-.abacus-quick-sort-option:hover {
- background-color: var(--interactive-accent);
- color: var(--text-on-accent);
-}
-
-.abacus-quick-sort-cancel {
- margin-top: 4px;
- align-self: flex-end;
-}
diff --git a/.obsidian/plugins/table-trimmer/main.js b/.obsidian/plugins/table-trimmer/main.js
index f162293..760b820 100644
--- a/.obsidian/plugins/table-trimmer/main.js
+++ b/.obsidian/plugins/table-trimmer/main.js
@@ -2,18 +2,12 @@
/* Table Blank Line Remover. Obsidian will not normally let you delete the blank line above a table in Live Preview. This plugin takes that section of the document and surgically removes the newline that gets reintroduced. */
-const { Plugin, Notice } = require("obsidian");
-
-const VERSION = "1.4.0";
+const { Plugin } = require("obsidian");
// Guard against pathological scans through a note that is mostly blank lines.
const MAX_BLANK_SCAN = 100;
-/* ------------------------------------------------------------------ *
- * Pure markdown helpers.
- *
- * They all take a `doc`: { count: number, get(i): string }, 0-indexed.
- * ------------------------------------------------------------------ */
+// The markdown helpers below all take a `doc`: { count: number, get(i): string }, 0-indexed.
function isBlank(text) {
return text.trim() === "";
@@ -93,12 +87,9 @@ function lastNonBlankAbove(doc, i) {
return j;
}
-/* ------------------------------------------------------------------ *
- * The edit: replace the lines around the table wholesale, leaving the
- * blank line out. Scoped to the smallest region that still reads as a
- * block to Obsidian rather than as a newline being deleted.
- * ------------------------------------------------------------------ */
-
+// The edit: replace the lines around the table wholesale, leaving the blank
+// line out. Scoped to the smallest region that still reads as a block to
+// Obsidian rather than as a newline being deleted.
function blockRewrite(doc, plan) {
const n = plan.blankLine;
const t = plan.tableLine;
@@ -129,11 +120,9 @@ function blockRewrite(doc, plan) {
};
}
-/* ------------------------------------------------------------------ *
- * Edit plans: { blankLine, caret, direction, tableLine }, where `caret`
- * is already in post-deletion coordinates and `tableLine` is in
- * pre-deletion coordinates. Null means "not our business".
- * ------------------------------------------------------------------ */
+/* Edit plans: { blankLine, caret, direction, tableLine }, where `caret` is
+ * already in post-deletion coordinates and `tableLine` is in pre-deletion
+ * coordinates. Null means "not our business". */
function planBackspace(doc, cursor) {
const { line, ch } = cursor;
@@ -202,22 +191,10 @@ function docFromEditor(editor) {
return { count: editor.lineCount(), get: (i) => editor.getLine(i) };
}
-/* ------------------------------------------------------------------ *
- * Plugin
- * ------------------------------------------------------------------ */
-
class TableBlankLineRemover extends Plugin {
onload() {
- console.log(`[TBLR] Table Blank Line Remover v${VERSION}`);
-
// Capture phase: ahead of CodeMirror and Obsidian's own handling.
this.registerDomEvent(window, "keydown", (evt) => this.onKeyDown(evt), { capture: true });
-
- this.addCommand({
- id: "tighten-headings-above-tables",
- name: "Remove blank lines between headings and tables in this note",
- editorCallback: (editor) => this.tightenHeadings(editor),
- });
}
onKeyDown(evt) {
@@ -270,65 +247,6 @@ class TableBlankLineRemover extends Plugin {
editor.setCursor(plan.caret);
return true;
}
-
- // Bulk pass. Deliberately limited to headings: a table pulled tight against
- // a paragraph gets absorbed into that paragraph by the markdown parser,
- // which is not a change anyone wants applied to a whole note at once.
- tightenHeadings(editor) {
- const doc = docFromEditor(editor);
- const targets = [];
-
- for (let i = 0; i < doc.count; i++) {
- if (!tableStartsAt(doc, i)) continue;
- const above = lastNonBlankAbove(doc, i);
- if (above < 0 || above === i - 1) continue;
- if (!/^\s{0,3}#{1,6}\s/.test(doc.get(above))) continue;
- targets.push({ table: i, firstBlank: above + 1 });
- }
-
- if (targets.length === 0) {
- new Notice("No blank lines between headings and tables.");
- return;
- }
-
- // Bottom-up, one blank line at a time, so the line numbers above each
- // removal stay valid. The table climbs by one after each success.
- let removed = 0;
- for (let t = targets.length - 1; t >= 0; t--) {
- const { table, firstBlank } = targets[t];
- let shift = 0;
- for (let i = table - 1; i >= firstBlank; i--) {
- const ok = this.applyPlan(editor, {
- blankLine: i,
- caret: { line: i, ch: 0 },
- direction: "above",
- tableLine: table - shift,
- });
- if (ok) {
- removed++;
- shift++;
- }
- }
- }
-
- new Notice(
- `Removed ${removed} blank line${removed === 1 ? "" : "s"} above ` +
- `${targets.length} table${targets.length === 1 ? "" : "s"}.`
- );
- }
}
module.exports = TableBlankLineRemover;
-// Obsidian accepts either shape depending on how the file was built; expose both.
-module.exports.default = TableBlankLineRemover;
-module.exports._internals = {
- isDelimiterRow,
- isTableRowLine,
- isTableStart,
- isInsideCodeBlock,
- tableStartsAt,
- tableStartFor,
- blockRewrite,
- planBackspace,
- planDelete,
-};