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 |
|
| 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 |
|
| 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
|
### Utility Commands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
@@ -171,6 +185,7 @@ This plugin merges and refactors functionality from:
|
|||||||
- **obsidian-tweaks** - Better formatting, directional copy/move, file operations
|
- **obsidian-tweaks** - Better formatting, directional copy/move, file operations
|
||||||
- **obsidian-editor-shortcuts** - Line operations, case transformation, navigation, multi-cursor support
|
- **obsidian-editor-shortcuts** - Line operations, case transformation, navigation, multi-cursor support
|
||||||
- **heading-toggler** - Heading toggle with formatting strip
|
- **heading-toggler** - Heading toggle with formatting strip
|
||||||
|
- **obsidian-sentence-navigator** - Sentence-level navigation and manipulation
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
871
src/main.ts
871
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 { App, PluginSettingTab, Setting } from 'obsidian';
|
||||||
import BindThemPlugin from './main';
|
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 = {
|
export const COMMAND_CATEGORIES = {
|
||||||
debug: false
|
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 {
|
export class BindThemSettingTab extends PluginSettingTab {
|
||||||
plugin: BindThemPlugin;
|
plugin: BindThemPlugin;
|
||||||
|
|
||||||
@@ -21,6 +171,27 @@ export class BindThemSettingTab extends PluginSettingTab {
|
|||||||
const { containerEl } = this;
|
const { containerEl } = this;
|
||||||
containerEl.empty();
|
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)
|
new Setting(containerEl)
|
||||||
.setName('Debug mode')
|
.setName('Debug mode')
|
||||||
.setDesc('Enable debug logging to console')
|
.setDesc('Enable debug logging to console')
|
||||||
@@ -32,12 +203,106 @@ export class BindThemSettingTab extends PluginSettingTab {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName('About')
|
.setName('Sentence regex')
|
||||||
.setDesc('BindThem combines features from obsidian-tweaks, obsidian-editor-shortcuts, and heading-toggler into a single plugin.')
|
.setDesc('Regular expression used to match sentences')
|
||||||
.setHeading();
|
.addText(text => text
|
||||||
|
.setValue(this.plugin.settings.sentenceRegexSource)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings.sentenceRegexSource = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
}));
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName('Included features')
|
.setName('Reset sentence regex')
|
||||||
.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');
|
.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