From 0ecf6835c99b72b993208ebf1c122d2c4e93b072 Mon Sep 17 00:00:00 2001 From: Heiko Joerg Schick Date: Sat, 21 Mar 2026 17:15:32 +0100 Subject: [PATCH] Add Markdown to Post Format command and fix non-breaking space before period MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New markdownToLinkedIn() function converts Markdown to LinkedIn/Facebook-ready text: ## headings → Unicode bold, **bold** → Unicode bold, _italic_ → Unicode italic, - list items → em-dash bullets; blank lines and heading spacing handled correctly - New "Convert Markdown to Post Format" command registered in commands.ts - Fix space-before-period cleanup to also strip U+00A0 (non-breaking space), which Obsidian inserts after bold/formatted text (/ \./g → /[ \u00A0]\./g) - Update README to document the new command and updated function list Co-Authored-By: Claude Sonnet 4.6 --- README.md | 6 ++++-- src/commands.ts | 12 +++++++++++- src/formatter.ts | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 210b598..8a23782 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ An [Obsidian](https://obsidian.md) plugin that transforms selected text into Uni - **Convert List Bullets to Em Dash** — prefixes every selected line with `— `, stripping any leading `* ` marker first - **Convert List to Numbered (Slash)** — numbers every selected line as `1/ item`, `2/ item`, ..., stripping any leading `* ` marker first - **Convert List to Numbered (Parentheses)** — numbers every selected line as `(1) item`, `(2) item`, ..., stripping any leading `* ` marker first +- **Convert Markdown to Post Format** — converts a Markdown selection to LinkedIn/Facebook-ready text: `##` headings → bold, `**bold**` → Unicode bold, `_italic_` → Unicode italic, `- ` list items → `— ` em-dash bullets - Non-mapped characters (punctuation, spaces, emoji, etc.) are passed through unchanged ## Usage @@ -27,6 +28,7 @@ An [Obsidian](https://obsidian.md) plugin that transforms selected text into Uni - **Convert List Bullets to Em Dash** — prefixes every line with `— ` (strips `* ` if present) - **Convert List to Numbered (Slash)** — numbers every line as `1/ item`, `2/ item`, ... (strips `* ` if present) - **Convert List to Numbered (Parentheses)** — numbers every line as `(1) item`, `(2) item`, ... (strips `* ` if present) + - **Convert Markdown to Post Format** — converts Markdown to LinkedIn/Facebook-ready text (headings, bold, italic, lists) The selected text is replaced in place. @@ -57,8 +59,8 @@ npm run lint # ESLint check | File | Purpose | |------|---------| | `src/unicode-maps.ts` | Builds character lookup maps from Unicode code point ranges | -| `src/formatter.ts` | `transformText(text, style)`, `cleanText(text)`, and `bulletToEmdash(text)` — text transformation functions | -| `src/commands.ts` | Registers the eight editor commands with the Obsidian plugin API | +| `src/formatter.ts` | `transformText`, `cleanText`, `bulletToEmdash`, `bulletToArrow`, `numberedListSlash`, `numberedListParens`, `markdownToLinkedIn` — text transformation functions | +| `src/commands.ts` | Registers the nine editor commands with the Obsidian plugin API | | `src/main.ts` | Plugin entry point — calls `registerCommands` on load | ### Unicode blocks used diff --git a/src/commands.ts b/src/commands.ts index e51b143..2cb90db 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,5 +1,5 @@ import { Plugin } from "obsidian"; -import { transformText, cleanText, bulletToEmdash, bulletToArrow, numberedListSlash, numberedListParens, FormatStyle } from "./formatter"; +import { transformText, cleanText, bulletToEmdash, bulletToArrow, numberedListSlash, numberedListParens, markdownToLinkedIn, FormatStyle } from "./formatter"; function addFormatCommand(plugin: Plugin, style: FormatStyle, name: string) { plugin.addCommand({ @@ -68,4 +68,14 @@ export function registerCommands(plugin: Plugin): void { } }, }); + plugin.addCommand({ + id: "unicode-formatter:markdown-to-linkedin", + name: "Convert Markdown to Post Format", + editorCallback: (editor) => { + const selection = editor.getSelection(); + if (selection) { + editor.replaceSelection(markdownToLinkedIn(selection)); + } + }, + }); } diff --git a/src/formatter.ts b/src/formatter.ts index ee867de..abb2198 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -46,3 +46,54 @@ export function numberedListParens(text: string): string { return `(${n}) ${content}`; }).join("\n"); } + +export function applyInlineMarkdown(text: string): string { + return text.replace(/\*\*(.+?)\*\*|_(.+?)_/g, (_match, bold, italic) => { + if (bold !== undefined) return transformText(bold, "bold"); + return transformText(italic, "italic"); + }); +} + +export function markdownToLinkedIn(text: string): string { + const lines = text.split("\n"); + const output: string[] = []; + let pendingBlanks = 0; + let skipNextBlanks = false; + let inListContext = false; + + for (const line of lines) { + if (line.trim() === "") { + if (!skipNextBlanks) pendingBlanks++; + continue; + } + + skipNextBlanks = false; + + if (line.startsWith("## ")) { + for (let i = 0; i < pendingBlanks; i++) output.push(""); + pendingBlanks = 0; + inListContext = false; + const headingText = line.slice(3); + output.push(transformText(applyInlineMarkdown(headingText), "bold") + " "); + skipNextBlanks = true; + continue; + } + + if (line.startsWith("- ")) { + if (!inListContext) { + for (let i = 0; i < pendingBlanks; i++) output.push(""); + } + pendingBlanks = 0; + output.push("— " + applyInlineMarkdown(line.slice(2)) + " "); + inListContext = true; + continue; + } + + for (let i = 0; i < pendingBlanks; i++) output.push(""); + pendingBlanks = 0; + inListContext = false; + output.push(applyInlineMarkdown(line)); + } + + return output.join("\n").replace(/[ \u00A0]\./g, ".").trimEnd(); +}