/* 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: () => FastTextColorPlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian8 = require("obsidian"); // src/FastTextColorSettings.ts var import_obsidian4 = require("obsidian"); // src/color/TextColor.ts var TextColor = class { /** * Create a basic Text Color * * @param {string} color - [TODO:description] * @param {string} id - the id or name of the color * @param {string} themeName - the associated theme that this color belongs to * @param {boolean} [italic] - italic text * @param {boolean} [bold] - bold text * @param {number} [cap_mode_index] - the index for the cap mode * @param {number} [line_mode_index] - the index for the line mode * @param {string} [keybind] - the associated keybind * @param {string} colorVariable - the builtin Css color variable that this color uses. */ constructor(color, id, themeName, italic = false, bold = false, cap_mode_index = 0, line_mode_index = 0, keybind = "", useCssColorVariable = false, colorVariable = "--color-base-00") { this.color = color; this.id = id; this.keybind = keybind; this.italic = italic; this.bold = bold; this.cap_mode = new CycleState(["normal", "all_caps", "small_caps"], cap_mode_index); this.line_mode = new CycleState(["none", "underline", "overline", "line-through"], line_mode_index); this.useCssColorVariable = useCssColorVariable; this.colorVariable = colorVariable; this.className = `${CSS_COLOR_PREFIX}${themeName}-${this.id}`; } getCssClass() { return `.${CSS_COLOR_PREFIX}${this.id} { color : ${this.useCssColorVariable ? `var(${this.colorVariable})` : this.color} ; ${this.italic ? "font-style: italic;\n" : ""} ${this.bold ? "font-weight: bold;\n" : ""} ${this.line_mode.state != "none" ? `text-decoration: ${this.line_mode.state}; ` : ""} ${this.cap_mode.state == "all_caps" ? "text-transform: uppercase;\n" : this.cap_mode.state == "small_caps" ? "font-variant: small-caps;\n" : ""} ${VAR_COLOR_PREFIX}${this.id} : ${this.color}; }`; } /** * get the inner css of the class for the color. * * @returns {string} the inner css. */ getInnerCss() { return `color : ${this.useCssColorVariable ? `var(${this.colorVariable})` : this.color}; ${this.italic ? "font-style: italic;\n" : ""}${this.bold ? "font-weight: bold;\n" : ""}${this.line_mode.state != "none" ? `text-decoration: ${this.line_mode.state}; ` : ""}${this.cap_mode.state == "all_caps" ? "text-transform: uppercase;\n" : this.cap_mode.state == "small_caps" ? "font-variant: small-caps;\n" : ""}`; } getCssInlineStyle() { return `color : ${this.useCssColorVariable ? `var(${this.colorVariable})` : this.color} ; ${this.italic ? "font-style: italic;" : ""} ${this.bold ? "font-weight: bold;" : ""} ${this.line_mode.state != "none" ? `text-decoration: ${this.line_mode.state};` : ""} ${this.cap_mode.state == "all_caps" ? "text-transform: uppercase;" : this.cap_mode.state == "small_caps" ? "font-variant: small-caps;" : ""} `; } }; var CycleState = class { constructor(states, index = 0) { this.states = states; if (states.length <= 0) { this.state = "error"; return; } this.state = this.states[index]; this.index = index; } cycle() { this.index = (this.index + 1) % this.states.length; this.state = this.states[this.index]; } }; // src/color/TextColorTheme.ts var TextColorTheme = class { constructor(name, colors) { this.colors = colors; this.name = name; } }; // src/utils/ConfirmationModal.ts var import_obsidian = require("obsidian"); var ConfirmationModal = class extends import_obsidian.Modal { constructor(app, message, heading) { super(app); this.message = message; this.heading = heading; this.confirmed = false; this.finished = false; } onOpen() { const { contentEl } = this; contentEl.createEl("h1", { text: this.heading }); contentEl.createDiv({ text: this.message }); new import_obsidian.Setting(contentEl).addButton((btn) => btn.setButtonText("OK").setCta().onClick(() => { this.finished = true; this.confirmed = true; })).addButton((btn) => btn.setButtonText("Cancel").setCta().onClick(() => { this.finished = true; })); } onClose() { let { contentEl } = this; contentEl.empty(); } }; async function confirmByModal(app, message = "", heading = "") { let modal = new ConfirmationModal(app, message != "" ? message : "Are you sure?", heading != "" ? heading : "Confirm"); modal.open(); while (!modal.finished) { await sleep(16); } let result = modal.confirmed; modal.close(); return result; } // src/utils/CreateNewThemeModal.ts var import_obsidian2 = require("obsidian"); var CreateNewThemeModal = class extends import_obsidian2.Modal { constructor(app, settings) { super(app); this.settings = settings; this.name = "newTheme"; } onOpen() { const { contentEl } = this; contentEl.createEl("h1", { text: "Create new theme" }); new import_obsidian2.Setting(contentEl).setName(this.name).addText((txt) => { txt.setValue(this.name).setPlaceholder("Theme name").onChange((value) => { this.name = value; this.evalNameErrors(); }); }); this.errorDiv = contentEl.createDiv(); this.errorDiv.addClass("ftc-theme-name-error"); new import_obsidian2.Setting(contentEl).addButton((btn) => { btn.setButtonText("create").onClick((evt) => { if (!this.evalNameErrors()) { return; } addTheme(this.settings, this.name, DEFAULT_COLORS); this.doOnSuccess(); this.close(); }); }).addButton((btn) => { btn.setButtonText("cancel").onClick((evt) => { this.close(); }); }); } onClose() { let { contentEl } = this; contentEl.empty(); } onSuccess(callback) { this.doOnSuccess = callback; } evalNameErrors() { if (this.settings.themes.some((theme) => { return theme.name == this.name; })) { this.errorDiv.innerText = "a theme with this name already exists."; return false; } if (this.name == "") { this.errorDiv.innerText = "name can not be empty."; return false; } this.errorDiv.innerText = ""; return true; } }; // src/utils/KeyBindModal.ts var import_obsidian3 = require("obsidian"); var KeyBindModal = class extends import_obsidian3.Modal { constructor(app) { super(app); this.finished = false; } onOpen() { const { contentEl } = this; contentEl.createEl("h1", { text: "Press any key" }); this.handler = this.handleKeypress.bind(this); window.addEventListener("keypress", this.handler); new import_obsidian3.Setting(contentEl).addButton((btn) => { btn.setButtonText("Cancel").onClick((evt) => { this.close(); }); }); } onClose() { let { contentEl } = this; contentEl.empty(); } handleKeypress(evt) { this.result = evt.key.toUpperCase(); this.modalEl.removeEventListener("keypress", this.handler, true); this.finished = true; } }; async function getKeyBindWithModal(app) { let modal = new KeyBindModal(app); modal.open(); while (!modal.finished) { await sleep(16); } let result = modal.result; modal.close(); return result; } // src/FastTextColorSettings.ts var CSS_COLOR_PREFIX = "ftc-color-"; var VAR_COLOR_PREFIX = "--ftc-color-"; var SETTINGS_VERSION = "3"; var BUILTIN_COLORS = [ new TextColor("#000000", "red", "builtin", false, false, 0, 0, "R", true, "--color-red"), new TextColor("#000000", "orange", "builtin", false, false, 0, 0, "O", true, "--color-orange"), new TextColor("#000000", "yellow", "builtin", false, false, 0, 0, "Y", true, "--color-yellow"), new TextColor("#000000", "green", "builtin", false, false, 0, 0, "G", true, "--color-green"), new TextColor("#000000", "cyan", "builtin", false, false, 0, 0, "C", true, "--color-cyan"), new TextColor("#000000", "blue", "builtin", false, false, 0, 0, "B", true, "--color-blue"), new TextColor("#000000", "purple", "builtin", false, false, 0, 0, "P", true, "--color-purple"), new TextColor("#000000", "pink", "builtin", false, false, 0, 0, "I", true, "--color-pink") ]; var DEFAULT_COLORS = [ new TextColor("#ff0000", `red`, "default", false, false, 0, 0, "R"), new TextColor("#00ff00", `green`, "default", false, false, 0, 0, "G"), new TextColor("#0000ff", `blue`, "default", false, false, 0, 0, "B"), new TextColor("#00ffff", `cyan`, "default", false, false, 0, 0, "C"), new TextColor("#ff00ff", `magenta`, "default", false, false, 0, 0, "M"), new TextColor("#ffff00", `yellow`, "default", false, false, 0, 0, "Y"), new TextColor("#000000", `black`, "default", false, false, 0, 0, "K") ]; var DEFAULT_SETTINGS = { themes: [new TextColorTheme("default", DEFAULT_COLORS), new TextColorTheme("builtin", BUILTIN_COLORS)], themeIndex: 0, version: SETTINGS_VERSION, interactiveDelimiters: true, useKeybindings: true, useNodeRebuilding: false }; function getColors(settings, index = -1) { if (index == -1) { index = settings.themeIndex; } return settings.themes[index].colors; } function getCurrentTheme(settings, index = -1) { if (index == -1) { index = settings.themeIndex; } return settings.themes[index]; } function addTheme(settings, name, colors = DEFAULT_COLORS) { settings.themes.push(new TextColorTheme(name, colors)); } function deleteTheme(settings, index = -1) { if (settings.themes.length <= 1) { return; } if (index == -1) { index = settings.themeIndex; } settings.themes.remove(settings.themes[index]); if (index <= settings.themeIndex) { settings.themeIndex = Math.max(settings.themeIndex - 1, 0); } } function updateSettings(settings) { switch (settings.version) { case "1": case "2": const colors = settings.colors.map((color) => { return new TextColor(color.color, color.id, "default", color.italic, color.bold, color.cap_mode.index, color.line_mode.index, color.keybind); }); const outSettings = { themes: [new TextColorTheme("default", colors)], themeIndex: 0, version: SETTINGS_VERSION, interactiveDelimiters: settings.interactiveDelimiters, useKeybindings: true, useNodeRebuilding: false }; return outSettings; default: console.log(`There is not update method for Settings Version ${settings.version}! ${settings}`); return DEFAULT_SETTINGS; } } var FastTextColorPluginSettingTab = class extends import_obsidian4.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; this.newId = ""; this.editThemeIndex = plugin.settings.themeIndex; } display() { const { containerEl } = this; const { settings } = this.plugin; containerEl.empty(); new import_obsidian4.Setting(containerEl).setName("Colors").setHeading(); new import_obsidian4.Setting(containerEl).setName("Set active theme").setDesc("Set global active theme.").addDropdown((dd) => { let count2 = 0; settings.themes.forEach((theme) => { dd.addOption(count2.toString(), theme.name); count2++; }); dd.setValue(settings.themeIndex.toString()); dd.onChange((value) => { settings.themeIndex = +value; this.plugin.saveSettings(); this.plugin.setCssVariables(); this.display(); }); }); new import_obsidian4.Setting(containerEl).setName("Edit themes").setDesc("Add new themes or edit existings ones.").setClass("ftc-settings-theme-header").addDropdown((dd) => { let count2 = 0; settings.themes.forEach((theme) => { dd.addOption(count2.toString(), theme.name); count2++; }); dd.setValue(this.editThemeIndex.toString()); dd.onChange((value) => { this.editThemeIndex = +value; this.display(); }); }).addButton((btn) => { btn.setIcon("plus").setTooltip("add new Theme").onClick((evt) => { const modal = new CreateNewThemeModal(this.app, settings); modal.onSuccess(() => { this.plugin.saveSettings(); this.display(); }); modal.open(); }); }).addButton((btn) => { btn.setIcon("trash").setTooltip("delete theme").onClick(async (evt) => { if (await confirmByModal(this.app, `Are you sure? The theme ${settings.themes[settings.themeIndex].name} will no longer be available. `)) { deleteTheme(settings, this.editThemeIndex); this.editThemeIndex = 0; this.plugin.saveSettings(); this.display(); } }); }); const themeColorsEl = containerEl.createDiv(); themeColorsEl.addClass("ftc-theme-colors"); let count = 1; getColors(settings, this.editThemeIndex).forEach((color) => { this.createColorSetting(themeColorsEl, color, count); count++; }); new import_obsidian4.Setting(containerEl).setName("Add new color to theme").setClass("ftc-settings-theme-footer").addText((txt) => { txt.setValue(this.newId == "" ? (getColors(settings).length + 1).toString() : this.newId).onChange((value) => { this.newId = value; }); }).addButton((btn) => { btn.setButtonText("+").onClick(async (evt) => { let colors = getColors(settings, this.editThemeIndex); if (colors.some((tColor) => { return tColor.id == this.newId; })) { new import_obsidian4.Notice(`color with id ${this.newId} already exists!`); } let newColorName = this.newId == "" ? (colors.length + 1).toString() : this.newId; colors.push(new TextColor("#ffffff", newColorName, getCurrentTheme(settings, this.editThemeIndex).name)); await this.plugin.saveSettings(); this.display(); }); }); new import_obsidian4.Setting(containerEl).setName("Other").setHeading(); new import_obsidian4.Setting(containerEl).setName("Interactive delimiters").setDesc("Use interactive delimiter to change colors inside the editor.").addToggle((tgl) => { tgl.setValue(settings.interactiveDelimiters).onChange(async (value) => { settings.interactiveDelimiters = value; await this.plugin.saveSettings(); }); }); new import_obsidian4.Setting(containerEl).setName("Use keybindings and colormenu").setDesc("If enabled will allow you to use keybindings to activate colors from a custom colormenu.").addToggle((tgl) => { tgl.setValue(settings.useKeybindings).onChange(async (value) => { settings.useKeybindings = value; await this.plugin.saveSettings(); }); }); } /** * Create a color row in the theme view * * @param {HTMLElement} container - the root container of the element. * @param {TextColor} tColor - the color to be used for display * @param {number} count - the index of the color */ createColorSetting(container, tColor, count) { let nameFragment = new DocumentFragment(); let nameDiv = nameFragment.createDiv(); nameDiv.addClass("ftc-name-div"); const key = nameDiv.createDiv(); key.innerText = `${tColor.id}`; const exampletext = nameDiv.createDiv(); exampletext.setAttr("style", tColor.getCssInlineStyle()); exampletext.innerText = `~={${tColor.id}}This is colored text=~`; let saveAndApply = async () => { await this.plugin.saveSettings(); this.plugin.setCssVariables(); exampletext.setAttr("style", tColor.getCssInlineStyle()); }; const setting = new import_obsidian4.Setting(container).setName(nameFragment).setClass("ftc-settings-item").addButton((btn) => { btn.setButtonText(`${tColor.keybind}`.toUpperCase()).setTooltip("set keybinding").setClass("key-indicator").onClick(async (evt) => { tColor.keybind = await getKeyBindWithModal(this.app); btn.setButtonText(`${tColor.keybind}`); await this.plugin.saveSettings(); this.plugin.setCssVariables(); }); }).addButton((btn) => { btn.setButtonText("B").setTooltip("Bold").setClass("ftc-format-item").onClick(async (evt) => { tColor.bold = !tColor.bold; btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.bold); btn.buttonEl.setCssStyles({ fontWeight: tColor.bold ? "bold" : "normal" }); saveAndApply(); }); btn.buttonEl.addClass("ftc-format-left"); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.bold); btn.buttonEl.setCssStyles({ fontWeight: tColor.bold ? "bold" : "normal" }); }).addButton((btn) => { btn.setButtonText("I").setTooltip("Italic").setClass("ftc-format-item").onClick(async (evt) => { tColor.italic = !tColor.italic; btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.italic); btn.buttonEl.setCssStyles({ fontStyle: tColor.italic ? "italic" : "normal" }); saveAndApply(); }); btn.buttonEl.addClass("ftc-format-middle"); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.italic); btn.buttonEl.setCssStyles({ fontStyle: tColor.italic ? "italic" : "normal" }); }).addButton((btn) => { btn.setButtonText("U").setTooltip("Lining").setClass("ftc-format-item").onClick(async (evt) => { tColor.line_mode.cycle(); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.line_mode.state != "none"); btn.buttonEl.setCssStyles({ textDecoration: tColor.line_mode.state }); saveAndApply(); }); btn.buttonEl.addClass("ftc-format-middle"); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.line_mode.state != "none"); btn.buttonEl.setCssStyles({ textDecoration: tColor.line_mode.state }); }).addButton((btn) => { btn.setButtonText("Tt").setTooltip("Capitalization").setClass("ftc-format-item").onClick(async (evt) => { tColor.cap_mode.cycle(); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.cap_mode.state != "normal"); btn.buttonEl.toggleClass("ftc-uppercase", tColor.cap_mode.state == "all_caps"); btn.buttonEl.toggleClass("ftc-small-caps", tColor.cap_mode.state == "small_caps"); await this.plugin.saveSettings(); this.plugin.setCssVariables(); exampletext.setAttr("style", tColor.getCssInlineStyle()); }); btn.buttonEl.addClass("ftc-format-right"); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.cap_mode.state != "normal"); btn.buttonEl.setCssStyles( tColor.cap_mode.state == "all_caps" ? { textTransform: "uppercase" } : tColor.cap_mode.state == "small_caps" ? { fontVariant: "small_caps" } : {} ); }); if (tColor.useCssColorVariable) { setting.addDropdown((dd) => { dd.addOptions({ "--color-red": "red", "--color-orange": "orange", "--color-yellow": "yellow", "--color-green": "green", "--color-cyan": "cyan", "--color-blue": "blue", "--color-purple": "purple", "--color-pink": "pink" // "--color-blue" : "50", // "--color-blue" : "60", // "--color-blue" : "70", // "--color-base-100": "100", }).setValue(tColor.colorVariable).onChange((value) => { tColor.colorVariable = value; saveAndApply(); }); }); } else { setting.addColorPicker((cb) => { cb.setValue(tColor.color).onChange(async (value) => { tColor.color = value; saveAndApply(); }); }); } setting.addButton((btn) => { btn.setTooltip("use builtin obsidian colors").setClass("ftc-format-item-small").onClick(async (evt) => { tColor.useCssColorVariable = !tColor.useCssColorVariable; btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.useCssColorVariable); saveAndApply(); this.display(); }); btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.useCssColorVariable); }).addButton((btn) => { btn.setIcon("chevron-up").setTooltip("move item up").setClass("ftc-move-btn-left").onClick(async (evt) => { moveColor(count - 1, -1, this.plugin.settings); await this.plugin.saveSettings(); this.display(); }); }).addButton((btn) => { btn.setIcon("chevron-down").setTooltip("move item down").setClass("ftc-move-btn-right").onClick(async (evt) => { moveColor(count - 1, 1, this.plugin.settings); await this.plugin.saveSettings(); this.display(); }); }).addButton((btn) => { btn.setIcon("trash").setTooltip("delete color").setClass("ftc-move-btn-right").onClick(async (evt) => { if (await confirmByModal( this.app, `Colored section whith the id "${tColor.id}" will no longer be colored until you add another color with that id.`, `Delete color: ${tColor.id}` )) { getColors(this.plugin.settings).remove(tColor); } await this.plugin.saveSettings(); this.display(); }); }); } }; function moveColor(index, direction, settings) { if (direction < 0 && index == 0 || direction > 0 && index == getColors(settings).length - 1) { return; } let temp = getColors(settings)[index + direction]; getColors(settings)[index + direction] = getColors(settings)[index]; getColors(settings)[index] = temp; } // src/rendering/TextColorViewPlugin.ts var import_state3 = require("@codemirror/state"); var import_view3 = require("@codemirror/view"); // src/rendering/TextColorStateField.ts var import_state = require("@codemirror/state"); // src/rendering/language/textColorLanguageParser.js var import_lr = require("@lezer/lr"); var parser = import_lr.LRParser.deserialize({ version: 14, states: "%dQQOPOOOcOQO'#C_OqOPO'#C^O!SOPO'#CmOOOO'#Co'#CoQQOPOOOOOO'#Ca'#CaO!eOQO,59UOOOO,58y,58yOOOO,59U,59UO!pOQO'#C_OOOO'#Cp'#CpO!xOPO'#CeOOOO'#Cf'#CfO#ZOPO'#CqO#lOPO'#CdO#lOPO'#CdO#zOPO'#C^OOOO,58x,58xOOOO'#Cr'#CrO$SOPO,59XOOOO,59X,59XOOOO-E6m-E6mOOOO,58{,58{OOOO1G.p1G.pO$eOQO'#CaOOOO-E6n-E6nOOOO,59],59]OOOO-E6o-E6oOOOO,59O,59OO#lOPO,59OOOOO-E6p-E6pOOOO1G.s1G.sOOOO1G.j1G.j", stateData: "$m~OSPOZSO[SObROhSO~OUVOVUO_XO`XO~OSYOZ]O[]O]]OhZO~OSPOZcO[cObeOhcO~OVgO_hO`hO~OUiOVUO~OhZOSXXZXX[XX]XX~OhZOSeXZeX[eX]eX~OSYOZ]O[]O]]O~OSYOhZO~OSPOZcO[cObpOhcO~OVgO~OSZ~", goto: "#lgPPhwP!UPP!Y!^!ePPP!oPP!wP!{#R#Z#fSSOTY^Q_`anTcRdWQORTdZaQ_`anTWPYTbQaS`QaRk^QXQSm_`RqnSSOTTcRdTSOTQTORfTU[Q^aRj[S_QaSl_nRn`QdRRod", nodeNames: "\u26A0 TextColor Expression TcLeft LMarker Description Color InnerMarker TcRight Text REnd RMarker ENDLN EOF Unfinished ColorEOF ColorWS CodeSection CODE", maxTerm: 24, skippedNodes: [0], repeatNodeCount: 4, tokenData: "%f~RcXY!^YZ!c]^!^pq!^qr!^rs!^s!_!^!_!`!p!`#O!^#O#P!}#P#S!^#S#T$q#T#r!^#r#s$v#s;'S!^;'S;=`%Z<%l~!^~O!^~~%a~!cOh~~!hPh~YZ!k~!pO[~~!uPh~#r#s!x~!}OZ~~#SXh~rs!^!P!Q!^#O#P!^#U#V!^#Y#Z!^#b#c!^#f#g!^#h#i!^#i#j#o~#rR!Q![#{!c!i#{#T#Z#{~$OR!Q![$X!c!i$X#T#Z$X~$[R!Q![$e!c!i$e#T#Z$e~$hR!Q![!^!c!i!^#T#Z!^~$vOb~~${Ph~!_!`%O~%RP#o#p%U~%ZOS~~%^P;=`<%l!^~%fO]~", tokenizers: [1, new import_lr.LocalTokenGroup("!X~R[X^wpqw#q#r|#y#zw$f$gw#BY#BZw$IS$I_w$I|$JOw$JT$JUw$KV$KWw&FU&FVw~~!R~|O`~~!ROV~~!WO_~~", 54, 6)], topRules: { "TextColor": [0, 1] }, tokenPrec: 164 }); // src/rendering/language/textColorLanguage.ts var import_language = require("@codemirror/language"); var textColorLanguage = import_language.LRLanguage.define({ name: "textColorLanguage", parser: parser.configure({}) }); // src/rendering/TextColorStateField.ts var import_common = require("@lezer/common"); var import_language2 = require("@codemirror/language"); var textColorParserField = import_state.StateField.define({ create(state) { const parsedTree = textColorLanguage.parser.parse(state.doc.toString()); return { tree: parsedTree, fragment: import_common.TreeFragment.addTree(parsedTree) }; }, update(value, transaction) { if (!transaction.docChanged) { return value; } const changed_ranges = []; transaction.changes.iterChangedRanges( (from, to, fromB, toB) => changed_ranges.push({ fromA: from, toA: to, fromB, toB }) ); let fragments = import_common.TreeFragment.applyChanges(value.fragment, changed_ranges); const tree = textColorLanguage.parser.parse(new import_language2.DocInput(transaction.state.doc), fragments); fragments = import_common.TreeFragment.addTree(tree, fragments); return { tree, fragment: fragments }; } }); // src/widgets/MarkerWidget.ts var import_view = require("@codemirror/view"); var MarkerWidget = class extends import_view.WidgetType { constructor() { super(); } toDOM(view) { const div = document.createElement("span"); return div; } }; // src/widgets/ColorWidget.ts var import_view2 = require("@codemirror/view"); var import_obsidian5 = require("obsidian"); // src/SettingsFacet.ts var import_state2 = require("@codemirror/state"); var settingsFacet = import_state2.Facet.define( { combine: (inputs) => { if (inputs.length <= 0) { return DEFAULT_SETTINGS; } return inputs[inputs.length - 1]; } } ); // src/widgets/ColorWidget.ts var ColorWidget = class extends import_view2.WidgetType { constructor(id, from, to, expressionTo, themeName) { super(); this.id = id; this.from = from; this.to = to; this.expressionTo = expressionTo; this.themeName = themeName; } toDOM(view) { const div = document.createElement("span"); div.addClass(`${CSS_COLOR_PREFIX}${this.themeName}-${this.id}`); div.addClass("ftc-color-delimiter"); div.innerText = "\u2B24"; const settings = view.state.facet(settingsFacet); div.onclick = (event) => { if (this.menu != null) { } view.dispatch({ selection: { anchor: this.from, head: this.to } }); }; div.onmouseover = (event) => { if (this.menu != null) { return; } this.menu = new import_obsidian5.Menu(); getColors(settings).forEach((tColor) => { this.menu.addItem((item) => { item.setTitle(tColor.id).onClick((evt) => { view.dispatch({ changes: { from: this.from, to: this.to, insert: tColor.id } }); }).setIcon("palette"); item.dom.addClass(tColor.className); }); }); this.menu.addItem((item) => { item.setTitle("Remove").setIcon("ban").onClick((evt) => { view.dispatch({ changes: [ { from: this.from - 3, to: this.to + 1, insert: "" }, { from: this.expressionTo - 2, to: this.expressionTo, insert: "" } ] }); }); }); const rect = div.getBoundingClientRect(); this.menu.showAtPosition({ x: rect.left, y: rect.bottom }); }; return div; } }; // src/rendering/TextColorViewPlugin.ts var import_obsidian6 = require("obsidian"); var TextColorViewPlugin = class { constructor(view) { this.decorations = this.buildDecorations(view); } update(update) { var _a; if (!isLivePreview(update.state)) { if (this.decorations.size > 0) { this.decorations = new import_state3.RangeSetBuilder().finish(); } this.notLivePreview = true; return; } if (this.notLivePreview) { this.notLivePreview = false; this.decorations = this.buildDecorations(update.view); return; } const selectionChanged = update.selectionSet && !((_a = update.view.plugin(import_obsidian6.livePreviewState)) == null ? void 0 : _a.mousedown); if (update.docChanged || update.viewportChanged || selectionChanged) { this.decorations = this.buildDecorations(update.view); } } destroy() { } buildDecorations(view) { const builder = new import_state3.RangeSetBuilder(); for (let { from, to } of view.visibleRanges) { view.state.field(textColorParserField).tree.iterate({ from, to, enter(node) { if (node.type.name == "TextColor") { return true; } if (node.type.name != "Expression") { return false; } handleExpression(node, builder, view.state); return false; } }); } return builder.finish(); } }; function isLivePreview(state) { return state.field(import_obsidian6.editorLivePreviewField).valueOf(); } function handleExpression(ExpressionNode, builder, state) { const from = ExpressionNode.from; let colorStack = []; const stateFrom = state.selection.main.from; const stateTo = state.selection.main.to; const settings = state.facet(settingsFacet); const frontmatterTheme = getThemeFromFrontmatter(state); const themeName = frontmatterTheme == "" ? getCurrentTheme(settings).name : frontmatterTheme; ExpressionNode.node.toTree().iterate({ // toTree allocates a tree, this might be a point of optimization. TODO optimization enter(node) { var _a, _b, _c, _d; switch (node.type.name) { case "RMarker": let inside = (_a = colorStack.pop()) == null ? void 0 : _a.inside; if (inside) { return true; } builder.add(node.from + from, node.to + from, import_view3.Decoration.replace({ widget: new MarkerWidget(), block: false })); return true; case "EOF": case "ENDLN": (_b = colorStack.pop()) == null ? void 0 : _b.inside; return true; case "TcLeft": if ((_c = colorStack.last()) == null ? void 0 : _c.inside) { return true; } builder.add(node.from + from, node.to + from, import_view3.Decoration.replace({ widget: new MarkerWidget(), block: false })); return true; case "Color": let color = state.sliceDoc(from + node.from, from + node.to); colorStack[colorStack.length - 1].color = color; if (((_d = colorStack.last()) == null ? void 0 : _d.inside) && settings.interactiveDelimiters) { if (stateFrom <= from + node.to && stateTo >= from + node.from) { return true; } const widget = new ColorWidget(color, node.from + from, node.to + from, ExpressionNode.to, themeName); builder.add(node.from + from, node.to + from, import_view3.Decoration.replace({ widget, block: false })); } return true; case "Text": builder.add(node.from + from, node.to + from, import_view3.Decoration.mark({ class: `${CSS_COLOR_PREFIX}${themeName}-${colorStack[colorStack.length - 1].color}` })); return false; case "Expression": colorStack.push({ color: "", inside: stateFrom <= from + node.to && stateTo >= from + node.from }); return true; default: break; } } }); } function getThemeFromFrontmatter(state) { var _a; const editorInfo = state.field(import_obsidian6.editorInfoField); const file = editorInfo.file; if (!file) { return ""; } const frontmatter = (_a = editorInfo.app.metadataCache.getFileCache(file)) == null ? void 0 : _a.frontmatter; if (!frontmatter) { return ""; } const name = frontmatter["ftcTheme"]; return name ? name : ""; } var pluginSpec = { decorations: (value) => value.decorations }; var textColorViewPlugin = import_view3.ViewPlugin.fromClass( TextColorViewPlugin, pluginSpec ); // src/utils/regularExpressions.ts var PREFIX = /\~\=\{\S+\}/g; var SUFFIX = /\=\~/g; // src/rendering/TextColorPostProcessor.ts var textColorPostProcessor = (el, context, settings) => { if (!el.innerHTML.match(PREFIX)) { return; } let themeName = context.frontmatter ? context.frontmatter["ftcTheme"] : null; themeName = themeName ? themeName : getCurrentTheme(settings).name; const emergencyCopy = el.cloneNode(true); try { rebuildNode(el, themeName); } catch (e) { console.error(`fatal in rebuildNode: ${e}`); el.childNodes.forEach((c) => { var _a; (_a = c.parentNode) == null ? void 0 : _a.removeChild(c); }); emergencyCopy.childNodes.forEach((c) => { el.appendChild(c); }); } return; }; function rebuildNode(node, themeName, level = 0, nodeStack = []) { var _a, _b, _c, _d; if (node.nodeName == "CODE") { return node; } if (level > 1e3) { console.error("fatal: reached depth 1000 in recursion"); } let lastLength = node.childNodes.length; for (let i = 0; i < node.childNodes.length; i++) { lastLength = node.childNodes.length; let childNode = node.childNodes.item(i); const text = childNode.nodeValue; if (nodeStack.last() != void 0 && nodeStack.last() != childNode && !(childNode.compareDocumentPosition(nodeStack.last()) & Node.DOCUMENT_POSITION_CONTAINS)) { (_a = childNode.parentNode) == null ? void 0 : _a.removeChild(childNode); (_b = nodeStack.last()) == null ? void 0 : _b.appendChild(childNode); if (lastLength > node.childNodes.length) { i -= lastLength - node.childNodes.length; } } if (childNode.nodeType != Node.TEXT_NODE) { childNode = rebuildNode(childNode, themeName, level + 1, nodeStack); continue; } if (text == null || text == "") { continue; } let prefix = GetFirstMatch(text, PREFIX); let suffix = GetFirstMatch(text, SUFFIX); if (prefix == null && suffix == null) { continue; } let nextPrefixPosition = prefix != null ? prefix.index : Number.POSITIVE_INFINITY; let nextSuffixPosition = suffix != null ? suffix.index : Number.POSITIVE_INFINITY; if (nextPrefixPosition == nextSuffixPosition) { console.error(`fatal: nextPrefixPosition and nextSuffixPosition are the same but not infinity!!: ${nextPrefixPosition}`); return node; } if (nextPrefixPosition < nextSuffixPosition) { prefix = prefix; let textBeforeDelim2 = text.slice(0, nextPrefixPosition); let textAfterDelim2 = text.slice(prefix.end); let prefixContent = prefix.value; let color = prefixContent.slice(3, prefixContent.length - 1); let colorSpan = document.createElement("span"); colorSpan.addClass(`${CSS_COLOR_PREFIX}${themeName}-${color}`); childNode.nodeValue = textBeforeDelim2; (_c = childNode.parentNode) == null ? void 0 : _c.insertAfter(colorSpan, childNode); let newNode2 = document.createTextNode(textAfterDelim2); colorSpan.appendChild(newNode2); nodeStack.push(colorSpan); continue; } let textBeforeDelim = text.slice(0, nextSuffixPosition); let textAfterDelim = text.slice(suffix.end); childNode.nodeValue = textBeforeDelim; let prevNode = nodeStack.pop(); let newNode = document.createTextNode(textAfterDelim); (_d = prevNode.parentNode) == null ? void 0 : _d.insertAfter(newNode, prevNode); continue; } return node; } function GetFirstMatch(text, regex) { const regexCopy = new RegExp(regex.source, "g"); const matches = []; let m = regexCopy.exec(text); if (m !== null) { return { index: m.index, value: m[0], end: m.index + m[0].length }; } return null; } // main.ts var import_state4 = require("@codemirror/state"); var import_view4 = require("@codemirror/view"); // src/color/TextColorFunctions.ts function applyColor(tColor, editor) { let prefix = `~={${tColor.id}}`; let suffix = `=~`; if (!editor.somethingSelected()) { editor.replaceSelection(prefix); let pos = editor.getCursor(); editor.replaceSelection(suffix); editor.setCursor(pos); return; } let selected = editor.getSelection(); let coloredText = `${prefix}${selected}${suffix}`; editor.replaceSelection(coloredText); } function removeColor(editor, view) { var _a, _b; const tree = view.state.field(textColorParserField).tree; let node = tree.resolveInner(view.state.selection.main.head); while (node.parent != null) { if (node.type.name != "Expression") { node = node.parent; continue; } const TcLeft = node.getChild("TcLeft"); const Rmarker = (_b = (_a = node.getChild("TcRight")) == null ? void 0 : _a.getChild("REnd")) == null ? void 0 : _b.getChild("RMarker"); view.dispatch({ changes: [ { from: TcLeft ? TcLeft.from : 0, to: TcLeft ? TcLeft.to : 0, insert: "" }, { from: Rmarker ? Rmarker.from : 0, to: Rmarker ? Rmarker.to : 0, insert: "" } ] }); return; } return; } // src/utils/ColorSuggestModal.ts var import_obsidian7 = require("obsidian"); var ColorSuggestModal = class extends import_obsidian7.SuggestModal { constructor(app, colors, editor) { super(app); this.colors = colors; this.editor = editor; } getSuggestions(query) { return this.colors.filter((tColor) => tColor.id.startsWith(query) || tColor.keybind == query); } renderSuggestion(tColor, el) { let div = el.createDiv(); div.innerText = tColor.id; div.setAttr("style", tColor.getCssInlineStyle()); } onChooseSuggestion(tColor, evt) { applyColor(tColor, this.editor); } }; // main.ts var MAX_MENU_ITEMS = 10; var FastTextColorPlugin = class extends import_obsidian8.Plugin { async onload() { await this.loadSettings(); this.registerEditorExtension(textColorParserField); this.registerEditorExtension(textColorViewPlugin); this.registerMarkdownPostProcessor((el, ctx) => { textColorPostProcessor(el, ctx, this.settings); }, -1e3); this.registerMarkdownPostProcessor((el, ctx) => { textColorPostProcessor(el, ctx, this.settings); }, 1e3); this.settingsCompartment = new import_state4.Compartment(); this.settingsExtension = this.settingsCompartment.of(settingsFacet.of(this.settings)); this.registerEditorExtension(this.settingsExtension); this.registerEditorExtension( import_state4.Prec.high( import_view4.keymap.of([ { key: "Tab", run: (editorView) => this.jumpOut(editorView) } ]) ) ); this.addCommand({ id: "change-text-color", name: "Change text color", editorCallback: (editor) => { this.openColorMenu(editor); } }); this.addCommand({ id: "remove-text-color", name: "Remove text color", editorCallback: (editor, view) => { const editorView = view.editor.cm; removeColor(editor, editorView); } }); this.registerEvent( this.app.workspace.on("editor-menu", (menu, editor, view) => { if (editor.getSelection() == "") { return; } menu.addItem((item) => { item.setSection("selection").setTitle("Color").setIcon("palette"); const submenu = item.setSubmenu(); getColors(this.settings).forEach((tColor) => { submenu.addItem((subitem) => { subitem.setTitle(tColor.id).setIcon("circle").onClick((evt) => { applyColor(tColor, editor); }); subitem.dom.addClass(tColor.className); subitem.iconEl.addClass(tColor.className); }); }); }); }) ); this.addSettingTab(new FastTextColorPluginSettingTab(this.app, this)); this.setCssVariables(); } onunload() { this.styleElement.remove(); this.closeColorMenu(); } async loadSettings() { const rawSettings = await this.loadData(); if (rawSettings && +rawSettings.version < +SETTINGS_VERSION) { console.log("outdated Settings! Trying to update."); this.settings = updateSettings(rawSettings); await this.saveData(this.settings); return; } this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); for (let j = 0; j < this.settings.themes.length; j++) { const colors = getColors(this.settings, j); for (let i = 0; i < colors.length; i++) { let obj = colors[i]; colors[i] = new TextColor(obj.color, obj.id, this.settings.themes[j].name, obj.italic, obj.bold, obj.cap_mode.index, obj.line_mode.index, obj.keybind, obj.useCssColorVariable, obj.colorVariable); } } } async saveSettings() { var _a; await this.saveData(this.settings); const view = this.app.workspace.getActiveViewOfType(import_obsidian8.MarkdownView); const editorView = (_a = view == null ? void 0 : view.editor) == null ? void 0 : _a.cm; if (editorView == null) { return; } editorView.dispatch({ effects: this.settingsCompartment.reconfigure(settingsFacet.of(this.settings)) }); } // create and open the color menu /** * opens the color menu and pushed the scope onto the keybindings. * * @param {Editor} editor - [TODO:description] */ openColorMenu(editor) { var _a; if (!this.settings.useKeybindings) { let modal = new ColorSuggestModal(this.app, getColors(this.settings), editor); modal.open(); return; } if (this.colorMenu != null) { return; } this.colorMenu = createDiv(); if (!this.colorMenu) { new import_obsidian8.Notice("could not create Colormenu!"); return; } let attributes = `bottom: 8.25em; grid-template-columns: ${"1fr ".repeat(getColors(this.settings).length)}`; this.colorMenu.setAttribute("style", attributes); this.colorMenu.setAttribute("id", "fast-color-menu"); this.colorMenu.addClass("fast-color-menu"); (_a = document.body.querySelector(".mod-vertical.mod-root")) == null ? void 0 : _a.insertAdjacentElement("afterbegin", this.colorMenu); for (let i = 0; i < Math.min(getColors(this.settings).length, MAX_MENU_ITEMS); i++) { this.createColorItem(this.colorMenu, getColors(this.settings)[i], i + 1, editor); } this.colorMenu.setAttribute("style", `left: calc(50% - ${this.colorMenu.offsetWidth}px / 2); ${attributes}`); if (!this.settings.useKeybindings) { return; } this.constructScope(editor); this.app.keymap.pushScope(this.scope); } closeColorMenu() { if (this.colorMenu) { this.colorMenu.remove(); this.colorMenu = null; } this.app.keymap.popScope(this.scope); } constructScope(editor) { this.scope = new import_obsidian8.Scope(); let { scope } = this; for (let i = 0; i < getColors(this.settings).length; i++) { const tColor = getColors(this.settings)[i]; scope.register([], tColor.keybind, (event) => { if (event.isComposing) { return true; } applyColor(tColor, editor); this.closeColorMenu(); return false; }); } scope.register([], "Escape", (event) => { if (event.isComposing) { return true; } this.closeColorMenu(); return false; }); scope.register([], "Delete", (event) => { if (event.isComposing) { return true; } this.closeColorMenu(); return false; }); scope.register([], "Backspace", (event) => { if (event.isComposing) { return true; } this.closeColorMenu(); return false; }); } /** * Move the cursor behind the next end marker. * * @param {EditorView} editorView * @returns {boolean} true if jump possible. */ jumpOut(editorView) { var _a; const state = editorView.state; const tree = state.field(textColorParserField).tree; const editor = (_a = this.app.workspace.getActiveViewOfType(import_obsidian8.MarkdownView)) == null ? void 0 : _a.editor; if (!editor) { return false; } let inner = tree.resolve(state.selection.main.head); if (inner.type.name == "Text" && inner.parent != null) { inner = inner.parent; } if (inner.type.name != "TcRight") { return false; } editor.setCursor(editor.offsetToPos(inner.to)); return true; } createColorItem(menu, tColor, counter, editor) { new import_obsidian8.ButtonComponent(menu).setButtonText(`${tColor.keybind}`).setClass("fast-color-menu-item").onClick(() => { let n = new import_obsidian8.Notice("activated color"); n.noticeEl.setAttr("style", `background-color: ${tColor.color}`); applyColor(tColor, editor); this.closeColorMenu(); }).buttonEl.setAttr("style", `background-color: ${tColor.color}`); } /** * creates the stylesheet needed for the colors in the root of the document. * A different set of classes is created for each theme. * */ setCssVariables() { if (!this.styleElement) { const root = document.querySelector(":root"); if (!root) { return; } this.styleElement = root.createEl("style"); this.styleElement.id = "fast-text-color-stylesheet"; } this.styleElement.innerText = ""; for (let i = 0; i < this.settings.themes.length; i++) { getColors(this.settings, i).forEach((tColor) => { const theme = this.settings.themes[i]; const className = `.${CSS_COLOR_PREFIX}${theme.name}-${tColor.id}`; let cssClass = `${className} { ${tColor.getInnerCss()}}`; this.styleElement.innerText += cssClass + "\n"; }); } } };