/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // main.ts var main_exports = {}; __export(main_exports, { default: () => RtlPlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian3 = require("obsidian"); // AutoDirPlugin.ts var import_view = require("@codemirror/view"); var import_state = require("@codemirror/state"); // globals.ts var RTL_CLASS = "is-rtl"; var AUTO_CLASS = "is-auto"; var STRONG_DIR_REGEX = /(?:([\p{sc=Arabic}\p{sc=Hebrew}\p{sc=Syriac}\p{sc=Thaana}])|([\p{sc=Armenian}\p{sc=Bengali}\p{sc=Bopomofo}\p{sc=Braille}\p{sc=Buhid}\p{sc=Canadian_Aboriginal}\p{sc=Cherokee}\p{sc=Cyrillic}\p{sc=Devanagari}\p{sc=Ethiopic}\p{sc=Georgian}\p{sc=Greek}\p{sc=Gujarati}\p{sc=Gurmukhi}\p{sc=Han}\p{sc=Hangul}\p{sc=Hanunoo}\p{sc=Hiragana}\p{sc=Inherited}\p{sc=Kannada}\p{sc=Katakana}\p{sc=Khmer}\p{sc=Lao}\p{sc=Latin}\p{sc=Limbu}\p{sc=Malayalam}\p{sc=Mongolian}\p{sc=Myanmar}\p{sc=Ogham}\p{sc=Oriya}\p{sc=Runic}\p{sc=Sinhala}\p{sc=Tagalog}\p{sc=Tagbanwa}\p{sc=Tamil}\p{sc=Telugu}\p{sc=Thai}\p{sc=Tibetan}\p{sc=Yi}]))/u; var detectDirection = (s) => { const match = s.match(STRONG_DIR_REGEX); if (match && match[1]) { return "rtl"; } else if (match && match[2]) { return "ltr"; } return null; }; // AutoDirPlugin.ts var import_obsidian = require("obsidian"); function getAutoDirectionPlugin(rtlPlugin) { return import_view.ViewPlugin.fromClass(class { constructor(view) { this.decorationRegions = []; this.active = false; this.rtlDec = import_view.Decoration.line({ attributes: { dir: "rtl" } }); this.ltrDec = import_view.Decoration.line({ attributes: { dir: "ltr" } }); this.emptyDirDec = import_view.Decoration.line({ attributes: { dir: "" } }); this.autoDec = import_view.Decoration.line({ attributes: { dir: "auto" } }); this.decorations = this.buildDecorations(); this.rtlPlugin = rtlPlugin; this.view = view; const editorInfo = this.view.state.field(import_obsidian.editorInfoField); if (editorInfo instanceof import_obsidian.MarkdownView) { this.rtlPlugin.adjustDirectionToView(editorInfo, this); } this.rtlPlugin.handleIframeEditor(this.view.dom, this.view, editorInfo.file, this); } update(vu) { if (vu.viewportChanged || vu.docChanged) { const regions = []; if (vu.docChanged) { vu.changes.iterChanges((fromA, toA, fromB, toB) => { const shift = toB - fromB - (toA - fromA); this.shiftDecorationRegions(shift < 0 ? toB : toA, shift); regions.push(...this.getLineRegions(vu.state.doc, fromB, toB)); }); } this.updateEx(vu.view, regions); } } destroy() { } setActive(active, view) { const forceUpdate = this.active !== active; this.active = active; this.decorations = this.buildDecorations(); if (forceUpdate) { this.updateEx(view); } } updateEx(view, regions = []) { if (regions.length === 0) { const { from, to } = view.viewport; regions = this.getLineRegions(view.state.doc, from, to); } for (const { from, to } of regions) { for (let pos = from; pos <= to; ) { const line = view.state.doc.lineAt(pos); let dec = this.emptyDirDec; if (this.active) { const s = view.state.doc.sliceString(line.from, line.to); const d = this.detectDecoration(s); dec = d ? d : this.lineBeforeDecoration(line.from); } this.addDecorationRegion({ from: line.from, to: line.to, dec }); pos = line.to + 1; } } this.decorations = this.buildDecorations(); } buildDecorations() { const builder = new import_state.RangeSetBuilder(); for (const dr of this.decorationRegions) { builder.add(dr.from, dr.from, dr.dec); } return builder.finish(); } addDecorationRegion(dr) { for (let i = 0; i < this.decorationRegions.length; i++) { if (this.decorationRegions[i].from < dr.from) { continue; } if (this.decorationRegions[i].from === dr.from) { this.decorationRegions[i] = dr; } else if (this.decorationRegions[i].from > dr.from) { this.decorationRegions.splice(i, 0, dr); } return; } this.decorationRegions.push(dr); } shiftDecorationRegions(from, amount) { if (amount === 0) { return; } for (let i = 0; i < this.decorationRegions.length; i++) { if (this.decorationRegions[i].from < from) { continue; } this.decorationRegions[i].from += amount; this.decorationRegions[i].to += amount; if (this.decorationRegions[i].from <= from) { this.decorationRegions.splice(i, 1); i--; } } } detectDecoration(s) { const direction = detectDirection(s.replace("- [x]", "")); switch (direction) { case "rtl": return this.rtlDec; case "ltr": return this.ltrDec; } return null; } lineBeforeDecoration(from, def = this.ltrDec) { const l = this.decorationRegions.length; if (l !== 0 && from > this.decorationRegions[l - 1].from) { return this.decorationRegions[l - 1].dec; } for (let i = 0; i < l; i++) { if (i !== 0 && this.decorationRegions[i].from >= from) { return this.decorationRegions[i - 1].dec; } } return def; } getLineRegions(doc, from, to) { const regions = []; for (let i = from; i <= to; i++) { const l = doc.lineAt(i); i = l.to; regions.push({ from: l.from, to: l.to }); } return regions; } }, { decorations: (v) => v.decorations }); } // AutoDirPostProcessor.ts var lastDetectedDir = "ltr"; var specialNodes = ["A", "STRONG", "EM", "DEL", "CODE"]; function breaksToDivs(el) { if (!el) return; if (el.tagName == "P") { const splitText = el.innerHTML.split("
"); if (splitText.length > 1) { let newInnerHtml = ""; splitText.map((line) => { newInnerHtml += `
${line}
`; }); el.innerHTML = newInnerHtml; } } if (el.children && el.children.length > 0) { for (let i = 0; i < el.children.length; i++) breaksToDivs(el.children[i]); } } function detectCanvasElement(el, ctx, setPreviewDirection) { const container = ctx.containerEl; if (container && container.closest) { const possibleCanvas = container.closest(".canvas-node-content"); if (possibleCanvas) { const markdownPreview = container.closest(".markdown-preview-view"); if (markdownPreview && markdownPreview instanceof HTMLDivElement) { setPreviewDirection(ctx.sourcePath, markdownPreview); } } } } var autoDirectionPostProcessor = (el, ctx, setPreviewDirection) => { let shouldAddDir = false, addedDir = false; const childNodes = []; detectCanvasElement(el, ctx, setPreviewDirection); breaksToDivs(el); for (let i = 0; i < el.childNodes.length; i++) { const n = el.childNodes[i]; if (!addedDir && n.nodeName === "#text" && n.nodeValue && n.nodeValue !== "\n") { const dir = detectDirection(n.nodeValue); if (dir) { addedDir = true; lastDetectedDir = dir; if (specialNodes.contains(el.nodeName) && el.parentElement) { addDirClassIfNotAddedBefore(el.parentElement, dirClass(dir)); } else { el.addClass(dirClass(dir)); if (el.parentElement && el.parentElement.nodeName === "LI") { addDirClassIfNotAddedBefore(el.parentElement, dirClass(dir)); } } } shouldAddDir = true; continue; } childNodes.push(n); if (i === el.childNodes.length - 1 && shouldAddDir && !addedDir) { el.addClass(dirClass(lastDetectedDir)); } } for (let i = 0; i < childNodes.length; i++) { autoDirectionPostProcessor(childNodes[i], ctx, setPreviewDirection); } if (el.nodeName === "UL") { const lis = el.querySelectorAll("li"); if (lis.length > 0 && lis[0].hasClass("esm-rtl")) { el.addClass(dirClass("rtl")); } } }; function dirClass(dir) { if (dir === "rtl") { return "esm-rtl"; } else { return "esm-ltr"; } } function addDirClassIfNotAddedBefore(el, cls) { if (el.hasClass("esm-rtl") || el.hasClass("esm-ltr")) { return; } el.addClass(cls); } // main.ts var import_view2 = require("@codemirror/view"); // settingsTab.ts var import_obsidian2 = require("obsidian"); var DEFAULT_SETTINGS = { fileDirections: {}, defaultDirection: "ltr", rememberPerFile: true, setNoteTitleDirection: true, setYamlDirection: false, statusBar: true }; var RtlSettingsTab = class extends import_obsidian2.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; this.settings = plugin.settings; } display() { let { containerEl } = this; containerEl.empty(); containerEl.createEl("h2", { text: "RTL Settings" }); this.plugin.syncDefaultDirection(); new import_obsidian2.Setting(containerEl).setName("Remember text direction per file").setDesc("Store and remember the text direction used for each file individually.").addToggle((toggle) => toggle.setValue(this.settings.rememberPerFile).onChange((value) => { this.settings.rememberPerFile = value; this.plugin.saveSettings(); this.plugin.adjustDirectionToActiveView(); })); new import_obsidian2.Setting(containerEl).setName("Default text direction").setDesc("What should be the default text direction in Obsidian?").addDropdown((dropdown) => dropdown.addOption("ltr", "LTR").addOption("rtl", "RTL").addOption("auto", "Auto").setValue(this.settings.defaultDirection).onChange((value) => { this.settings.defaultDirection = value; this.app.vault.setConfig("rightToLeft", value == "rtl"); this.plugin.saveSettings(); this.plugin.adjustDirectionToActiveView(); })); new import_obsidian2.Setting(containerEl).setName("Set note title direction").setDesc("In RTL notes, also set the direction of the note title.").addToggle((toggle) => toggle.setValue(this.settings.setNoteTitleDirection).onChange((value) => { this.settings.setNoteTitleDirection = value; this.plugin.saveSettings(); this.plugin.adjustDirectionToActiveView(); })); new import_obsidian2.Setting(containerEl).setName("Set YAML direction in Preview").setDesc("For RTL notes, preview YAML blocks as RTL. (When turning off, restart of Obsidian is required.)").addToggle((toggle) => { var _a; return toggle.setValue((_a = this.settings.setYamlDirection) != null ? _a : false).onChange((value) => { this.settings.setYamlDirection = value; this.plugin.saveSettings(); this.plugin.adjustDirectionToActiveView(); }); }); new import_obsidian2.Setting(containerEl).setName("Show status bar item").setDesc("Show a clickable status bar item showing the current direction.").addToggle((toggle) => { var _a; return toggle.setValue((_a = this.settings.statusBar) != null ? _a : true).onChange((value) => { this.settings.statusBar = value; this.plugin.saveSettings(); this.plugin.adjustDirectionToActiveView(); }); }); } }; // main.ts var RtlPlugin = class extends import_obsidian3.Plugin { constructor() { super(...arguments); this.settings = null; this.statusBarItem = null; this.statusBarText = null; } async onload() { this.addCommand({ id: "switch-text-direction", name: "Switch Text Direction (LTR->RTL->auto)", icon: "arrow-left-right", callback: () => { const view = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView); if (!view || !(view == null ? void 0 : view.editor)) return; this.switchDocumentDirection(view.editor, view); } }); this.autoDirectionPlugin = getAutoDirectionPlugin(this); this.registerEditorExtension(this.autoDirectionPlugin); this.registerEditorExtension(import_view2.EditorView.perLineTextDirection.of(true)); this.registerMarkdownPostProcessor((el, ctx) => { autoDirectionPostProcessor(el, ctx, (path, markdownPreviewElement) => this.setCanvasPreviewDirection(path, markdownPreviewElement)); }); await this.convertLegacySettings(); await this.loadSettings(); this.addSettingTab(new RtlSettingsTab(this.app, this)); this.app.workspace.on("active-leaf-change", async (leaf) => { this.adjustDirectionToActiveView(); this.updateStatusBar(); }); this.app.workspace.on("file-open", async (file, ctx) => { this.adjustDirectionToActiveView(); this.updateStatusBar(); }); this.registerEvent(this.app.vault.on("delete", (file) => { if (file && file.path && file.path in this.settings.fileDirections) { delete this.settings.fileDirections[file.path]; this.saveSettings(); } })); this.registerEvent(this.app.vault.on("rename", (file, oldPath) => { if (file && file.path && oldPath in this.settings.fileDirections) { this.settings.fileDirections[file.path] = this.settings.fileDirections[oldPath]; delete this.settings.fileDirections[oldPath]; this.saveSettings(); } })); this.statusBarItem = this.addStatusBarItem(); const languageIcon = (0, import_obsidian3.getIcon)("arrow-left-right"); this.statusBarItem.appendChild(languageIcon); this.statusBarText = this.statusBarItem.createEl("span"); this.statusBarText.style.marginLeft = "5px"; this.statusBarItem.title = "Text direction"; this.statusBarItem.addClass("mod-clickable"); this.statusBarItem.addEventListener("click", (_ev) => { const view = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView); if (!view || !(view == null ? void 0 : view.editor)) return; this.switchDocumentDirection(view.editor, view); }); } onunload() { const view = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView); if (view && (view == null ? void 0 : view.editor)) { const editorView = view.editor.cm; this.adjustAutoDirection(editorView, "ltr"); } console.log("unloading RTL plugin"); } adjustDirectionToActiveView() { const view = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView); if (!view) return; this.adjustDirectionToView(view); } adjustDirectionToView(view, autoDirectionPlugin) { if (!view) return; this.syncDefaultDirection(); const file = view == null ? void 0 : view.file; const editor = view == null ? void 0 : view.editor; const editorView = editor == null ? void 0 : editor.cm; if (file && file.path && editorView) { const [requiredDirection, _usedDefault] = this.getRequiredFileDirection(file); this.setMarkdownViewDirection(view, editor, editorView, requiredDirection, autoDirectionPlugin); } } getRequiredFileDirection(file) { if (!file) { return [this.settings.defaultDirection, true]; } if (!(file instanceof import_obsidian3.TFile)) return null; let requiredDirection = null; const frontMatterDirection = this.getFrontMatterDirection(file); let usedDefault = false; if (frontMatterDirection) { if (frontMatterDirection == "rtl" || frontMatterDirection == "ltr" || frontMatterDirection == "auto") requiredDirection = frontMatterDirection; else console.log("Front matter direction in file", file.path, "is unknown:", frontMatterDirection); } else if (this.settings.rememberPerFile && file.path in this.settings.fileDirections) { requiredDirection = this.settings.fileDirections[file.path]; } else { requiredDirection = this.settings.defaultDirection; usedDefault = true; } return [requiredDirection, usedDefault]; } async saveSettings() { await this.saveData(this.settings); } async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async convertLegacySettings() { const legacySettingsPath = ".obsidian/rtl.json"; if (await this.app.vault.adapter.exists(legacySettingsPath)) { const legacyContent = await this.app.vault.adapter.read(legacySettingsPath); if (legacyContent) { this.settings = JSON.parse(legacyContent); } this.app.vault.adapter.remove(legacySettingsPath); new import_obsidian3.Notice("RTL Plugin: legacy settings were converted to the new format"); this.saveSettings(); } } updateStatusBar() { let hide = true; let usedDefault = false; const view = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView); if (view && (view == null ? void 0 : view.editor)) { const direction = this.getDocumentDirection(view.editor, view); if (view.file && view.file.path) [, usedDefault] = this.getRequiredFileDirection(view.file); if (this.settings.statusBar) { let directionString = direction === "auto" ? "auto" : direction === "ltr" ? "LTR" : "RTL"; let statusString = ""; if (usedDefault) statusString = `Default (${direction})`; else { if (direction === "auto") statusString = "Auto"; else statusString = directionString; } this.statusBarText.textContent = statusString; this.statusBarItem.style.display = null; hide = false; } } if (hide) this.hideStatusBar(); } hideStatusBar() { this.statusBarItem.style.display = "none"; } handleIframeEditor(editorDiv, editorView, file, autoDirectionPlugin) { const isInIframe = editorDiv.closest(".mod-inside-iframe"); if (isInIframe) { if (editorDiv instanceof HTMLDivElement) { const [requiredDirection, _] = this.getRequiredFileDirection(file); this.adjustAutoDirection(editorView, requiredDirection, autoDirectionPlugin); this.setDocumentDirectionForEditorDiv(editorDiv, requiredDirection); } } } setMarkdownViewDirection(view, editor, editorView, newDirection, autoDirectionPlugin) { if (!view || !editor) { this.hideStatusBar(); return; } let title = editorView.dom.querySelector(".inline-title"); if (!title) { title = view.previewMode.containerEl.querySelector(".inline-title"); } title == null ? void 0 : title.setAttribute("dir", newDirection === "auto" ? "auto" : ""); this.adjustAutoDirection(editorView, newDirection, autoDirectionPlugin); const editorDivs = view.contentEl.getElementsByClassName("cm-editor"); for (const editorDiv of editorDivs) { if (editorDiv instanceof HTMLDivElement) this.setDocumentDirectionForEditorDiv(editorDiv, newDirection); } const markdownPreviews = view.contentEl.getElementsByClassName("markdown-preview-view"); for (const preview of markdownPreviews) { if (preview instanceof HTMLDivElement) this.setDocumentDirectionForReadingDiv(preview, newDirection); } if (this.settings.setNoteTitleDirection) { const container = view.containerEl.parentElement; let header = container.getElementsByClassName("view-header-title-container"); header[0].style.direction = newDirection; } editor.refresh(); if (newDirection !== "auto") { this.setExportDirection(newDirection); } } adjustAutoDirection(editorView, newDirection, autoDirectionPlugin) { const autoDirection = autoDirectionPlugin != null ? autoDirectionPlugin : editorView.plugin(this.autoDirectionPlugin); if (autoDirection) { autoDirection.setActive(newDirection === "auto", editorView); if (!autoDirectionPlugin) editorView.dispatch(); } } setDocumentDirectionForEditorDiv(editorDiv, newDirection) { editorDiv.style.direction = newDirection === "auto" ? "" : newDirection; this.addDirectionClassToEl(editorDiv.parentElement, newDirection); } setDocumentDirectionForReadingDiv(readingDiv, newDirection) { readingDiv.style.direction = newDirection === "auto" ? "" : newDirection; this.addDirectionClassToEl(readingDiv, newDirection); readingDiv.classList.remove("rtl-yaml"); if (newDirection !== "auto" && this.settings.setYamlDirection) readingDiv.classList.add("rtl-yaml"); } setCanvasPreviewDirection(path, markdownPreviewElement) { const file = this.app.vault.getAbstractFileByPath(path); const [requiredDirection, _] = this.getRequiredFileDirection(file); this.setDocumentDirectionForReadingDiv(markdownPreviewElement, requiredDirection); } addDirectionClassToEl(el, direction) { switch (direction) { case "rtl": el.classList.remove(AUTO_CLASS); el.classList.add(RTL_CLASS); break; case "auto": el.classList.remove(RTL_CLASS); el.classList.add(AUTO_CLASS); break; default: el.classList.remove(RTL_CLASS); el.classList.remove(AUTO_CLASS); } } setExportDirection(newDirection) { this.replacePageStyleByString("searched and replaced", `/* This is searched and replaced by the plugin */ @media print { body { direction: ${newDirection}; } }`, true); } replacePageStyleByString(searchString, newStyle, addIfNotFound) { let alreadyExists = false; let style = this.findPageStyle(searchString); if (style) { if (style.getText() === searchString) alreadyExists = true; else style.setText(newStyle); } else if (addIfNotFound) { let style2 = document.createElement("style"); style2.textContent = newStyle; document.head.appendChild(style2); } return style && !alreadyExists; } findPageStyle(regex) { let styles = document.head.getElementsByTagName("style"); for (let style of styles) { if (style.getText().match(regex)) return style; } return null; } switchDocumentDirection(editor, view) { let newDirection = this.getDocumentDirection(editor, view); if (newDirection === null) { new import_obsidian3.Notice("Obsidian RTL can't set the direction of this document"); return; } let displayName = ""; switch (newDirection) { case "ltr": newDirection = "rtl"; displayName = "RTL"; break; case "rtl": newDirection = "auto"; displayName = "Auto"; break; case "auto": newDirection = "ltr"; displayName = "LTR"; break; } if (view instanceof import_obsidian3.MarkdownView) { const editorView = view.editor.cm; this.setMarkdownViewDirection(view, editor, editorView, newDirection); if (this.settings.rememberPerFile && view.file && view.file.path) { this.settings.fileDirections[view.file.path] = newDirection; this.saveSettings(); } new import_obsidian3.Notice(`Document direction set to ${displayName}`, 2e3); this.updateStatusBar(); } else { const canvasView = this.getCanvasContext(view); if (canvasView) { if (view.file) new import_obsidian3.Notice("To change a canvas card direction, open the document separately and reload the canvas."); else new import_obsidian3.Notice("Can't change the direction of a card without a file."); } } } getCanvasContext(ctx) { if (ctx instanceof import_obsidian3.MarkdownView) return null; const possibleCanvasContainer = ctx == null ? void 0 : ctx.containerEl; if (possibleCanvasContainer && possibleCanvasContainer.hasClass("canvas-node-content")) return possibleCanvasContainer; } getDocumentDirection(_editor, ctx) { let refElement = null; if (ctx instanceof import_obsidian3.MarkdownView) { refElement = ctx.contentEl; } else { refElement = this.getCanvasContext(ctx); } if (refElement === null) return null; const rtlEditors = refElement.getElementsByClassName(RTL_CLASS), autoEditors = refElement.getElementsByClassName(AUTO_CLASS); if (rtlEditors.length > 0) return "rtl"; else if (autoEditors.length > 0) return "auto"; else return "ltr"; } getFrontMatterDirection(file) { const fileCache = this.app.metadataCache.getFileCache(file); const frontMatter = fileCache == null ? void 0 : fileCache.frontmatter; if (frontMatter && (frontMatter == null ? void 0 : frontMatter.direction)) { try { const direction = frontMatter.direction; return direction; } catch (error) { } } } syncDefaultDirection() { const obsidianDirection = this.app.vault.getConfig("rightToLeft") ? "rtl" : "ltr"; if (obsidianDirection != this.settings.defaultDirection && this.settings.defaultDirection !== "auto") { this.settings.defaultDirection = obsidianDirection; this.saveSettings(); } } };