|
@@ -0,0 +1,1261 @@
|
|
|
+/*
|
|
|
+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";
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|