main.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. /*
  2. THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
  3. if you want to view the source, please visit the github repository of this plugin
  4. */
  5. var __defProp = Object.defineProperty;
  6. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  7. var __getOwnPropNames = Object.getOwnPropertyNames;
  8. var __hasOwnProp = Object.prototype.hasOwnProperty;
  9. var __export = (target, all) => {
  10. for (var name in all)
  11. __defProp(target, name, { get: all[name], enumerable: true });
  12. };
  13. var __copyProps = (to, from, except, desc) => {
  14. if (from && typeof from === "object" || typeof from === "function") {
  15. for (let key of __getOwnPropNames(from))
  16. if (!__hasOwnProp.call(to, key) && key !== except)
  17. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  18. }
  19. return to;
  20. };
  21. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  22. // main.ts
  23. var main_exports = {};
  24. __export(main_exports, {
  25. default: () => FastTextColorPlugin
  26. });
  27. module.exports = __toCommonJS(main_exports);
  28. var import_obsidian8 = require("obsidian");
  29. // src/FastTextColorSettings.ts
  30. var import_obsidian4 = require("obsidian");
  31. // src/color/TextColor.ts
  32. var TextColor = class {
  33. /**
  34. * Create a basic Text Color
  35. *
  36. * @param {string} color - [TODO:description]
  37. * @param {string} id - the id or name of the color
  38. * @param {string} themeName - the associated theme that this color belongs to
  39. * @param {boolean} [italic] - italic text
  40. * @param {boolean} [bold] - bold text
  41. * @param {number} [cap_mode_index] - the index for the cap mode
  42. * @param {number} [line_mode_index] - the index for the line mode
  43. * @param {string} [keybind] - the associated keybind
  44. * @param {string} colorVariable - the builtin Css color variable that this color uses.
  45. */
  46. constructor(color, id, themeName, italic = false, bold = false, cap_mode_index = 0, line_mode_index = 0, keybind = "", useCssColorVariable = false, colorVariable = "--color-base-00") {
  47. this.color = color;
  48. this.id = id;
  49. this.keybind = keybind;
  50. this.italic = italic;
  51. this.bold = bold;
  52. this.cap_mode = new CycleState(["normal", "all_caps", "small_caps"], cap_mode_index);
  53. this.line_mode = new CycleState(["none", "underline", "overline", "line-through"], line_mode_index);
  54. this.useCssColorVariable = useCssColorVariable;
  55. this.colorVariable = colorVariable;
  56. this.className = `${CSS_COLOR_PREFIX}${themeName}-${this.id}`;
  57. }
  58. getCssClass() {
  59. return `.${CSS_COLOR_PREFIX}${this.id} {
  60. color : ${this.useCssColorVariable ? `var(${this.colorVariable})` : this.color}
  61. ;
  62. ${this.italic ? "font-style: italic;\n" : ""}
  63. ${this.bold ? "font-weight: bold;\n" : ""}
  64. ${this.line_mode.state != "none" ? `text-decoration: ${this.line_mode.state};
  65. ` : ""}
  66. ${this.cap_mode.state == "all_caps" ? "text-transform: uppercase;\n" : this.cap_mode.state == "small_caps" ? "font-variant: small-caps;\n" : ""}
  67. ${VAR_COLOR_PREFIX}${this.id} : ${this.color};
  68. }`;
  69. }
  70. /**
  71. * get the inner css of the class for the color.
  72. *
  73. * @returns {string} the inner css.
  74. */
  75. getInnerCss() {
  76. return `color : ${this.useCssColorVariable ? `var(${this.colorVariable})` : this.color};
  77. ${this.italic ? "font-style: italic;\n" : ""}${this.bold ? "font-weight: bold;\n" : ""}${this.line_mode.state != "none" ? `text-decoration: ${this.line_mode.state};
  78. ` : ""}${this.cap_mode.state == "all_caps" ? "text-transform: uppercase;\n" : this.cap_mode.state == "small_caps" ? "font-variant: small-caps;\n" : ""}`;
  79. }
  80. getCssInlineStyle() {
  81. return `color : ${this.useCssColorVariable ? `var(${this.colorVariable})` : this.color}
  82. ;
  83. ${this.italic ? "font-style: italic;" : ""}
  84. ${this.bold ? "font-weight: bold;" : ""}
  85. ${this.line_mode.state != "none" ? `text-decoration: ${this.line_mode.state};` : ""}
  86. ${this.cap_mode.state == "all_caps" ? "text-transform: uppercase;" : this.cap_mode.state == "small_caps" ? "font-variant: small-caps;" : ""}
  87. `;
  88. }
  89. };
  90. var CycleState = class {
  91. constructor(states, index = 0) {
  92. this.states = states;
  93. if (states.length <= 0) {
  94. this.state = "error";
  95. return;
  96. }
  97. this.state = this.states[index];
  98. this.index = index;
  99. }
  100. cycle() {
  101. this.index = (this.index + 1) % this.states.length;
  102. this.state = this.states[this.index];
  103. }
  104. };
  105. // src/color/TextColorTheme.ts
  106. var TextColorTheme = class {
  107. constructor(name, colors) {
  108. this.colors = colors;
  109. this.name = name;
  110. }
  111. };
  112. // src/utils/ConfirmationModal.ts
  113. var import_obsidian = require("obsidian");
  114. var ConfirmationModal = class extends import_obsidian.Modal {
  115. constructor(app, message, heading) {
  116. super(app);
  117. this.message = message;
  118. this.heading = heading;
  119. this.confirmed = false;
  120. this.finished = false;
  121. }
  122. onOpen() {
  123. const { contentEl } = this;
  124. contentEl.createEl("h1", { text: this.heading });
  125. contentEl.createDiv({ text: this.message });
  126. new import_obsidian.Setting(contentEl).addButton((btn) => btn.setButtonText("OK").setCta().onClick(() => {
  127. this.finished = true;
  128. this.confirmed = true;
  129. })).addButton((btn) => btn.setButtonText("Cancel").setCta().onClick(() => {
  130. this.finished = true;
  131. }));
  132. }
  133. onClose() {
  134. let { contentEl } = this;
  135. contentEl.empty();
  136. }
  137. };
  138. async function confirmByModal(app, message = "", heading = "") {
  139. let modal = new ConfirmationModal(app, message != "" ? message : "Are you sure?", heading != "" ? heading : "Confirm");
  140. modal.open();
  141. while (!modal.finished) {
  142. await sleep(16);
  143. }
  144. let result = modal.confirmed;
  145. modal.close();
  146. return result;
  147. }
  148. // src/utils/CreateNewThemeModal.ts
  149. var import_obsidian2 = require("obsidian");
  150. var CreateNewThemeModal = class extends import_obsidian2.Modal {
  151. constructor(app, settings) {
  152. super(app);
  153. this.settings = settings;
  154. this.name = "newTheme";
  155. }
  156. onOpen() {
  157. const { contentEl } = this;
  158. contentEl.createEl("h1", { text: "Create new theme" });
  159. new import_obsidian2.Setting(contentEl).setName(this.name).addText((txt) => {
  160. txt.setValue(this.name).setPlaceholder("Theme name").onChange((value) => {
  161. this.name = value;
  162. this.evalNameErrors();
  163. });
  164. });
  165. this.errorDiv = contentEl.createDiv();
  166. this.errorDiv.addClass("ftc-theme-name-error");
  167. new import_obsidian2.Setting(contentEl).addButton((btn) => {
  168. btn.setButtonText("create").onClick((evt) => {
  169. if (!this.evalNameErrors()) {
  170. return;
  171. }
  172. addTheme(this.settings, this.name, DEFAULT_COLORS);
  173. this.doOnSuccess();
  174. this.close();
  175. });
  176. }).addButton((btn) => {
  177. btn.setButtonText("cancel").onClick((evt) => {
  178. this.close();
  179. });
  180. });
  181. }
  182. onClose() {
  183. let { contentEl } = this;
  184. contentEl.empty();
  185. }
  186. onSuccess(callback) {
  187. this.doOnSuccess = callback;
  188. }
  189. evalNameErrors() {
  190. if (this.settings.themes.some((theme) => {
  191. return theme.name == this.name;
  192. })) {
  193. this.errorDiv.innerText = "a theme with this name already exists.";
  194. return false;
  195. }
  196. if (this.name == "") {
  197. this.errorDiv.innerText = "name can not be empty.";
  198. return false;
  199. }
  200. this.errorDiv.innerText = "";
  201. return true;
  202. }
  203. };
  204. // src/utils/KeyBindModal.ts
  205. var import_obsidian3 = require("obsidian");
  206. var KeyBindModal = class extends import_obsidian3.Modal {
  207. constructor(app) {
  208. super(app);
  209. this.finished = false;
  210. }
  211. onOpen() {
  212. const { contentEl } = this;
  213. contentEl.createEl("h1", { text: "Press any key" });
  214. this.handler = this.handleKeypress.bind(this);
  215. window.addEventListener("keypress", this.handler);
  216. new import_obsidian3.Setting(contentEl).addButton((btn) => {
  217. btn.setButtonText("Cancel").onClick((evt) => {
  218. this.close();
  219. });
  220. });
  221. }
  222. onClose() {
  223. let { contentEl } = this;
  224. contentEl.empty();
  225. }
  226. handleKeypress(evt) {
  227. this.result = evt.key.toUpperCase();
  228. this.modalEl.removeEventListener("keypress", this.handler, true);
  229. this.finished = true;
  230. }
  231. };
  232. async function getKeyBindWithModal(app) {
  233. let modal = new KeyBindModal(app);
  234. modal.open();
  235. while (!modal.finished) {
  236. await sleep(16);
  237. }
  238. let result = modal.result;
  239. modal.close();
  240. return result;
  241. }
  242. // src/FastTextColorSettings.ts
  243. var CSS_COLOR_PREFIX = "ftc-color-";
  244. var VAR_COLOR_PREFIX = "--ftc-color-";
  245. var SETTINGS_VERSION = "3";
  246. var BUILTIN_COLORS = [
  247. new TextColor("#000000", "red", "builtin", false, false, 0, 0, "R", true, "--color-red"),
  248. new TextColor("#000000", "orange", "builtin", false, false, 0, 0, "O", true, "--color-orange"),
  249. new TextColor("#000000", "yellow", "builtin", false, false, 0, 0, "Y", true, "--color-yellow"),
  250. new TextColor("#000000", "green", "builtin", false, false, 0, 0, "G", true, "--color-green"),
  251. new TextColor("#000000", "cyan", "builtin", false, false, 0, 0, "C", true, "--color-cyan"),
  252. new TextColor("#000000", "blue", "builtin", false, false, 0, 0, "B", true, "--color-blue"),
  253. new TextColor("#000000", "purple", "builtin", false, false, 0, 0, "P", true, "--color-purple"),
  254. new TextColor("#000000", "pink", "builtin", false, false, 0, 0, "I", true, "--color-pink")
  255. ];
  256. var DEFAULT_COLORS = [
  257. new TextColor("#ff0000", `red`, "default", false, false, 0, 0, "R"),
  258. new TextColor("#00ff00", `green`, "default", false, false, 0, 0, "G"),
  259. new TextColor("#0000ff", `blue`, "default", false, false, 0, 0, "B"),
  260. new TextColor("#00ffff", `cyan`, "default", false, false, 0, 0, "C"),
  261. new TextColor("#ff00ff", `magenta`, "default", false, false, 0, 0, "M"),
  262. new TextColor("#ffff00", `yellow`, "default", false, false, 0, 0, "Y"),
  263. new TextColor("#000000", `black`, "default", false, false, 0, 0, "K")
  264. ];
  265. var DEFAULT_SETTINGS = {
  266. themes: [new TextColorTheme("default", DEFAULT_COLORS), new TextColorTheme("builtin", BUILTIN_COLORS)],
  267. themeIndex: 0,
  268. version: SETTINGS_VERSION,
  269. interactiveDelimiters: true,
  270. useKeybindings: true,
  271. useNodeRebuilding: false
  272. };
  273. function getColors(settings, index = -1) {
  274. if (index == -1) {
  275. index = settings.themeIndex;
  276. }
  277. return settings.themes[index].colors;
  278. }
  279. function getCurrentTheme(settings, index = -1) {
  280. if (index == -1) {
  281. index = settings.themeIndex;
  282. }
  283. return settings.themes[index];
  284. }
  285. function addTheme(settings, name, colors = DEFAULT_COLORS) {
  286. settings.themes.push(new TextColorTheme(name, colors));
  287. }
  288. function deleteTheme(settings, index = -1) {
  289. if (settings.themes.length <= 1) {
  290. return;
  291. }
  292. if (index == -1) {
  293. index = settings.themeIndex;
  294. }
  295. settings.themes.remove(settings.themes[index]);
  296. if (index <= settings.themeIndex) {
  297. settings.themeIndex = Math.max(settings.themeIndex - 1, 0);
  298. }
  299. }
  300. function updateSettings(settings) {
  301. switch (settings.version) {
  302. case "1":
  303. case "2":
  304. const colors = settings.colors.map((color) => {
  305. return new TextColor(color.color, color.id, "default", color.italic, color.bold, color.cap_mode.index, color.line_mode.index, color.keybind);
  306. });
  307. const outSettings = {
  308. themes: [new TextColorTheme("default", colors)],
  309. themeIndex: 0,
  310. version: SETTINGS_VERSION,
  311. interactiveDelimiters: settings.interactiveDelimiters,
  312. useKeybindings: true,
  313. useNodeRebuilding: false
  314. };
  315. return outSettings;
  316. default:
  317. console.log(`There is not update method for Settings Version ${settings.version}!
  318. ${settings}`);
  319. return DEFAULT_SETTINGS;
  320. }
  321. }
  322. var FastTextColorPluginSettingTab = class extends import_obsidian4.PluginSettingTab {
  323. constructor(app, plugin) {
  324. super(app, plugin);
  325. this.plugin = plugin;
  326. this.newId = "";
  327. this.editThemeIndex = plugin.settings.themeIndex;
  328. }
  329. display() {
  330. const { containerEl } = this;
  331. const { settings } = this.plugin;
  332. containerEl.empty();
  333. new import_obsidian4.Setting(containerEl).setName("Colors").setHeading();
  334. new import_obsidian4.Setting(containerEl).setName("Set active theme").setDesc("Set global active theme.").addDropdown((dd) => {
  335. let count2 = 0;
  336. settings.themes.forEach((theme) => {
  337. dd.addOption(count2.toString(), theme.name);
  338. count2++;
  339. });
  340. dd.setValue(settings.themeIndex.toString());
  341. dd.onChange((value) => {
  342. settings.themeIndex = +value;
  343. this.plugin.saveSettings();
  344. this.plugin.setCssVariables();
  345. this.display();
  346. });
  347. });
  348. new import_obsidian4.Setting(containerEl).setName("Edit themes").setDesc("Add new themes or edit existings ones.").setClass("ftc-settings-theme-header").addDropdown((dd) => {
  349. let count2 = 0;
  350. settings.themes.forEach((theme) => {
  351. dd.addOption(count2.toString(), theme.name);
  352. count2++;
  353. });
  354. dd.setValue(this.editThemeIndex.toString());
  355. dd.onChange((value) => {
  356. this.editThemeIndex = +value;
  357. this.display();
  358. });
  359. }).addButton((btn) => {
  360. btn.setIcon("plus").setTooltip("add new Theme").onClick((evt) => {
  361. const modal = new CreateNewThemeModal(this.app, settings);
  362. modal.onSuccess(() => {
  363. this.plugin.saveSettings();
  364. this.display();
  365. });
  366. modal.open();
  367. });
  368. }).addButton((btn) => {
  369. btn.setIcon("trash").setTooltip("delete theme").onClick(async (evt) => {
  370. if (await confirmByModal(this.app, `Are you sure?
  371. The theme ${settings.themes[settings.themeIndex].name} will no longer be available. `)) {
  372. deleteTheme(settings, this.editThemeIndex);
  373. this.editThemeIndex = 0;
  374. this.plugin.saveSettings();
  375. this.display();
  376. }
  377. });
  378. });
  379. const themeColorsEl = containerEl.createDiv();
  380. themeColorsEl.addClass("ftc-theme-colors");
  381. let count = 1;
  382. getColors(settings, this.editThemeIndex).forEach((color) => {
  383. this.createColorSetting(themeColorsEl, color, count);
  384. count++;
  385. });
  386. new import_obsidian4.Setting(containerEl).setName("Add new color to theme").setClass("ftc-settings-theme-footer").addText((txt) => {
  387. txt.setValue(this.newId == "" ? (getColors(settings).length + 1).toString() : this.newId).onChange((value) => {
  388. this.newId = value;
  389. });
  390. }).addButton((btn) => {
  391. btn.setButtonText("+").onClick(async (evt) => {
  392. let colors = getColors(settings, this.editThemeIndex);
  393. if (colors.some((tColor) => {
  394. return tColor.id == this.newId;
  395. })) {
  396. new import_obsidian4.Notice(`color with id ${this.newId} already exists!`);
  397. }
  398. let newColorName = this.newId == "" ? (colors.length + 1).toString() : this.newId;
  399. colors.push(new TextColor("#ffffff", newColorName, getCurrentTheme(settings, this.editThemeIndex).name));
  400. await this.plugin.saveSettings();
  401. this.display();
  402. });
  403. });
  404. new import_obsidian4.Setting(containerEl).setName("Other").setHeading();
  405. new import_obsidian4.Setting(containerEl).setName("Interactive delimiters").setDesc("Use interactive delimiter to change colors inside the editor.").addToggle((tgl) => {
  406. tgl.setValue(settings.interactiveDelimiters).onChange(async (value) => {
  407. settings.interactiveDelimiters = value;
  408. await this.plugin.saveSettings();
  409. });
  410. });
  411. 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) => {
  412. tgl.setValue(settings.useKeybindings).onChange(async (value) => {
  413. settings.useKeybindings = value;
  414. await this.plugin.saveSettings();
  415. });
  416. });
  417. }
  418. /**
  419. * Create a color row in the theme view
  420. *
  421. * @param {HTMLElement} container - the root container of the element.
  422. * @param {TextColor} tColor - the color to be used for display
  423. * @param {number} count - the index of the color
  424. */
  425. createColorSetting(container, tColor, count) {
  426. let nameFragment = new DocumentFragment();
  427. let nameDiv = nameFragment.createDiv();
  428. nameDiv.addClass("ftc-name-div");
  429. const key = nameDiv.createDiv();
  430. key.innerText = `${tColor.id}`;
  431. const exampletext = nameDiv.createDiv();
  432. exampletext.setAttr("style", tColor.getCssInlineStyle());
  433. exampletext.innerText = `~={${tColor.id}}This is colored text=~`;
  434. let saveAndApply = async () => {
  435. await this.plugin.saveSettings();
  436. this.plugin.setCssVariables();
  437. exampletext.setAttr("style", tColor.getCssInlineStyle());
  438. };
  439. const setting = new import_obsidian4.Setting(container).setName(nameFragment).setClass("ftc-settings-item").addButton((btn) => {
  440. btn.setButtonText(`${tColor.keybind}`.toUpperCase()).setTooltip("set keybinding").setClass("key-indicator").onClick(async (evt) => {
  441. tColor.keybind = await getKeyBindWithModal(this.app);
  442. btn.setButtonText(`${tColor.keybind}`);
  443. await this.plugin.saveSettings();
  444. this.plugin.setCssVariables();
  445. });
  446. }).addButton((btn) => {
  447. btn.setButtonText("B").setTooltip("Bold").setClass("ftc-format-item").onClick(async (evt) => {
  448. tColor.bold = !tColor.bold;
  449. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.bold);
  450. btn.buttonEl.setCssStyles({ fontWeight: tColor.bold ? "bold" : "normal" });
  451. saveAndApply();
  452. });
  453. btn.buttonEl.addClass("ftc-format-left");
  454. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.bold);
  455. btn.buttonEl.setCssStyles({ fontWeight: tColor.bold ? "bold" : "normal" });
  456. }).addButton((btn) => {
  457. btn.setButtonText("I").setTooltip("Italic").setClass("ftc-format-item").onClick(async (evt) => {
  458. tColor.italic = !tColor.italic;
  459. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.italic);
  460. btn.buttonEl.setCssStyles({ fontStyle: tColor.italic ? "italic" : "normal" });
  461. saveAndApply();
  462. });
  463. btn.buttonEl.addClass("ftc-format-middle");
  464. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.italic);
  465. btn.buttonEl.setCssStyles({ fontStyle: tColor.italic ? "italic" : "normal" });
  466. }).addButton((btn) => {
  467. btn.setButtonText("U").setTooltip("Lining").setClass("ftc-format-item").onClick(async (evt) => {
  468. tColor.line_mode.cycle();
  469. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.line_mode.state != "none");
  470. btn.buttonEl.setCssStyles({ textDecoration: tColor.line_mode.state });
  471. saveAndApply();
  472. });
  473. btn.buttonEl.addClass("ftc-format-middle");
  474. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.line_mode.state != "none");
  475. btn.buttonEl.setCssStyles({ textDecoration: tColor.line_mode.state });
  476. }).addButton((btn) => {
  477. btn.setButtonText("Tt").setTooltip("Capitalization").setClass("ftc-format-item").onClick(async (evt) => {
  478. tColor.cap_mode.cycle();
  479. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.cap_mode.state != "normal");
  480. btn.buttonEl.toggleClass("ftc-uppercase", tColor.cap_mode.state == "all_caps");
  481. btn.buttonEl.toggleClass("ftc-small-caps", tColor.cap_mode.state == "small_caps");
  482. await this.plugin.saveSettings();
  483. this.plugin.setCssVariables();
  484. exampletext.setAttr("style", tColor.getCssInlineStyle());
  485. });
  486. btn.buttonEl.addClass("ftc-format-right");
  487. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.cap_mode.state != "normal");
  488. btn.buttonEl.setCssStyles(
  489. tColor.cap_mode.state == "all_caps" ? { textTransform: "uppercase" } : tColor.cap_mode.state == "small_caps" ? { fontVariant: "small_caps" } : {}
  490. );
  491. });
  492. if (tColor.useCssColorVariable) {
  493. setting.addDropdown((dd) => {
  494. dd.addOptions({
  495. "--color-red": "red",
  496. "--color-orange": "orange",
  497. "--color-yellow": "yellow",
  498. "--color-green": "green",
  499. "--color-cyan": "cyan",
  500. "--color-blue": "blue",
  501. "--color-purple": "purple",
  502. "--color-pink": "pink"
  503. // "--color-blue" : "50",
  504. // "--color-blue" : "60",
  505. // "--color-blue" : "70",
  506. // "--color-base-100": "100",
  507. }).setValue(tColor.colorVariable).onChange((value) => {
  508. tColor.colorVariable = value;
  509. saveAndApply();
  510. });
  511. });
  512. } else {
  513. setting.addColorPicker((cb) => {
  514. cb.setValue(tColor.color).onChange(async (value) => {
  515. tColor.color = value;
  516. saveAndApply();
  517. });
  518. });
  519. }
  520. setting.addButton((btn) => {
  521. btn.setTooltip("use builtin obsidian colors").setClass("ftc-format-item-small").onClick(async (evt) => {
  522. tColor.useCssColorVariable = !tColor.useCssColorVariable;
  523. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.useCssColorVariable);
  524. saveAndApply();
  525. this.display();
  526. });
  527. btn.buttonEl.toggleClass("ftc-format-item-enabled", tColor.useCssColorVariable);
  528. }).addButton((btn) => {
  529. btn.setIcon("chevron-up").setTooltip("move item up").setClass("ftc-move-btn-left").onClick(async (evt) => {
  530. moveColor(count - 1, -1, this.plugin.settings);
  531. await this.plugin.saveSettings();
  532. this.display();
  533. });
  534. }).addButton((btn) => {
  535. btn.setIcon("chevron-down").setTooltip("move item down").setClass("ftc-move-btn-right").onClick(async (evt) => {
  536. moveColor(count - 1, 1, this.plugin.settings);
  537. await this.plugin.saveSettings();
  538. this.display();
  539. });
  540. }).addButton((btn) => {
  541. btn.setIcon("trash").setTooltip("delete color").setClass("ftc-move-btn-right").onClick(async (evt) => {
  542. if (await confirmByModal(
  543. this.app,
  544. `Colored section whith the id "${tColor.id}" will no longer be colored until you add another color with that id.`,
  545. `Delete color: ${tColor.id}`
  546. )) {
  547. getColors(this.plugin.settings).remove(tColor);
  548. }
  549. await this.plugin.saveSettings();
  550. this.display();
  551. });
  552. });
  553. }
  554. };
  555. function moveColor(index, direction, settings) {
  556. if (direction < 0 && index == 0 || direction > 0 && index == getColors(settings).length - 1) {
  557. return;
  558. }
  559. let temp = getColors(settings)[index + direction];
  560. getColors(settings)[index + direction] = getColors(settings)[index];
  561. getColors(settings)[index] = temp;
  562. }
  563. // src/rendering/TextColorViewPlugin.ts
  564. var import_state3 = require("@codemirror/state");
  565. var import_view3 = require("@codemirror/view");
  566. // src/rendering/TextColorStateField.ts
  567. var import_state = require("@codemirror/state");
  568. // src/rendering/language/textColorLanguageParser.js
  569. var import_lr = require("@lezer/lr");
  570. var parser = import_lr.LRParser.deserialize({
  571. version: 14,
  572. 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",
  573. 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~",
  574. goto: "#lgPPhwP!UPP!Y!^!ePPP!oPP!wP!{#R#Z#fSSOTY^Q_`anTcRdWQORTdZaQ_`anTWPYTbQaS`QaRk^QXQSm_`RqnSSOTTcRdTSOTQTORfTU[Q^aRj[S_QaSl_nRn`QdRRod",
  575. nodeNames: "\u26A0 TextColor Expression TcLeft LMarker Description Color InnerMarker TcRight Text REnd RMarker ENDLN EOF Unfinished ColorEOF ColorWS CodeSection CODE",
  576. maxTerm: 24,
  577. skippedNodes: [0],
  578. repeatNodeCount: 4,
  579. 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]~",
  580. 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)],
  581. topRules: { "TextColor": [0, 1] },
  582. tokenPrec: 164
  583. });
  584. // src/rendering/language/textColorLanguage.ts
  585. var import_language = require("@codemirror/language");
  586. var textColorLanguage = import_language.LRLanguage.define({
  587. name: "textColorLanguage",
  588. parser: parser.configure({})
  589. });
  590. // src/rendering/TextColorStateField.ts
  591. var import_common = require("@lezer/common");
  592. var import_language2 = require("@codemirror/language");
  593. var textColorParserField = import_state.StateField.define({
  594. create(state) {
  595. const parsedTree = textColorLanguage.parser.parse(state.doc.toString());
  596. return {
  597. tree: parsedTree,
  598. fragment: import_common.TreeFragment.addTree(parsedTree)
  599. };
  600. },
  601. update(value, transaction) {
  602. if (!transaction.docChanged) {
  603. return value;
  604. }
  605. const changed_ranges = [];
  606. transaction.changes.iterChangedRanges(
  607. (from, to, fromB, toB) => changed_ranges.push({ fromA: from, toA: to, fromB, toB })
  608. );
  609. let fragments = import_common.TreeFragment.applyChanges(value.fragment, changed_ranges);
  610. const tree = textColorLanguage.parser.parse(new import_language2.DocInput(transaction.state.doc), fragments);
  611. fragments = import_common.TreeFragment.addTree(tree, fragments);
  612. return { tree, fragment: fragments };
  613. }
  614. });
  615. // src/widgets/MarkerWidget.ts
  616. var import_view = require("@codemirror/view");
  617. var MarkerWidget = class extends import_view.WidgetType {
  618. constructor() {
  619. super();
  620. }
  621. toDOM(view) {
  622. const div = document.createElement("span");
  623. return div;
  624. }
  625. };
  626. // src/widgets/ColorWidget.ts
  627. var import_view2 = require("@codemirror/view");
  628. var import_obsidian5 = require("obsidian");
  629. // src/SettingsFacet.ts
  630. var import_state2 = require("@codemirror/state");
  631. var settingsFacet = import_state2.Facet.define(
  632. {
  633. combine: (inputs) => {
  634. if (inputs.length <= 0) {
  635. return DEFAULT_SETTINGS;
  636. }
  637. return inputs[inputs.length - 1];
  638. }
  639. }
  640. );
  641. // src/widgets/ColorWidget.ts
  642. var ColorWidget = class extends import_view2.WidgetType {
  643. constructor(id, from, to, expressionTo, themeName) {
  644. super();
  645. this.id = id;
  646. this.from = from;
  647. this.to = to;
  648. this.expressionTo = expressionTo;
  649. this.themeName = themeName;
  650. }
  651. toDOM(view) {
  652. const div = document.createElement("span");
  653. div.addClass(`${CSS_COLOR_PREFIX}${this.themeName}-${this.id}`);
  654. div.addClass("ftc-color-delimiter");
  655. div.innerText = "\u2B24";
  656. const settings = view.state.facet(settingsFacet);
  657. div.onclick = (event) => {
  658. if (this.menu != null) {
  659. }
  660. view.dispatch({
  661. selection: {
  662. anchor: this.from,
  663. head: this.to
  664. }
  665. });
  666. };
  667. div.onmouseover = (event) => {
  668. if (this.menu != null) {
  669. return;
  670. }
  671. this.menu = new import_obsidian5.Menu();
  672. getColors(settings).forEach((tColor) => {
  673. this.menu.addItem((item) => {
  674. item.setTitle(tColor.id).onClick((evt) => {
  675. view.dispatch({
  676. changes: {
  677. from: this.from,
  678. to: this.to,
  679. insert: tColor.id
  680. }
  681. });
  682. }).setIcon("palette");
  683. item.dom.addClass(tColor.className);
  684. });
  685. });
  686. this.menu.addItem((item) => {
  687. item.setTitle("Remove").setIcon("ban").onClick((evt) => {
  688. view.dispatch({
  689. changes: [
  690. {
  691. from: this.from - 3,
  692. to: this.to + 1,
  693. insert: ""
  694. },
  695. {
  696. from: this.expressionTo - 2,
  697. to: this.expressionTo,
  698. insert: ""
  699. }
  700. ]
  701. });
  702. });
  703. });
  704. const rect = div.getBoundingClientRect();
  705. this.menu.showAtPosition({ x: rect.left, y: rect.bottom });
  706. };
  707. return div;
  708. }
  709. };
  710. // src/rendering/TextColorViewPlugin.ts
  711. var import_obsidian6 = require("obsidian");
  712. var TextColorViewPlugin = class {
  713. constructor(view) {
  714. this.decorations = this.buildDecorations(view);
  715. }
  716. update(update) {
  717. var _a;
  718. if (!isLivePreview(update.state)) {
  719. if (this.decorations.size > 0) {
  720. this.decorations = new import_state3.RangeSetBuilder().finish();
  721. }
  722. this.notLivePreview = true;
  723. return;
  724. }
  725. if (this.notLivePreview) {
  726. this.notLivePreview = false;
  727. this.decorations = this.buildDecorations(update.view);
  728. return;
  729. }
  730. const selectionChanged = update.selectionSet && !((_a = update.view.plugin(import_obsidian6.livePreviewState)) == null ? void 0 : _a.mousedown);
  731. if (update.docChanged || update.viewportChanged || selectionChanged) {
  732. this.decorations = this.buildDecorations(update.view);
  733. }
  734. }
  735. destroy() {
  736. }
  737. buildDecorations(view) {
  738. const builder = new import_state3.RangeSetBuilder();
  739. for (let { from, to } of view.visibleRanges) {
  740. view.state.field(textColorParserField).tree.iterate({
  741. from,
  742. to,
  743. enter(node) {
  744. if (node.type.name == "TextColor") {
  745. return true;
  746. }
  747. if (node.type.name != "Expression") {
  748. return false;
  749. }
  750. handleExpression(node, builder, view.state);
  751. return false;
  752. }
  753. });
  754. }
  755. return builder.finish();
  756. }
  757. };
  758. function isLivePreview(state) {
  759. return state.field(import_obsidian6.editorLivePreviewField).valueOf();
  760. }
  761. function handleExpression(ExpressionNode, builder, state) {
  762. const from = ExpressionNode.from;
  763. let colorStack = [];
  764. const stateFrom = state.selection.main.from;
  765. const stateTo = state.selection.main.to;
  766. const settings = state.facet(settingsFacet);
  767. const frontmatterTheme = getThemeFromFrontmatter(state);
  768. const themeName = frontmatterTheme == "" ? getCurrentTheme(settings).name : frontmatterTheme;
  769. ExpressionNode.node.toTree().iterate({
  770. // toTree allocates a tree, this might be a point of optimization. TODO optimization
  771. enter(node) {
  772. var _a, _b, _c, _d;
  773. switch (node.type.name) {
  774. case "RMarker":
  775. let inside = (_a = colorStack.pop()) == null ? void 0 : _a.inside;
  776. if (inside) {
  777. return true;
  778. }
  779. builder.add(node.from + from, node.to + from, import_view3.Decoration.replace({ widget: new MarkerWidget(), block: false }));
  780. return true;
  781. case "EOF":
  782. case "ENDLN":
  783. (_b = colorStack.pop()) == null ? void 0 : _b.inside;
  784. return true;
  785. case "TcLeft":
  786. if ((_c = colorStack.last()) == null ? void 0 : _c.inside) {
  787. return true;
  788. }
  789. builder.add(node.from + from, node.to + from, import_view3.Decoration.replace({ widget: new MarkerWidget(), block: false }));
  790. return true;
  791. case "Color":
  792. let color = state.sliceDoc(from + node.from, from + node.to);
  793. colorStack[colorStack.length - 1].color = color;
  794. if (((_d = colorStack.last()) == null ? void 0 : _d.inside) && settings.interactiveDelimiters) {
  795. if (stateFrom <= from + node.to && stateTo >= from + node.from) {
  796. return true;
  797. }
  798. const widget = new ColorWidget(color, node.from + from, node.to + from, ExpressionNode.to, themeName);
  799. builder.add(node.from + from, node.to + from, import_view3.Decoration.replace({ widget, block: false }));
  800. }
  801. return true;
  802. case "Text":
  803. builder.add(node.from + from, node.to + from, import_view3.Decoration.mark({ class: `${CSS_COLOR_PREFIX}${themeName}-${colorStack[colorStack.length - 1].color}` }));
  804. return false;
  805. case "Expression":
  806. colorStack.push({ color: "", inside: stateFrom <= from + node.to && stateTo >= from + node.from });
  807. return true;
  808. default:
  809. break;
  810. }
  811. }
  812. });
  813. }
  814. function getThemeFromFrontmatter(state) {
  815. var _a;
  816. const editorInfo = state.field(import_obsidian6.editorInfoField);
  817. const file = editorInfo.file;
  818. if (!file) {
  819. return "";
  820. }
  821. const frontmatter = (_a = editorInfo.app.metadataCache.getFileCache(file)) == null ? void 0 : _a.frontmatter;
  822. if (!frontmatter) {
  823. return "";
  824. }
  825. const name = frontmatter["ftcTheme"];
  826. return name ? name : "";
  827. }
  828. var pluginSpec = {
  829. decorations: (value) => value.decorations
  830. };
  831. var textColorViewPlugin = import_view3.ViewPlugin.fromClass(
  832. TextColorViewPlugin,
  833. pluginSpec
  834. );
  835. // src/utils/regularExpressions.ts
  836. var PREFIX = /\~\=\{\S+\}/g;
  837. var SUFFIX = /\=\~/g;
  838. // src/rendering/TextColorPostProcessor.ts
  839. var textColorPostProcessor = (el, context, settings) => {
  840. if (!el.innerHTML.match(PREFIX)) {
  841. return;
  842. }
  843. let themeName = context.frontmatter ? context.frontmatter["ftcTheme"] : null;
  844. themeName = themeName ? themeName : getCurrentTheme(settings).name;
  845. const emergencyCopy = el.cloneNode(true);
  846. try {
  847. rebuildNode(el, themeName);
  848. } catch (e) {
  849. console.error(`fatal in rebuildNode: ${e}`);
  850. el.childNodes.forEach((c) => {
  851. var _a;
  852. (_a = c.parentNode) == null ? void 0 : _a.removeChild(c);
  853. });
  854. emergencyCopy.childNodes.forEach((c) => {
  855. el.appendChild(c);
  856. });
  857. }
  858. return;
  859. };
  860. function rebuildNode(node, themeName, level = 0, nodeStack = []) {
  861. var _a, _b, _c, _d;
  862. if (node.nodeName == "CODE") {
  863. return node;
  864. }
  865. if (level > 1e3) {
  866. console.error("fatal: reached depth 1000 in recursion");
  867. }
  868. let lastLength = node.childNodes.length;
  869. for (let i = 0; i < node.childNodes.length; i++) {
  870. lastLength = node.childNodes.length;
  871. let childNode = node.childNodes.item(i);
  872. const text = childNode.nodeValue;
  873. if (nodeStack.last() != void 0 && nodeStack.last() != childNode && !(childNode.compareDocumentPosition(nodeStack.last()) & Node.DOCUMENT_POSITION_CONTAINS)) {
  874. (_a = childNode.parentNode) == null ? void 0 : _a.removeChild(childNode);
  875. (_b = nodeStack.last()) == null ? void 0 : _b.appendChild(childNode);
  876. if (lastLength > node.childNodes.length) {
  877. i -= lastLength - node.childNodes.length;
  878. }
  879. }
  880. if (childNode.nodeType != Node.TEXT_NODE) {
  881. childNode = rebuildNode(childNode, themeName, level + 1, nodeStack);
  882. continue;
  883. }
  884. if (text == null || text == "") {
  885. continue;
  886. }
  887. let prefix = GetFirstMatch(text, PREFIX);
  888. let suffix = GetFirstMatch(text, SUFFIX);
  889. if (prefix == null && suffix == null) {
  890. continue;
  891. }
  892. let nextPrefixPosition = prefix != null ? prefix.index : Number.POSITIVE_INFINITY;
  893. let nextSuffixPosition = suffix != null ? suffix.index : Number.POSITIVE_INFINITY;
  894. if (nextPrefixPosition == nextSuffixPosition) {
  895. console.error(`fatal: nextPrefixPosition and nextSuffixPosition are the same but not infinity!!: ${nextPrefixPosition}`);
  896. return node;
  897. }
  898. if (nextPrefixPosition < nextSuffixPosition) {
  899. prefix = prefix;
  900. let textBeforeDelim2 = text.slice(0, nextPrefixPosition);
  901. let textAfterDelim2 = text.slice(prefix.end);
  902. let prefixContent = prefix.value;
  903. let color = prefixContent.slice(3, prefixContent.length - 1);
  904. let colorSpan = document.createElement("span");
  905. colorSpan.addClass(`${CSS_COLOR_PREFIX}${themeName}-${color}`);
  906. childNode.nodeValue = textBeforeDelim2;
  907. (_c = childNode.parentNode) == null ? void 0 : _c.insertAfter(colorSpan, childNode);
  908. let newNode2 = document.createTextNode(textAfterDelim2);
  909. colorSpan.appendChild(newNode2);
  910. nodeStack.push(colorSpan);
  911. continue;
  912. }
  913. let textBeforeDelim = text.slice(0, nextSuffixPosition);
  914. let textAfterDelim = text.slice(suffix.end);
  915. childNode.nodeValue = textBeforeDelim;
  916. let prevNode = nodeStack.pop();
  917. let newNode = document.createTextNode(textAfterDelim);
  918. (_d = prevNode.parentNode) == null ? void 0 : _d.insertAfter(newNode, prevNode);
  919. continue;
  920. }
  921. return node;
  922. }
  923. function GetFirstMatch(text, regex) {
  924. const regexCopy = new RegExp(regex.source, "g");
  925. const matches = [];
  926. let m = regexCopy.exec(text);
  927. if (m !== null) {
  928. return {
  929. index: m.index,
  930. value: m[0],
  931. end: m.index + m[0].length
  932. };
  933. }
  934. return null;
  935. }
  936. // main.ts
  937. var import_state4 = require("@codemirror/state");
  938. var import_view4 = require("@codemirror/view");
  939. // src/color/TextColorFunctions.ts
  940. function applyColor(tColor, editor) {
  941. let prefix = `~={${tColor.id}}`;
  942. let suffix = `=~`;
  943. if (!editor.somethingSelected()) {
  944. editor.replaceSelection(prefix);
  945. let pos = editor.getCursor();
  946. editor.replaceSelection(suffix);
  947. editor.setCursor(pos);
  948. return;
  949. }
  950. let selected = editor.getSelection();
  951. let coloredText = `${prefix}${selected}${suffix}`;
  952. editor.replaceSelection(coloredText);
  953. }
  954. function removeColor(editor, view) {
  955. var _a, _b;
  956. const tree = view.state.field(textColorParserField).tree;
  957. let node = tree.resolveInner(view.state.selection.main.head);
  958. while (node.parent != null) {
  959. if (node.type.name != "Expression") {
  960. node = node.parent;
  961. continue;
  962. }
  963. const TcLeft = node.getChild("TcLeft");
  964. const Rmarker = (_b = (_a = node.getChild("TcRight")) == null ? void 0 : _a.getChild("REnd")) == null ? void 0 : _b.getChild("RMarker");
  965. view.dispatch({
  966. changes: [
  967. {
  968. from: TcLeft ? TcLeft.from : 0,
  969. to: TcLeft ? TcLeft.to : 0,
  970. insert: ""
  971. },
  972. {
  973. from: Rmarker ? Rmarker.from : 0,
  974. to: Rmarker ? Rmarker.to : 0,
  975. insert: ""
  976. }
  977. ]
  978. });
  979. return;
  980. }
  981. return;
  982. }
  983. // src/utils/ColorSuggestModal.ts
  984. var import_obsidian7 = require("obsidian");
  985. var ColorSuggestModal = class extends import_obsidian7.SuggestModal {
  986. constructor(app, colors, editor) {
  987. super(app);
  988. this.colors = colors;
  989. this.editor = editor;
  990. }
  991. getSuggestions(query) {
  992. return this.colors.filter((tColor) => tColor.id.startsWith(query) || tColor.keybind == query);
  993. }
  994. renderSuggestion(tColor, el) {
  995. let div = el.createDiv();
  996. div.innerText = tColor.id;
  997. div.setAttr("style", tColor.getCssInlineStyle());
  998. }
  999. onChooseSuggestion(tColor, evt) {
  1000. applyColor(tColor, this.editor);
  1001. }
  1002. };
  1003. // main.ts
  1004. var MAX_MENU_ITEMS = 10;
  1005. var FastTextColorPlugin = class extends import_obsidian8.Plugin {
  1006. async onload() {
  1007. await this.loadSettings();
  1008. this.registerEditorExtension(textColorParserField);
  1009. this.registerEditorExtension(textColorViewPlugin);
  1010. this.registerMarkdownPostProcessor((el, ctx) => {
  1011. textColorPostProcessor(el, ctx, this.settings);
  1012. }, -1e3);
  1013. this.registerMarkdownPostProcessor((el, ctx) => {
  1014. textColorPostProcessor(el, ctx, this.settings);
  1015. }, 1e3);
  1016. this.settingsCompartment = new import_state4.Compartment();
  1017. this.settingsExtension = this.settingsCompartment.of(settingsFacet.of(this.settings));
  1018. this.registerEditorExtension(this.settingsExtension);
  1019. this.registerEditorExtension(
  1020. import_state4.Prec.high(
  1021. import_view4.keymap.of([
  1022. {
  1023. key: "Tab",
  1024. run: (editorView) => this.jumpOut(editorView)
  1025. }
  1026. ])
  1027. )
  1028. );
  1029. this.addCommand({
  1030. id: "change-text-color",
  1031. name: "Change text color",
  1032. editorCallback: (editor) => {
  1033. this.openColorMenu(editor);
  1034. }
  1035. });
  1036. this.addCommand({
  1037. id: "remove-text-color",
  1038. name: "Remove text color",
  1039. editorCallback: (editor, view) => {
  1040. const editorView = view.editor.cm;
  1041. removeColor(editor, editorView);
  1042. }
  1043. });
  1044. this.registerEvent(
  1045. this.app.workspace.on("editor-menu", (menu, editor, view) => {
  1046. if (editor.getSelection() == "") {
  1047. return;
  1048. }
  1049. menu.addItem((item) => {
  1050. item.setSection("selection").setTitle("Color").setIcon("palette");
  1051. const submenu = item.setSubmenu();
  1052. getColors(this.settings).forEach((tColor) => {
  1053. submenu.addItem((subitem) => {
  1054. subitem.setTitle(tColor.id).setIcon("circle").onClick((evt) => {
  1055. applyColor(tColor, editor);
  1056. });
  1057. subitem.dom.addClass(tColor.className);
  1058. subitem.iconEl.addClass(tColor.className);
  1059. });
  1060. });
  1061. });
  1062. })
  1063. );
  1064. this.addSettingTab(new FastTextColorPluginSettingTab(this.app, this));
  1065. this.setCssVariables();
  1066. }
  1067. onunload() {
  1068. this.styleElement.remove();
  1069. this.closeColorMenu();
  1070. }
  1071. async loadSettings() {
  1072. const rawSettings = await this.loadData();
  1073. if (rawSettings && +rawSettings.version < +SETTINGS_VERSION) {
  1074. console.log("outdated Settings! Trying to update.");
  1075. this.settings = updateSettings(rawSettings);
  1076. await this.saveData(this.settings);
  1077. return;
  1078. }
  1079. this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
  1080. for (let j = 0; j < this.settings.themes.length; j++) {
  1081. const colors = getColors(this.settings, j);
  1082. for (let i = 0; i < colors.length; i++) {
  1083. let obj = colors[i];
  1084. 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);
  1085. }
  1086. }
  1087. }
  1088. async saveSettings() {
  1089. var _a;
  1090. await this.saveData(this.settings);
  1091. const view = this.app.workspace.getActiveViewOfType(import_obsidian8.MarkdownView);
  1092. const editorView = (_a = view == null ? void 0 : view.editor) == null ? void 0 : _a.cm;
  1093. if (editorView == null) {
  1094. return;
  1095. }
  1096. editorView.dispatch({
  1097. effects: this.settingsCompartment.reconfigure(settingsFacet.of(this.settings))
  1098. });
  1099. }
  1100. // create and open the color menu
  1101. /**
  1102. * opens the color menu and pushed the scope onto the keybindings.
  1103. *
  1104. * @param {Editor} editor - [TODO:description]
  1105. */
  1106. openColorMenu(editor) {
  1107. var _a;
  1108. if (!this.settings.useKeybindings) {
  1109. let modal = new ColorSuggestModal(this.app, getColors(this.settings), editor);
  1110. modal.open();
  1111. return;
  1112. }
  1113. if (this.colorMenu != null) {
  1114. return;
  1115. }
  1116. this.colorMenu = createDiv();
  1117. if (!this.colorMenu) {
  1118. new import_obsidian8.Notice("could not create Colormenu!");
  1119. return;
  1120. }
  1121. let attributes = `bottom: 8.25em; grid-template-columns: ${"1fr ".repeat(getColors(this.settings).length)}`;
  1122. this.colorMenu.setAttribute("style", attributes);
  1123. this.colorMenu.setAttribute("id", "fast-color-menu");
  1124. this.colorMenu.addClass("fast-color-menu");
  1125. (_a = document.body.querySelector(".mod-vertical.mod-root")) == null ? void 0 : _a.insertAdjacentElement("afterbegin", this.colorMenu);
  1126. for (let i = 0; i < Math.min(getColors(this.settings).length, MAX_MENU_ITEMS); i++) {
  1127. this.createColorItem(this.colorMenu, getColors(this.settings)[i], i + 1, editor);
  1128. }
  1129. this.colorMenu.setAttribute("style", `left: calc(50% - ${this.colorMenu.offsetWidth}px / 2); ${attributes}`);
  1130. if (!this.settings.useKeybindings) {
  1131. return;
  1132. }
  1133. this.constructScope(editor);
  1134. this.app.keymap.pushScope(this.scope);
  1135. }
  1136. closeColorMenu() {
  1137. if (this.colorMenu) {
  1138. this.colorMenu.remove();
  1139. this.colorMenu = null;
  1140. }
  1141. this.app.keymap.popScope(this.scope);
  1142. }
  1143. constructScope(editor) {
  1144. this.scope = new import_obsidian8.Scope();
  1145. let { scope } = this;
  1146. for (let i = 0; i < getColors(this.settings).length; i++) {
  1147. const tColor = getColors(this.settings)[i];
  1148. scope.register([], tColor.keybind, (event) => {
  1149. if (event.isComposing) {
  1150. return true;
  1151. }
  1152. applyColor(tColor, editor);
  1153. this.closeColorMenu();
  1154. return false;
  1155. });
  1156. }
  1157. scope.register([], "Escape", (event) => {
  1158. if (event.isComposing) {
  1159. return true;
  1160. }
  1161. this.closeColorMenu();
  1162. return false;
  1163. });
  1164. scope.register([], "Delete", (event) => {
  1165. if (event.isComposing) {
  1166. return true;
  1167. }
  1168. this.closeColorMenu();
  1169. return false;
  1170. });
  1171. scope.register([], "Backspace", (event) => {
  1172. if (event.isComposing) {
  1173. return true;
  1174. }
  1175. this.closeColorMenu();
  1176. return false;
  1177. });
  1178. }
  1179. /**
  1180. * Move the cursor behind the next end marker.
  1181. *
  1182. * @param {EditorView} editorView
  1183. * @returns {boolean} true if jump possible.
  1184. */
  1185. jumpOut(editorView) {
  1186. var _a;
  1187. const state = editorView.state;
  1188. const tree = state.field(textColorParserField).tree;
  1189. const editor = (_a = this.app.workspace.getActiveViewOfType(import_obsidian8.MarkdownView)) == null ? void 0 : _a.editor;
  1190. if (!editor) {
  1191. return false;
  1192. }
  1193. let inner = tree.resolve(state.selection.main.head);
  1194. if (inner.type.name == "Text" && inner.parent != null) {
  1195. inner = inner.parent;
  1196. }
  1197. if (inner.type.name != "TcRight") {
  1198. return false;
  1199. }
  1200. editor.setCursor(editor.offsetToPos(inner.to));
  1201. return true;
  1202. }
  1203. createColorItem(menu, tColor, counter, editor) {
  1204. new import_obsidian8.ButtonComponent(menu).setButtonText(`${tColor.keybind}`).setClass("fast-color-menu-item").onClick(() => {
  1205. let n = new import_obsidian8.Notice("activated color");
  1206. n.noticeEl.setAttr("style", `background-color: ${tColor.color}`);
  1207. applyColor(tColor, editor);
  1208. this.closeColorMenu();
  1209. }).buttonEl.setAttr("style", `background-color: ${tColor.color}`);
  1210. }
  1211. /**
  1212. * creates the stylesheet needed for the colors in the root of the document.
  1213. * A different set of classes is created for each theme.
  1214. *
  1215. */
  1216. setCssVariables() {
  1217. if (!this.styleElement) {
  1218. const root = document.querySelector(":root");
  1219. if (!root) {
  1220. return;
  1221. }
  1222. this.styleElement = root.createEl("style");
  1223. this.styleElement.id = "fast-text-color-stylesheet";
  1224. }
  1225. this.styleElement.innerText = "";
  1226. for (let i = 0; i < this.settings.themes.length; i++) {
  1227. getColors(this.settings, i).forEach((tColor) => {
  1228. const theme = this.settings.themes[i];
  1229. const className = `.${CSS_COLOR_PREFIX}${theme.name}-${tColor.id}`;
  1230. let cssClass = `${className} {
  1231. ${tColor.getInnerCss()}}`;
  1232. this.styleElement.innerText += cssClass + "\n";
  1233. });
  1234. }
  1235. }
  1236. };