diff --git a/README.md b/README.md index a41f908..8e06af9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Unicode Text Formatter -An [Obsidian](https://obsidian.md) plugin that transforms selected text into Unicode Mathematical Alphanumeric Symbols — Sans-Serif Bold, Italic, and Bold-Italic — for use in LinkedIn and Facebook posts. +An [Obsidian](https://obsidian.md) plugin that transforms selected text into Unicode Mathematical Alphanumeric Symbols — Sans-Serif Bold, Italic, and Bold-Italic — plus Circled and Fullwidth characters — for use in social media posts. ## Features - **Unicode Bold** — converts letters and digits to sans-serif bold (𝗔𝗕𝗖 / 𝗮𝗯𝗰 / 𝟬𝟭𝟮) - **Unicode Italic** — converts letters to sans-serif italic (𝘈𝘉𝘊 / 𝘢𝘣𝘤) - **Unicode Bold Italic** — converts letters to sans-serif bold italic (𝘼𝘽𝘾 / 𝙖𝙗𝙘) +- **Unicode Fullwidth** — converts letters and digits to fullwidth forms (ABC / abc / 012) +- **Unicode Circled** — converts letters and digits to circled forms (ⒶⒷⒸ / ⓐⓑⓒ / ①②③) - **Remove Unicode Formatting** — converts any formatted Unicode characters back to plain ASCII - **Convert List Bullets to Arrow** — prefixes every selected line with `→ `, stripping any leading `* ` marker first - **Convert List Bullets to Em Dash** — prefixes every selected line with `— `, stripping any leading `* ` marker first @@ -23,6 +25,8 @@ An [Obsidian](https://obsidian.md) plugin that transforms selected text into Uni - **Format as Unicode Bold** - **Format as Unicode Italic** - **Format as Unicode Bold Italic** + - **Format as Fullwidth** + - **Format as Circled Letters/Numbers** - **Remove Unicode Formatting** — reverses any of the above back to plain ASCII - **Convert List Bullets to Arrow** — prefixes every line with `→ ` (strips `* ` if present) - **Convert List Bullets to Em Dash** — prefixes every line with `— ` (strips `* ` if present) @@ -60,7 +64,7 @@ npm run lint # ESLint check |------|---------| | `src/unicode-maps.ts` | Builds character lookup maps from Unicode code point ranges | | `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/commands.ts` | Registers the eleven editor commands with the Obsidian plugin API | | `src/main.ts` | Plugin entry point — calls `registerCommands` on load | ### Unicode blocks used @@ -70,6 +74,8 @@ npm run lint # ESLint check | Sans-Serif Bold | U+1D5D4–U+1D5ED | U+1D5EE–U+1D607 | U+1D7EC–U+1D7F5 | | Sans-Serif Italic | U+1D608–U+1D621 | U+1D622–U+1D63B | — | | Sans-Serif Bold-Italic | U+1D63C–U+1D655 | U+1D656–U+1D66F | — | +| Fullwidth | U+FF21–U+FF3A | U+FF41–U+FF5A | U+FF10–U+FF19 | +| Circled | U+24B6–U+24CF | U+24D0–U+24E9 | U+2460–U+2468, U+24EA | ## Releasing diff --git a/src/commands.ts b/src/commands.ts index 57dfe14..17a544f 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -20,6 +20,8 @@ export function registerCommands(plugin: UnicodeFormatterPlugin): void { addFormatCommand(plugin, "bold", "Format as unicode bold"); addFormatCommand(plugin, "italic", "Format as unicode italic"); addFormatCommand(plugin, "bold-italic", "Format as unicode bold italic"); + addFormatCommand(plugin, "fullwidth", "Format as fullwidth"); + addFormatCommand(plugin, "circled", "Format as circled letters/numbers"); plugin.addCommand({ id: "unicode-formatter:clean", name: "Remove unicode formatting", diff --git a/src/formatter.ts b/src/formatter.ts index 7f0ed35..db21ebc 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -1,11 +1,13 @@ -import { BOLD_MAP, ITALIC_MAP, BOLD_ITALIC_MAP, UNICODE_TO_ASCII_MAP, UNICODE_TO_STYLE } from "./unicode-maps"; +import { BOLD_MAP, ITALIC_MAP, BOLD_ITALIC_MAP, FULLWIDTH_MAP, CIRCLED_MAP, UNICODE_TO_ASCII_MAP, UNICODE_TO_STYLE } from "./unicode-maps"; -export type FormatStyle = "bold" | "italic" | "bold-italic"; +export type FormatStyle = "bold" | "italic" | "bold-italic" | "fullwidth" | "circled"; const STYLE_MAPS: Record> = { "bold": BOLD_MAP, "italic": ITALIC_MAP, "bold-italic": BOLD_ITALIC_MAP, + "fullwidth": FULLWIDTH_MAP, + "circled": CIRCLED_MAP, }; export function transformText(text: string, style: FormatStyle): string { diff --git a/src/unicode-maps.ts b/src/unicode-maps.ts index e09568c..94c64ad 100644 --- a/src/unicode-maps.ts +++ b/src/unicode-maps.ts @@ -25,18 +25,38 @@ export const ITALIC_MAP: Record = buildMap(0x1D608, 0x1D622); // Sans-Serif Bold-Italic: U+1D63C–U+1D655 (upper), U+1D656–U+1D66F (lower) export const BOLD_ITALIC_MAP: Record = buildMap(0x1D63C, 0x1D656); +// Fullwidth: U+FF21–U+FF3A (upper), U+FF41–U+FF5A (lower), U+FF10–U+FF19 (digits) +export const FULLWIDTH_MAP: Record = buildMap(0xFF21, 0xFF41, 0xFF10); + +// Circled: U+24B6–U+24CF (upper), U+24D0–U+24E9 (lower), digits 1-9: U+2460–U+2468, 0: U+24EA +function buildCircledMap(): Record { + const map: Record = {}; + for (let i = 0; i < 26; i++) { + map[String.fromCharCode(65 + i)] = String.fromCodePoint(0x24B6 + i); + map[String.fromCharCode(97 + i)] = String.fromCodePoint(0x24D0 + i); + } + map["0"] = String.fromCodePoint(0x24EA); + for (let i = 0; i < 9; i++) { + map[String.fromCharCode(49 + i)] = String.fromCodePoint(0x2460 + i); + } + return map; +} +export const CIRCLED_MAP: Record = buildCircledMap(); + // Reverse map: Unicode symbol → ASCII character export const UNICODE_TO_ASCII_MAP: Map = new Map( - [BOLD_MAP, ITALIC_MAP, BOLD_ITALIC_MAP].flatMap(m => + [BOLD_MAP, ITALIC_MAP, BOLD_ITALIC_MAP, FULLWIDTH_MAP, CIRCLED_MAP].flatMap(m => Object.entries(m).map(([ascii, unicode]) => [unicode, ascii] as [string, string]) ) ); -export type FontStyle = "bold" | "italic" | "bold-italic"; +export type FontStyle = "bold" | "italic" | "bold-italic" | "fullwidth" | "circled"; // Reverse map: Unicode symbol → font style export const UNICODE_TO_STYLE: Map = new Map([ ...Object.values(BOLD_MAP).map(ch => [ch, "bold"] as [string, FontStyle]), ...Object.values(ITALIC_MAP).map(ch => [ch, "italic"] as [string, FontStyle]), ...Object.values(BOLD_ITALIC_MAP).map(ch => [ch, "bold-italic"] as [string, FontStyle]), + ...Object.values(FULLWIDTH_MAP).map(ch => [ch, "fullwidth"] as [string, FontStyle]), + ...Object.values(CIRCLED_MAP).map(ch => [ch, "circled"] as [string, FontStyle]), ]);