markdown-actions 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index-new.js", "../src/core/style-args.js", "../src/core/selection-new.js", "../src/core/insertion-new.js", "../src/operations/block-new.js", "../src/operations/list-new.js", "../src/core/style-text.js", "../src/core/detection.js"],
4
+ "sourcesContent": ["/**\n * markdown-actions - Lightweight markdown toolbar functionality\n * Exact implementation matching GitHub's markdown-toolbar-element\n */\n\nimport { mergeStyleWithDefaults } from './core/style-args.js'\nimport { styleSelectedText } from './core/style-text.js'\nimport { setUndoMethod } from './core/insertion-new.js'\nimport { preserveSelection, expandSelectionToLine } from './core/selection-new.js'\nimport { getActiveFormats as getActive, hasFormat as has } from './core/detection.js'\n\n/**\n * Format configurations matching GitHub's manualStyles\n * GitHub: index.ts lines 86-118\n */\nconst FORMATS = {\n bold: { prefix: '**', suffix: '**', trimFirst: true },\n italic: { prefix: '_', suffix: '_', trimFirst: true },\n code: { \n prefix: '`', \n suffix: '`', \n blockPrefix: '```', \n blockSuffix: '```' \n },\n link: { \n prefix: '[', \n suffix: '](url)', \n replaceNext: 'url', \n scanFor: 'https?://' \n },\n quote: { \n prefix: '> ', \n multiline: true, \n surroundWithNewlines: true \n },\n bulletList: { \n prefix: '- ', \n multiline: true, \n unorderedList: true \n },\n numberedList: { \n prefix: '1. ', \n multiline: true, \n orderedList: true \n },\n header1: { prefix: '# ' },\n header2: { prefix: '## ' },\n header3: { prefix: '### ' },\n header4: { prefix: '#### ' },\n header5: { prefix: '##### ' },\n header6: { prefix: '###### ' }\n}\n\n/**\n * Apply style to textarea\n * Mimics GitHub's applyStyle function (index.ts lines 801-827)\n */\nfunction applyStyle(textarea, format) {\n const style = mergeStyleWithDefaults(format)\n textarea.focus()\n styleSelectedText(textarea, style)\n}\n\n/**\n * Toggle bold formatting\n */\nexport function toggleBold(textarea) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, FORMATS.bold)\n}\n\n/**\n * Toggle italic formatting\n */\nexport function toggleItalic(textarea) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, FORMATS.italic)\n}\n\n/**\n * Toggle code formatting\n */\nexport function toggleCode(textarea) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, FORMATS.code)\n}\n\n/**\n * Insert or toggle link formatting\n */\nexport function insertLink(textarea, options = {}) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n \n let format = { ...FORMATS.link }\n \n // Override with custom URL if provided\n if (options.url) {\n format.suffix = `](${options.url})`\n format.replaceNext = ''\n }\n \n // Handle custom text if provided\n if (options.text && !textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)) {\n const pos = textarea.selectionStart\n textarea.value = textarea.value.slice(0, pos) + options.text + textarea.value.slice(pos)\n textarea.selectionStart = pos\n textarea.selectionEnd = pos + options.text.length\n }\n \n applyStyle(textarea, format)\n}\n\n/**\n * Toggle bullet list formatting\n */\nexport function toggleBulletList(textarea) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, FORMATS.bulletList)\n}\n\n/**\n * Toggle numbered list formatting\n */\nexport function toggleNumberedList(textarea) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, FORMATS.numberedList)\n}\n\n/**\n * Toggle quote formatting\n */\nexport function toggleQuote(textarea) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, FORMATS.quote)\n}\n\n/**\n * Insert header with specific level\n */\nexport function insertHeader(textarea, level = 1) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n if (level < 1 || level > 6) level = 1\n \n const headerKey = `header${level}`\n const format = FORMATS[headerKey] || FORMATS.header1\n \n // Select current line\n const value = textarea.value\n const start = textarea.selectionStart\n let lineStart = start\n let lineEnd = start\n \n // Find line boundaries\n while (lineStart > 0 && value[lineStart - 1] !== '\\n') {\n lineStart--\n }\n while (lineEnd < value.length && value[lineEnd] !== '\\n') {\n lineEnd++\n }\n \n textarea.selectionStart = lineStart\n textarea.selectionEnd = lineEnd\n \n const currentLine = value.slice(lineStart, lineEnd)\n \n // Remove any existing header formatting\n const cleanedLine = currentLine.replace(/^#{1,6}\\s*/, '')\n \n // Apply new header\n const newLine = format.prefix + cleanedLine\n \n // Use our style system\n const tempSelection = textarea.selectionStart\n textarea.value = value.slice(0, lineStart) + newLine + value.slice(lineEnd)\n textarea.selectionStart = lineStart + format.prefix.length\n textarea.selectionEnd = lineStart + newLine.length\n}\n\n/**\n * Get active formats at cursor position\n */\nexport function getActiveFormats(textarea) {\n return getActive(textarea)\n}\n\n/**\n * Check if format is active at cursor\n */\nexport function hasFormat(textarea, format) {\n return has(textarea, format)\n}\n\n/**\n * Expand selection based on options\n */\nexport function expandSelection(textarea, options = {}) {\n if (!textarea) return\n \n const { toWord, toLine } = options\n const { selectionStart, selectionEnd, value } = textarea\n \n if (toLine) {\n expandSelectionToLine(textarea)\n } else if (toWord && selectionStart === selectionEnd) {\n // Find word boundaries\n let start = selectionStart\n let end = selectionEnd\n \n // Move start back to word boundary\n while (start > 0 && !/\\s/.test(value[start - 1])) {\n start--\n }\n \n // Move end forward to word boundary\n while (end < value.length && !/\\s/.test(value[end])) {\n end++\n }\n \n textarea.selectionStart = start\n textarea.selectionEnd = end\n }\n}\n\n/**\n * Apply custom format\n */\nexport function applyCustomFormat(textarea, format) {\n if (!textarea || textarea.disabled || textarea.readOnly) return\n applyStyle(textarea, format)\n}\n\n/**\n * Preserve selection during callback\n */\nexport { preserveSelection }\n\n/**\n * Configure undo method\n */\nexport { setUndoMethod }\n\n/**\n * Default export with all functions\n */\nexport default {\n toggleBold,\n toggleItalic,\n toggleCode,\n insertLink,\n toggleBulletList,\n toggleNumberedList,\n toggleQuote,\n insertHeader,\n getActiveFormats,\n hasFormat,\n expandSelection,\n applyCustomFormat,\n preserveSelection,\n setUndoMethod\n}", "/**\n * Style arguments type definition and defaults\n * Matches GitHub's implementation exactly\n */\n\n/**\n * Get default style arguments\n * MUST match GitHub's defaults exactly (index.ts lines 805-818)\n */\nexport function getDefaultStyleArgs() {\n return {\n prefix: '',\n suffix: '',\n blockPrefix: '',\n blockSuffix: '',\n multiline: false,\n replaceNext: '',\n prefixSpace: false,\n scanFor: '',\n surroundWithNewlines: false,\n orderedList: false,\n unorderedList: false,\n trimFirst: false\n }\n}\n\n/**\n * Merge style with defaults\n * Ensures ALL properties are present\n */\nexport function mergeStyleWithDefaults(style) {\n return { ...getDefaultStyleArgs(), ...style }\n}", "/**\n * Selection utilities - EXACT match to GitHub's implementation\n * Based on markdown-toolbar-element index.ts\n */\n\n/**\n * Check if string contains multiple lines\n * GitHub: index.ts line 419-421\n */\nexport function isMultipleLines(string) {\n return string.trim().split('\\n').length > 1\n}\n\n/**\n * Repeat string n times\n * GitHub: index.ts line 423-425\n */\nfunction repeat(string, n) {\n return Array(n + 1).join(string)\n}\n\n/**\n * Find word selection start\n * GitHub: index.ts line 427-433\n */\nexport function wordSelectionStart(text, i) {\n let index = i\n while (text[index] && text[index - 1] != null && !text[index - 1].match(/\\s/)) {\n index--\n }\n return index\n}\n\n/**\n * Find word selection end\n * GitHub: index.ts line 435-442\n */\nexport function wordSelectionEnd(text, i, multiline) {\n let index = i\n const breakpoint = multiline ? /\\n/ : /\\s/\n while (text[index] && !text[index].match(breakpoint)) {\n index++\n }\n return index\n}\n\n/**\n * Expand selection to line boundaries\n * GitHub: index.ts line 502-515\n * CRITICAL: This modifies the textarea selection directly!\n */\nexport function expandSelectionToLine(textarea) {\n const lines = textarea.value.split('\\n')\n let counter = 0\n for (let index = 0; index < lines.length; index++) {\n const lineLength = lines[index].length + 1\n if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {\n textarea.selectionStart = counter\n }\n if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {\n textarea.selectionEnd = counter + lineLength - 1\n }\n counter += lineLength\n }\n}\n\n/**\n * Expand selected text\n * GitHub: index.ts line 517-537\n * CRITICAL: This modifies the textarea selection to include format markers if they exist\n */\nexport function expandSelectedText(textarea, prefixToUse, suffixToUse, multiline = false) {\n if (textarea.selectionStart === textarea.selectionEnd) {\n textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart)\n textarea.selectionEnd = wordSelectionEnd(textarea.value, textarea.selectionEnd, multiline)\n } else {\n const expandedSelectionStart = textarea.selectionStart - prefixToUse.length\n const expandedSelectionEnd = textarea.selectionEnd + suffixToUse.length\n const beginsWithPrefix = textarea.value.slice(expandedSelectionStart, textarea.selectionStart) === prefixToUse\n const endsWithSuffix = textarea.value.slice(textarea.selectionEnd, expandedSelectionEnd) === suffixToUse\n if (beginsWithPrefix && endsWithSuffix) {\n textarea.selectionStart = expandedSelectionStart\n textarea.selectionEnd = expandedSelectionEnd\n }\n }\n return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)\n}\n\n/**\n * Calculate newlines to surround selected text\n * GitHub: index.ts line 544-573\n */\nexport function newlinesToSurroundSelectedText(textarea) {\n const beforeSelection = textarea.value.slice(0, textarea.selectionStart)\n const afterSelection = textarea.value.slice(textarea.selectionEnd)\n\n const breaksBefore = beforeSelection.match(/\\n*$/)\n const breaksAfter = afterSelection.match(/^\\n*/)\n const newlinesBeforeSelection = breaksBefore ? breaksBefore[0].length : 0\n const newlinesAfterSelection = breaksAfter ? breaksAfter[0].length : 0\n\n let newlinesToAppend\n let newlinesToPrepend\n\n if (beforeSelection.match(/\\S/) && newlinesBeforeSelection < 2) {\n newlinesToAppend = repeat('\\n', 2 - newlinesBeforeSelection)\n }\n\n if (afterSelection.match(/\\S/) && newlinesAfterSelection < 2) {\n newlinesToPrepend = repeat('\\n', 2 - newlinesAfterSelection)\n }\n\n if (newlinesToAppend == null) {\n newlinesToAppend = ''\n }\n\n if (newlinesToPrepend == null) {\n newlinesToPrepend = ''\n }\n\n return { newlinesToAppend, newlinesToPrepend }\n}\n\n/**\n * Preserve selection helper (not in GitHub's but useful)\n */\nexport function preserveSelection(textarea, callback) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n const scrollTop = textarea.scrollTop\n \n callback()\n \n textarea.selectionStart = start\n textarea.selectionEnd = end\n textarea.scrollTop = scrollTop\n}", "/**\n * Text insertion with undo/redo support\n * EXACT match to GitHub's implementation\n * Based on markdown-toolbar-element index.ts lines 444-485\n */\n\nlet canInsertText = null\n\n/**\n * Insert text into textarea\n * GitHub: index.ts lines 446-485\n * CRITICAL: Uses the CURRENT selection (which may have been expanded)\n */\nexport function insertText(textarea, { text, selectionStart, selectionEnd }) {\n const originalSelectionStart = textarea.selectionStart\n const before = textarea.value.slice(0, originalSelectionStart)\n const after = textarea.value.slice(textarea.selectionEnd)\n\n if (canInsertText === null || canInsertText === true) {\n textarea.contentEditable = 'true'\n try {\n canInsertText = document.execCommand('insertText', false, text)\n } catch (error) {\n canInsertText = false\n }\n textarea.contentEditable = 'false'\n }\n\n if (canInsertText && !textarea.value.slice(0, textarea.selectionStart).endsWith(text)) {\n canInsertText = false\n }\n\n if (!canInsertText) {\n try {\n document.execCommand('ms-beginUndoUnit')\n } catch (e) {\n // Do nothing.\n }\n textarea.value = before + text + after\n try {\n document.execCommand('ms-endUndoUnit')\n } catch (e) {\n // Do nothing.\n }\n textarea.dispatchEvent(new CustomEvent('input', { bubbles: true, cancelable: true }))\n }\n\n if (selectionStart != null && selectionEnd != null) {\n textarea.setSelectionRange(selectionStart, selectionEnd)\n } else {\n textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd)\n }\n}\n\n/**\n * Set undo method (for configuration)\n */\nexport function setUndoMethod(method) {\n if (method === 'native') {\n canInsertText = true\n } else if (method === 'manual') {\n canInsertText = false\n } else {\n canInsertText = null\n }\n}", "/**\n * Block and multiline operations - EXACT match to GitHub's implementation\n * Based on markdown-toolbar-element index.ts lines 581-670\n */\n\nimport { \n isMultipleLines, \n expandSelectedText, \n newlinesToSurroundSelectedText \n} from '../core/selection-new.js'\n\n/**\n * Block style operation\n * GitHub: index.ts lines 581-646\n */\nexport function blockStyle(textarea, arg) {\n let newlinesToAppend\n let newlinesToPrepend\n\n const { prefix, suffix, blockPrefix, blockSuffix, replaceNext, prefixSpace, scanFor, surroundWithNewlines } = arg\n const originalSelectionStart = textarea.selectionStart\n const originalSelectionEnd = textarea.selectionEnd\n\n let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)\n let prefixToUse = isMultipleLines(selectedText) && blockPrefix.length > 0 ? `${blockPrefix}\\n` : prefix\n let suffixToUse = isMultipleLines(selectedText) && blockSuffix.length > 0 ? `\\n${blockSuffix}` : suffix\n\n if (prefixSpace) {\n const beforeSelection = textarea.value[textarea.selectionStart - 1]\n if (textarea.selectionStart !== 0 && beforeSelection != null && !beforeSelection.match(/\\s/)) {\n prefixToUse = ` ${prefixToUse}`\n }\n }\n selectedText = expandSelectedText(textarea, prefixToUse, suffixToUse, arg.multiline)\n let selectionStart = textarea.selectionStart\n let selectionEnd = textarea.selectionEnd\n const hasReplaceNext = replaceNext.length > 0 && suffixToUse.indexOf(replaceNext) > -1 && selectedText.length > 0\n if (surroundWithNewlines) {\n const ref = newlinesToSurroundSelectedText(textarea)\n newlinesToAppend = ref.newlinesToAppend\n newlinesToPrepend = ref.newlinesToPrepend\n prefixToUse = newlinesToAppend + prefix\n suffixToUse += newlinesToPrepend\n }\n\n if (selectedText.startsWith(prefixToUse) && selectedText.endsWith(suffixToUse)) {\n const replacementText = selectedText.slice(prefixToUse.length, selectedText.length - suffixToUse.length)\n if (originalSelectionStart === originalSelectionEnd) {\n let position = originalSelectionStart - prefixToUse.length\n position = Math.max(position, selectionStart)\n position = Math.min(position, selectionStart + replacementText.length)\n selectionStart = selectionEnd = position\n } else {\n selectionEnd = selectionStart + replacementText.length\n }\n return { text: replacementText, selectionStart, selectionEnd }\n } else if (!hasReplaceNext) {\n let replacementText = prefixToUse + selectedText + suffixToUse\n selectionStart = originalSelectionStart + prefixToUse.length\n selectionEnd = originalSelectionEnd + prefixToUse.length\n const whitespaceEdges = selectedText.match(/^\\s*|\\s*$/g)\n if (arg.trimFirst && whitespaceEdges) {\n const leadingWhitespace = whitespaceEdges[0] || ''\n const trailingWhitespace = whitespaceEdges[1] || ''\n replacementText = leadingWhitespace + prefixToUse + selectedText.trim() + suffixToUse + trailingWhitespace\n selectionStart += leadingWhitespace.length\n selectionEnd -= trailingWhitespace.length\n }\n return { text: replacementText, selectionStart, selectionEnd }\n } else if (scanFor.length > 0 && selectedText.match(scanFor)) {\n suffixToUse = suffixToUse.replace(replaceNext, selectedText)\n const replacementText = prefixToUse + selectedText + suffixToUse\n selectionStart = selectionEnd = selectionStart + prefixToUse.length\n return { text: replacementText, selectionStart, selectionEnd }\n } else {\n const replacementText = prefixToUse + selectedText + suffixToUse\n selectionStart = selectionStart + prefixToUse.length + selectedText.length + suffixToUse.indexOf(replaceNext)\n selectionEnd = selectionStart + replaceNext.length\n return { text: replacementText, selectionStart, selectionEnd }\n }\n}\n\n/**\n * Multiline style operation\n * GitHub: index.ts lines 648-670\n */\nexport function multilineStyle(textarea, arg) {\n const { prefix, suffix, surroundWithNewlines } = arg\n let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)\n let selectionStart = textarea.selectionStart\n let selectionEnd = textarea.selectionEnd\n const lines = text.split('\\n')\n const undoStyle = lines.every(line => line.startsWith(prefix) && line.endsWith(suffix))\n\n if (undoStyle) {\n text = lines.map(line => line.slice(prefix.length, line.length - suffix.length)).join('\\n')\n selectionEnd = selectionStart + text.length\n } else {\n text = lines.map(line => prefix + line + suffix).join('\\n')\n if (surroundWithNewlines) {\n const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea)\n selectionStart += newlinesToAppend.length\n selectionEnd = selectionStart + text.length\n text = newlinesToAppend + text + newlinesToPrepend\n }\n }\n\n return { text, selectionStart, selectionEnd }\n}", "/**\n * List operations - EXACT match to GitHub's implementation\n * Based on markdown-toolbar-element index.ts lines 676-784\n */\n\nimport { expandSelectionToLine, newlinesToSurroundSelectedText } from '../core/selection-new.js'\n\n/**\n * Undo ordered list style\n * GitHub: index.ts lines 676-689\n */\nfunction undoOrderedListStyle(text) {\n const lines = text.split('\\n')\n const orderedListRegex = /^\\d+\\.\\s+/\n const shouldUndoOrderedList = lines.every(line => orderedListRegex.test(line))\n let result = lines\n if (shouldUndoOrderedList) {\n result = lines.map(line => line.replace(orderedListRegex, ''))\n }\n\n return {\n text: result.join('\\n'),\n processed: shouldUndoOrderedList\n }\n}\n\n/**\n * Undo unordered list style\n * GitHub: index.ts lines 691-704\n */\nfunction undoUnorderedListStyle(text) {\n const lines = text.split('\\n')\n const unorderedListPrefix = '- '\n const shouldUndoUnorderedList = lines.every(line => line.startsWith(unorderedListPrefix))\n let result = lines\n if (shouldUndoUnorderedList) {\n result = lines.map(line => line.slice(unorderedListPrefix.length, line.length))\n }\n\n return {\n text: result.join('\\n'),\n processed: shouldUndoUnorderedList\n }\n}\n\n/**\n * Make prefix for list item\n * GitHub: index.ts lines 706-712\n */\nfunction makePrefix(index, unorderedList) {\n if (unorderedList) {\n return '- '\n } else {\n return `${index + 1}. `\n }\n}\n\n/**\n * Clear existing list style\n * GitHub: index.ts lines 714-728\n */\nfunction clearExistingListStyle(style, selectedText) {\n let undoResultOpositeList\n let undoResult\n let pristineText\n if (style.orderedList) {\n undoResult = undoOrderedListStyle(selectedText)\n undoResultOpositeList = undoUnorderedListStyle(undoResult.text)\n pristineText = undoResultOpositeList.text\n } else {\n undoResult = undoUnorderedListStyle(selectedText)\n undoResultOpositeList = undoOrderedListStyle(undoResult.text)\n pristineText = undoResultOpositeList.text\n }\n return [undoResult, undoResultOpositeList, pristineText]\n}\n\n/**\n * Apply list style to text\n * GitHub: index.ts lines 730-784\n * CRITICAL: This function modifies the textarea selection via expandSelectionToLine\n */\nexport function listStyle(textarea, style) {\n const noInitialSelection = textarea.selectionStart === textarea.selectionEnd\n let selectionStart = textarea.selectionStart\n let selectionEnd = textarea.selectionEnd\n\n // Select whole line - MODIFIES textarea.selectionStart and textarea.selectionEnd\n expandSelectionToLine(textarea)\n\n const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)\n\n // If the user intent was to do an undo, we will stop after this.\n // Otherwise, we will still undo to other list type to prevent list stacking\n const [undoResult, undoResultOpositeList, pristineText] = clearExistingListStyle(style, selectedText)\n\n const prefixedLines = pristineText.split('\\n').map((value, index) => {\n return `${makePrefix(index, style.unorderedList)}${value}`\n })\n\n const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {\n return previousValue + makePrefix(currentIndex, style.unorderedList).length\n }, 0)\n\n const totalPrefixLengthOpositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {\n return previousValue + makePrefix(currentIndex, !style.unorderedList).length\n }, 0)\n\n if (undoResult.processed) {\n if (noInitialSelection) {\n selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0)\n selectionEnd = selectionStart\n } else {\n selectionStart = textarea.selectionStart\n selectionEnd = textarea.selectionEnd - totalPrefixLength\n }\n return { text: pristineText, selectionStart, selectionEnd }\n }\n\n const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea)\n const text = newlinesToAppend + prefixedLines.join('\\n') + newlinesToPrepend\n\n if (noInitialSelection) {\n selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0)\n selectionEnd = selectionStart\n } else {\n if (undoResultOpositeList.processed) {\n selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0)\n selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOpositeList\n } else {\n selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0)\n selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength\n }\n }\n\n return { text, selectionStart, selectionEnd }\n}", "/**\n * Main text styling orchestration\n * EXACT match to GitHub's implementation\n * Based on markdown-toolbar-element index.ts lines 487-500\n */\n\nimport { isMultipleLines } from './selection-new.js'\nimport { insertText } from './insertion-new.js'\nimport { blockStyle, multilineStyle } from '../operations/block-new.js'\nimport { listStyle } from '../operations/list-new.js'\n\n/**\n * Style selected text in textarea\n * GitHub: index.ts lines 487-500\n * CRITICAL: This is the main orchestrator that decides which operation to use\n */\nexport function styleSelectedText(textarea, styleArgs) {\n const text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)\n\n let result\n if (styleArgs.orderedList || styleArgs.unorderedList) {\n result = listStyle(textarea, styleArgs)\n } else if (styleArgs.multiline && isMultipleLines(text)) {\n result = multilineStyle(textarea, styleArgs)\n } else {\n result = blockStyle(textarea, styleArgs)\n }\n\n insertText(textarea, result)\n}", "/**\n * Format detection utilities\n */\n\nimport { FORMATS } from './formats.js'\n\n/**\n * Check if text has a specific format applied\n */\nfunction hasFormatApplied(text, format) {\n if (!format.prefix) return false\n \n if (format.suffix) {\n return text.startsWith(format.prefix) && text.endsWith(format.suffix)\n } else {\n return text.startsWith(format.prefix)\n }\n}\n\n/**\n * Get active formats at cursor position\n */\nexport function getActiveFormats(textarea) {\n if (!textarea) return []\n \n const formats = []\n const { selectionStart, selectionEnd, value } = textarea\n \n // Get current line for line-based formats\n const lines = value.split('\\n')\n let lineStart = 0\n let currentLine = ''\n \n for (const line of lines) {\n if (selectionStart >= lineStart && selectionStart <= lineStart + line.length) {\n currentLine = line\n break\n }\n lineStart += line.length + 1\n }\n \n // Check line-based formats\n if (currentLine.startsWith('- ')) {\n if (currentLine.startsWith('- [ ] ') || currentLine.startsWith('- [x] ')) {\n formats.push('task-list')\n } else {\n formats.push('bullet-list')\n }\n }\n \n if (/^\\d+\\.\\s/.test(currentLine)) {\n formats.push('numbered-list')\n }\n \n if (currentLine.startsWith('> ')) {\n formats.push('quote')\n }\n \n if (currentLine.startsWith('# ')) formats.push('header')\n if (currentLine.startsWith('## ')) formats.push('header-2')\n if (currentLine.startsWith('### ')) formats.push('header-3')\n \n // Check inline formats by looking around cursor\n const lookBehind = Math.max(0, selectionStart - 10)\n const lookAhead = Math.min(value.length, selectionEnd + 10)\n const surrounding = value.slice(lookBehind, lookAhead)\n \n // Check for bold\n if (surrounding.includes('**')) {\n const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)\n const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))\n const lastOpenBold = beforeCursor.lastIndexOf('**')\n const nextCloseBold = afterCursor.indexOf('**')\n if (lastOpenBold !== -1 && nextCloseBold !== -1) {\n formats.push('bold')\n }\n }\n \n // Check for italic\n if (surrounding.includes('_')) {\n const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)\n const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))\n const lastOpenItalic = beforeCursor.lastIndexOf('_')\n const nextCloseItalic = afterCursor.indexOf('_')\n if (lastOpenItalic !== -1 && nextCloseItalic !== -1) {\n formats.push('italic')\n }\n }\n \n // Check for code\n if (surrounding.includes('`')) {\n const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)\n const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))\n if (beforeCursor.includes('`') && afterCursor.includes('`')) {\n formats.push('code')\n }\n }\n \n // Check for link\n if (surrounding.includes('[') && surrounding.includes(']')) {\n const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)\n const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))\n const lastOpenBracket = beforeCursor.lastIndexOf('[')\n const nextCloseBracket = afterCursor.indexOf(']')\n if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {\n const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10)\n if (afterBracket.startsWith('(')) {\n formats.push('link')\n }\n }\n }\n \n return formats\n}\n\n/**\n * Check if specific format is active at cursor\n */\nexport function hasFormat(textarea, format) {\n const activeFormats = getActiveFormats(textarea)\n return activeFormats.includes(format)\n}\n\n/**\n * Expand selection based on options\n */\nexport function expandSelection(textarea, options = {}) {\n if (!textarea) return\n \n const { toWord, toLine, toFormat } = options\n const { selectionStart, selectionEnd, value } = textarea\n \n if (toLine) {\n // Find line boundaries\n const lines = value.split('\\n')\n let lineStart = 0\n let lineEnd = 0\n let currentPos = 0\n \n for (const line of lines) {\n if (selectionStart >= currentPos && selectionStart <= currentPos + line.length) {\n lineStart = currentPos\n lineEnd = currentPos + line.length\n break\n }\n currentPos += line.length + 1\n }\n \n textarea.selectionStart = lineStart\n textarea.selectionEnd = lineEnd\n } else if (toWord && selectionStart === selectionEnd) {\n // Find word boundaries\n let start = selectionStart\n let end = selectionEnd\n \n // Move start back to word boundary\n while (start > 0 && !/\\s/.test(value[start - 1])) {\n start--\n }\n \n // Move end forward to word boundary\n while (end < value.length && !/\\s/.test(value[end])) {\n end++\n }\n \n textarea.selectionStart = start\n textarea.selectionEnd = end\n }\n}"],
5
+ "mappings": "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",
6
+ "names": ["getActiveFormats", "hasFormat", "FORMATS", "getActiveFormats", "hasFormat", "getActiveFormats", "hasFormat"]
7
+ }
@@ -0,0 +1,19 @@
1
+ var markdownActions=(()=>{var O=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames,B=Object.getOwnPropertySymbols;var U=Object.prototype.hasOwnProperty,fe=Object.prototype.propertyIsEnumerable;var R=(e,n,t)=>n in e?O(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t,w=(e,n)=>{for(var t in n||(n={}))U.call(n,t)&&R(e,t,n[t]);if(B)for(var t of B(n))fe.call(n,t)&&R(e,t,n[t]);return e};var de=(e,n)=>{for(var t in n)O(e,t,{get:n[t],enumerable:!0})},pe=(e,n,t,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of ue(n))!U.call(e,l)&&l!==t&&O(e,l,{get:()=>n[l],enumerable:!(i=ce(n,l))||i.enumerable});return e};var he=e=>pe(O({},"__esModule",{value:!0}),e);var Ee={};de(Ee,{applyCustomFormat:()=>oe,default:()=>Le,expandSelection:()=>se,getActiveFormats:()=>ie,hasFormat:()=>le,insertHeader:()=>te,insertLink:()=>Y,preserveSelection:()=>M,setUndoMethod:()=>C,toggleBold:()=>G,toggleBulletList:()=>Z,toggleCode:()=>X,toggleItalic:()=>J,toggleNumberedList:()=>ee,toggleQuote:()=>ne});function Se(){return{prefix:"",suffix:"",blockPrefix:"",blockSuffix:"",multiline:!1,replaceNext:"",prefixSpace:!1,scanFor:"",surroundWithNewlines:!1,orderedList:!1,unorderedList:!1,trimFirst:!1}}function $(e){return w(w({},Se()),e)}function W(e){return e.trim().split(`
2
+ `).length>1}function _(e,n){return Array(n+1).join(e)}function me(e,n){let t=n;for(;e[t]&&e[t-1]!=null&&!e[t-1].match(/\s/);)t--;return t}function ge(e,n,t){let i=n,l=t?/\n/:/\s/;for(;e[i]&&!e[i].match(l);)i++;return i}function F(e){let n=e.value.split(`
3
+ `),t=0;for(let i=0;i<n.length;i++){let l=n[i].length+1;e.selectionStart>=t&&e.selectionStart<t+l&&(e.selectionStart=t),e.selectionEnd>=t&&e.selectionEnd<t+l&&(e.selectionEnd=t+l-1),t+=l}}function I(e,n,t,i=!1){if(e.selectionStart===e.selectionEnd)e.selectionStart=me(e.value,e.selectionStart),e.selectionEnd=ge(e.value,e.selectionEnd,i);else{let l=e.selectionStart-n.length,o=e.selectionEnd+t.length,r=e.value.slice(l,e.selectionStart)===n,s=e.value.slice(e.selectionEnd,o)===t;r&&s&&(e.selectionStart=l,e.selectionEnd=o)}return e.value.slice(e.selectionStart,e.selectionEnd)}function k(e){let n=e.value.slice(0,e.selectionStart),t=e.value.slice(e.selectionEnd),i=n.match(/\n*$/),l=t.match(/^\n*/),o=i?i[0].length:0,r=l?l[0].length:0,s,f;return n.match(/\S/)&&o<2&&(s=_(`
4
+ `,2-o)),t.match(/\S/)&&r<2&&(f=_(`
5
+ `,2-r)),s==null&&(s=""),f==null&&(f=""),{newlinesToAppend:s,newlinesToPrepend:f}}function M(e,n){let t=e.selectionStart,i=e.selectionEnd,l=e.scrollTop;n(),e.selectionStart=t,e.selectionEnd=i,e.scrollTop=l}var a=null;function j(e,{text:n,selectionStart:t,selectionEnd:i}){let l=e.selectionStart,o=e.value.slice(0,l),r=e.value.slice(e.selectionEnd);if(a===null||a===!0){e.contentEditable="true";try{a=document.execCommand("insertText",!1,n)}catch(s){a=!1}e.contentEditable="false"}if(a&&!e.value.slice(0,e.selectionStart).endsWith(n)&&(a=!1),!a){try{document.execCommand("ms-beginUndoUnit")}catch(s){}e.value=o+n+r;try{document.execCommand("ms-endUndoUnit")}catch(s){}e.dispatchEvent(new CustomEvent("input",{bubbles:!0,cancelable:!0}))}t!=null&&i!=null?e.setSelectionRange(t,i):e.setSelectionRange(l,e.selectionEnd)}function C(e){e==="native"?a=!0:e==="manual"?a=!1:a=null}function D(e,n){let t,i,{prefix:l,suffix:o,blockPrefix:r,blockSuffix:s,replaceNext:f,prefixSpace:b,scanFor:c,surroundWithNewlines:h}=n,S=e.selectionStart,L=e.selectionEnd,u=e.value.slice(e.selectionStart,e.selectionEnd),d=W(u)&&r.length>0?`${r}
6
+ `:l,m=W(u)&&s.length>0?`
7
+ ${s}`:o;if(b){let g=e.value[e.selectionStart-1];e.selectionStart!==0&&g!=null&&!g.match(/\s/)&&(d=` ${d}`)}u=I(e,d,m,n.multiline);let p=e.selectionStart,E=e.selectionEnd,re=f.length>0&&m.indexOf(f)>-1&&u.length>0;if(h){let g=k(e);t=g.newlinesToAppend,i=g.newlinesToPrepend,d=t+l,m+=i}if(u.startsWith(d)&&u.endsWith(m)){let g=u.slice(d.length,u.length-m.length);if(S===L){let x=S-d.length;x=Math.max(x,p),x=Math.min(x,p+g.length),p=E=x}else E=p+g.length;return{text:g,selectionStart:p,selectionEnd:E}}else if(re)if(c.length>0&&u.match(c)){m=m.replace(f,u);let g=d+u+m;return p=E=p+d.length,{text:g,selectionStart:p,selectionEnd:E}}else{let g=d+u+m;return p=p+d.length+u.length+m.indexOf(f),E=p+f.length,{text:g,selectionStart:p,selectionEnd:E}}else{let g=d+u+m;p=S+d.length,E=L+d.length;let x=u.match(/^\s*|\s*$/g);if(n.trimFirst&&x){let N=x[0]||"",A=x[1]||"";g=N+d+u.trim()+m+A,p+=N.length,E-=A.length}return{text:g,selectionStart:p,selectionEnd:E}}}function q(e,n){let{prefix:t,suffix:i,surroundWithNewlines:l}=n,o=e.value.slice(e.selectionStart,e.selectionEnd),r=e.selectionStart,s=e.selectionEnd,f=o.split(`
8
+ `);if(f.every(c=>c.startsWith(t)&&c.endsWith(i)))o=f.map(c=>c.slice(t.length,c.length-i.length)).join(`
9
+ `),s=r+o.length;else if(o=f.map(c=>t+c+i).join(`
10
+ `),l){let{newlinesToAppend:c,newlinesToPrepend:h}=k(e);r+=c.length,s=r+o.length,o=c+o+h}return{text:o,selectionStart:r,selectionEnd:s}}function V(e){let n=e.split(`
11
+ `),t=/^\d+\.\s+/,i=n.every(o=>t.test(o)),l=n;return i&&(l=n.map(o=>o.replace(t,""))),{text:l.join(`
12
+ `),processed:i}}function H(e){let n=e.split(`
13
+ `),t="- ",i=n.every(o=>o.startsWith(t)),l=n;return i&&(l=n.map(o=>o.slice(t.length,o.length))),{text:l.join(`
14
+ `),processed:i}}function y(e,n){return n?"- ":`${e+1}. `}function be(e,n){let t,i,l;return e.orderedList?(i=V(n),t=H(i.text),l=t.text):(i=H(n),t=V(i.text),l=t.text),[i,t,l]}function K(e,n){let t=e.selectionStart===e.selectionEnd,i=e.selectionStart,l=e.selectionEnd;F(e);let o=e.value.slice(e.selectionStart,e.selectionEnd),[r,s,f]=be(n,o),b=f.split(`
15
+ `).map((d,m)=>`${y(m,n.unorderedList)}${d}`),c=b.reduce((d,m,p)=>d+y(p,n.unorderedList).length,0),h=b.reduce((d,m,p)=>d+y(p,!n.unorderedList).length,0);if(r.processed)return t?(i=Math.max(i-y(0,n.unorderedList).length,0),l=i):(i=e.selectionStart,l=e.selectionEnd-c),{text:f,selectionStart:i,selectionEnd:l};let{newlinesToAppend:S,newlinesToPrepend:L}=k(e),u=S+b.join(`
16
+ `)+L;return t?(i=Math.max(i+y(0,n.unorderedList).length+S.length,0),l=i):s.processed?(i=Math.max(e.selectionStart+S.length,0),l=e.selectionEnd+S.length+c-h):(i=Math.max(e.selectionStart+S.length,0),l=e.selectionEnd+S.length+c),{text:u,selectionStart:i,selectionEnd:l}}function Q(e,n){let t=e.value.slice(e.selectionStart,e.selectionEnd),i;n.orderedList||n.unorderedList?i=K(e,n):n.multiline&&W(t)?i=q(e,n):i=D(e,n),j(e,i)}function P(e){if(!e)return[];let n=[],{selectionStart:t,selectionEnd:i,value:l}=e,o=l.split(`
17
+ `),r=0,s="";for(let h of o){if(t>=r&&t<=r+h.length){s=h;break}r+=h.length+1}s.startsWith("- ")&&(s.startsWith("- [ ] ")||s.startsWith("- [x] ")?n.push("task-list"):n.push("bullet-list")),/^\d+\.\s/.test(s)&&n.push("numbered-list"),s.startsWith("> ")&&n.push("quote"),s.startsWith("# ")&&n.push("header"),s.startsWith("## ")&&n.push("header-2"),s.startsWith("### ")&&n.push("header-3");let f=Math.max(0,t-10),b=Math.min(l.length,i+10),c=l.slice(f,b);if(c.includes("**")){let h=l.slice(Math.max(0,t-100),t),S=l.slice(i,Math.min(l.length,i+100)),L=h.lastIndexOf("**"),u=S.indexOf("**");L!==-1&&u!==-1&&n.push("bold")}if(c.includes("_")){let h=l.slice(Math.max(0,t-100),t),S=l.slice(i,Math.min(l.length,i+100)),L=h.lastIndexOf("_"),u=S.indexOf("_");L!==-1&&u!==-1&&n.push("italic")}if(c.includes("`")){let h=l.slice(Math.max(0,t-100),t),S=l.slice(i,Math.min(l.length,i+100));h.includes("`")&&S.includes("`")&&n.push("code")}if(c.includes("[")&&c.includes("]")){let h=l.slice(Math.max(0,t-100),t),S=l.slice(i,Math.min(l.length,i+100)),L=h.lastIndexOf("["),u=S.indexOf("]");L!==-1&&u!==-1&&l.slice(i+u+1,i+u+10).startsWith("(")&&n.push("link")}return n}function z(e,n){return P(e).includes(n)}var T={bold:{prefix:"**",suffix:"**",trimFirst:!0},italic:{prefix:"_",suffix:"_",trimFirst:!0},code:{prefix:"`",suffix:"`",blockPrefix:"```",blockSuffix:"```"},link:{prefix:"[",suffix:"](url)",replaceNext:"url",scanFor:"https?://"},quote:{prefix:"> ",multiline:!0,surroundWithNewlines:!0},bulletList:{prefix:"- ",multiline:!0,unorderedList:!0},numberedList:{prefix:"1. ",multiline:!0,orderedList:!0},header1:{prefix:"# "},header2:{prefix:"## "},header3:{prefix:"### "},header4:{prefix:"#### "},header5:{prefix:"##### "},header6:{prefix:"###### "}};function v(e,n){let t=$(n);e.focus(),Q(e,t)}function G(e){!e||e.disabled||e.readOnly||v(e,T.bold)}function J(e){!e||e.disabled||e.readOnly||v(e,T.italic)}function X(e){!e||e.disabled||e.readOnly||v(e,T.code)}function Y(e,n={}){if(!e||e.disabled||e.readOnly)return;let t=w({},T.link);if(n.url&&(t.suffix=`](${n.url})`,t.replaceNext=""),n.text&&!e.value.slice(e.selectionStart,e.selectionEnd)){let i=e.selectionStart;e.value=e.value.slice(0,i)+n.text+e.value.slice(i),e.selectionStart=i,e.selectionEnd=i+n.text.length}v(e,t)}function Z(e){!e||e.disabled||e.readOnly||v(e,T.bulletList)}function ee(e){!e||e.disabled||e.readOnly||v(e,T.numberedList)}function ne(e){!e||e.disabled||e.readOnly||v(e,T.quote)}function te(e,n=1){if(!e||e.disabled||e.readOnly)return;(n<1||n>6)&&(n=1);let t=`header${n}`,i=T[t]||T.header1,l=e.value,o=e.selectionStart,r=o,s=o;for(;r>0&&l[r-1]!==`
18
+ `;)r--;for(;s<l.length&&l[s]!==`
19
+ `;)s++;e.selectionStart=r,e.selectionEnd=s;let b=l.slice(r,s).replace(/^#{1,6}\s*/,""),c=i.prefix+b,h=e.selectionStart;e.value=l.slice(0,r)+c+l.slice(s),e.selectionStart=r+i.prefix.length,e.selectionEnd=r+c.length}function ie(e){return P(e)}function le(e,n){return z(e,n)}function se(e,n={}){if(!e)return;let{toWord:t,toLine:i}=n,{selectionStart:l,selectionEnd:o,value:r}=e;if(i)F(e);else if(t&&l===o){let s=l,f=o;for(;s>0&&!/\s/.test(r[s-1]);)s--;for(;f<r.length&&!/\s/.test(r[f]);)f++;e.selectionStart=s,e.selectionEnd=f}}function oe(e,n){!e||e.disabled||e.readOnly||v(e,n)}var Le={toggleBold:G,toggleItalic:J,toggleCode:X,insertLink:Y,toggleBulletList:Z,toggleNumberedList:ee,toggleQuote:ne,insertHeader:te,getActiveFormats:ie,hasFormat:le,expandSelection:se,applyCustomFormat:oe,preserveSelection:M,setUndoMethod:C};return he(Ee);})();
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "markdown-actions",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight, zero-dependency JavaScript library for adding markdown formatting functionality to any textarea element",
5
+ "main": "dist/markdown-actions.js",
6
+ "module": "dist/markdown-actions.esm.js",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "node esbuild.config.js",
10
+ "watch": "node esbuild.config.js --watch",
11
+ "serve": "http-server -p 3000 -c-1",
12
+ "dev": "concurrently \"npm run watch\" \"npm run serve\"",
13
+ "test": "echo \"Error: no test specified\" && exit 1"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/panphora/markdown-actions.git"
18
+ },
19
+ "keywords": [
20
+ "markdown",
21
+ "editor",
22
+ "textarea",
23
+ "formatting",
24
+ "toolbar",
25
+ "wysiwyg",
26
+ "lightweight"
27
+ ],
28
+ "author": "David Miranda",
29
+ "license": "MIT",
30
+ "bugs": {
31
+ "url": "https://github.com/panphora/markdown-actions/issues"
32
+ },
33
+ "homepage": "https://github.com/panphora/markdown-actions#readme",
34
+ "devDependencies": {
35
+ "concurrently": "^8.2.2",
36
+ "esbuild": "^0.19.11",
37
+ "http-server": "^14.1.1"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "src",
42
+ "README.md",
43
+ "LICENSE"
44
+ ]
45
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Format detection utilities
3
+ */
4
+
5
+ import { FORMATS } from './formats.js'
6
+
7
+ /**
8
+ * Check if text has a specific format applied
9
+ */
10
+ function hasFormatApplied(text, format) {
11
+ if (!format.prefix) return false
12
+
13
+ if (format.suffix) {
14
+ return text.startsWith(format.prefix) && text.endsWith(format.suffix)
15
+ } else {
16
+ return text.startsWith(format.prefix)
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Get active formats at cursor position
22
+ */
23
+ export function getActiveFormats(textarea) {
24
+ if (!textarea) return []
25
+
26
+ const formats = []
27
+ const { selectionStart, selectionEnd, value } = textarea
28
+
29
+ // Get current line for line-based formats
30
+ const lines = value.split('\n')
31
+ let lineStart = 0
32
+ let currentLine = ''
33
+
34
+ for (const line of lines) {
35
+ if (selectionStart >= lineStart && selectionStart <= lineStart + line.length) {
36
+ currentLine = line
37
+ break
38
+ }
39
+ lineStart += line.length + 1
40
+ }
41
+
42
+ // Check line-based formats
43
+ if (currentLine.startsWith('- ')) {
44
+ if (currentLine.startsWith('- [ ] ') || currentLine.startsWith('- [x] ')) {
45
+ formats.push('task-list')
46
+ } else {
47
+ formats.push('bullet-list')
48
+ }
49
+ }
50
+
51
+ if (/^\d+\.\s/.test(currentLine)) {
52
+ formats.push('numbered-list')
53
+ }
54
+
55
+ if (currentLine.startsWith('> ')) {
56
+ formats.push('quote')
57
+ }
58
+
59
+ if (currentLine.startsWith('# ')) formats.push('header')
60
+ if (currentLine.startsWith('## ')) formats.push('header-2')
61
+ if (currentLine.startsWith('### ')) formats.push('header-3')
62
+
63
+ // Check inline formats by looking around cursor
64
+ const lookBehind = Math.max(0, selectionStart - 10)
65
+ const lookAhead = Math.min(value.length, selectionEnd + 10)
66
+ const surrounding = value.slice(lookBehind, lookAhead)
67
+
68
+ // Check for bold
69
+ if (surrounding.includes('**')) {
70
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)
71
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))
72
+ const lastOpenBold = beforeCursor.lastIndexOf('**')
73
+ const nextCloseBold = afterCursor.indexOf('**')
74
+ if (lastOpenBold !== -1 && nextCloseBold !== -1) {
75
+ formats.push('bold')
76
+ }
77
+ }
78
+
79
+ // Check for italic
80
+ if (surrounding.includes('_')) {
81
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)
82
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))
83
+ const lastOpenItalic = beforeCursor.lastIndexOf('_')
84
+ const nextCloseItalic = afterCursor.indexOf('_')
85
+ if (lastOpenItalic !== -1 && nextCloseItalic !== -1) {
86
+ formats.push('italic')
87
+ }
88
+ }
89
+
90
+ // Check for code
91
+ if (surrounding.includes('`')) {
92
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)
93
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))
94
+ if (beforeCursor.includes('`') && afterCursor.includes('`')) {
95
+ formats.push('code')
96
+ }
97
+ }
98
+
99
+ // Check for link
100
+ if (surrounding.includes('[') && surrounding.includes(']')) {
101
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart)
102
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100))
103
+ const lastOpenBracket = beforeCursor.lastIndexOf('[')
104
+ const nextCloseBracket = afterCursor.indexOf(']')
105
+ if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
106
+ const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10)
107
+ if (afterBracket.startsWith('(')) {
108
+ formats.push('link')
109
+ }
110
+ }
111
+ }
112
+
113
+ return formats
114
+ }
115
+
116
+ /**
117
+ * Check if specific format is active at cursor
118
+ */
119
+ export function hasFormat(textarea, format) {
120
+ const activeFormats = getActiveFormats(textarea)
121
+ return activeFormats.includes(format)
122
+ }
123
+
124
+ /**
125
+ * Expand selection based on options
126
+ */
127
+ export function expandSelection(textarea, options = {}) {
128
+ if (!textarea) return
129
+
130
+ const { toWord, toLine, toFormat } = options
131
+ const { selectionStart, selectionEnd, value } = textarea
132
+
133
+ if (toLine) {
134
+ // Find line boundaries
135
+ const lines = value.split('\n')
136
+ let lineStart = 0
137
+ let lineEnd = 0
138
+ let currentPos = 0
139
+
140
+ for (const line of lines) {
141
+ if (selectionStart >= currentPos && selectionStart <= currentPos + line.length) {
142
+ lineStart = currentPos
143
+ lineEnd = currentPos + line.length
144
+ break
145
+ }
146
+ currentPos += line.length + 1
147
+ }
148
+
149
+ textarea.selectionStart = lineStart
150
+ textarea.selectionEnd = lineEnd
151
+ } else if (toWord && selectionStart === selectionEnd) {
152
+ // Find word boundaries
153
+ let start = selectionStart
154
+ let end = selectionEnd
155
+
156
+ // Move start back to word boundary
157
+ while (start > 0 && !/\s/.test(value[start - 1])) {
158
+ start--
159
+ }
160
+
161
+ // Move end forward to word boundary
162
+ while (end < value.length && !/\s/.test(value[end])) {
163
+ end++
164
+ }
165
+
166
+ textarea.selectionStart = start
167
+ textarea.selectionEnd = end
168
+ }
169
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Format definitions for markdown syntax
3
+ */
4
+
5
+ export const FORMATS = {
6
+ bold: {
7
+ prefix: '**',
8
+ suffix: '**',
9
+ trimFirst: true
10
+ },
11
+ italic: {
12
+ prefix: '_',
13
+ suffix: '_',
14
+ trimFirst: true
15
+ },
16
+ code: {
17
+ prefix: '`',
18
+ suffix: '`',
19
+ blockPrefix: '```',
20
+ blockSuffix: '```'
21
+ },
22
+ link: {
23
+ prefix: '[',
24
+ suffix: '](url)',
25
+ replaceNext: 'url',
26
+ scanFor: 'https?://'
27
+ },
28
+ bulletList: {
29
+ prefix: '- ',
30
+ multiline: true,
31
+ unorderedList: true
32
+ },
33
+ numberedList: {
34
+ prefix: '1. ',
35
+ multiline: true,
36
+ orderedList: true
37
+ },
38
+ quote: {
39
+ prefix: '> ',
40
+ multiline: true,
41
+ surroundWithNewlines: true
42
+ },
43
+ header1: { prefix: '# ' },
44
+ header2: { prefix: '## ' },
45
+ header3: { prefix: '### ' },
46
+ header4: { prefix: '#### ' },
47
+ header5: { prefix: '##### ' },
48
+ header6: { prefix: '###### ' }
49
+ }
50
+
51
+ /**
52
+ * Default style configuration
53
+ */
54
+ export function getDefaultStyle() {
55
+ return {
56
+ prefix: '',
57
+ suffix: '',
58
+ blockPrefix: '',
59
+ blockSuffix: '',
60
+ multiline: false,
61
+ replaceNext: '',
62
+ prefixSpace: false,
63
+ scanFor: '',
64
+ surroundWithNewlines: false,
65
+ orderedList: false,
66
+ unorderedList: false,
67
+ trimFirst: false
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Merge format with defaults
73
+ */
74
+ export function mergeWithDefaults(format) {
75
+ return { ...getDefaultStyle(), ...format }
76
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Text insertion with undo/redo support
3
+ * EXACT match to GitHub's implementation
4
+ * Based on markdown-toolbar-element index.ts lines 444-485
5
+ */
6
+
7
+ let canInsertText = null
8
+
9
+ /**
10
+ * Insert text into textarea
11
+ * GitHub: index.ts lines 446-485
12
+ * CRITICAL: Uses the CURRENT selection (which may have been expanded)
13
+ */
14
+ export function insertText(textarea, { text, selectionStart, selectionEnd }) {
15
+ const originalSelectionStart = textarea.selectionStart
16
+ const before = textarea.value.slice(0, originalSelectionStart)
17
+ const after = textarea.value.slice(textarea.selectionEnd)
18
+
19
+ if (canInsertText === null || canInsertText === true) {
20
+ textarea.contentEditable = 'true'
21
+ try {
22
+ canInsertText = document.execCommand('insertText', false, text)
23
+ } catch (error) {
24
+ canInsertText = false
25
+ }
26
+ textarea.contentEditable = 'false'
27
+ }
28
+
29
+ if (canInsertText && !textarea.value.slice(0, textarea.selectionStart).endsWith(text)) {
30
+ canInsertText = false
31
+ }
32
+
33
+ if (!canInsertText) {
34
+ try {
35
+ document.execCommand('ms-beginUndoUnit')
36
+ } catch (e) {
37
+ // Do nothing.
38
+ }
39
+ textarea.value = before + text + after
40
+ try {
41
+ document.execCommand('ms-endUndoUnit')
42
+ } catch (e) {
43
+ // Do nothing.
44
+ }
45
+ textarea.dispatchEvent(new CustomEvent('input', { bubbles: true, cancelable: true }))
46
+ }
47
+
48
+ if (selectionStart != null && selectionEnd != null) {
49
+ textarea.setSelectionRange(selectionStart, selectionEnd)
50
+ } else {
51
+ textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd)
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Set undo method (for configuration)
57
+ */
58
+ export function setUndoMethod(method) {
59
+ if (method === 'native') {
60
+ canInsertText = true
61
+ } else if (method === 'manual') {
62
+ canInsertText = false
63
+ } else {
64
+ canInsertText = null
65
+ }
66
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Text insertion system with undo/redo support
3
+ * Extracted and adapted from GitHub's markdown-toolbar-element
4
+ */
5
+
6
+ let canInsertText = null
7
+
8
+ /**
9
+ * Insert text at current position with undo/redo support
10
+ * @param {HTMLTextAreaElement} textarea - Target textarea
11
+ * @param {Object} options - Text and selection options
12
+ * @param {string} options.text - Text to insert
13
+ * @param {number} [options.selectionStart] - New selection start
14
+ * @param {number} [options.selectionEnd] - New selection end
15
+ */
16
+ export function insertText(textarea, { text, selectionStart, selectionEnd }) {
17
+ const originalSelectionStart = textarea.selectionStart
18
+ const before = textarea.value.slice(0, originalSelectionStart)
19
+ const after = textarea.value.slice(textarea.selectionEnd)
20
+
21
+ // Store the original value to check if execCommand actually changed it
22
+ const originalValue = textarea.value
23
+
24
+ if (canInsertText === null || canInsertText === true) {
25
+ textarea.contentEditable = 'true'
26
+ try {
27
+ canInsertText = document.execCommand('insertText', false, text)
28
+ } catch (error) {
29
+ canInsertText = false
30
+ }
31
+ textarea.contentEditable = 'false'
32
+ }
33
+
34
+ if (canInsertText && !textarea.value.slice(0, textarea.selectionStart).endsWith(text)) {
35
+ canInsertText = false
36
+ }
37
+
38
+ if (!canInsertText) {
39
+ // Only do manual insertion if execCommand didn't change the value
40
+ if (textarea.value === originalValue) {
41
+ try {
42
+ document.execCommand('ms-beginUndoUnit')
43
+ } catch (e) {
44
+ // Do nothing.
45
+ }
46
+ textarea.value = before + text + after
47
+ try {
48
+ document.execCommand('ms-endUndoUnit')
49
+ } catch (e) {
50
+ // Do nothing.
51
+ }
52
+ textarea.dispatchEvent(new CustomEvent('input', { bubbles: true, cancelable: true }))
53
+ }
54
+ }
55
+
56
+ if (selectionStart != null && selectionEnd != null) {
57
+ textarea.setSelectionRange(selectionStart, selectionEnd)
58
+ } else {
59
+ textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd)
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Configure undo method
65
+ * @param {'native' | 'manual' | 'auto'} method - Undo method to use
66
+ */
67
+ export function setUndoMethod(method) {
68
+ switch (method) {
69
+ case 'native':
70
+ canInsertText = true
71
+ break
72
+ case 'manual':
73
+ canInsertText = false
74
+ break
75
+ case 'auto':
76
+ canInsertText = null
77
+ break
78
+ }
79
+ }