### Real-Time Symbol Replacement with Keyboard Events (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt This TypeScript code implements a keyboard event handler that detects word boundaries and applies symbol replacements as the user types. It checks for word start and end conditions, utilizes a settings object for defined replacements, and handles undo functionality for the last replacement. It also includes checks to avoid replacements within code blocks or math expressions. ```typescript private keyDownEvent(event: KeyboardEvent) { const lastReplacementTemp = { ...this.lastReplacement }; this.lastReplacement.active = false; const editor = this.app.workspace.activeEditor?.editor; if (!editor) return; const cursor = editor.getCursor(); const line = editor.getLine(cursor.line); // Check if word boundary reached (space, Enter, or punctuation) if (this.isWordEnd(event, false)) { let from = -1; let sequence = ''; // Scan backwards to find the start of the word for (let i = cursor.ch - 1; i >= 0; i--) { if (this.isWordStart(line, i)) { const excludeWordStartIndex = i + 1; from = excludeWordStartIndex; sequence = line.slice(excludeWordStartIndex, cursor.ch); if (this.settings.replacements[sequence] && !this.settings.replacements[sequence].disabled) { break; } } } const replacement = this.settings.replacements[sequence]; if (!replacement || replacement.disabled) return; const replaceCharacter = replacement.value; // Apply replacement if not in code blocks or math expressions if (replaceCharacter && sequence.length > 0 && from !== -1 && !this.isCursorInUnwantedBlocks(editor)) { this.applyReplacement(editor, cursor, from, replaceCharacter); // Track replacement for undo functionality this.lastReplacement = { active: true, sequence: sequence, from: from, line: cursor.line, key: event.key, }; replacement.count = replacement.count ? replacement.count + 1 : 1; this.saveSettings(); } } // Handle backspace to undo last replacement else if (event.key === 'Backspace') { if (!lastReplacementTemp.active) return; const replacement = this.settings.replacements[lastReplacementTemp.sequence]; if (!replacement || replacement.disabled) return; // Restore original text let replaceCharacter = replacement.replaced + lastReplacementTemp.key; this.applyReplacement(editor, cursor, lastReplacementTemp.from, replaceCharacter); } } private isWordStart(line: string, i: number) { return this.isSpaceCharacter(line, i) || (this.settings.flexibleWordsStart && ['(', '«', "'", '"', '*'].includes(line.charAt(i))); } private isWordEnd(event: KeyboardEvent, isSpacebar: boolean) { const flexibleEndings = ['.', ',', '!', '?', ':', ';', ')', '»', "'", '"', 'Enter', '\n', '*']; return event.key === ' ' || isSpacebar || (this.settings.flexibleWordsEnd && flexibleEndings.includes(event.key)); } ``` -------------------------------- ### Plugin Initialization and Event Registration (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt Initializes the Enhanced Symbols Prettifier plugin, registers the settings tab, adds commands, and sets up a listener for keyboard events to enable real-time symbol replacement. It extends Obsidian's Plugin base class and manages core functionalities. ```typescript import { Editor, Plugin } from 'obsidian'; import { EnhancedSymbolsPrettifierSettingsTab } from './settings/settings'; import { DEFAULT_SETTINGS, Settings } from './settings/defaultSettings'; export default class EnhancedSymbolsPrettifier extends Plugin { settings: Settings; lastReplacement = { active: false, sequence: '', from: 0, line: 0, key: '', }; async onload() { await this.loadSettings(); // Register settings tab this.addSettingTab(new EnhancedSymbolsPrettifierSettingsTab(this.app, this)); // Register commands this.addCommand({ id: 'format-symbols', name: 'Prettify existing symbols in document', editorCallback: (editor) => this.prettifyInDocument(editor), }); // Monitor keyboard events for real-time replacement this.registerDomEvent(window, 'keydown', (event: KeyboardEvent) => { this.keyDownEvent(event); }); } } ``` -------------------------------- ### DataMapper: Export & Import Shortcuts (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt Handles exporting shortcuts to JSON files and importing shortcuts from JSON files. It manages the collection, formatting, and file creation for exports, and parses and merges imported data for shortcuts. Dependencies include browser APIs for file handling and Obsidian's Setting and Notice components. ```typescript import { Settings, Substitution } from './defaultSettings'; interface DataExport { [key: string]: Substitution; } export class DataMapper { constructor(private settings: Settings) {} public exportGroup(group: string): void { const exportData: DataExport = {}; // Collect shortcuts from selected group Object.keys(this.settings.replacements).forEach((key) => { const replacement = this.settings.replacements[key]; if (group === 'all' || replacement.group === group) { exportData[key] = { ...replacement }; // Remove temporary data delete exportData[key].count; delete exportData[key].disabled; } }); // Create downloadable JSON file const blob = new Blob([JSON.stringify(exportData)], { type: 'application/json', }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const groupFilename = group.replace(/[^a-z0-9]/gi, '-').toLowerCase(); const date = new Date().toISOString().split('T')[0]; a.download = `enhanced-symbols-prettifier-${groupFilename}-export-${date}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } } // Import functionality in settings tab new Setting(containerEl) .setName('Import shortcuts from file') .setDesc('Import additional shortcuts from a JSON export file. Any conflicting existing shortcuts will be overridden.') .addButton((button) => button .setButtonText('Import') .setCta() .onClick(async () => { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = async () => { if (input.files && input.files.length > 0) { const file = input.files[0]; const reader = new FileReader(); reader.onload = async () => { let data = {}; try { const text = reader.result as string; data = JSON.parse(text); } catch (error) { new Notice('Invalid JSON file'); return; } // Merge with existing shortcuts this.plugin.settings.replacements = { ...this.plugin.settings.replacements, ...data, }; await this.plugin.saveSettings(); this.display(); new Notice('Shortcuts imported'); }; reader.readAsText(file); } }; input.click(); }) ); ``` -------------------------------- ### Settings Management (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt Handles loading, saving, and validating plugin settings. It merges user settings with defaults, ensures consistency in replacement keys, and provides a method to restore default settings. This ensures data integrity across plugin versions. ```typescript async loadSettings() { // Merge saved settings with defaults this.settings = Object.assign( {}, DEFAULT_SETTINGS, await this.loadData() ); // Ensure replacement keys match their content this.settings.replacements = Object.assign( {}, DEFAULT_SETTINGS.replacements, this.settings.replacements ); this.validateSettings(); } async saveSettings() { this.validateSettings(); await this.saveData(this.settings); } private validateSettings() { const keys = Object.keys(this.settings.replacements); keys.forEach((key) => { const replacement = this.settings.replacements[key]; // Fix mismatched keys if (replacement.replaced !== key) { this.settings.replacements[replacement.replaced] = replacement; delete this.settings.replacements[key]; } }); } async restoreDefaultSettings() { this.settings = JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); await this.saveSettings(); } ``` -------------------------------- ### Development Environment Configuration (JavaScript) Source: https://github.com/noam-sc/obsidian-enhanced-symbols-prettifier/blob/main/README.md This JavaScript snippet defines the environment variable for the Obsidian plugin's export path during development. It requires the path to the Obsidian vault's plugin directory to be specified. ```javascript export const obsidianExportPath = '/.obsidian/plugins/obsidian-symbols-prettifier'; ``` -------------------------------- ### Define Default Settings Interface and Configuration (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt Defines the TypeScript interfaces for symbol replacements and the overall settings structure. It also provides the `DEFAULT_SETTINGS` object, which contains a pre-populated map of common text replacements to their corresponding Unicode symbols, categorized for organization. ```typescript export interface Settings { replacements: Record; exclusions?: string[]; flexibleWordsStart?: boolean; flexibleWordsEnd?: boolean; } export interface Substitution { replaced: string; // The text to match value: string; // The replacement symbol disabled?: boolean; // Whether this replacement is active group: string; // Category for organization count?: number; // Usage statistics } export const DEFAULT_SETTINGS = { replacements: { '->': { replaced: '->', value: '→', group: 'Arrows' }, '<-': { replaced: '<-', value: '←', group: 'Arrows' }, '<->': { replaced: '<->', value: '↔', group: 'Arrows' }, '<=>': { replaced: '<=>', value: '⇔', group: 'Arrows' }, '=/=': { replaced: '=/=', value: '≠', group: 'Mathematical Operators' }, '~=': { replaced: '~=', value: '≈', group: 'Mathematical Operators' }, 'sqrt': { replaced: 'sqrt', value: '√', group: 'Mathematical Operators' }, 'pi': { replaced: 'pi', value: 'π', group: 'Mathematical Operators' }, 'inf': { replaced: 'inf', value: '∞', group: 'Mathematical Operators' }, 'delta': { replaced: 'delta', value: 'Δ', group: 'Greek Letters' }, 'alpha': { replaced: 'alpha', value: 'α', group: 'Greek Letters' }, 'w/': { replaced: 'w/', value: 'with', group: 'Words' }, 'w/o': { replaced: 'w/o', value: 'without', group: 'Words' }, '(1)': { replaced: '(1)', value: '1️⃣', group: 'Numbers' }, '...': { replaced: '...', value: '…', group: 'Miscellaneous' }, '(c)': { replaced: '(c)', value: '©', group: 'Miscellaneous' }, }, exclusions: [], flexibleWordsStart: true, flexibleWordsEnd: true, }; ``` -------------------------------- ### Display and Manage Symbol Replacements in Obsidian Settings (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt This TypeScript code defines the settings tab UI for the Enhanced Symbols Prettifier Obsidian plugin. It allows users to view, edit, disable, and delete symbol replacement rules. The code handles user input for the replaced symbol, the replacement value, and group-level toggles, persisting changes using Obsidian's saveSettings API. ```typescript import { PluginSettingTab, App, Setting, Notice } from 'obsidian'; export class EnhancedSymbolsPrettifierSettingsTab extends PluginSettingTab { plugin: EnhancedSymbolsPrettifier; displayReplacement(replacement: Substitution, i: number, containerEl: HTMLElement): void { new Setting(containerEl) .setName(`${i}.`) .setDesc(`${replacement.count ? 'Triggered ' + replacement.count + ' time' + (replacement.count > 1 ? 's' : '') : ''}`) .addText((text) => text .setPlaceholder('To replace') .setValue(replacement.replaced) .onChange(async (newKey) => { if (this.plugin.settings.replacements[newKey] && newKey !== replacement.replaced) { new Notice(`Shortcut "${newKey}" already exists. Please choose a different name.`); return; } // Update key const oldKey = replacement.replaced; this.plugin.settings.replacements[oldKey].replaced = newKey; this.plugin.settings.replacements[newKey] = this.plugin.settings.replacements[oldKey]; delete this.plugin.settings.replacements[oldKey]; await this.plugin.saveSettings(); }) ) .addText((text) => text .setPlaceholder('Replace with') .setValue(replacement.value) .onChange(async (val) => { this.plugin.settings.replacements[replacement.replaced].value = val; await this.plugin.saveSettings(); }) ) .addToggle((toggle) => toggle .setValue(!replacement.disabled) .onChange(async (value) => { this.plugin.settings.replacements[replacement.replaced].disabled = !value; await this.plugin.saveSettings(); }) ) .addButton((button) => button.setIcon('x').onClick(async () => { delete this.plugin.settings.replacements[replacement.replaced]; await this.plugin.saveSettings(); this.display(); }) ); } displayGroup(group: string, containerEl: HTMLElement): void { new Setting(containerEl).setName(group).setHeading(); // Group-level toggle new Setting(containerEl).setName('Disable group').addToggle((toggle) => toggle .setValue( Object.values(this.plugin.settings.replacements) .filter((r) => r.group === group) .filter((r) => !r.disabled) .length === 0 ) .onChange(async (value) => { for (const key in this.plugin.settings.replacements) { if (this.plugin.settings.replacements[key].group === group) { this.plugin.settings.replacements[key].disabled = value; } } await this.plugin.saveSettings(); this.display(); }) ); // Display all shortcuts in group let i = 0; for (const key in this.plugin.settings.replacements) { if (this.plugin.settings.replacements[key].group === group) { i++; this.displayReplacement(this.plugin.settings.replacements[key], i, containerEl); } } // Add new shortcut button new Setting(containerEl).setName('Add new symbol').addButton((button) => button.setIcon('plus').setCta().onClick(async () => { this.plugin.settings.replacements[''] = { replaced: '', value: '', disabled: false, group: group, }; await this.plugin.saveSettings(); this.display(); }) ); } } ``` -------------------------------- ### SearchCursor: Regex Text Searching (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt Provides an efficient regex-based text search utility for finding matches within document content. It allows searching forwards from a specified caret position, returning match boundaries. Dependencies include standard JavaScript RegExp object. ```typescript export class SearchCursor { public readonly regex: RegExp; private _from: number; private _to: number; private _caret: number; constructor( public text: string, regex: RegExp | string, private readonly _originalCaret: number ) { if (regex instanceof RegExp) { this.regex = regex; } else { this.regex = new RegExp(regex as string); } this.reset(); } public reset(): void { this._from = this._originalCaret; this._to = this._originalCaret; this._caret = this._originalCaret; } public findNext(): RegExpMatchArray | undefined { const text = this.text.slice(this._caret); const match = text.match(this.regex); if (match?.index == null) { return undefined; } // Handle capturing groups if (match.length === 4) { this._from = this._caret + match.index + match[1].length; this._to = this._caret + match.index + match[1].length + match[2].length; this._caret = this._to; return match; } this._from = this._caret + match.index; this._to = this._caret + match.index + match[0].length; this._caret = this._to; return match; } public to(): number { return this._to; } public from(): number { return this._from; } } // Usage example for finding all matches const searchCursor = new SearchCursor(documentText, /pattern/g, 0); while (searchCursor.findNext() !== undefined) { const matchStart = searchCursor.from(); const matchEnd = searchCursor.to(); console.log(`Match found at ${matchStart}-${matchEnd}`); } ``` -------------------------------- ### ShortcutsFinder Class: Analyze Vault and Suggest Shortcuts (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt This TypeScript class, ShortcutsFinder, analyzes markdown documents within an Obsidian vault to identify the most frequent words. It then generates potential shortcuts for these words using consonant-based abbreviation algorithms. The class handles exclusions for common words and user-defined shortcuts, returning a mapping of suggested shortcuts to their most frequent associated word. ```typescript export default class ShortcutsFinder { plugin: EnhancedSymbolsPrettifier; top_words: Record = {}; suggested_shortcuts: Record> = {}; excluded_shortcuts: string[] = []; excluded_words: string[] = []; constructor( plugin: EnhancedSymbolsPrettifier, excluded_shortcuts: string[] = [], excluded_words: string[] = [] ) { this.plugin = plugin; this.excluded_shortcuts = excluded_shortcuts; this.excluded_words = excluded_words; } public async findShortcuts() { await this.fetchDocuments(); return this.findSuggestedShortcuts(); } private async fetchDocuments() { const files = this.plugin.app.vault.getMarkdownFiles(); const documents = await Promise.all( files.map((file) => this.plugin.app.vault.cachedRead(file)) ); this.findTopWords(documents); } private findTopWords(documents: string[]) { const threeLetterWords: Record = {}; const wordCount = documents.reduce((acc, doc) => { const words = doc.split(/\s+/); words.forEach((word) => { word = word.replace(/[^a-zA-ZÀ-ÿ'-]/g, ''); if (this.excluded_words.includes(word)) return; // Track 3-letter words to avoid conflicts if (word.length == 3) { threeLetterWords[word] = (threeLetterWords[word] || 0) + 1; } if (word.length < 4) return; acc[word] = (acc[word] || 0) + 1; }); return acc; }, {} as Record); // Exclude common 3-letter words from shortcuts const sortedThreeLetterWords = Object.keys(threeLetterWords) .sort((a, b) => threeLetterWords[b] - threeLetterWords[a]); this.excluded_shortcuts.push(...sortedThreeLetterWords.slice(0, 15)); // Keep top 100 words const sortedWords = Object.keys(wordCount) .sort((a, b) => wordCount[b] - wordCount[a]); this.top_words = sortedWords.slice(0, 100).reduce((acc, word) => { acc[word] = wordCount[word]; return acc; }, {} as Record); } private findShortcutAssociated(word: string) { // Special case for 4-letter words: use first + last letter if (word.length == 4) { return word[0] + word[3]; } word = word.replace(/[^a-zA-ZÀ-ÿ]/g, ''); // Use first 3 consonants const consonants = word.match(/[^aeiou]/g); if (consonants && consonants.length >= 3) { return consonants.slice(0, 3).join(''); } // Fallback: first 3 characters return word.slice(0, 3); } private findSuggestedShortcuts() { // Generate shortcuts for all top words Object.keys(this.top_words).forEach((word) => { const shortcut = this.findShortcutAssociated(word); if (!this.suggested_shortcuts[shortcut]) { this.suggested_shortcuts[shortcut] = {}; } this.suggested_shortcuts[shortcut][word] = this.top_words[word]; }); // For each shortcut, keep only the most frequent word Object.keys(this.suggested_shortcuts).forEach((shortcut) => { const words = this.suggested_shortcuts[shortcut]; const maxWord = Object.keys(words).reduce((a, b) => words[a] > words[b] ? a : b ); this.suggested_shortcuts[shortcut] = { [maxWord]: words[maxWord] }; }); // Remove excluded shortcuts this.excluded_shortcuts.forEach((shortcut) => { delete this.suggested_shortcuts[shortcut]; }); return this.suggested_shortcuts; } } ``` -------------------------------- ### Prettify/Unprettify Symbols in Document (TypeScript) Source: https://context7.com/noam-sc/obsidian-enhanced-symbols-prettifier/llms.txt Processes the entire current document to replace symbols with their prettified or unprettified versions. It identifies symbols using regular expressions, filters out matches within code blocks, and applies replacements, updating the editor and saving settings. Includes a method to generate the appropriate regex based on whether to prettify or unprettify. ```typescript private prettifyInDocument(editor: Editor, reverse = false) { let value = editor.getValue(); const codeBlocks = this.getCodeBlocks(value); let matchedChars: { from: number; to: number }[] = []; // Find all matches using regex const searchCursor = new SearchCursor(value, this.getRegex(reverse), 0); while (searchCursor.findNext() !== undefined) { matchedChars.push({ from: searchCursor.from(), to: searchCursor.to(), }); } // Filter out matches inside code blocks matchedChars = matchedChars.filter((matchedChar) => { return !codeBlocks.some( (cb) => cb.from <= matchedChar.from && cb.to >= matchedChar.to ); }); // Replace all matches let diff = 0; let replacementsCount = 0; matchedChars.forEach((matchedChar) => { const symbol = value.substring( matchedChar.from - diff, matchedChar.to - diff ); let replacement; if (reverse) { replacement = Object.entries(this.settings.replacements).find( ([, r]) => r.value === symbol )?.[1]; } else { replacement = this.settings.replacements[symbol]; } if (!replacement || replacement.disabled) return; const character = reverse ? replacement.replaced : replacement.value; value = value.substring(0, matchedChar.from - diff) + character + value.substring(matchedChar.to - diff); diff += symbol.length - character.length; replacementsCount++; replacement.count = replacement.count ? replacement.count + 1 : 1; }); editor.setValue(value); this.saveSettings(); new Notice( replacementsCount === 0 ? 'No symbols found to replace' : replacementsCount === 1 ? 'Replaced 1 symbol' : `Replaced ${replacementsCount} symbols` ); } private getRegex(reverse = false): RegExp { const matchChars = Object.entries(this.settings.replacements).reduce( (prev, [key, replacement]) => { const curr = reverse ? replacement.value : replacement.replaced; if (prev.length === 0) { return prev + this.escapeRegExp(curr); } else if (curr.length === 0 || replacement.disabled) { return prev; } return prev + '|' + this.escapeRegExp(curr); }, '' ); // Match only at word boundaries return new RegExp( '(? { const searchCursor = new SearchCursor(editor.getValue(), unwantedBlock, 0); while (searchCursor.findNext() !== undefined) { const offset = editor.posToOffset(editor.getCursor()); if (searchCursor.from() <= offset && searchCursor.to() >= offset) { return true; } } return false; }); } private getCodeBlocks(input: string) { const result: { from: number; to: number }[] = []; const codeBlock = /```\w*[^`]+```/; const searchCursor = new SearchCursor(input, codeBlock, 0); while (searchCursor.findNext() !== undefined) { result.push({ from: searchCursor.from(), to: searchCursor.to() }); } return result; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.