main.js
   1  "use strict";
   2  var __create = Object.create;
   3  var __defProp = Object.defineProperty;
   4  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
   5  var __getOwnPropNames = Object.getOwnPropertyNames;
   6  var __getProtoOf = Object.getPrototypeOf;
   7  var __hasOwnProp = Object.prototype.hasOwnProperty;
   8  var __commonJS = (cb, mod) => function __require() {
   9    return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
  10  };
  11  var __export = (target, all) => {
  12    for (var name in all)
  13      __defProp(target, name, { get: all[name], enumerable: true });
  14  };
  15  var __copyProps = (to, from, except, desc) => {
  16    if (from && typeof from === "object" || typeof from === "function") {
  17      for (let key of __getOwnPropNames(from))
  18        if (!__hasOwnProp.call(to, key) && key !== except)
  19          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  20    }
  21    return to;
  22  };
  23  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  24    // If the importer is in node compatibility mode or this is not an ESM
  25    // file that has been converted to a CommonJS file using a Babel-
  26    // compatible transform (i.e. "__esModule" has not been set), then set
  27    // "default" to the CommonJS "module.exports" for node compatibility.
  28    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  29    mod
  30  ));
  31  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  32  
  33  // node_modules/obsidian-daily-notes-interface/dist/main.js
  34  var require_main = __commonJS({
  35    "node_modules/obsidian-daily-notes-interface/dist/main.js"(exports) {
  36      "use strict";
  37      Object.defineProperty(exports, "__esModule", { value: true });
  38      var obsidian = require("obsidian");
  39      var DEFAULT_DAILY_NOTE_FORMAT = "YYYY-MM-DD";
  40      var DEFAULT_WEEKLY_NOTE_FORMAT = "gggg-[W]ww";
  41      var DEFAULT_MONTHLY_NOTE_FORMAT = "YYYY-MM";
  42      var DEFAULT_QUARTERLY_NOTE_FORMAT = "YYYY-[Q]Q";
  43      var DEFAULT_YEARLY_NOTE_FORMAT = "YYYY";
  44      function shouldUsePeriodicNotesSettings(periodicity) {
  45        var _a, _b;
  46        const periodicNotes = window.app.plugins.getPlugin("periodic-notes");
  47        return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a[periodicity]) == null ? void 0 : _b.enabled);
  48      }
  49      function getDailyNoteSettings2() {
  50        var _a, _b, _c, _d;
  51        try {
  52          const { internalPlugins, plugins } = window.app;
  53          if (shouldUsePeriodicNotesSettings("daily")) {
  54            const { format: format2, folder: folder2, template: template2 } = ((_b = (_a = plugins.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.daily) || {};
  55            return {
  56              format: format2 || DEFAULT_DAILY_NOTE_FORMAT,
  57              folder: (folder2 == null ? void 0 : folder2.trim()) || "",
  58              template: (template2 == null ? void 0 : template2.trim()) || ""
  59            };
  60          }
  61          const { folder, format, template } = ((_d = (_c = internalPlugins.getPluginById("daily-notes")) == null ? void 0 : _c.instance) == null ? void 0 : _d.options) || {};
  62          return {
  63            format: format || DEFAULT_DAILY_NOTE_FORMAT,
  64            folder: (folder == null ? void 0 : folder.trim()) || "",
  65            template: (template == null ? void 0 : template.trim()) || ""
  66          };
  67        } catch (err) {
  68          console.info("No custom daily note settings found!", err);
  69        }
  70      }
  71      function getWeeklyNoteSettings() {
  72        var _a, _b, _c, _d, _e, _f, _g;
  73        try {
  74          const pluginManager = window.app.plugins;
  75          const calendarSettings = (_a = pluginManager.getPlugin("calendar")) == null ? void 0 : _a.options;
  76          const periodicNotesSettings = (_c = (_b = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _b.settings) == null ? void 0 : _c.weekly;
  77          if (shouldUsePeriodicNotesSettings("weekly")) {
  78            return {
  79              format: periodicNotesSettings.format || DEFAULT_WEEKLY_NOTE_FORMAT,
  80              folder: ((_d = periodicNotesSettings.folder) == null ? void 0 : _d.trim()) || "",
  81              template: ((_e = periodicNotesSettings.template) == null ? void 0 : _e.trim()) || ""
  82            };
  83          }
  84          const settings = calendarSettings || {};
  85          return {
  86            format: settings.weeklyNoteFormat || DEFAULT_WEEKLY_NOTE_FORMAT,
  87            folder: ((_f = settings.weeklyNoteFolder) == null ? void 0 : _f.trim()) || "",
  88            template: ((_g = settings.weeklyNoteTemplate) == null ? void 0 : _g.trim()) || ""
  89          };
  90        } catch (err) {
  91          console.info("No custom weekly note settings found!", err);
  92        }
  93      }
  94      function getMonthlyNoteSettings() {
  95        var _a, _b, _c, _d;
  96        const pluginManager = window.app.plugins;
  97        try {
  98          const settings = shouldUsePeriodicNotesSettings("monthly") && ((_b = (_a = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.monthly) || {};
  99          return {
 100            format: settings.format || DEFAULT_MONTHLY_NOTE_FORMAT,
 101            folder: ((_c = settings.folder) == null ? void 0 : _c.trim()) || "",
 102            template: ((_d = settings.template) == null ? void 0 : _d.trim()) || ""
 103          };
 104        } catch (err) {
 105          console.info("No custom monthly note settings found!", err);
 106        }
 107      }
 108      function getQuarterlyNoteSettings() {
 109        var _a, _b, _c, _d;
 110        const pluginManager = window.app.plugins;
 111        try {
 112          const settings = shouldUsePeriodicNotesSettings("quarterly") && ((_b = (_a = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.quarterly) || {};
 113          return {
 114            format: settings.format || DEFAULT_QUARTERLY_NOTE_FORMAT,
 115            folder: ((_c = settings.folder) == null ? void 0 : _c.trim()) || "",
 116            template: ((_d = settings.template) == null ? void 0 : _d.trim()) || ""
 117          };
 118        } catch (err) {
 119          console.info("No custom quarterly note settings found!", err);
 120        }
 121      }
 122      function getYearlyNoteSettings() {
 123        var _a, _b, _c, _d;
 124        const pluginManager = window.app.plugins;
 125        try {
 126          const settings = shouldUsePeriodicNotesSettings("yearly") && ((_b = (_a = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.yearly) || {};
 127          return {
 128            format: settings.format || DEFAULT_YEARLY_NOTE_FORMAT,
 129            folder: ((_c = settings.folder) == null ? void 0 : _c.trim()) || "",
 130            template: ((_d = settings.template) == null ? void 0 : _d.trim()) || ""
 131          };
 132        } catch (err) {
 133          console.info("No custom yearly note settings found!", err);
 134        }
 135      }
 136      function join(...partSegments) {
 137        let parts = [];
 138        for (let i = 0, l = partSegments.length; i < l; i++) {
 139          parts = parts.concat(partSegments[i].split("/"));
 140        }
 141        const newParts = [];
 142        for (let i = 0, l = parts.length; i < l; i++) {
 143          const part = parts[i];
 144          if (!part || part === ".")
 145            continue;
 146          else
 147            newParts.push(part);
 148        }
 149        if (parts[0] === "")
 150          newParts.unshift("");
 151        return newParts.join("/");
 152      }
 153      function basename(fullPath) {
 154        let base = fullPath.substring(fullPath.lastIndexOf("/") + 1);
 155        if (base.lastIndexOf(".") != -1)
 156          base = base.substring(0, base.lastIndexOf("."));
 157        return base;
 158      }
 159      async function ensureFolderExists(path) {
 160        const dirs = path.replace(/\\/g, "/").split("/");
 161        dirs.pop();
 162        if (dirs.length) {
 163          const dir = join(...dirs);
 164          if (!window.app.vault.getAbstractFileByPath(dir)) {
 165            await window.app.vault.createFolder(dir);
 166          }
 167        }
 168      }
 169      async function getNotePath(directory, filename) {
 170        if (!filename.endsWith(".md")) {
 171          filename += ".md";
 172        }
 173        const path = obsidian.normalizePath(join(directory, filename));
 174        await ensureFolderExists(path);
 175        return path;
 176      }
 177      async function getTemplateInfo(template) {
 178        const { metadataCache, vault } = window.app;
 179        const templatePath = obsidian.normalizePath(template);
 180        if (templatePath === "/") {
 181          return Promise.resolve(["", null]);
 182        }
 183        try {
 184          const templateFile = metadataCache.getFirstLinkpathDest(templatePath, "");
 185          const contents = await vault.cachedRead(templateFile);
 186          const IFoldInfo = window.app.foldManager.load(templateFile);
 187          return [contents, IFoldInfo];
 188        } catch (err) {
 189          console.error(`Failed to read the daily note template '${templatePath}'`, err);
 190          new obsidian.Notice("Failed to read the daily note template");
 191          return ["", null];
 192        }
 193      }
 194      function getDateUID(date, granularity = "day") {
 195        const ts = date.clone().startOf(granularity).format();
 196        return `${granularity}-${ts}`;
 197      }
 198      function removeEscapedCharacters(format) {
 199        return format.replace(/\[[^\]]*\]/g, "");
 200      }
 201      function isFormatAmbiguous(format, granularity) {
 202        if (granularity === "week") {
 203          const cleanFormat = removeEscapedCharacters(format);
 204          return /w{1,2}/i.test(cleanFormat) && (/M{1,4}/.test(cleanFormat) || /D{1,4}/.test(cleanFormat));
 205        }
 206        return false;
 207      }
 208      function getDateFromFile(file, granularity) {
 209        return getDateFromFilename(file.basename, granularity);
 210      }
 211      function getDateFromPath(path, granularity) {
 212        return getDateFromFilename(basename(path), granularity);
 213      }
 214      function getDateFromFilename(filename, granularity) {
 215        const getSettings = {
 216          day: getDailyNoteSettings2,
 217          week: getWeeklyNoteSettings,
 218          month: getMonthlyNoteSettings,
 219          quarter: getQuarterlyNoteSettings,
 220          year: getYearlyNoteSettings
 221        };
 222        const format = getSettings[granularity]().format.split("/").pop();
 223        const noteDate = window.moment(filename, format, true);
 224        if (!noteDate.isValid()) {
 225          return null;
 226        }
 227        if (isFormatAmbiguous(format, granularity)) {
 228          if (granularity === "week") {
 229            const cleanFormat = removeEscapedCharacters(format);
 230            if (/w{1,2}/i.test(cleanFormat)) {
 231              return window.moment(
 232                filename,
 233                // If format contains week, remove day & month formatting
 234                format.replace(/M{1,4}/g, "").replace(/D{1,4}/g, ""),
 235                false
 236              );
 237            }
 238          }
 239        }
 240        return noteDate;
 241      }
 242      var DailyNotesFolderMissingError = class extends Error {
 243      };
 244      async function createDailyNote(date) {
 245        const app = window.app;
 246        const { vault } = app;
 247        const moment2 = window.moment;
 248        const { template, format, folder } = getDailyNoteSettings2();
 249        const [templateContents, IFoldInfo] = await getTemplateInfo(template);
 250        const filename = date.format(format);
 251        const normalizedPath = await getNotePath(folder, filename);
 252        try {
 253          const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, moment2().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename).replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
 254            const now = moment2();
 255            const currentDate = date.clone().set({
 256              hour: now.get("hour"),
 257              minute: now.get("minute"),
 258              second: now.get("second")
 259            });
 260            if (calc) {
 261              currentDate.add(parseInt(timeDelta, 10), unit);
 262            }
 263            if (momentFormat) {
 264              return currentDate.format(momentFormat.substring(1).trim());
 265            }
 266            return currentDate.format(format);
 267          }).replace(/{{\s*yesterday\s*}}/gi, date.clone().subtract(1, "day").format(format)).replace(/{{\s*tomorrow\s*}}/gi, date.clone().add(1, "d").format(format)));
 268          app.foldManager.save(createdFile, IFoldInfo);
 269          return createdFile;
 270        } catch (err) {
 271          console.error(`Failed to create file: '${normalizedPath}'`, err);
 272          new obsidian.Notice("Unable to create new file.");
 273        }
 274      }
 275      function getDailyNote(date, dailyNotes) {
 276        var _a;
 277        return (_a = dailyNotes[getDateUID(date, "day")]) != null ? _a : null;
 278      }
 279      function getAllDailyNotes() {
 280        const { vault } = window.app;
 281        const { folder } = getDailyNoteSettings2();
 282        const dailyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
 283        if (!dailyNotesFolder) {
 284          throw new DailyNotesFolderMissingError("Failed to find daily notes folder");
 285        }
 286        const dailyNotes = {};
 287        obsidian.Vault.recurseChildren(dailyNotesFolder, (note) => {
 288          if (note instanceof obsidian.TFile) {
 289            const date = getDateFromFile(note, "day");
 290            if (date) {
 291              const dateString = getDateUID(date, "day");
 292              dailyNotes[dateString] = note;
 293            }
 294          }
 295        });
 296        return dailyNotes;
 297      }
 298      var WeeklyNotesFolderMissingError = class extends Error {
 299      };
 300      function getDaysOfWeek() {
 301        const { moment: moment2 } = window;
 302        let weekStart = moment2.localeData()._week.dow;
 303        const daysOfWeek = [
 304          "sunday",
 305          "monday",
 306          "tuesday",
 307          "wednesday",
 308          "thursday",
 309          "friday",
 310          "saturday"
 311        ];
 312        while (weekStart) {
 313          daysOfWeek.push(daysOfWeek.shift());
 314          weekStart--;
 315        }
 316        return daysOfWeek;
 317      }
 318      function getDayOfWeekNumericalValue(dayOfWeekName) {
 319        return getDaysOfWeek().indexOf(dayOfWeekName.toLowerCase());
 320      }
 321      async function createWeeklyNote(date) {
 322        const { vault } = window.app;
 323        const { template, format, folder } = getWeeklyNoteSettings();
 324        const [templateContents, IFoldInfo] = await getTemplateInfo(template);
 325        const filename = date.format(format);
 326        const normalizedPath = await getNotePath(folder, filename);
 327        try {
 328          const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
 329            const now = window.moment();
 330            const currentDate = date.clone().set({
 331              hour: now.get("hour"),
 332              minute: now.get("minute"),
 333              second: now.get("second")
 334            });
 335            if (calc) {
 336              currentDate.add(parseInt(timeDelta, 10), unit);
 337            }
 338            if (momentFormat) {
 339              return currentDate.format(momentFormat.substring(1).trim());
 340            }
 341            return currentDate.format(format);
 342          }).replace(/{{\s*title\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*:(.*?)}}/gi, (_, dayOfWeek, momentFormat) => {
 343            const day = getDayOfWeekNumericalValue(dayOfWeek);
 344            return date.weekday(day).format(momentFormat.trim());
 345          }));
 346          window.app.foldManager.save(createdFile, IFoldInfo);
 347          return createdFile;
 348        } catch (err) {
 349          console.error(`Failed to create file: '${normalizedPath}'`, err);
 350          new obsidian.Notice("Unable to create new file.");
 351        }
 352      }
 353      function getWeeklyNote(date, weeklyNotes) {
 354        var _a;
 355        return (_a = weeklyNotes[getDateUID(date, "week")]) != null ? _a : null;
 356      }
 357      function getAllWeeklyNotes() {
 358        const weeklyNotes = {};
 359        if (!appHasWeeklyNotesPluginLoaded()) {
 360          return weeklyNotes;
 361        }
 362        const { vault } = window.app;
 363        const { folder } = getWeeklyNoteSettings();
 364        const weeklyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
 365        if (!weeklyNotesFolder) {
 366          throw new WeeklyNotesFolderMissingError("Failed to find weekly notes folder");
 367        }
 368        obsidian.Vault.recurseChildren(weeklyNotesFolder, (note) => {
 369          if (note instanceof obsidian.TFile) {
 370            const date = getDateFromFile(note, "week");
 371            if (date) {
 372              const dateString = getDateUID(date, "week");
 373              weeklyNotes[dateString] = note;
 374            }
 375          }
 376        });
 377        return weeklyNotes;
 378      }
 379      var MonthlyNotesFolderMissingError = class extends Error {
 380      };
 381      async function createMonthlyNote(date) {
 382        const { vault } = window.app;
 383        const { template, format, folder } = getMonthlyNoteSettings();
 384        const [templateContents, IFoldInfo] = await getTemplateInfo(template);
 385        const filename = date.format(format);
 386        const normalizedPath = await getNotePath(folder, filename);
 387        try {
 388          const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
 389            const now = window.moment();
 390            const currentDate = date.clone().set({
 391              hour: now.get("hour"),
 392              minute: now.get("minute"),
 393              second: now.get("second")
 394            });
 395            if (calc) {
 396              currentDate.add(parseInt(timeDelta, 10), unit);
 397            }
 398            if (momentFormat) {
 399              return currentDate.format(momentFormat.substring(1).trim());
 400            }
 401            return currentDate.format(format);
 402          }).replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename));
 403          window.app.foldManager.save(createdFile, IFoldInfo);
 404          return createdFile;
 405        } catch (err) {
 406          console.error(`Failed to create file: '${normalizedPath}'`, err);
 407          new obsidian.Notice("Unable to create new file.");
 408        }
 409      }
 410      function getMonthlyNote(date, monthlyNotes) {
 411        var _a;
 412        return (_a = monthlyNotes[getDateUID(date, "month")]) != null ? _a : null;
 413      }
 414      function getAllMonthlyNotes() {
 415        const monthlyNotes = {};
 416        if (!appHasMonthlyNotesPluginLoaded()) {
 417          return monthlyNotes;
 418        }
 419        const { vault } = window.app;
 420        const { folder } = getMonthlyNoteSettings();
 421        const monthlyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
 422        if (!monthlyNotesFolder) {
 423          throw new MonthlyNotesFolderMissingError("Failed to find monthly notes folder");
 424        }
 425        obsidian.Vault.recurseChildren(monthlyNotesFolder, (note) => {
 426          if (note instanceof obsidian.TFile) {
 427            const date = getDateFromFile(note, "month");
 428            if (date) {
 429              const dateString = getDateUID(date, "month");
 430              monthlyNotes[dateString] = note;
 431            }
 432          }
 433        });
 434        return monthlyNotes;
 435      }
 436      var QuarterlyNotesFolderMissingError = class extends Error {
 437      };
 438      async function createQuarterlyNote(date) {
 439        const { vault } = window.app;
 440        const { template, format, folder } = getQuarterlyNoteSettings();
 441        const [templateContents, IFoldInfo] = await getTemplateInfo(template);
 442        const filename = date.format(format);
 443        const normalizedPath = await getNotePath(folder, filename);
 444        try {
 445          const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
 446            const now = window.moment();
 447            const currentDate = date.clone().set({
 448              hour: now.get("hour"),
 449              minute: now.get("minute"),
 450              second: now.get("second")
 451            });
 452            if (calc) {
 453              currentDate.add(parseInt(timeDelta, 10), unit);
 454            }
 455            if (momentFormat) {
 456              return currentDate.format(momentFormat.substring(1).trim());
 457            }
 458            return currentDate.format(format);
 459          }).replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename));
 460          window.app.foldManager.save(createdFile, IFoldInfo);
 461          return createdFile;
 462        } catch (err) {
 463          console.error(`Failed to create file: '${normalizedPath}'`, err);
 464          new obsidian.Notice("Unable to create new file.");
 465        }
 466      }
 467      function getQuarterlyNote(date, quarterly) {
 468        var _a;
 469        return (_a = quarterly[getDateUID(date, "quarter")]) != null ? _a : null;
 470      }
 471      function getAllQuarterlyNotes() {
 472        const quarterly = {};
 473        if (!appHasQuarterlyNotesPluginLoaded()) {
 474          return quarterly;
 475        }
 476        const { vault } = window.app;
 477        const { folder } = getQuarterlyNoteSettings();
 478        const quarterlyFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
 479        if (!quarterlyFolder) {
 480          throw new QuarterlyNotesFolderMissingError("Failed to find quarterly notes folder");
 481        }
 482        obsidian.Vault.recurseChildren(quarterlyFolder, (note) => {
 483          if (note instanceof obsidian.TFile) {
 484            const date = getDateFromFile(note, "quarter");
 485            if (date) {
 486              const dateString = getDateUID(date, "quarter");
 487              quarterly[dateString] = note;
 488            }
 489          }
 490        });
 491        return quarterly;
 492      }
 493      var YearlyNotesFolderMissingError = class extends Error {
 494      };
 495      async function createYearlyNote(date) {
 496        const { vault } = window.app;
 497        const { template, format, folder } = getYearlyNoteSettings();
 498        const [templateContents, IFoldInfo] = await getTemplateInfo(template);
 499        const filename = date.format(format);
 500        const normalizedPath = await getNotePath(folder, filename);
 501        try {
 502          const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
 503            const now = window.moment();
 504            const currentDate = date.clone().set({
 505              hour: now.get("hour"),
 506              minute: now.get("minute"),
 507              second: now.get("second")
 508            });
 509            if (calc) {
 510              currentDate.add(parseInt(timeDelta, 10), unit);
 511            }
 512            if (momentFormat) {
 513              return currentDate.format(momentFormat.substring(1).trim());
 514            }
 515            return currentDate.format(format);
 516          }).replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename));
 517          window.app.foldManager.save(createdFile, IFoldInfo);
 518          return createdFile;
 519        } catch (err) {
 520          console.error(`Failed to create file: '${normalizedPath}'`, err);
 521          new obsidian.Notice("Unable to create new file.");
 522        }
 523      }
 524      function getYearlyNote(date, yearlyNotes) {
 525        var _a;
 526        return (_a = yearlyNotes[getDateUID(date, "year")]) != null ? _a : null;
 527      }
 528      function getAllYearlyNotes() {
 529        const yearlyNotes = {};
 530        if (!appHasYearlyNotesPluginLoaded()) {
 531          return yearlyNotes;
 532        }
 533        const { vault } = window.app;
 534        const { folder } = getYearlyNoteSettings();
 535        const yearlyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
 536        if (!yearlyNotesFolder) {
 537          throw new YearlyNotesFolderMissingError("Failed to find yearly notes folder");
 538        }
 539        obsidian.Vault.recurseChildren(yearlyNotesFolder, (note) => {
 540          if (note instanceof obsidian.TFile) {
 541            const date = getDateFromFile(note, "year");
 542            if (date) {
 543              const dateString = getDateUID(date, "year");
 544              yearlyNotes[dateString] = note;
 545            }
 546          }
 547        });
 548        return yearlyNotes;
 549      }
 550      function appHasDailyNotesPluginLoaded() {
 551        var _a, _b;
 552        const { app } = window;
 553        const dailyNotesPlugin = app.internalPlugins.plugins["daily-notes"];
 554        if (dailyNotesPlugin && dailyNotesPlugin.enabled) {
 555          return true;
 556        }
 557        const periodicNotes = app.plugins.getPlugin("periodic-notes");
 558        return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.daily) == null ? void 0 : _b.enabled);
 559      }
 560      function appHasWeeklyNotesPluginLoaded() {
 561        var _a, _b;
 562        const { app } = window;
 563        if (app.plugins.getPlugin("calendar")) {
 564          return true;
 565        }
 566        const periodicNotes = app.plugins.getPlugin("periodic-notes");
 567        return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.weekly) == null ? void 0 : _b.enabled);
 568      }
 569      function appHasMonthlyNotesPluginLoaded() {
 570        var _a, _b;
 571        const { app } = window;
 572        const periodicNotes = app.plugins.getPlugin("periodic-notes");
 573        return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.monthly) == null ? void 0 : _b.enabled);
 574      }
 575      function appHasQuarterlyNotesPluginLoaded() {
 576        var _a, _b;
 577        const { app } = window;
 578        const periodicNotes = app.plugins.getPlugin("periodic-notes");
 579        return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.quarterly) == null ? void 0 : _b.enabled);
 580      }
 581      function appHasYearlyNotesPluginLoaded() {
 582        var _a, _b;
 583        const { app } = window;
 584        const periodicNotes = app.plugins.getPlugin("periodic-notes");
 585        return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.yearly) == null ? void 0 : _b.enabled);
 586      }
 587      function getPeriodicNoteSettings(granularity) {
 588        const getSettings = {
 589          day: getDailyNoteSettings2,
 590          week: getWeeklyNoteSettings,
 591          month: getMonthlyNoteSettings,
 592          quarter: getQuarterlyNoteSettings,
 593          year: getYearlyNoteSettings
 594        }[granularity];
 595        return getSettings();
 596      }
 597      function createPeriodicNote(granularity, date) {
 598        const createFn = {
 599          day: createDailyNote,
 600          month: createMonthlyNote,
 601          week: createWeeklyNote
 602        };
 603        return createFn[granularity](date);
 604      }
 605      exports.DEFAULT_DAILY_NOTE_FORMAT = DEFAULT_DAILY_NOTE_FORMAT;
 606      exports.DEFAULT_MONTHLY_NOTE_FORMAT = DEFAULT_MONTHLY_NOTE_FORMAT;
 607      exports.DEFAULT_QUARTERLY_NOTE_FORMAT = DEFAULT_QUARTERLY_NOTE_FORMAT;
 608      exports.DEFAULT_WEEKLY_NOTE_FORMAT = DEFAULT_WEEKLY_NOTE_FORMAT;
 609      exports.DEFAULT_YEARLY_NOTE_FORMAT = DEFAULT_YEARLY_NOTE_FORMAT;
 610      exports.appHasDailyNotesPluginLoaded = appHasDailyNotesPluginLoaded;
 611      exports.appHasMonthlyNotesPluginLoaded = appHasMonthlyNotesPluginLoaded;
 612      exports.appHasQuarterlyNotesPluginLoaded = appHasQuarterlyNotesPluginLoaded;
 613      exports.appHasWeeklyNotesPluginLoaded = appHasWeeklyNotesPluginLoaded;
 614      exports.appHasYearlyNotesPluginLoaded = appHasYearlyNotesPluginLoaded;
 615      exports.createDailyNote = createDailyNote;
 616      exports.createMonthlyNote = createMonthlyNote;
 617      exports.createPeriodicNote = createPeriodicNote;
 618      exports.createQuarterlyNote = createQuarterlyNote;
 619      exports.createWeeklyNote = createWeeklyNote;
 620      exports.createYearlyNote = createYearlyNote;
 621      exports.getAllDailyNotes = getAllDailyNotes;
 622      exports.getAllMonthlyNotes = getAllMonthlyNotes;
 623      exports.getAllQuarterlyNotes = getAllQuarterlyNotes;
 624      exports.getAllWeeklyNotes = getAllWeeklyNotes;
 625      exports.getAllYearlyNotes = getAllYearlyNotes;
 626      exports.getDailyNote = getDailyNote;
 627      exports.getDailyNoteSettings = getDailyNoteSettings2;
 628      exports.getDateFromFile = getDateFromFile;
 629      exports.getDateFromPath = getDateFromPath;
 630      exports.getDateUID = getDateUID;
 631      exports.getMonthlyNote = getMonthlyNote;
 632      exports.getMonthlyNoteSettings = getMonthlyNoteSettings;
 633      exports.getPeriodicNoteSettings = getPeriodicNoteSettings;
 634      exports.getQuarterlyNote = getQuarterlyNote;
 635      exports.getQuarterlyNoteSettings = getQuarterlyNoteSettings;
 636      exports.getTemplateInfo = getTemplateInfo;
 637      exports.getWeeklyNote = getWeeklyNote;
 638      exports.getWeeklyNoteSettings = getWeeklyNoteSettings;
 639      exports.getYearlyNote = getYearlyNote;
 640      exports.getYearlyNoteSettings = getYearlyNoteSettings;
 641    }
 642  });
 643  
 644  // src/main.ts
 645  var main_exports = {};
 646  __export(main_exports, {
 647    default: () => ThePlugin
 648  });
 649  module.exports = __toCommonJS(main_exports);
 650  var import_obsidian11 = require("obsidian");
 651  
 652  // src/ui/SettingsTab.ts
 653  var import_obsidian5 = require("obsidian");
 654  
 655  // src/features/themes.ts
 656  var import_obsidian3 = require("obsidian");
 657  
 658  // src/features/githubUtils.ts
 659  var import_obsidian = require("obsidian");
 660  var GITHUB_RAW_USERCONTENT_PATH = "https://raw.githubusercontent.com/";
 661  var isPrivateRepo = async (repository, debugLogging = true, personalAccessToken = "") => {
 662    const URL2 = `https://api.github.com/repos/${repository}`;
 663    try {
 664      const response = await (0, import_obsidian.request)({
 665        url: URL2,
 666        headers: personalAccessToken ? {
 667          Authorization: `Token ${personalAccessToken}`
 668        } : {}
 669      });
 670      const data = await JSON.parse(response);
 671      return data.private;
 672    } catch (e) {
 673      if (debugLogging)
 674        console.log("error in isPrivateRepo", URL2, e);
 675      return false;
 676    }
 677  };
 678  var grabReleaseFileFromRepository = async (repository, version, fileName, debugLogging = true, personalAccessToken = "") => {
 679    try {
 680      const isPrivate = await isPrivateRepo(repository, debugLogging, personalAccessToken);
 681      if (isPrivate) {
 682        const URL2 = `https://api.github.com/repos/${repository}/releases`;
 683        const response = await (0, import_obsidian.request)({
 684          url: URL2,
 685          headers: {
 686            Authorization: `Token ${personalAccessToken}`
 687          }
 688        });
 689        const data = await JSON.parse(response);
 690        const release = data.find((release2) => release2.tag_name === version);
 691        if (!release) {
 692          return null;
 693        }
 694        const asset = release.assets.find(
 695          (asset2) => asset2.name === fileName
 696        );
 697        if (!asset) {
 698          return null;
 699        }
 700        const download = await (0, import_obsidian.request)({
 701          url: asset.url,
 702          headers: {
 703            Authorization: `Token ${personalAccessToken}`,
 704            Accept: "application/octet-stream"
 705          }
 706        });
 707        return download === "Not Found" || download === `{"error":"Not Found"}` ? null : download;
 708      } else {
 709        const URL2 = `https://github.com/${repository}/releases/download/${version}/${fileName}`;
 710        const download = await (0, import_obsidian.request)({
 711          url: URL2,
 712          headers: personalAccessToken ? {
 713            Authorization: `Token ${personalAccessToken}`
 714          } : {}
 715        });
 716        return download === "Not Found" || download === `{"error":"Not Found"}` ? null : download;
 717      }
 718    } catch (error) {
 719      if (debugLogging)
 720        console.log("error in grabReleaseFileFromRepository", URL, error);
 721      return null;
 722    }
 723  };
 724  var grabManifestJsonFromRepository = async (repositoryPath, rootManifest = true, debugLogging = true, personalAccessToken = "") => {
 725    const manifestJsonPath = GITHUB_RAW_USERCONTENT_PATH + repositoryPath + (rootManifest ? "/HEAD/manifest.json" : "/HEAD/manifest-beta.json");
 726    if (debugLogging)
 727      console.log("grabManifestJsonFromRepository manifestJsonPath", manifestJsonPath);
 728    try {
 729      const response = await (0, import_obsidian.request)({
 730        url: manifestJsonPath,
 731        headers: personalAccessToken ? {
 732          Authorization: `Token ${personalAccessToken}`
 733        } : {}
 734      });
 735      if (debugLogging)
 736        console.log("grabManifestJsonFromRepository response", response);
 737      return response === "404: Not Found" ? null : await JSON.parse(response);
 738    } catch (error) {
 739      if (error !== "Error: Request failed, status 404" && debugLogging) {
 740        console.log(
 741          `error in grabManifestJsonFromRepository for ${manifestJsonPath}`,
 742          error
 743        );
 744      }
 745      return null;
 746    }
 747  };
 748  var grabCommmunityPluginList = async (debugLogging = true) => {
 749    const pluginListUrl = `https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-plugins.json`;
 750    try {
 751      const response = await (0, import_obsidian.request)({ url: pluginListUrl });
 752      return response === "404: Not Found" ? null : await JSON.parse(response);
 753    } catch (error) {
 754      if (debugLogging)
 755        console.log("error in grabCommmunityPluginList", error);
 756      return null;
 757    }
 758  };
 759  var grabCommmunityThemesList = async (debugLogging = true) => {
 760    const themesUrl = `https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-css-themes.json`;
 761    try {
 762      const response = await (0, import_obsidian.request)({ url: themesUrl });
 763      return response === "404: Not Found" ? null : await JSON.parse(response);
 764    } catch (error) {
 765      if (debugLogging)
 766        console.log("error in grabCommmunityThemesList", error);
 767      return null;
 768    }
 769  };
 770  var grabCommmunityThemeCssFile = async (repositoryPath, betaVersion = false, debugLogging) => {
 771    const themesUrl = `https://raw.githubusercontent.com/${repositoryPath}/HEAD/theme${betaVersion ? "-beta" : ""}.css`;
 772    try {
 773      const response = await (0, import_obsidian.request)({ url: themesUrl });
 774      return response === "404: Not Found" ? null : response;
 775    } catch (error) {
 776      if (debugLogging)
 777        console.log("error in grabCommmunityThemeCssFile", error);
 778      return null;
 779    }
 780  };
 781  var grabCommmunityThemeManifestFile = async (repositoryPath, debugLogging = true) => {
 782    const themesUrl = `https://raw.githubusercontent.com/${repositoryPath}/HEAD/manifest.json`;
 783    try {
 784      const response = await (0, import_obsidian.request)({ url: themesUrl });
 785      return response === "404: Not Found" ? null : response;
 786    } catch (error) {
 787      if (debugLogging)
 788        console.log("error in grabCommmunityThemeManifestFile", error);
 789      return null;
 790    }
 791  };
 792  var checksum = (str) => {
 793    let sum = 0;
 794    for (let i = 0; i < str.length; i++) {
 795      sum += str.charCodeAt(i);
 796    }
 797    return sum;
 798  };
 799  var checksumForString = (str) => {
 800    return checksum(str).toString();
 801  };
 802  var grabChecksumOfThemeCssFile = async (repositoryPath, betaVersion, debugLogging) => {
 803    const themeCss = await grabCommmunityThemeCssFile(
 804      repositoryPath,
 805      betaVersion,
 806      debugLogging
 807    );
 808    return themeCss ? checksumForString(themeCss) : "0";
 809  };
 810  var grabLastCommitInfoForFile = async (repositoryPath, path, debugLogging = true) => {
 811    const url = `https://api.github.com/repos/${repositoryPath}/commits?path=${path}&page=1&per_page=1`;
 812    try {
 813      const response = await (0, import_obsidian.request)({ url });
 814      return response === "404: Not Found" ? null : JSON.parse(response);
 815    } catch (error) {
 816      if (debugLogging)
 817        console.log("error in grabLastCommitInfoForAFile", error);
 818      return null;
 819    }
 820  };
 821  var grabLastCommitDateForFile = async (repositoryPath, path) => {
 822    var _a;
 823    const test = await grabLastCommitInfoForFile(repositoryPath, path);
 824    if (test && test.length > 0 && ((_a = test[0].commit.committer) == null ? void 0 : _a.date)) {
 825      return test[0].commit.committer.date;
 826    } else {
 827      return "";
 828    }
 829  };
 830  
 831  // src/settings.ts
 832  var DEFAULT_SETTINGS = {
 833    pluginList: [],
 834    pluginSubListFrozenVersion: [],
 835    themesList: [],
 836    updateAtStartup: true,
 837    updateThemesAtStartup: true,
 838    enableAfterInstall: true,
 839    loggingEnabled: false,
 840    loggingPath: "BRAT-log",
 841    loggingVerboseEnabled: false,
 842    debuggingMode: false,
 843    notificationsEnabled: true,
 844    personalAccessToken: ""
 845  };
 846  function addBetaPluginToList(plugin, repositoryPath, specifyVersion = "") {
 847    let save = false;
 848    if (!plugin.settings.pluginList.contains(repositoryPath)) {
 849      plugin.settings.pluginList.unshift(repositoryPath);
 850      save = true;
 851    }
 852    if (specifyVersion !== "" && plugin.settings.pluginSubListFrozenVersion.filter((x) => x.repo === repositoryPath).length === 0) {
 853      plugin.settings.pluginSubListFrozenVersion.unshift({
 854        repo: repositoryPath,
 855        version: specifyVersion
 856      });
 857      save = true;
 858    }
 859    if (save) {
 860      void plugin.saveSettings();
 861    }
 862  }
 863  function existBetaPluginInList(plugin, repositoryPath) {
 864    return plugin.settings.pluginList.contains(repositoryPath);
 865  }
 866  function addBetaThemeToList(plugin, repositoryPath, themeCss) {
 867    const newTheme = {
 868      repo: repositoryPath,
 869      lastUpdate: checksumForString(themeCss)
 870    };
 871    plugin.settings.themesList.unshift(newTheme);
 872    void plugin.saveSettings();
 873  }
 874  function existBetaThemeinInList(plugin, repositoryPath) {
 875    const testIfThemExists = plugin.settings.themesList.find(
 876      (t) => t.repo === repositoryPath
 877    );
 878    return testIfThemExists ? true : false;
 879  }
 880  function updateBetaThemeLastUpdateChecksum(plugin, repositoryPath, checksum2) {
 881    plugin.settings.themesList.forEach((t) => {
 882      if (t.repo === repositoryPath) {
 883        t.lastUpdate = checksum2;
 884        void plugin.saveSettings();
 885      }
 886    });
 887  }
 888  
 889  // src/utils/notifications.ts
 890  var import_obsidian2 = require("obsidian");
 891  function toastMessage(plugin, msg, timeoutInSeconds = 10, contextMenuCallback) {
 892    if (!plugin.settings.notificationsEnabled)
 893      return;
 894    const additionalInfo = contextMenuCallback ? import_obsidian2.Platform.isDesktop ? "(click=dismiss, right-click=Info)" : "(click=dismiss)" : "";
 895    const newNotice = new import_obsidian2.Notice(
 896      `BRAT
 897  ${msg}
 898  ${additionalInfo}`,
 899      timeoutInSeconds * 1e3
 900    );
 901    if (contextMenuCallback)
 902      newNotice.noticeEl.oncontextmenu = () => {
 903        contextMenuCallback();
 904      };
 905  }
 906  
 907  // src/utils/internetconnection.ts
 908  async function isConnectedToInternet() {
 909    try {
 910      const online = await fetch("https://obsidian.md/?" + Math.random());
 911      return online.status >= 200 && online.status < 300;
 912    } catch (err) {
 913      return false;
 914    }
 915  }
 916  
 917  // src/features/themes.ts
 918  var themeSave = async (plugin, cssGithubRepository, newInstall) => {
 919    let themeCss = await grabCommmunityThemeCssFile(
 920      cssGithubRepository,
 921      true,
 922      plugin.settings.debuggingMode
 923    );
 924    if (!themeCss)
 925      themeCss = await grabCommmunityThemeCssFile(
 926        cssGithubRepository,
 927        false,
 928        plugin.settings.debuggingMode
 929      );
 930    if (!themeCss) {
 931      toastMessage(
 932        plugin,
 933        "There is no theme.css or theme-beta.css file in the root path of this repository, so there is no theme to install."
 934      );
 935      return false;
 936    }
 937    const themeManifest = await grabCommmunityThemeManifestFile(
 938      cssGithubRepository,
 939      plugin.settings.debuggingMode
 940    );
 941    if (!themeManifest) {
 942      toastMessage(
 943        plugin,
 944        "There is no manifest.json file in the root path of this repository, so theme cannot be installed."
 945      );
 946      return false;
 947    }
 948    const manifestInfo = await JSON.parse(themeManifest);
 949    const themeTargetFolderPath = (0, import_obsidian3.normalizePath)(themesRootPath(plugin) + manifestInfo.name);
 950    const { adapter } = plugin.app.vault;
 951    if (!await adapter.exists(themeTargetFolderPath))
 952      await adapter.mkdir(themeTargetFolderPath);
 953    await adapter.write((0, import_obsidian3.normalizePath)(themeTargetFolderPath + "/theme.css"), themeCss);
 954    await adapter.write(
 955      (0, import_obsidian3.normalizePath)(themeTargetFolderPath + "/manifest.json"),
 956      themeManifest
 957    );
 958    updateBetaThemeLastUpdateChecksum(
 959      plugin,
 960      cssGithubRepository,
 961      checksumForString(themeCss)
 962    );
 963    let msg = ``;
 964    if (newInstall) {
 965      addBetaThemeToList(plugin, cssGithubRepository, themeCss);
 966      msg = `${manifestInfo.name} theme installed from ${cssGithubRepository}. `;
 967      setTimeout(() => {
 968        plugin.app.customCss.setTheme(manifestInfo.name);
 969      }, 500);
 970    } else {
 971      msg = `${manifestInfo.name} theme updated from ${cssGithubRepository}.`;
 972    }
 973    void plugin.log(msg + `[Theme Info](https://github.com/${cssGithubRepository})`, false);
 974    toastMessage(plugin, msg, 20, () => {
 975      window.open(`https://github.com/${cssGithubRepository}`);
 976    });
 977    return true;
 978  };
 979  var themesCheckAndUpdates = async (plugin, showInfo) => {
 980    if (!await isConnectedToInternet()) {
 981      console.log("BRAT: No internet detected.");
 982      return;
 983    }
 984    let newNotice;
 985    const msg1 = `Checking for beta theme updates STARTED`;
 986    await plugin.log(msg1, true);
 987    if (showInfo && plugin.settings.notificationsEnabled)
 988      newNotice = new import_obsidian3.Notice(`BRAT
 989  ${msg1}`, 3e4);
 990    for (const t of plugin.settings.themesList) {
 991      let lastUpdateOnline = await grabChecksumOfThemeCssFile(
 992        t.repo,
 993        true,
 994        plugin.settings.debuggingMode
 995      );
 996      if (lastUpdateOnline === "0")
 997        lastUpdateOnline = await grabChecksumOfThemeCssFile(
 998          t.repo,
 999          false,
1000          plugin.settings.debuggingMode
1001        );
1002      console.log("BRAT: lastUpdateOnline", lastUpdateOnline);
1003      if (lastUpdateOnline !== t.lastUpdate)
1004        await themeSave(plugin, t.repo, false);
1005    }
1006    const msg2 = `Checking for beta theme updates COMPLETED`;
1007    (async () => {
1008      await plugin.log(msg2, true);
1009    })();
1010    if (showInfo) {
1011      if (plugin.settings.notificationsEnabled && newNotice)
1012        newNotice.hide();
1013      toastMessage(plugin, msg2);
1014    }
1015  };
1016  var themeDelete = (plugin, cssGithubRepository) => {
1017    plugin.settings.themesList = plugin.settings.themesList.filter(
1018      (t) => t.repo !== cssGithubRepository
1019    );
1020    void plugin.saveSettings();
1021    const msg = `Removed ${cssGithubRepository} from BRAT themes list and will no longer be updated. However, the theme files still exist in the vault. To remove them, go into Settings > Appearance and remove the theme.`;
1022    void plugin.log(msg, true);
1023    toastMessage(plugin, msg);
1024  };
1025  var themesRootPath = (plugin) => {
1026    return (0, import_obsidian3.normalizePath)(plugin.app.vault.configDir + "/themes") + "/";
1027  };
1028  
1029  // src/ui/AddNewTheme.ts
1030  var import_obsidian4 = require("obsidian");
1031  
1032  // src/ui/Promotional.ts
1033  var promotionalLinks = (containerEl, settingsTab = true) => {
1034    const linksDiv = containerEl.createEl("div");
1035    linksDiv.style.float = "right";
1036    if (!settingsTab) {
1037      linksDiv.style.padding = "10px";
1038      linksDiv.style.paddingLeft = "15px";
1039      linksDiv.style.paddingRight = "15px";
1040    } else {
1041      linksDiv.style.padding = "15px";
1042      linksDiv.style.paddingLeft = "15px";
1043      linksDiv.style.paddingRight = "15px";
1044      linksDiv.style.marginLeft = "15px";
1045    }
1046    const twitterSpan = linksDiv.createDiv("coffee");
1047    twitterSpan.addClass("ex-twitter-span");
1048    twitterSpan.style.paddingLeft = "10px";
1049    const captionText = twitterSpan.createDiv();
1050    captionText.innerText = "Learn more about my work at:";
1051    twitterSpan.appendChild(captionText);
1052    const twitterLink = twitterSpan.createEl("a", { href: "https://tfthacker.com" });
1053    twitterLink.innerText = "https://tfthacker.com";
1054    return linksDiv;
1055  };
1056  
1057  // src/ui/AddNewTheme.ts
1058  var AddNewTheme = class extends import_obsidian4.Modal {
1059    constructor(plugin, openSettingsTabAfterwards = false) {
1060      super(plugin.app);
1061      this.plugin = plugin;
1062      this.address = "";
1063      this.openSettingsTabAfterwards = openSettingsTabAfterwards;
1064    }
1065    async submitForm() {
1066      if (this.address === "")
1067        return;
1068      const scrubbedAddress = this.address.replace("https://github.com/", "");
1069      if (existBetaThemeinInList(this.plugin, scrubbedAddress)) {
1070        toastMessage(this.plugin, `This theme is already in the list for beta testing`, 10);
1071        return;
1072      }
1073      if (await themeSave(this.plugin, scrubbedAddress, true)) {
1074        this.close();
1075      }
1076    }
1077    onOpen() {
1078      this.contentEl.createEl("h4", { text: "Github repository for beta theme:" });
1079      this.contentEl.createEl("form", {}, (formEl) => {
1080        formEl.addClass("brat-modal");
1081        new import_obsidian4.Setting(formEl).addText((textEl) => {
1082          textEl.setPlaceholder(
1083            "Repository (example: https://github.com/GitubUserName/repository-name"
1084          );
1085          textEl.setValue(this.address);
1086          textEl.onChange((value) => {
1087            this.address = value.trim();
1088          });
1089          textEl.inputEl.addEventListener("keydown", (e) => {
1090            if (e.key === "Enter" && this.address !== " ") {
1091              e.preventDefault();
1092              void this.submitForm();
1093            }
1094          });
1095          textEl.inputEl.style.width = "100%";
1096          window.setTimeout(() => {
1097            const title = document.querySelector(".setting-item-info");
1098            if (title)
1099              title.remove();
1100            textEl.inputEl.focus();
1101          }, 10);
1102        });
1103        formEl.createDiv("modal-button-container", (buttonContainerEl) => {
1104          buttonContainerEl.createEl("button", { attr: { type: "button" }, text: "Never mind" }).addEventListener("click", () => {
1105            this.close();
1106          });
1107          buttonContainerEl.createEl("button", {
1108            attr: { type: "submit" },
1109            cls: "mod-cta",
1110            text: "Add Theme"
1111          });
1112        });
1113        const newDiv = formEl.createDiv();
1114        newDiv.style.borderTop = "1px solid #ccc";
1115        newDiv.style.marginTop = "30px";
1116        const byTfThacker = newDiv.createSpan();
1117        byTfThacker.innerHTML = "BRAT by <a href='https://bit.ly/o42-twitter'>TFTHacker</a>";
1118        byTfThacker.style.fontStyle = "italic";
1119        newDiv.appendChild(byTfThacker);
1120        promotionalLinks(newDiv, false);
1121        window.setTimeout(() => {
1122          const title = formEl.querySelectorAll(".brat-modal .setting-item-info");
1123          title.forEach((titleEl) => {
1124            titleEl.remove();
1125          });
1126        }, 50);
1127        formEl.addEventListener("submit", (e) => {
1128          e.preventDefault();
1129          if (this.address !== "")
1130            void this.submitForm();
1131        });
1132      });
1133    }
1134    onClose() {
1135      if (this.openSettingsTabAfterwards) {
1136        this.plugin.app.setting.open();
1137        this.plugin.app.setting.openTabById(this.plugin.APP_ID);
1138      }
1139    }
1140  };
1141  
1142  // src/ui/SettingsTab.ts
1143  var createLink = (githubResource, optionalText) => {
1144    const newLink = new DocumentFragment();
1145    const linkElement = document.createElement("a");
1146    linkElement.textContent = githubResource;
1147    linkElement.href = `https://github.com/${githubResource}`;
1148    newLink.appendChild(linkElement);
1149    if (optionalText) {
1150      const textNode = document.createTextNode(optionalText);
1151      newLink.appendChild(textNode);
1152    }
1153    return newLink;
1154  };
1155  var BratSettingsTab = class extends import_obsidian5.PluginSettingTab {
1156    constructor(app, plugin) {
1157      super(app, plugin);
1158      this.plugin = plugin;
1159    }
1160    display() {
1161      const { containerEl } = this;
1162      containerEl.empty();
1163      new import_obsidian5.Setting(containerEl).setName("Auto-enable plugins after installation").setDesc(
1164        'If enabled beta plugins will be automatically enabled after installtion by default. Note: you can toggle this on and off for each plugin in the "Add Plugin" form.'
1165      ).addToggle((cb) => {
1166        cb.setValue(this.plugin.settings.enableAfterInstall);
1167        cb.onChange(async (value) => {
1168          this.plugin.settings.enableAfterInstall = value;
1169          await this.plugin.saveSettings();
1170        });
1171      });
1172      new import_obsidian5.Setting(containerEl).setName("Auto-update plugins at startup").setDesc(
1173        "If enabled all beta plugins will be checked for updates each time Obsidian starts. Note: this does not update frozen version plugins."
1174      ).addToggle((cb) => {
1175        cb.setValue(this.plugin.settings.updateAtStartup);
1176        cb.onChange(async (value) => {
1177          this.plugin.settings.updateAtStartup = value;
1178          await this.plugin.saveSettings();
1179        });
1180      });
1181      new import_obsidian5.Setting(containerEl).setName("Auto-update themes at startup").setDesc(
1182        "If enabled all beta themes will be checked for updates each time Obsidian starts."
1183      ).addToggle((cb) => {
1184        cb.setValue(this.plugin.settings.updateThemesAtStartup);
1185        cb.onChange(async (value) => {
1186          this.plugin.settings.updateThemesAtStartup = value;
1187          await this.plugin.saveSettings();
1188        });
1189      });
1190      promotionalLinks(containerEl, true);
1191      containerEl.createEl("hr");
1192      containerEl.createEl("h2", { text: "Beta Plugin List" });
1193      containerEl.createEl("div", {
1194        text: `The following is a list of beta plugins added via the command palette "Add a beta plugin for testing" or "Add a beta plugin with frozen version for testing". A frozen version is a specific release of a plugin based on its releease tag. `
1195      });
1196      containerEl.createEl("p");
1197      containerEl.createEl("div", {
1198        text: `Click the x button next to a plugin to remove it from the list.`
1199      });
1200      containerEl.createEl("p");
1201      containerEl.createEl("span").createEl("b", { text: "Note: " });
1202      containerEl.createSpan({
1203        text: "This does not delete the plugin, this should be done from the  Community Plugins tab in Settings."
1204      });
1205      new import_obsidian5.Setting(containerEl).addButton((cb) => {
1206        cb.setButtonText("Add Beta plugin");
1207        cb.onClick(() => {
1208          this.plugin.app.setting.close();
1209          this.plugin.betaPlugins.displayAddNewPluginModal(true, false);
1210        });
1211      });
1212      const pluginSubListFrozenVersionNames = new Set(
1213        this.plugin.settings.pluginSubListFrozenVersion.map((x) => x.repo)
1214      );
1215      for (const bp of this.plugin.settings.pluginList) {
1216        if (pluginSubListFrozenVersionNames.has(bp)) {
1217          continue;
1218        }
1219        new import_obsidian5.Setting(containerEl).setName(createLink(bp)).addButton((btn) => {
1220          btn.setIcon("cross");
1221          btn.setTooltip("Delete this beta plugin");
1222          btn.onClick(() => {
1223            if (btn.buttonEl.textContent === "")
1224              btn.setButtonText("Click once more to confirm removal");
1225            else {
1226              const { buttonEl } = btn;
1227              const { parentElement } = buttonEl;
1228              if (parentElement == null ? void 0 : parentElement.parentElement) {
1229                parentElement.parentElement.remove();
1230                this.plugin.betaPlugins.deletePlugin(bp);
1231              }
1232            }
1233          });
1234        });
1235      }
1236      new import_obsidian5.Setting(containerEl).addButton((cb) => {
1237        cb.setButtonText("Add Beta plugin with frozen version");
1238        cb.onClick(() => {
1239          this.plugin.app.setting.close();
1240          this.plugin.betaPlugins.displayAddNewPluginModal(true, true);
1241        });
1242      });
1243      for (const bp of this.plugin.settings.pluginSubListFrozenVersion) {
1244        new import_obsidian5.Setting(containerEl).setName(createLink(bp.repo, ` (version ${bp.version})`)).addButton((btn) => {
1245          btn.setIcon("cross");
1246          btn.setTooltip("Delete this beta plugin");
1247          btn.onClick(() => {
1248            if (btn.buttonEl.textContent === "")
1249              btn.setButtonText("Click once more to confirm removal");
1250            else {
1251              const { buttonEl } = btn;
1252              const { parentElement } = buttonEl;
1253              if (parentElement == null ? void 0 : parentElement.parentElement) {
1254                parentElement.parentElement.remove();
1255                this.plugin.betaPlugins.deletePlugin(bp.repo);
1256              }
1257            }
1258          });
1259        });
1260      }
1261      containerEl.createEl("h2", { text: "Beta Themes List" });
1262      new import_obsidian5.Setting(containerEl).addButton((cb) => {
1263        cb.setButtonText("Add Beta Theme");
1264        cb.onClick(() => {
1265          this.plugin.app.setting.close();
1266          new AddNewTheme(this.plugin).open();
1267        });
1268      });
1269      for (const bp of this.plugin.settings.themesList) {
1270        new import_obsidian5.Setting(containerEl).setName(createLink(bp.repo)).addButton((btn) => {
1271          btn.setIcon("cross");
1272          btn.setTooltip("Delete this beta theme");
1273          btn.onClick(() => {
1274            if (btn.buttonEl.textContent === "")
1275              btn.setButtonText("Click once more to confirm removal");
1276            else {
1277              const { buttonEl } = btn;
1278              const { parentElement } = buttonEl;
1279              if (parentElement == null ? void 0 : parentElement.parentElement) {
1280                parentElement.parentElement.remove();
1281                themeDelete(this.plugin, bp.repo);
1282              }
1283            }
1284          });
1285        });
1286      }
1287      containerEl.createEl("h2", { text: "Monitoring" });
1288      new import_obsidian5.Setting(containerEl).setName("Enable Notifications").setDesc(
1289        "BRAT will provide popup notifications for its various activities. Turn this off means  no notifications from BRAT."
1290      ).addToggle((cb) => {
1291        cb.setValue(this.plugin.settings.notificationsEnabled);
1292        cb.onChange(async (value) => {
1293          this.plugin.settings.notificationsEnabled = value;
1294          await this.plugin.saveSettings();
1295        });
1296      });
1297      new import_obsidian5.Setting(containerEl).setName("Enable Logging").setDesc("Plugin updates will be logged to a file in the log file.").addToggle((cb) => {
1298        cb.setValue(this.plugin.settings.loggingEnabled);
1299        cb.onChange(async (value) => {
1300          this.plugin.settings.loggingEnabled = value;
1301          await this.plugin.saveSettings();
1302        });
1303      });
1304      new import_obsidian5.Setting(this.containerEl).setName("BRAT Log File Location").setDesc("Logs will be saved to this file. Don't add .md to the file name.").addSearch((cb) => {
1305        cb.setPlaceholder("Example: BRAT-log").setValue(this.plugin.settings.loggingPath).onChange(async (newFolder) => {
1306          this.plugin.settings.loggingPath = newFolder;
1307          await this.plugin.saveSettings();
1308        });
1309      });
1310      new import_obsidian5.Setting(containerEl).setName("Enable Verbose Logging").setDesc("Get a lot  more information in  the log.").addToggle((cb) => {
1311        cb.setValue(this.plugin.settings.loggingVerboseEnabled);
1312        cb.onChange(async (value) => {
1313          this.plugin.settings.loggingVerboseEnabled = value;
1314          await this.plugin.saveSettings();
1315        });
1316      });
1317      new import_obsidian5.Setting(containerEl).setName("Debugging Mode").setDesc(
1318        "Atomic Bomb level console logging. Can be used for troubleshoting and development."
1319      ).addToggle((cb) => {
1320        cb.setValue(this.plugin.settings.debuggingMode);
1321        cb.onChange(async (value) => {
1322          this.plugin.settings.debuggingMode = value;
1323          await this.plugin.saveSettings();
1324        });
1325      });
1326      new import_obsidian5.Setting(containerEl).setName("Personal Access Token").setDesc(
1327        "If you need to access private repositories, enter the personal access token here."
1328      ).addText((text) => {
1329        var _a;
1330        text.setPlaceholder("Enter your personal access token").setValue((_a = this.plugin.settings.personalAccessToken) != null ? _a : "").onChange(async (value) => {
1331          this.plugin.settings.personalAccessToken = value;
1332          await this.plugin.saveSettings();
1333        });
1334      });
1335    }
1336  };
1337  
1338  // src/ui/AddNewPluginModal.ts
1339  var import_obsidian6 = require("obsidian");
1340  var AddNewPluginModal = class extends import_obsidian6.Modal {
1341    constructor(plugin, betaPlugins, openSettingsTabAfterwards = false, useFrozenVersion = false) {
1342      super(plugin.app);
1343      this.plugin = plugin;
1344      this.betaPlugins = betaPlugins;
1345      this.address = "";
1346      this.openSettingsTabAfterwards = openSettingsTabAfterwards;
1347      this.useFrozenVersion = useFrozenVersion;
1348      this.enableAfterInstall = plugin.settings.enableAfterInstall;
1349      this.version = "";
1350    }
1351    async submitForm() {
1352      if (this.address === "")
1353        return;
1354      let scrubbedAddress = this.address.replace("https://github.com/", "");
1355      if (scrubbedAddress.endsWith(".git"))
1356        scrubbedAddress = scrubbedAddress.slice(0, -4);
1357      if (existBetaPluginInList(this.plugin, scrubbedAddress)) {
1358        toastMessage(
1359          this.plugin,
1360          `This plugin is already in the list for beta testing`,
1361          10
1362        );
1363        return;
1364      }
1365      const result = await this.betaPlugins.addPlugin(
1366        scrubbedAddress,
1367        false,
1368        false,
1369        false,
1370        this.version,
1371        false,
1372        this.enableAfterInstall
1373      );
1374      if (result) {
1375        this.close();
1376      }
1377    }
1378    onOpen() {
1379      this.contentEl.createEl("h4", { text: "Github repository for beta plugin:" });
1380      this.contentEl.createEl("form", {}, (formEl) => {
1381        formEl.addClass("brat-modal");
1382        new import_obsidian6.Setting(formEl).addText((textEl) => {
1383          textEl.setPlaceholder(
1384            "Repository (example: https://github.com/GitubUserName/repository-name)"
1385          );
1386          textEl.setValue(this.address);
1387          textEl.onChange((value) => {
1388            this.address = value.trim();
1389          });
1390          textEl.inputEl.addEventListener("keydown", (e) => {
1391            if (e.key === "Enter" && this.address !== " ") {
1392              if (this.useFrozenVersion && this.version !== "" || !this.useFrozenVersion) {
1393                e.preventDefault();
1394                void this.submitForm();
1395              }
1396            }
1397          });
1398          textEl.inputEl.style.width = "100%";
1399        });
1400        if (this.useFrozenVersion) {
1401          new import_obsidian6.Setting(formEl).addText((textEl) => {
1402            textEl.setPlaceholder("Specify the release version tag (example: 1.0.0)");
1403            textEl.onChange((value) => {
1404              this.version = value.trim();
1405            });
1406            textEl.inputEl.style.width = "100%";
1407          });
1408        }
1409        formEl.createDiv("modal-button-container", (buttonContainerEl) => {
1410          buttonContainerEl.createEl(
1411            "label",
1412            {
1413              cls: "mod-checkbox"
1414            },
1415            (labelEl) => {
1416              const checkboxEl = labelEl.createEl("input", {
1417                attr: { tabindex: -1 },
1418                type: "checkbox"
1419              });
1420              checkboxEl.checked = this.enableAfterInstall;
1421              checkboxEl.addEventListener("click", () => {
1422                this.enableAfterInstall = checkboxEl.checked;
1423              });
1424              labelEl.appendText("Enable after installing the plugin");
1425            }
1426          );
1427          buttonContainerEl.createEl("button", { attr: { type: "button" }, text: "Never mind" }).addEventListener("click", () => {
1428            this.close();
1429          });
1430          buttonContainerEl.createEl("button", {
1431            attr: { type: "submit" },
1432            cls: "mod-cta",
1433            text: "Add Plugin"
1434          });
1435        });
1436        const newDiv = formEl.createDiv();
1437        newDiv.style.borderTop = "1px solid #ccc";
1438        newDiv.style.marginTop = "30px";
1439        const byTfThacker = newDiv.createSpan();
1440        byTfThacker.innerHTML = "BRAT by <a href='https://bit.ly/o42-twitter'>TFTHacker</a>";
1441        byTfThacker.style.fontStyle = "italic";
1442        newDiv.appendChild(byTfThacker);
1443        promotionalLinks(newDiv, false);
1444        window.setTimeout(() => {
1445          const title = formEl.querySelectorAll(".brat-modal .setting-item-info");
1446          title.forEach((titleEl) => {
1447            titleEl.remove();
1448          });
1449        }, 50);
1450        formEl.addEventListener("submit", (e) => {
1451          e.preventDefault();
1452          if (this.address !== "") {
1453            if (this.useFrozenVersion && this.version !== "" || !this.useFrozenVersion) {
1454              void this.submitForm();
1455            }
1456          }
1457        });
1458      });
1459    }
1460    onClose() {
1461      if (this.openSettingsTabAfterwards) {
1462        this.plugin.app.setting.open();
1463        this.plugin.app.setting.openTabById(this.plugin.APP_ID);
1464      }
1465    }
1466  };
1467  
1468  // src/features/BetaPlugins.ts
1469  var import_obsidian7 = require("obsidian");
1470  var BetaPlugins = class {
1471    constructor(plugin) {
1472      this.plugin = plugin;
1473    }
1474    /**
1475     * opens the AddNewPluginModal to get info for  a new beta plugin
1476     * @param openSettingsTabAfterwards - will open settings screen afterwards. Used when this command is called from settings tab
1477     * @param useFrozenVersion - install the plugin using frozen version.
1478     */
1479    displayAddNewPluginModal(openSettingsTabAfterwards = false, useFrozenVersion = false) {
1480      const newPlugin = new AddNewPluginModal(
1481        this.plugin,
1482        this,
1483        openSettingsTabAfterwards,
1484        useFrozenVersion
1485      );
1486      newPlugin.open();
1487    }
1488    /**
1489     * Validates that a GitHub repository is plugin
1490     *
1491     * @param repositoryPath - GithubUser/RepositoryName (example: TfThacker/obsidian42-brat)
1492     * @param getBetaManifest - test the beta version of the manifest, not at the root
1493     * @param false - [false description]
1494     * @param reportIssues - will display notices as it finds issues
1495     *
1496     * @returns the manifest file if found, or null if its incomplete
1497     */
1498    async validateRepository(repositoryPath, getBetaManifest = false, reportIssues = false) {
1499      const noticeTimeout = 15;
1500      const manifestJson = await grabManifestJsonFromRepository(
1501        repositoryPath,
1502        !getBetaManifest,
1503        this.plugin.settings.debuggingMode,
1504        this.plugin.settings.personalAccessToken
1505      );
1506      if (!manifestJson) {
1507        if (reportIssues) {
1508          toastMessage(
1509            this.plugin,
1510            `${repositoryPath}
1511  This does not seem to be an obsidian plugin, as there is no manifest.json file.`,
1512            noticeTimeout
1513          );
1514          console.error(
1515            "BRAT: validateRepository",
1516            repositoryPath,
1517            getBetaManifest,
1518            reportIssues
1519          );
1520        }
1521        return null;
1522      }
1523      if (!("id" in manifestJson)) {
1524        if (reportIssues)
1525          toastMessage(
1526            this.plugin,
1527            `${repositoryPath}
1528  The plugin id attribute for the release is missing from the manifest file`,
1529            noticeTimeout
1530          );
1531        return null;
1532      }
1533      if (!("version" in manifestJson)) {
1534        if (reportIssues)
1535          toastMessage(
1536            this.plugin,
1537            `${repositoryPath}
1538  The version attribute for the release is missing from the manifest file`,
1539            noticeTimeout
1540          );
1541        return null;
1542      }
1543      return manifestJson;
1544    }
1545    /**
1546     * Gets all the release files based on the version number in the manifest
1547     *
1548     * @param repositoryPath - path to the GitHub repository
1549     * @param manifest       - manifest file
1550     * @param getManifest    - grab the remote manifest file
1551     * @param specifyVersion - grab the specified version if set
1552     *
1553     * @returns all relase files as strings based on the ReleaseFiles interaface
1554     */
1555    async getAllReleaseFiles(repositoryPath, manifest, getManifest, specifyVersion = "") {
1556      const version = specifyVersion === "" ? manifest.version : specifyVersion;
1557      const reallyGetManifestOrNot = getManifest || specifyVersion !== "";
1558      console.log({ reallyGetManifestOrNot, version });
1559      return {
1560        mainJs: await grabReleaseFileFromRepository(
1561          repositoryPath,
1562          version,
1563          "main.js",
1564          this.plugin.settings.debuggingMode,
1565          this.plugin.settings.personalAccessToken
1566        ),
1567        manifest: reallyGetManifestOrNot ? await grabReleaseFileFromRepository(
1568          repositoryPath,
1569          version,
1570          "manifest.json",
1571          this.plugin.settings.debuggingMode,
1572          this.plugin.settings.personalAccessToken
1573        ) : "",
1574        styles: await grabReleaseFileFromRepository(
1575          repositoryPath,
1576          version,
1577          "styles.css",
1578          this.plugin.settings.debuggingMode,
1579          this.plugin.settings.personalAccessToken
1580        )
1581      };
1582    }
1583    /**
1584     * Writes the plugin release files to the local obsidian .plugins folder
1585     *
1586     * @param betaPluginId - the id of the plugin (not the repository path)
1587     * @param relFiles     - release file as strings, based on the ReleaseFiles interface
1588     *
1589     */
1590    async writeReleaseFilesToPluginFolder(betaPluginId, relFiles) {
1591      var _a, _b;
1592      const pluginTargetFolderPath = (0, import_obsidian7.normalizePath)(this.plugin.app.vault.configDir + "/plugins/" + betaPluginId) + "/";
1593      const { adapter } = this.plugin.app.vault;
1594      if (!await adapter.exists(pluginTargetFolderPath) || !await adapter.exists(pluginTargetFolderPath + "manifest.json")) {
1595        await adapter.mkdir(pluginTargetFolderPath);
1596      }
1597      await adapter.write(pluginTargetFolderPath + "main.js", (_a = relFiles.mainJs) != null ? _a : "");
1598      await adapter.write(
1599        pluginTargetFolderPath + "manifest.json",
1600        (_b = relFiles.manifest) != null ? _b : ""
1601      );
1602      if (relFiles.styles)
1603        await adapter.write(pluginTargetFolderPath + "styles.css", relFiles.styles);
1604    }
1605    /**
1606     * Primary function for adding a new beta plugin to Obsidian.
1607     * Also this function is used for updating existing plugins.
1608     *
1609     * @param repositoryPath    - path to GitHub repository formated as USERNAME/repository
1610     * @param updatePluginFiles - true if this is just an update not an install
1611     * @param seeIfUpdatedOnly  - if true, and updatePluginFiles true, will just check for updates, but not do the update. will report to user that there is a new plugin
1612     * @param reportIfNotUpdted - if true, report if an update has not succed
1613     * @param specifyVersion    - if not empty, need to install a specified version instead of the value in manifest-beta.json
1614     * @param forceReinstall    - if true, will force a reinstall of the plugin, even if it is already installed
1615     *
1616     * @returns true if succeeds
1617     */
1618    async addPlugin(repositoryPath, updatePluginFiles = false, seeIfUpdatedOnly = false, reportIfNotUpdted = false, specifyVersion = "", forceReinstall = false, enableAfterInstall = this.plugin.settings.enableAfterInstall) {
1619      if (this.plugin.settings.debuggingMode)
1620        console.log(
1621          "BRAT: addPlugin",
1622          repositoryPath,
1623          updatePluginFiles,
1624          seeIfUpdatedOnly,
1625          reportIfNotUpdted,
1626          specifyVersion,
1627          forceReinstall,
1628          enableAfterInstall
1629        );
1630      const noticeTimeout = 10;
1631      let primaryManifest = await this.validateRepository(repositoryPath, true, false);
1632      const usingBetaManifest = primaryManifest ? true : false;
1633      if (!usingBetaManifest)
1634        primaryManifest = await this.validateRepository(repositoryPath, false, true);
1635      if (primaryManifest === null) {
1636        const msg = `${repositoryPath}
1637  A manifest.json or manifest-beta.json file does not exist in the root directory of the repository. This plugin cannot be installed.`;
1638        await this.plugin.log(msg, true);
1639        toastMessage(this.plugin, msg, noticeTimeout);
1640        return false;
1641      }
1642      if (!Object.hasOwn(primaryManifest, "version")) {
1643        const msg = `${repositoryPath}
1644  The manifest${usingBetaManifest ? "-beta" : ""}.json file in the root directory of the repository does not have a version number in the file. This plugin cannot be installed.`;
1645        await this.plugin.log(msg, true);
1646        toastMessage(this.plugin, msg, noticeTimeout);
1647        return false;
1648      }
1649      if (!Object.hasOwn(primaryManifest, "minAppVersion")) {
1650        if (!(0, import_obsidian7.requireApiVersion)(primaryManifest.minAppVersion)) {
1651          const msg = `Plugin: ${repositoryPath}
1652  
1653  The manifest${usingBetaManifest ? "-beta" : ""}.json for this plugin indicates that the Obsidian version of the app needs to be ${primaryManifest.minAppVersion}, but this installation of Obsidian is ${import_obsidian7.apiVersion}. 
1654  
1655  You will need to update your Obsidian to use this plugin or contact the plugin developer for more information.`;
1656          await this.plugin.log(msg, true);
1657          toastMessage(this.plugin, msg, 30);
1658          return false;
1659        }
1660      }
1661      const getRelease = async () => {
1662        const rFiles = await this.getAllReleaseFiles(
1663          repositoryPath,
1664          // @ts-expect-error typescript will complain that this can be null, but in this case it won't be
1665          primaryManifest,
1666          usingBetaManifest,
1667          specifyVersion
1668        );
1669        console.log("rFiles", rFiles);
1670        if (usingBetaManifest || rFiles.manifest === "")
1671          rFiles.manifest = JSON.stringify(primaryManifest);
1672        if (this.plugin.settings.debuggingMode)
1673          console.log("BRAT: rFiles.manifest", usingBetaManifest, rFiles);
1674        if (rFiles.mainJs === null) {
1675          const msg = `${repositoryPath}
1676  The release is not complete and cannot be download. main.js is missing from the Release`;
1677          await this.plugin.log(msg, true);
1678          toastMessage(this.plugin, msg, noticeTimeout);
1679          return null;
1680        }
1681        return rFiles;
1682      };
1683      if (!updatePluginFiles || forceReinstall) {
1684        const releaseFiles = await getRelease();
1685        if (releaseFiles === null)
1686          return false;
1687        await this.writeReleaseFilesToPluginFolder(primaryManifest.id, releaseFiles);
1688        if (!forceReinstall)
1689          addBetaPluginToList(this.plugin, repositoryPath, specifyVersion);
1690        if (enableAfterInstall) {
1691          const { plugins } = this.plugin.app;
1692          const pluginTargetFolderPath = (0, import_obsidian7.normalizePath)(
1693            plugins.getPluginFolder() + "/" + primaryManifest.id
1694          );
1695          await plugins.loadManifest(pluginTargetFolderPath);
1696          await plugins.enablePluginAndSave(primaryManifest.id);
1697        }
1698        await this.plugin.app.plugins.loadManifests();
1699        if (forceReinstall) {
1700          await this.reloadPlugin(primaryManifest.id);
1701          await this.plugin.log(`${repositoryPath} reinstalled`, true);
1702          toastMessage(
1703            this.plugin,
1704            `${repositoryPath}
1705  Plugin has been reinstalled and reloaded.`,
1706            noticeTimeout
1707          );
1708        } else {
1709          const versionText = specifyVersion === "" ? "" : ` (version: ${specifyVersion})`;
1710          let msg = `${repositoryPath}${versionText}
1711  The plugin has been registered with BRAT.`;
1712          if (!enableAfterInstall) {
1713            msg += " You may still need to enable it the Community Plugin List.";
1714          }
1715          await this.plugin.log(msg, true);
1716          toastMessage(this.plugin, msg, noticeTimeout);
1717        }
1718      } else {
1719        const pluginTargetFolderPath = this.plugin.app.vault.configDir + "/plugins/" + primaryManifest.id + "/";
1720        let localManifestContents = "";
1721        try {
1722          localManifestContents = await this.plugin.app.vault.adapter.read(
1723            pluginTargetFolderPath + "manifest.json"
1724          );
1725        } catch (e) {
1726          if (e.errno === -4058 || e.errno === -2) {
1727            await this.addPlugin(
1728              repositoryPath,
1729              false,
1730              usingBetaManifest,
1731              false,
1732              specifyVersion
1733            );
1734            return true;
1735          } else
1736            console.log(
1737              "BRAT - Local Manifest Load",
1738              primaryManifest.id,
1739              JSON.stringify(e, null, 2)
1740            );
1741        }
1742        if (specifyVersion !== "" || this.plugin.settings.pluginSubListFrozenVersion.map((x) => x.repo).includes(repositoryPath)) {
1743          toastMessage(
1744            this.plugin,
1745            `The version of ${repositoryPath} is frozen, not updating.`,
1746            3
1747          );
1748          return false;
1749        }
1750        const localManifestJson = await JSON.parse(
1751          localManifestContents
1752        );
1753        if (localManifestJson.version !== primaryManifest.version) {
1754          const releaseFiles = await getRelease();
1755          if (releaseFiles === null)
1756            return false;
1757          if (seeIfUpdatedOnly) {
1758            const msg = `There is an update available for ${primaryManifest.id} from version ${localManifestJson.version} to ${primaryManifest.version}. `;
1759            await this.plugin.log(
1760              msg + `[Release Info](https://github.com/${repositoryPath}/releases/tag/${primaryManifest.version})`,
1761              true
1762            );
1763            toastMessage(this.plugin, msg, 30, () => {
1764              if (primaryManifest) {
1765                window.open(
1766                  `https://github.com/${repositoryPath}/releases/tag/${primaryManifest.version}`
1767                );
1768              }
1769            });
1770          } else {
1771            await this.writeReleaseFilesToPluginFolder(primaryManifest.id, releaseFiles);
1772            await this.plugin.app.plugins.loadManifests();
1773            await this.reloadPlugin(primaryManifest.id);
1774            const msg = `${primaryManifest.id}
1775  Plugin has been updated from version ${localManifestJson.version} to ${primaryManifest.version}. `;
1776            await this.plugin.log(
1777              msg + `[Release Info](https://github.com/${repositoryPath}/releases/tag/${primaryManifest.version})`,
1778              true
1779            );
1780            toastMessage(this.plugin, msg, 30, () => {
1781              if (primaryManifest) {
1782                window.open(
1783                  `https://github.com/${repositoryPath}/releases/tag/${primaryManifest.version}`
1784                );
1785              }
1786            });
1787          }
1788        } else if (reportIfNotUpdted)
1789          toastMessage(this.plugin, `No update available for ${repositoryPath}`, 3);
1790      }
1791      return true;
1792    }
1793    /**
1794     * reloads a plugin (assuming it has been enabled by user)
1795     * pjeby, Thanks Bro https://github.com/pjeby/hot-reload/blob/master/main.js
1796     *
1797     * @param pluginName - name of plugin
1798     *
1799     */
1800    async reloadPlugin(pluginName) {
1801      const { plugins } = this.plugin.app;
1802      try {
1803        await plugins.disablePlugin(pluginName);
1804        await plugins.enablePlugin(pluginName);
1805      } catch (e) {
1806        if (this.plugin.settings.debuggingMode)
1807          console.log("reload plugin", e);
1808      }
1809    }
1810    /**
1811     * updates a beta plugin
1812     *
1813     * @param repositoryPath - repository path on GitHub
1814     * @param onlyCheckDontUpdate - only looks for update
1815     *
1816     */
1817    async updatePlugin(repositoryPath, onlyCheckDontUpdate = false, reportIfNotUpdted = false, forceReinstall = false) {
1818      const result = await this.addPlugin(
1819        repositoryPath,
1820        true,
1821        onlyCheckDontUpdate,
1822        reportIfNotUpdted,
1823        "",
1824        forceReinstall
1825      );
1826      if (!result && !onlyCheckDontUpdate)
1827        toastMessage(this.plugin, `${repositoryPath}
1828  Update of plugin failed.`);
1829      return result;
1830    }
1831    /**
1832     * walks through the list of plugins without frozen version and performs an update
1833     *
1834     * @param showInfo - should this with a started/completed message - useful when ran from CP
1835     *
1836     */
1837    async checkForPluginUpdatesAndInstallUpdates(showInfo = false, onlyCheckDontUpdate = false) {
1838      if (!await isConnectedToInternet()) {
1839        console.log("BRAT: No internet detected.");
1840        return;
1841      }
1842      let newNotice;
1843      const msg1 = `Checking for plugin updates STARTED`;
1844      await this.plugin.log(msg1, true);
1845      if (showInfo && this.plugin.settings.notificationsEnabled)
1846        newNotice = new import_obsidian7.Notice(`BRAT
1847  ${msg1}`, 3e4);
1848      const pluginSubListFrozenVersionNames = new Set(
1849        this.plugin.settings.pluginSubListFrozenVersion.map((f) => f.repo)
1850      );
1851      for (const bp of this.plugin.settings.pluginList) {
1852        if (pluginSubListFrozenVersionNames.has(bp)) {
1853          continue;
1854        }
1855        await this.updatePlugin(bp, onlyCheckDontUpdate);
1856      }
1857      const msg2 = `Checking for plugin updates COMPLETED`;
1858      await this.plugin.log(msg2, true);
1859      if (showInfo) {
1860        if (newNotice) {
1861          newNotice.hide();
1862        }
1863        toastMessage(this.plugin, msg2, 10);
1864      }
1865    }
1866    /**
1867     * Removes the beta plugin from the list of beta plugins (does not delete them from disk)
1868     *
1869     * @param betaPluginID - repository path
1870     *
1871     */
1872    deletePlugin(repositoryPath) {
1873      const msg = `Removed ${repositoryPath} from BRAT plugin list`;
1874      void this.plugin.log(msg, true);
1875      this.plugin.settings.pluginList = this.plugin.settings.pluginList.filter(
1876        (b) => b !== repositoryPath
1877      );
1878      this.plugin.settings.pluginSubListFrozenVersion = this.plugin.settings.pluginSubListFrozenVersion.filter(
1879        (b) => b.repo !== repositoryPath
1880      );
1881      void this.plugin.saveSettings();
1882    }
1883    /**
1884     * Returns a list of plugins that are currently enabled or currently disabled
1885     *
1886     * @param enabled - true for enabled plugins, false for disabled plutings
1887     *
1888     * @returns manifests  of plugins
1889     */
1890    getEnabledDisabledPlugins(enabled) {
1891      const pl = this.plugin.app.plugins;
1892      const manifests = Object.values(pl.manifests);
1893      const enabledPlugins = Object.values(pl.plugins).map(
1894        (p) => p.manifest
1895      );
1896      return enabled ? manifests.filter(
1897        (manifest) => enabledPlugins.find((pluginName) => manifest.id === pluginName.id)
1898      ) : manifests.filter(
1899        (manifest) => !enabledPlugins.find((pluginName) => manifest.id === pluginName.id)
1900      );
1901    }
1902  };
1903  
1904  // src/ui/icons.ts
1905  var import_obsidian8 = require("obsidian");
1906  function addIcons() {
1907    (0, import_obsidian8.addIcon)(
1908      "BratIcon",
1909      `<path fill="currentColor" stroke="currentColor"  d="M 41.667969 41.667969 C 41.667969 39.367188 39.800781 37.5 37.5 37.5 C 35.199219 37.5 33.332031 39.367188 33.332031 41.667969 C 33.332031 43.96875 35.199219 45.832031 37.5 45.832031 C 39.800781 45.832031 41.667969 43.96875 41.667969 41.667969 Z M 60.417969 58.582031 C 59.460938 58.023438 58.320312 57.867188 57.25 58.148438 C 56.179688 58.429688 55.265625 59.125 54.707031 60.082031 C 53.746094 61.777344 51.949219 62.820312 50 62.820312 C 48.050781 62.820312 46.253906 61.777344 45.292969 60.082031 C 44.734375 59.125 43.820312 58.429688 42.75 58.148438 C 41.679688 57.867188 40.539062 58.023438 39.582031 58.582031 C 37.597656 59.726562 36.910156 62.257812 38.042969 64.25 C 40.5 68.53125 45.0625 71.171875 50 71.171875 C 54.9375 71.171875 59.5 68.53125 61.957031 64.25 C 63.089844 62.257812 62.402344 59.726562 60.417969 58.582031 Z M 62.5 37.5 C 60.199219 37.5 58.332031 39.367188 58.332031 41.667969 C 58.332031 43.96875 60.199219 45.832031 62.5 45.832031 C 64.800781 45.832031 66.667969 43.96875 66.667969 41.667969 C 66.667969 39.367188 64.800781 37.5 62.5 37.5 Z M 50 8.332031 C 26.988281 8.332031 8.332031 26.988281 8.332031 50 C 8.332031 73.011719 26.988281 91.667969 50 91.667969 C 73.011719 91.667969 91.667969 73.011719 91.667969 50 C 91.667969 26.988281 73.011719 8.332031 50 8.332031 Z M 50 83.332031 C 33.988281 83.402344 20.191406 72.078125 17.136719 56.363281 C 14.078125 40.644531 22.628906 24.976562 37.5 19.042969 C 37.457031 19.636719 37.457031 20.238281 37.5 20.832031 C 37.5 27.738281 43.097656 33.332031 50 33.332031 C 52.300781 33.332031 54.167969 31.46875 54.167969 29.167969 C 54.167969 26.867188 52.300781 25 50 25 C 47.699219 25 45.832031 23.132812 45.832031 20.832031 C 45.832031 18.53125 47.699219 16.667969 50 16.667969 C 68.410156 16.667969 83.332031 31.589844 83.332031 50 C 83.332031 68.410156 68.410156 83.332031 50 83.332031 Z M 50 83.332031 " />`
1910    );
1911  }
1912  
1913  // src/utils/logging.ts
1914  var import_obsidian9 = require("obsidian");
1915  var import_obsidian_daily_notes_interface = __toESM(require_main());
1916  async function logger(plugin, textToLog, verboseLoggingOn = false) {
1917    if (plugin.settings.debuggingMode)
1918      console.log("BRAT: " + textToLog);
1919    if (plugin.settings.loggingEnabled) {
1920      if (!plugin.settings.loggingVerboseEnabled && verboseLoggingOn) {
1921        return;
1922      } else {
1923        const fileName = plugin.settings.loggingPath + ".md";
1924        const dateOutput = "[[" + (0, import_obsidian9.moment)().format((0, import_obsidian_daily_notes_interface.getDailyNoteSettings)().format).toString() + "]] " + (0, import_obsidian9.moment)().format("HH:mm");
1925        const os = window.require("os");
1926        const machineName = import_obsidian9.Platform.isDesktop ? os.hostname() : "MOBILE";
1927        let output = dateOutput + " " + machineName + " " + textToLog.replace("\n", " ") + "\n\n";
1928        if (await plugin.app.vault.adapter.exists(fileName)) {
1929          const fileContents = await plugin.app.vault.adapter.read(fileName);
1930          output = output + fileContents;
1931          const file = plugin.app.vault.getAbstractFileByPath(fileName);
1932          await plugin.app.vault.modify(file, output);
1933        } else
1934          await plugin.app.vault.create(fileName, output);
1935      }
1936    }
1937  }
1938  
1939  // src/ui/GenericFuzzySuggester.ts
1940  var import_obsidian10 = require("obsidian");
1941  var GenericFuzzySuggester = class extends import_obsidian10.FuzzySuggestModal {
1942    constructor(plugin) {
1943      super(plugin.app);
1944      this.data = [];
1945      this.scope.register(["Shift"], "Enter", (evt) => {
1946        this.enterTrigger(evt);
1947      });
1948      this.scope.register(["Ctrl"], "Enter", (evt) => {
1949        this.enterTrigger(evt);
1950      });
1951    }
1952    setSuggesterData(suggesterData) {
1953      this.data = suggesterData;
1954    }
1955    display(callBack) {
1956      this.callbackFunction = callBack;
1957      this.open();
1958    }
1959    getItems() {
1960      return this.data;
1961    }
1962    getItemText(item) {
1963      return item.display;
1964    }
1965    onChooseItem() {
1966      return;
1967    }
1968    renderSuggestion(item, el) {
1969      el.createEl("div", { text: item.item.display });
1970    }
1971    enterTrigger(evt) {
1972      var _a;
1973      const selectedText = (_a = document.querySelector(".suggestion-item.is-selected div")) == null ? void 0 : _a.textContent;
1974      const item = this.data.find((i) => i.display === selectedText);
1975      if (item) {
1976        this.invokeCallback(item, evt);
1977        this.close();
1978      }
1979    }
1980    onChooseSuggestion(item, evt) {
1981      this.invokeCallback(item.item, evt);
1982    }
1983    invokeCallback(item, evt) {
1984      if (typeof this.callbackFunction === "function") {
1985        this.callbackFunction(item, evt);
1986      }
1987    }
1988  };
1989  
1990  // src/ui/PluginCommands.ts
1991  var PluginCommands = class {
1992    constructor(plugin) {
1993      this.bratCommands = [
1994        {
1995          id: "BRAT-AddBetaPlugin",
1996          icon: "BratIcon",
1997          name: "Plugins: Add a beta plugin for testing",
1998          showInRibbon: true,
1999          callback: () => {
2000            this.plugin.betaPlugins.displayAddNewPluginModal(false, false);
2001          }
2002        },
2003        {
2004          id: "BRAT-AddBetaPluginWithFrozenVersion",
2005          icon: "BratIcon",
2006          name: "Plugins: Add a beta plugin with frozen version based on a release tag",
2007          showInRibbon: true,
2008          callback: () => {
2009            this.plugin.betaPlugins.displayAddNewPluginModal(false, true);
2010          }
2011        },
2012        {
2013          id: "BRAT-checkForUpdatesAndUpdate",
2014          icon: "BratIcon",
2015          name: "Plugins: Check for updates to all beta plugins and UPDATE",
2016          showInRibbon: true,
2017          callback: async () => {
2018            await this.plugin.betaPlugins.checkForPluginUpdatesAndInstallUpdates(true, false);
2019          }
2020        },
2021        {
2022          id: "BRAT-checkForUpdatesAndDontUpdate",
2023          icon: "BratIcon",
2024          name: "Plugins: Only check for updates to beta plugins, but don't Update",
2025          showInRibbon: true,
2026          callback: async () => {
2027            await this.plugin.betaPlugins.checkForPluginUpdatesAndInstallUpdates(true, true);
2028          }
2029        },
2030        {
2031          id: "BRAT-updateOnePlugin",
2032          icon: "BratIcon",
2033          name: "Plugins: Choose a single plugin version to update",
2034          showInRibbon: true,
2035          callback: () => {
2036            const pluginSubListFrozenVersionNames = new Set(
2037              this.plugin.settings.pluginSubListFrozenVersion.map((f) => f.repo)
2038            );
2039            const pluginList = Object.values(this.plugin.settings.pluginList).filter((f) => !pluginSubListFrozenVersionNames.has(f)).map((m) => {
2040              return { display: m, info: m };
2041            });
2042            const gfs = new GenericFuzzySuggester(this.plugin);
2043            gfs.setSuggesterData(pluginList);
2044            gfs.display((results) => {
2045              const msg = `Checking for updates for ${results.info}`;
2046              void this.plugin.log(msg, true);
2047              toastMessage(this.plugin, `
2048  ${msg}`, 3);
2049              void this.plugin.betaPlugins.updatePlugin(results.info, false, true);
2050            });
2051          }
2052        },
2053        {
2054          id: "BRAT-reinstallOnePlugin",
2055          icon: "BratIcon",
2056          name: "Plugins: Choose a single plugin to reinstall",
2057          showInRibbon: true,
2058          callback: () => {
2059            const pluginSubListFrozenVersionNames = new Set(
2060              this.plugin.settings.pluginSubListFrozenVersion.map((f) => f.repo)
2061            );
2062            const pluginList = Object.values(this.plugin.settings.pluginList).filter((f) => !pluginSubListFrozenVersionNames.has(f)).map((m) => {
2063              return { display: m, info: m };
2064            });
2065            const gfs = new GenericFuzzySuggester(this.plugin);
2066            gfs.setSuggesterData(pluginList);
2067            gfs.display((results) => {
2068              const msg = `Reinstalling ${results.info}`;
2069              toastMessage(this.plugin, `
2070  ${msg}`, 3);
2071              void this.plugin.log(msg, true);
2072              void this.plugin.betaPlugins.updatePlugin(
2073                results.info,
2074                false,
2075                false,
2076                true
2077              );
2078            });
2079          }
2080        },
2081        {
2082          id: "BRAT-restartPlugin",
2083          icon: "BratIcon",
2084          name: "Plugins: Restart a plugin that is already installed",
2085          showInRibbon: true,
2086          callback: () => {
2087            const pluginList = Object.values(
2088              this.plugin.app.plugins.manifests
2089            ).map((m) => {
2090              return { display: m.id, info: m.id };
2091            });
2092            const gfs = new GenericFuzzySuggester(this.plugin);
2093            gfs.setSuggesterData(pluginList);
2094            gfs.display((results) => {
2095              toastMessage(
2096                this.plugin,
2097                `${results.info}
2098  Plugin reloading .....`,
2099                5
2100              );
2101              void this.plugin.betaPlugins.reloadPlugin(results.info);
2102            });
2103          }
2104        },
2105        {
2106          id: "BRAT-disablePlugin",
2107          icon: "BratIcon",
2108          name: "Plugins: Disable a plugin - toggle it off",
2109          showInRibbon: true,
2110          callback: () => {
2111            const pluginList = this.plugin.betaPlugins.getEnabledDisabledPlugins(true).map((manifest) => {
2112              return { display: `${manifest.name} (${manifest.id})`, info: manifest.id };
2113            });
2114            const gfs = new GenericFuzzySuggester(this.plugin);
2115            gfs.setSuggesterData(pluginList);
2116            gfs.display((results) => {
2117              void this.plugin.log(`${results.display} plugin disabled`, false);
2118              if (this.plugin.settings.debuggingMode)
2119                console.log(results.info);
2120              void this.plugin.app.plugins.disablePluginAndSave(results.info);
2121            });
2122          }
2123        },
2124        {
2125          id: "BRAT-enablePlugin",
2126          icon: "BratIcon",
2127          name: "Plugins: Enable a plugin - toggle it on",
2128          showInRibbon: true,
2129          callback: () => {
2130            const pluginList = this.plugin.betaPlugins.getEnabledDisabledPlugins(false).map((manifest) => {
2131              return { display: `${manifest.name} (${manifest.id})`, info: manifest.id };
2132            });
2133            const gfs = new GenericFuzzySuggester(this.plugin);
2134            gfs.setSuggesterData(pluginList);
2135            gfs.display((results) => {
2136              void this.plugin.log(`${results.display} plugin enabled`, false);
2137              void this.plugin.app.plugins.enablePluginAndSave(results.info);
2138            });
2139          }
2140        },
2141        {
2142          id: "BRAT-openGitHubZRepository",
2143          icon: "BratIcon",
2144          name: "Plugins: Open the GitHub repository for a plugin",
2145          showInRibbon: true,
2146          callback: async () => {
2147            const communityPlugins = await grabCommmunityPluginList(
2148              this.plugin.settings.debuggingMode
2149            );
2150            if (communityPlugins) {
2151              const communityPluginList = Object.values(
2152                communityPlugins
2153              ).map((p) => {
2154                return { display: `Plugin: ${p.name}  (${p.repo})`, info: p.repo };
2155              });
2156              const bratList = Object.values(
2157                this.plugin.settings.pluginList
2158              ).map((p) => {
2159                return { display: "BRAT: " + p, info: p };
2160              });
2161              communityPluginList.forEach((si) => bratList.push(si));
2162              const gfs = new GenericFuzzySuggester(this.plugin);
2163              gfs.setSuggesterData(bratList);
2164              gfs.display((results) => {
2165                if (results.info)
2166                  window.open(`https://github.com/${results.info}`);
2167              });
2168            }
2169          }
2170        },
2171        {
2172          id: "BRAT-openGitHubRepoTheme",
2173          icon: "BratIcon",
2174          name: "Themes: Open the GitHub repository for a theme (appearance)",
2175          showInRibbon: true,
2176          callback: async () => {
2177            const communityTheme = await grabCommmunityThemesList(
2178              this.plugin.settings.debuggingMode
2179            );
2180            if (communityTheme) {
2181              const communityThemeList = Object.values(communityTheme).map(
2182                (p) => {
2183                  return { display: `Theme: ${p.name}  (${p.repo})`, info: p.repo };
2184                }
2185              );
2186              const gfs = new GenericFuzzySuggester(this.plugin);
2187              gfs.setSuggesterData(communityThemeList);
2188              gfs.display((results) => {
2189                if (results.info)
2190                  window.open(`https://github.com/${results.info}`);
2191              });
2192            }
2193          }
2194        },
2195        {
2196          id: "BRAT-opentPluginSettings",
2197          icon: "BratIcon",
2198          name: "Plugins: Open Plugin Settings Tab",
2199          showInRibbon: true,
2200          callback: () => {
2201            const settings = this.plugin.app.setting;
2202            const listOfPluginSettingsTabs = Object.values(
2203              settings.pluginTabs
2204            ).map((t) => {
2205              return { display: "Plugin: " + t.name, info: t.id };
2206            });
2207            const gfs = new GenericFuzzySuggester(this.plugin);
2208            const listOfCoreSettingsTabs = Object.values(
2209              settings.settingTabs
2210            ).map((t) => {
2211              return { display: "Core: " + t.name, info: t.id };
2212            });
2213            listOfPluginSettingsTabs.forEach((si) => listOfCoreSettingsTabs.push(si));
2214            gfs.setSuggesterData(listOfCoreSettingsTabs);
2215            gfs.display((results) => {
2216              settings.open();
2217              settings.openTabById(results.info);
2218            });
2219          }
2220        },
2221        {
2222          id: "BRAT-GrabBetaTheme",
2223          icon: "BratIcon",
2224          name: "Themes: Grab a beta theme for testing from a Github repository",
2225          showInRibbon: true,
2226          callback: () => {
2227            new AddNewTheme(this.plugin).open();
2228          }
2229        },
2230        {
2231          id: "BRAT-updateBetaThemes",
2232          icon: "BratIcon",
2233          name: "Themes: Update beta themes",
2234          showInRibbon: true,
2235          callback: async () => {
2236            await themesCheckAndUpdates(this.plugin, true);
2237          }
2238        },
2239        {
2240          id: "BRAT-allCommands",
2241          icon: "BratIcon",
2242          name: "All Commands list",
2243          showInRibbon: false,
2244          callback: () => {
2245            this.ribbonDisplayCommands();
2246          }
2247        }
2248      ];
2249      this.plugin = plugin;
2250      this.bratCommands.forEach((item) => {
2251        this.plugin.addCommand({
2252          id: item.id,
2253          name: item.name,
2254          icon: item.icon,
2255          callback: () => {
2256            item.callback();
2257          }
2258        });
2259      });
2260    }
2261    ribbonDisplayCommands() {
2262      const bratCommandList = [];
2263      this.bratCommands.forEach((cmd) => {
2264        if (cmd.showInRibbon)
2265          bratCommandList.push({ display: cmd.name, info: cmd.callback });
2266      });
2267      const gfs = new GenericFuzzySuggester(this.plugin);
2268      const settings = this.plugin.app.setting;
2269      const listOfCoreSettingsTabs = Object.values(
2270        settings.settingTabs
2271      ).map((t) => {
2272        return {
2273          display: "Core: " + t.name,
2274          info: () => {
2275            settings.open();
2276            settings.openTabById(t.id);
2277          }
2278        };
2279      });
2280      const listOfPluginSettingsTabs = Object.values(
2281        settings.pluginTabs
2282      ).map((t) => {
2283        return {
2284          display: "Plugin: " + t.name,
2285          info: () => {
2286            settings.open();
2287            settings.openTabById(t.id);
2288          }
2289        };
2290      });
2291      bratCommandList.push({
2292        display: "---- Core Plugin Settings ----",
2293        info: () => {
2294          this.ribbonDisplayCommands();
2295        }
2296      });
2297      listOfCoreSettingsTabs.forEach((si) => bratCommandList.push(si));
2298      bratCommandList.push({
2299        display: "---- Plugin Settings ----",
2300        info: () => {
2301          this.ribbonDisplayCommands();
2302        }
2303      });
2304      listOfPluginSettingsTabs.forEach((si) => bratCommandList.push(si));
2305      gfs.setSuggesterData(bratCommandList);
2306      gfs.display((results) => {
2307        if (typeof results.info === "function") {
2308          results.info();
2309        }
2310      });
2311    }
2312  };
2313  
2314  // src/utils/BratAPI.ts
2315  var BratAPI = class {
2316    constructor(plugin) {
2317      this.console = (logDescription, ...outputs) => {
2318        console.log("BRAT: " + logDescription, ...outputs);
2319      };
2320      this.themes = {
2321        themeseCheckAndUpates: async (showInfo) => {
2322          await themesCheckAndUpdates(this.plugin, showInfo);
2323        },
2324        themeInstallTheme: async (cssGithubRepository) => {
2325          const scrubbedAddress = cssGithubRepository.replace("https://github.com/", "");
2326          await themeSave(this.plugin, scrubbedAddress, true);
2327        },
2328        themesDelete: (cssGithubRepository) => {
2329          const scrubbedAddress = cssGithubRepository.replace("https://github.com/", "");
2330          themeDelete(this.plugin, scrubbedAddress);
2331        },
2332        grabCommmunityThemeCssFile: async (repositoryPath, betaVersion = false) => {
2333          return await grabCommmunityThemeCssFile(
2334            repositoryPath,
2335            betaVersion,
2336            this.plugin.settings.debuggingMode
2337          );
2338        },
2339        grabChecksumOfThemeCssFile: async (repositoryPath, betaVersion = false) => {
2340          return await grabChecksumOfThemeCssFile(
2341            repositoryPath,
2342            betaVersion,
2343            this.plugin.settings.debuggingMode
2344          );
2345        },
2346        grabLastCommitDateForFile: async (repositoryPath, path) => {
2347          return await grabLastCommitDateForFile(repositoryPath, path);
2348        }
2349      };
2350      this.plugin = plugin;
2351    }
2352  };
2353  
2354  // src/main.ts
2355  var ThePlugin = class extends import_obsidian11.Plugin {
2356    constructor() {
2357      super(...arguments);
2358      this.APP_NAME = "BRAT";
2359      this.APP_ID = "obsidian42-brat";
2360      this.settings = DEFAULT_SETTINGS;
2361      this.betaPlugins = new BetaPlugins(this);
2362      this.commands = new PluginCommands(this);
2363      this.bratApi = new BratAPI(this);
2364      this.obsidianProtocolHandler = (params) => {
2365        if (!params.plugin && !params.theme) {
2366          toastMessage(this, `Could not locate the repository from the URL.`, 10);
2367          return;
2368        }
2369        for (const which of ["plugin", "theme"]) {
2370          if (params[which]) {
2371            const modal = which === "plugin" ? new AddNewPluginModal(this, this.betaPlugins) : new AddNewTheme(this);
2372            modal.address = params[which];
2373            modal.open();
2374            return;
2375          }
2376        }
2377      };
2378    }
2379    async onload() {
2380      console.log("loading " + this.APP_NAME);
2381      await this.loadSettings();
2382      this.addSettingTab(new BratSettingsTab(this.app, this));
2383      addIcons();
2384      this.showRibbonButton();
2385      this.registerObsidianProtocolHandler("brat", this.obsidianProtocolHandler);
2386      this.app.workspace.onLayoutReady(() => {
2387        if (this.settings.updateAtStartup) {
2388          setTimeout(() => {
2389            void this.betaPlugins.checkForPluginUpdatesAndInstallUpdates(false);
2390          }, 6e4);
2391        }
2392        if (this.settings.updateThemesAtStartup) {
2393          setTimeout(() => {
2394            void themesCheckAndUpdates(this, false);
2395          }, 12e4);
2396        }
2397        setTimeout(() => {
2398          window.bratAPI = this.bratApi;
2399        }, 500);
2400      });
2401    }
2402    showRibbonButton() {
2403      this.addRibbonIcon("BratIcon", "BRAT", () => {
2404        this.commands.ribbonDisplayCommands();
2405      });
2406    }
2407    async log(textToLog, verbose = false) {
2408      await logger(this, textToLog, verbose);
2409    }
2410    onunload() {
2411      console.log("unloading " + this.APP_NAME);
2412    }
2413    async loadSettings() {
2414      this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
2415    }
2416    async saveSettings() {
2417      await this.saveData(this.settings);
2418    }
2419  };