From 1600412a6db6446a4d4f6ce4ec6a6eb228274f67 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 11 Sep 2026 15:11:03 -0400 Subject: [PATCH] Add a table-insert toolbar button to the markdown editor (#109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The markdown toolbar offered bold/italic/code/heading/list/quote/link but no table option, despite README describing table rendering as a supported feature — the parser already renders manually-typed table syntax correctly, this was purely a discoverability gap for a user who wouldn't otherwise know the exact `| Header | Header |` / `|---|---|` syntax to type from scratch. Added toolbarTable(), which inserts a 2-column starter template (with a leading newline only when needed, matching the table syntax's requirement of a full line to itself) matching exactly what parseMarkdownTables()'s detection regex expects, wired into the toolbar's existing data-toolbar-action dispatch. Verified via jsdom that the inserted template parses into a real HTML table. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv --- assets/js/markdown.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/assets/js/markdown.js b/assets/js/markdown.js index b9653ce..172ff9a 100644 --- a/assets/js/markdown.js +++ b/assets/js/markdown.js @@ -506,6 +506,25 @@ function toolbarHeading(textareaId) { textarea.dispatchEvent(new Event('input', { bubbles: true })); } +function toolbarTable(textareaId) { + const textarea = document.getElementById(textareaId); + if (!textarea) return; + + const start = textarea.selectionStart; + const text = textarea.value; + + // Insert on its own line(s), matching the blank-line-before convention + // toolbarList/toolbarHeading rely on the surrounding text for — a table + // needs a full line to itself both before and after the separator row. + const needsLeadingNewline = start > 0 && text[start - 1] !== '\n'; + const template = (needsLeadingNewline ? '\n' : '') + + '| Header 1 | Header 2 |\n' + + '| --- | --- |\n' + + '| Cell 1 | Cell 2 |\n'; + + insertMarkdownText(textareaId, template); +} + function toolbarQuote(textareaId) { const textarea = document.getElementById(textareaId); if (!textarea) return; @@ -544,6 +563,7 @@ function createEditorToolbar(textareaId, containerId) { + `; @@ -563,6 +583,7 @@ function createEditorToolbar(textareaId, containerId) { case 'heading': toolbarHeading(targetId); break; case 'list': toolbarList(targetId); break; case 'quote': toolbarQuote(targetId); break; + case 'table': toolbarTable(targetId); break; case 'link': toolbarLink(targetId); break; } }); @@ -578,6 +599,7 @@ window.toolbarLink = toolbarLink; window.toolbarList = toolbarList; window.toolbarHeading = toolbarHeading; window.toolbarQuote = toolbarQuote; +window.toolbarTable = toolbarTable; window.createEditorToolbar = createEditorToolbar; window.insertMarkdownFormat = insertMarkdownFormat; window.insertMarkdownText = insertMarkdownText;