pad readme, make hotkeys optional
This commit is contained in:
15
README.md
15
README.md
@@ -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
|
||||
|
||||
|
||||
847
src/main.ts
847
src/main.ts
File diff suppressed because it is too large
Load Diff
283
src/settings.ts
283
src/settings.ts
@@ -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();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user