diff --git a/manifest.json b/manifest.json index dfa940e..16b58df 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "sample-plugin", - "name": "Sample Plugin", + "id": "unicode-text-formatter", + "name": "Unicode Text Formatter", "version": "1.0.0", "minAppVersion": "0.15.0", - "description": "Demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", - "authorUrl": "https://obsidian.md", - "fundingUrl": "https://obsidian.md/pricing", + "description": "Format selected text as Unicode Sans-Serif Bold, Italic, or Bold-Italic for LinkedIn and Facebook posts.", + "author": "schihei", + "authorUrl": "", + "fundingUrl": "", "isDesktopOnly": false } diff --git a/src/commands.ts b/src/commands.ts new file mode 100644 index 0000000..88c5cca --- /dev/null +++ b/src/commands.ts @@ -0,0 +1,21 @@ +import { Plugin } from "obsidian"; +import { transformText, FormatStyle } from "./formatter"; + +function addFormatCommand(plugin: Plugin, style: FormatStyle, name: string) { + plugin.addCommand({ + id: `unicode-formatter:${style}`, + name, + editorCallback: (editor) => { + const selection = editor.getSelection(); + if (selection) { + editor.replaceSelection(transformText(selection, style)); + } + }, + }); +} + +export function registerCommands(plugin: Plugin): void { + addFormatCommand(plugin, "bold", "Format as Unicode Bold"); + addFormatCommand(plugin, "italic", "Format as Unicode Italic"); + addFormatCommand(plugin, "bold-italic", "Format as Unicode Bold Italic"); +} diff --git a/src/formatter.ts b/src/formatter.ts new file mode 100644 index 0000000..36a5908 --- /dev/null +++ b/src/formatter.ts @@ -0,0 +1,14 @@ +import { BOLD_MAP, ITALIC_MAP, BOLD_ITALIC_MAP } from "./unicode-maps"; + +export type FormatStyle = "bold" | "italic" | "bold-italic"; + +const STYLE_MAPS: Record> = { + "bold": BOLD_MAP, + "italic": ITALIC_MAP, + "bold-italic": BOLD_ITALIC_MAP, +}; + +export function transformText(text: string, style: FormatStyle): string { + const map = STYLE_MAPS[style]; + return [...text].map(ch => map[ch] ?? ch).join(""); +} diff --git a/src/main.ts b/src/main.ts index 6fe0c83..5aa22f8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,99 +1,11 @@ -import {App, Editor, MarkdownView, Modal, Notice, Plugin} from 'obsidian'; -import {DEFAULT_SETTINGS, MyPluginSettings, SampleSettingTab} from "./settings"; - -// Remember to rename these classes and interfaces! - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; +import { Plugin } from 'obsidian'; +import { registerCommands } from './commands'; +export default class UnicodeFormatterPlugin extends Plugin { async onload() { - await this.loadSettings(); - - // This creates an icon in the left ribbon. - this.addRibbonIcon('dice', 'Sample', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - }); - - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status bar text'); - - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-modal-simple', - name: 'Open modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'replace-selected', - name: 'Replace selected content', - editorCallback: (editor: Editor, view: MarkdownView) => { - editor.replaceSelection('Sample editor command'); - } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-modal-complex', - name: 'Open modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } - - // This command will only show up in Command Palette when the check function returns true - return true; - } - return false; - } - }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); - - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - new Notice("Click"); - }); - - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); - + registerCommands(this); } onunload() { } - - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial); - } - - async saveSettings() { - await this.saveData(this.settings); - } -} - -class SampleModal extends Modal { - constructor(app: App) { - super(app); - } - - onOpen() { - let {contentEl} = this; - contentEl.setText('Woah!'); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } } diff --git a/src/settings.ts b/src/settings.ts deleted file mode 100644 index 352121e..0000000 --- a/src/settings.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {App, PluginSettingTab, Setting} from "obsidian"; -import MyPlugin from "./main"; - -export interface MyPluginSettings { - mySetting: string; -} - -export const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; - - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - new Setting(containerEl) - .setName('Settings #1') - .setDesc('It\'s a secret') - .addText(text => text - .setPlaceholder('Enter your secret') - .setValue(this.plugin.settings.mySetting) - .onChange(async (value) => { - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); - } -} diff --git a/src/unicode-maps.ts b/src/unicode-maps.ts new file mode 100644 index 0000000..d3b5796 --- /dev/null +++ b/src/unicode-maps.ts @@ -0,0 +1,26 @@ +function buildMap( + upperStart: number, + lowerStart: number, + digitStart?: number +): Record { + const map: Record = {}; + for (let i = 0; i < 26; i++) { + map[String.fromCharCode(65 + i)] = String.fromCodePoint(upperStart + i); + map[String.fromCharCode(97 + i)] = String.fromCodePoint(lowerStart + i); + } + if (digitStart !== undefined) { + for (let i = 0; i < 10; i++) { + map[String.fromCharCode(48 + i)] = String.fromCodePoint(digitStart + i); + } + } + return map; +} + +// Sans-Serif Bold: U+1D5D4–U+1D5ED (upper), U+1D5EE–U+1D607 (lower), U+1D7EC–U+1D7F5 (digits) +export const BOLD_MAP: Record = buildMap(0x1D5D4, 0x1D5EE, 0x1D7EC); + +// Sans-Serif Italic: U+1D608–U+1D621 (upper), U+1D622–U+1D63B (lower) +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);