pad readme, make hotkeys optional
Some checks failed
Node.js build / build (20.x) (push) Has been cancelled
Node.js build / build (22.x) (push) Has been cancelled

This commit is contained in:
2026-02-24 22:00:12 -05:00
parent 44b505b4e6
commit 28ebf6a4a8
3 changed files with 788 additions and 381 deletions

View File

@@ -124,6 +124,20 @@ Quick file management commands.
| Duplicate File | Create a copy of the current file in the same folder |
| New Adjacent File | Create a new file in the same folder as the current file |
### Sentence Navigation
Navigate and manipulate text at the sentence level. Perfect for prose editing and working with paragraph-style content.
| Command | Description |
|---------|-------------|
| Delete to start of sentence | Remove all text from cursor to the beginning of the current sentence |
| Delete to end of sentence | Remove all text from cursor to the end of the current sentence |
| Select to start of sentence | Select text from cursor to the beginning of the current sentence |
| Select to end of sentence | Select text from cursor to the end of the current sentence |
| Move to start of current sentence | Jump the cursor to the beginning of the current sentence |
| Move to start of next sentence | Jump the cursor to the beginning of the next sentence |
| Select current sentence | Select the entire sentence where the cursor is located |
### Utility Commands
| Command | Description |
@@ -171,6 +185,7 @@ This plugin merges and refactors functionality from:
- **obsidian-tweaks** - Better formatting, directional copy/move, file operations
- **obsidian-editor-shortcuts** - Line operations, case transformation, navigation, multi-cursor support
- **heading-toggler** - Heading toggle with formatting strip
- **obsidian-sentence-navigator** - Sentence-level navigation and manipulation
## License

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,164 @@
import { App, PluginSettingTab, Setting } from 'obsidian';
import BindThemPlugin from './main';
export interface BindThemSettings {
debug: boolean;
// ============================================================
// Command Definitions - All commands organized by category
// ============================================================
export interface CommandDefinition {
id: string;
name: string;
description?: string;
}
export const DEFAULT_SETTINGS: BindThemSettings = {
debug: false
export const COMMAND_CATEGORIES = {
betterFormatting: 'Better Formatting',
directionalCopyMove: 'Directional Copy/Move',
toggleHeading: 'Toggle Heading',
sentenceNavigator: 'Sentence Navigator',
lineOperations: 'Line Operations',
caseTransformation: 'Case Transformation',
navigation: 'Navigation',
cursorMovement: 'Cursor Movement',
fileOperations: 'File Operations',
utility: 'Utility'
} as const;
export const COMMANDS: Record<keyof typeof COMMAND_CATEGORIES, CommandDefinition[]> = {
betterFormatting: [
{ id: 'toggle-bold-underscore', name: 'Toggle bold underscore' },
{ id: 'toggle-bold-asterisk', name: 'Toggle bold asterisk' },
{ id: 'toggle-italics-underscore', name: 'Toggle italics underscore' },
{ id: 'toggle-italics-asterisk', name: 'Toggle italics asterisk' },
{ id: 'toggle-code', name: 'Toggle code' },
{ id: 'toggle-comment', name: 'Toggle comment' },
{ id: 'toggle-highlight', name: 'Toggle highlight' },
{ id: 'toggle-strikethrough', name: 'Toggle strikethrough' },
{ id: 'toggle-math-inline', name: 'Toggle math inline' },
{ id: 'toggle-math-block', name: 'Toggle math block' }
],
directionalCopyMove: [
{ id: 'copy-up', name: 'Copy up' },
{ id: 'copy-down', name: 'Copy down' },
{ id: 'copy-left', name: 'Copy left' },
{ id: 'copy-right', name: 'Copy right' },
{ id: 'move-left', name: 'Move left' },
{ id: 'move-right', name: 'Move right' }
],
toggleHeading: [
{ id: 'toggle-heading-1', name: 'Toggle heading 1' },
{ id: 'toggle-heading-2', name: 'Toggle heading 2' },
{ id: 'toggle-heading-3', name: 'Toggle heading 3' },
{ id: 'toggle-heading-4', name: 'Toggle heading 4' },
{ id: 'toggle-heading-5', name: 'Toggle heading 5' },
{ id: 'toggle-heading-6', name: 'Toggle heading 6' },
{ id: 'toggle-heading-strip-1', name: 'Toggle heading 1 (strip formatting)' },
{ id: 'toggle-heading-strip-2', name: 'Toggle heading 2 (strip formatting)' },
{ id: 'toggle-heading-strip-3', name: 'Toggle heading 3 (strip formatting)' },
{ id: 'toggle-heading-strip-4', name: 'Toggle heading 4 (strip formatting)' },
{ id: 'toggle-heading-strip-5', name: 'Toggle heading 5 (strip formatting)' },
{ id: 'toggle-heading-strip-6', name: 'Toggle heading 6 (strip formatting)' }
],
sentenceNavigator: [
{ id: 'delete-to-start-of-sentence', name: 'Delete to start of sentence' },
{ id: 'delete-to-end-of-sentence', name: 'Delete to end of sentence' },
{ id: 'select-to-start-of-sentence', name: 'Select to start of sentence' },
{ id: 'select-to-end-of-sentence', name: 'Select to end of sentence' },
{ id: 'move-to-start-of-current-sentence', name: 'Move to start of current sentence' },
{ id: 'move-to-start-of-next-sentence', name: 'Move to start of next sentence' },
{ id: 'select-sentence', name: 'Select current sentence' }
],
lineOperations: [
{ id: 'insert-line-above', name: 'Insert line above' },
{ id: 'insert-line-below', name: 'Insert line below' },
{ id: 'delete-line', name: 'Delete line' },
{ id: 'delete-to-start-of-line', name: 'Delete to start of line' },
{ id: 'delete-to-end-of-line', name: 'Delete to end of line' },
{ id: 'join-lines', name: 'Join lines' },
{ id: 'duplicate-line', name: 'Duplicate line' },
{ id: 'copy-line-up', name: 'Copy line up' },
{ id: 'copy-line-down', name: 'Copy line down' },
{ id: 'select-word', name: 'Select word' },
{ id: 'select-line', name: 'Select line' }
],
caseTransformation: [
{ id: 'transform-uppercase', name: 'Transform to uppercase' },
{ id: 'transform-lowercase', name: 'Transform to lowercase' },
{ id: 'transform-titlecase', name: 'Transform to title case' },
{ id: 'toggle-case', name: 'Toggle case' }
],
navigation: [
{ id: 'go-to-line-start', name: 'Go to line start' },
{ id: 'go-to-line-end', name: 'Go to line end' },
{ id: 'go-to-first-line', name: 'Go to first line' },
{ id: 'go-to-last-line', name: 'Go to last line' },
{ id: 'go-to-line-number', name: 'Go to line number' },
{ id: 'go-to-next-heading', name: 'Go to next heading' },
{ id: 'go-to-prev-heading', name: 'Go to previous heading' }
],
cursorMovement: [
{ id: 'move-cursor-up', name: 'Move cursor up' },
{ id: 'move-cursor-down', name: 'Move cursor down' },
{ id: 'move-cursor-left', name: 'Move cursor left' },
{ id: 'move-cursor-right', name: 'Move cursor right' },
{ id: 'go-to-prev-word', name: 'Go to previous word' },
{ id: 'go-to-next-word', name: 'Go to next word' },
{ id: 'insert-cursor-above', name: 'Insert cursor above' },
{ id: 'insert-cursor-below', name: 'Insert cursor below' }
],
fileOperations: [
{ id: 'duplicate-file', name: 'Duplicate file' },
{ id: 'new-adjacent-file', name: 'New adjacent file' }
],
utility: [
{ id: 'toggle-line-numbers', name: 'Toggle line numbers' },
{ id: 'undo', name: 'Undo' },
{ id: 'redo', name: 'Redo' }
]
};
// Get all command IDs as an array
export function getAllCommandIds(): string[] {
const ids: string[] = [];
for (const commands of Object.values(COMMANDS)) {
for (const cmd of commands) {
ids.push(cmd.id);
}
}
return ids;
}
// Create default enabled settings (all enabled by default)
export function createDefaultEnabledCommands(): Record<string, boolean> {
const enabled: Record<string, boolean> = {};
for (const cmdId of getAllCommandIds()) {
enabled[cmdId] = true;
}
return enabled;
}
// ============================================================
// Settings Interface
// ============================================================
export interface BindThemSettings {
debug: boolean;
sentenceRegexSource: string;
enabledCommands: Record<string, boolean>;
}
export const DEFAULT_SENTENCE_REGEX = '[^.!?\\s][^.!?]*(?:[.!?](?![\'"]?\\s|$)[^.!?]*)*[.!?]?[\'"]?(?=\\s|$)';
export const DEFAULT_SETTINGS: BindThemSettings = {
debug: false,
sentenceRegexSource: DEFAULT_SENTENCE_REGEX,
enabledCommands: createDefaultEnabledCommands()
};
// ============================================================
// Settings Tab
// ============================================================
export class BindThemSettingTab extends PluginSettingTab {
plugin: BindThemPlugin;
@@ -21,6 +171,27 @@ export class BindThemSettingTab extends PluginSettingTab {
const { containerEl } = this;
containerEl.empty();
// Hotkeys link at the top
containerEl.createEl('a', {
text: 'Configure keyboard shortcuts for BindThem',
cls: 'bindthem-hotkeys-link',
attr: {
href: '#',
style: 'display: inline-block; margin-bottom: 1em; color: var(--text-accent); cursor: pointer;'
}
}, (el) => {
el.addEventListener('click', (e) => {
e.preventDefault();
// @ts-ignore - openHotkeys is not in the type definitions
const hotkeysTab = this.app.setting.openTabById('hotkeys');
if (hotkeysTab) {
// @ts-ignore - searchComponent is not in the type definitions
hotkeysTab.searchComponent.setValue('BindThem');
}
});
});
// General settings
new Setting(containerEl)
.setName('Debug mode')
.setDesc('Enable debug logging to console')
@@ -32,12 +203,106 @@ export class BindThemSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('About')
.setDesc('BindThem combines features from obsidian-tweaks, obsidian-editor-shortcuts, and heading-toggler into a single plugin.')
.setHeading();
.setName('Sentence regex')
.setDesc('Regular expression used to match sentences')
.addText(text => text
.setValue(this.plugin.settings.sentenceRegexSource)
.onChange(async (value) => {
this.plugin.settings.sentenceRegexSource = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Included features')
.setDesc('• Better Formatting - Smart toggle for bold, italic, code, highlight, etc.\n• Directional Copy/Move - Copy or move text in any direction\n• Toggle Headings (H1-H6) - Including strip formatting option\n• Editor Shortcuts - Insert/delete lines, join lines, case transform\n• Navigation - Go to line, headings, word navigation\n• Multi-cursor support - Insert cursor above/below\n• File operations - Duplicate file, create adjacent file');
.setName('Reset sentence regex')
.addButton(button => button
.setButtonText('Reset')
.onClick(async () => {
this.plugin.settings.sentenceRegexSource = DEFAULT_SENTENCE_REGEX;
await this.plugin.saveSettings();
this.display();
}));
// Commands section header
containerEl.createEl('h2', { text: 'Command Availability' });
containerEl.createEl('p', {
text: 'Toggle which commands are available in Obsidian. Disabled commands will not appear in the command palette.',
cls: 'bindthem-settings-desc'
});
// Category toggle buttons
new Setting(containerEl)
.setName('Enable/Disable All')
.setDesc('Toggle all commands on or off')
.addButton(button => button
.setButtonText('Enable All')
.onClick(async () => {
for (const cmdId of getAllCommandIds()) {
this.plugin.settings.enabledCommands[cmdId] = true;
}
await this.plugin.saveSettings();
this.display();
}))
.addButton(button => button
.setButtonText('Disable All')
.setWarning()
.onClick(async () => {
for (const cmdId of getAllCommandIds()) {
this.plugin.settings.enabledCommands[cmdId] = false;
}
await this.plugin.saveSettings();
this.display();
}));
// Render each category
for (const [categoryKey, categoryName] of Object.entries(COMMAND_CATEGORIES)) {
this.renderCategory(containerEl, categoryKey, categoryName);
}
// About section
new Setting(containerEl)
.setName('About')
.setDesc('BindThem combines features from obsidian-tweaks, obsidian-editor-shortcuts, heading-toggler, and obsidian-sentence-navigator.')
.setHeading();
}
private renderCategory(containerEl: HTMLElement, categoryKey: string, categoryName: string): void {
const commands = COMMANDS[categoryKey as keyof typeof COMMANDS];
if (!commands || commands.length === 0) return;
// Category header with toggle all
const categorySetting = new Setting(containerEl)
.setName(categoryName)
.setHeading();
// Check if all commands in this category are enabled
const allEnabled = commands.every(cmd => this.plugin.settings.enabledCommands[cmd.id] !== false);
const someEnabled = commands.some(cmd => this.plugin.settings.enabledCommands[cmd.id] !== false);
// Add toggle all button for this category
categorySetting.addToggle(toggle => {
toggle
.setValue(allEnabled)
.setTooltip(someEnabled && !allEnabled ? 'Some commands disabled - click to enable all' : 'Toggle all commands in category')
.onChange(async (value) => {
for (const cmd of commands) {
this.plugin.settings.enabledCommands[cmd.id] = value;
}
await this.plugin.saveSettings();
this.display();
});
});
// Render individual commands
for (const cmd of commands) {
new Setting(containerEl)
.setName(cmd.name)
.setDesc(cmd.description || `Command ID: ${cmd.id}`)
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enabledCommands[cmd.id] !== false)
.onChange(async (value) => {
this.plugin.settings.enabledCommands[cmd.id] = value;
await this.plugin.saveSettings();
}));
}
}
}