commit b2958c50ebd0f2356a773d8d7027d1d96fe6a68b Author: Molly Zaugg Date: Thu Jul 23 14:20:36 2026 -0400 Started creating vault diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..126f66f --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.trash +logs + +.DS_Store + +0*/ +1*/ +2*/ +3*/ +4*/ +5*/ +6*/ +7*/ +8*/ diff --git a/.obsidian/app.json b/.obsidian/app.json new file mode 100644 index 0000000..774b15a --- /dev/null +++ b/.obsidian/app.json @@ -0,0 +1,5 @@ +{ + "newFileLocation": "current", + "trashOption": "local", + "promptDelete": false +} \ No newline at end of file diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json new file mode 100644 index 0000000..43fbc14 --- /dev/null +++ b/.obsidian/appearance.json @@ -0,0 +1,4 @@ +{ + "theme": "obsidian", + "accentColor": "#ff42ba" +} \ No newline at end of file diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json new file mode 100644 index 0000000..3c9cdda --- /dev/null +++ b/.obsidian/community-plugins.json @@ -0,0 +1,9 @@ +[ + "highlightr-plus", + "folder-notes", + "templater-obsidian", + "obsidian-meta-bind-plugin", + "obsidian-excalidraw-plugin", + "gitea-cards", + "pdf-plus" +] \ No newline at end of file diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json new file mode 100644 index 0000000..4723328 --- /dev/null +++ b/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": true, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": true, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": true, + "audio-recorder": false, + "workspaces": true, + "file-recovery": true, + "publish": false, + "sync": false, + "bases": true, + "webviewer": true +} \ No newline at end of file diff --git a/.obsidian/daily-notes.json b/.obsidian/daily-notes.json new file mode 100644 index 0000000..b195b4c --- /dev/null +++ b/.obsidian/daily-notes.json @@ -0,0 +1,4 @@ +{ + "format": "DD-MM_MMM-YYYY", + "folder": "51 Journal/Daily" +} \ No newline at end of file diff --git a/.obsidian/graph.json b/.obsidian/graph.json new file mode 100644 index 0000000..42a46ec --- /dev/null +++ b/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 1, + "close": true +} \ No newline at end of file diff --git a/.obsidian/plugins/folder-notes/data.json b/.obsidian/plugins/folder-notes/data.json new file mode 100644 index 0000000..1c1ac57 --- /dev/null +++ b/.obsidian/plugins/folder-notes/data.json @@ -0,0 +1,148 @@ +{ + "syncFolderName": true, + "ctrlKey": true, + "altKey": false, + "hideFolderNote": true, + "templatePath": "", + "autoCreate": false, + "autoCreateFocusFiles": true, + "autoCreateForAttachmentFolder": false, + "autoCreateForFiles": false, + "enableCollapsing": false, + "excludeFolders": [ + { + "detached": false, + "type": "folder", + "id": "9c2c1c2e-d952-485f-8ef1-7bd7363527a0", + "path": "99 Toolkit/Templates/Project", + "subFolders": true, + "disableSync": true, + "disableAutoCreate": true, + "disableFolderNote": false, + "enableCollapsing": false, + "position": 0, + "excludeFromFolderOverview": false, + "string": "", + "hideInSettings": false, + "showFolderNote": false + } + ], + "whitelistFolders": [], + "showDeleteConfirmation": true, + "underlineFolder": true, + "stopWhitespaceCollapsing": true, + "underlineFolderInPath": true, + "openFolderNoteOnClickInPath": true, + "openInNewTab": false, + "focusExistingTab": false, + "oldFolderNoteName": "{{folder_name}}", + "folderNoteName": "{{folder_name}}", + "folderNoteType": ".md", + "disableFolderHighlighting": false, + "newFolderNoteName": "{{folder_name}}", + "storageLocation": "insideFolder", + "syncDelete": false, + "showRenameConfirmation": true, + "defaultOverview": { + "id": "", + "folderPath": "", + "title": "{{folderName}} overview", + "showTitle": false, + "depth": 3, + "includeTypes": [ + "folder", + "markdown" + ], + "style": "list", + "disableFileTag": false, + "sortBy": "name", + "sortByAsc": true, + "showEmptyFolders": false, + "onlyIncludeSubfolders": false, + "storeFolderCondition": true, + "showFolderNotes": false, + "disableCollapseIcon": true, + "alwaysCollapse": false, + "autoSync": true, + "allowDragAndDrop": true, + "hideLinkList": true, + "hideFolderOverview": false, + "useActualLinks": false, + "fmtpIntegration": false, + "titleSize": 1, + "isInCallout": false, + "useWikilinks": true + }, + "useSubmenus": true, + "syncMove": true, + "frontMatterTitle": { + "enabled": false, + "explorer": true, + "path": true + }, + "settingsTab": "general", + "supportedFileTypes": [ + "md", + "canvas", + "base" + ], + "boldName": false, + "boldNameInPath": false, + "cursiveName": false, + "cursiveNameInPath": false, + "disableOpenFolderNoteOnClick": false, + "openByClick": true, + "openWithCtrl": false, + "openWithAlt": false, + "excludeFolderDefaultSettings": { + "type": "folder", + "path": "", + "id": "9b521f29-87cb-4bd8-bbae-a3cbe8e67684", + "subFolders": true, + "disableSync": true, + "disableAutoCreate": true, + "disableFolderNote": false, + "enableCollapsing": false, + "position": 0, + "excludeFromFolderOverview": false, + "string": "", + "hideInSettings": false, + "detached": false, + "showFolderNote": false + }, + "excludePatternDefaultSettings": { + "type": "pattern", + "path": "", + "id": "5fbc117d-6a81-42d4-9575-dce443a1875d", + "subFolders": true, + "disableSync": true, + "disableAutoCreate": true, + "disableFolderNote": false, + "enableCollapsing": false, + "position": 0, + "excludeFromFolderOverview": false, + "string": "", + "hideInSettings": false, + "detached": false, + "showFolderNote": false + }, + "hideCollapsingIcon": false, + "hideCollapsingIconForEmptyFolders": false, + "tabManagerEnabled": true, + "ignoreAttachmentFolder": true, + "deleteFilesAction": "trash", + "openSidebar": { + "mobile": false, + "desktop": true + }, + "highlightFolder": true, + "persistentSettingsTab": { + "afterRestart": true, + "afterChangingTab": true + }, + "firstTimeInsertOverview": true, + "fvGlobalSettings": { + "autoUpdateLinks": false + }, + "hideFolderNoteNameInPath": false +} \ No newline at end of file diff --git a/.obsidian/plugins/folder-notes/main.js b/.obsidian/plugins/folder-notes/main.js new file mode 100644 index 0000000..762ba18 --- /dev/null +++ b/.obsidian/plugins/folder-notes/main.js @@ -0,0 +1,7733 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// node_modules/front-matter-plugin-api-provider/lib/contracts/Api.js +var require_Api = __commonJS({ + "node_modules/front-matter-plugin-api-provider/lib/contracts/Api.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + } +}); + +// node_modules/front-matter-plugin-api-provider/lib/contracts/EventDispatcher.js +var require_EventDispatcher = __commonJS({ + "node_modules/front-matter-plugin-api-provider/lib/contracts/EventDispatcher.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + } +}); + +// node_modules/front-matter-plugin-api-provider/lib/contracts/Resolver.js +var require_Resolver = __commonJS({ + "node_modules/front-matter-plugin-api-provider/lib/contracts/Resolver.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + } +}); + +// node_modules/front-matter-plugin-api-provider/lib/index.js +var require_lib = __commonJS({ + "node_modules/front-matter-plugin-api-provider/lib/index.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) { + if (k2 === void 0) + k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + } : function(o, m, k, k2) { + if (k2 === void 0) + k2 = k; + o[k2] = m[k]; + }); + var __exportStar = exports && exports.__exportStar || function(m, exports2) { + for (var p in m) + if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) + __createBinding(exports2, m, p); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getApiSafe = exports.getDefer = exports.isPluginEnabled = exports.PluginNotEnabledError = exports.pluginId = void 0; + __exportStar(require_Api(), exports); + __exportStar(require_EventDispatcher(), exports); + __exportStar(require_Resolver(), exports); + exports.pluginId = "obsidian-front-matter-title-plugin"; + var PluginNotEnabledError = class extends Error { + }; + exports.PluginNotEnabledError = PluginNotEnabledError; + function isPluginEnabled(app) { + var _a, _b, _c; + return (_c = (_b = (_a = app === null || app === void 0 ? void 0 : app.plugins) === null || _a === void 0 ? void 0 : _a.enabledPlugins) === null || _b === void 0 ? void 0 : _b.has(exports.pluginId)) !== null && _c !== void 0 ? _c : false; + } + exports.isPluginEnabled = isPluginEnabled; + function getDefer3(app) { + var _a, _b, _c, _d; + const plugin = (_b = (_a = app === null || app === void 0 ? void 0 : app.plugins) === null || _a === void 0 ? void 0 : _a.getPlugin(exports.pluginId)) !== null && _b !== void 0 ? _b : null; + const defer = (_d = (_c = plugin === null || plugin === void 0 ? void 0 : plugin.getDefer) === null || _c === void 0 ? void 0 : _c.call(plugin)) !== null && _d !== void 0 ? _d : null; + if (defer === null) { + throw new PluginNotEnabledError(`Plugin ${exports.pluginId} is not enabled or old version`); + } + return defer; + } + exports.getDefer = getDefer3; + function getApiSafe(app) { + return new ApiWrapper(null, app); + } + exports.getApiSafe = getApiSafe; + var ApiWrapper = class { + constructor(api, app) { + this.api = api; + this.app = app; + } + before() { + if (this.api !== null) { + return; + } + const defer = this.getDeffer(); + if (defer === null) { + return; + } + const api = defer.getApi(); + if (api === null) { + return defer.awaitPlugin().then(() => { + this.api = defer.getApi(); + }); + } else { + this.api = api; + } + } + getDeffer() { + try { + return getDefer3(this.app); + } catch (e) { + if (e instanceof PluginNotEnabledError) { + return null; + } + throw e; + } + } + getResolverFactory() { + var _a, _b; + this.before(); + return (_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.getResolverFactory()) !== null && _b !== void 0 ? _b : null; + } + getEventDispatcher() { + var _a, _b; + this.before(); + return (_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.getEventDispatcher()) !== null && _b !== void 0 ? _b : null; + } + getEnabledFeatures() { + var _a, _b; + this.before(); + return (_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.getEnabledFeatures()) !== null && _b !== void 0 ? _b : []; + } + }; + } +}); + +// src/main.ts +var main_exports = {}; +__export(main_exports, { + default: () => FolderNotesPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian51 = require("obsidian"); + +// src/settings/SettingsTab.ts +var import_obsidian39 = require("obsidian"); + +// src/modals/ExistingNote.ts +var import_obsidian = require("obsidian"); +var ExistingFolderNoteModal = class extends import_obsidian.Modal { + constructor(app, plugin, file, folder, folderNote) { + super(app); + this.plugin = plugin; + this.app = app; + this.file = file; + this.folder = folder; + this.folderNote = folderNote; + } + onOpen() { + var _a; + const { contentEl } = this; + contentEl.createEl("h2", { text: "A folder note for this folder already exists" }); + const setting = new import_obsidian.Setting(contentEl); + setting.infoEl.createEl("p", { text: "Are you sure you want to turn the note into a folder note and rename the existing folder note?" }); + (_a = setting.infoEl.parentElement) == null ? void 0 : _a.classList.add("fn-delete-confirmation-modal"); + const buttonContainer = setting.infoEl.createEl("div", { cls: "fn-delete-confirmation-modal-buttons" }); + if (import_obsidian.Platform.isMobileApp) { + const confirmButton = buttonContainer.createEl("button", { + text: "Rename and don't ask again" + }); + confirmButton.classList.add("mod-warning", "fn-confirmation-modal-button"); + confirmButton.addEventListener("click", () => { + this.plugin.settings.showRenameConfirmation = false; + void this.plugin.saveSettings(); + this.close(); + turnIntoFolderNote(this.plugin, this.file, this.folder, this.folderNote, true); + }); + } else { + const checkbox = buttonContainer.createEl("input", { type: "checkbox" }); + checkbox.addEventListener("change", (e) => { + const target = e.target; + if (target.checked) { + this.plugin.settings.showRenameConfirmation = false; + } else { + this.plugin.settings.showRenameConfirmation = true; + } + }); + const checkBoxText = buttonContainer.createEl("span", { text: "Don't ask again" }); + checkBoxText.addEventListener("click", () => { + checkbox.click(); + }); + } + const button = buttonContainer.createEl("button", { text: "Rename" }); + button.classList.add("mod-warning", "fn-confirmation-modal-button"); + button.addEventListener("click", () => { + void this.plugin.saveSettings(); + this.close(); + turnIntoFolderNote(this.plugin, this.file, this.folder, this.folderNote, true); + }); + button.focus(); + const cancelButton = buttonContainer.createEl("button", { text: "Cancel" }); + cancelButton.addEventListener("click", () => { + this.close(); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/template.ts +var import_obsidian2 = require("obsidian"); +async function applyTemplate(plugin, file, leaf, templatePath) { + const fileContent = await plugin.app.vault.read(file).catch((err) => { + console.error(`Error reading file ${file.path}:`, err); + }); + if (fileContent !== "") + return; + const templateFile = templatePath ? plugin.app.vault.getAbstractFileByPath(templatePath) : null; + if (templateFile && templateFile instanceof import_obsidian2.TFile) { + try { + const { + templatesEnabled, + templaterEnabled, + templatesPlugin, + templaterPlugin + } = getTemplatePlugins(plugin.app); + const templateContent = await plugin.app.vault.read(templateFile); + if (templateContent.includes("==\u26A0 Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this activeDocument. \u26A0==")) { + return; + } + if (templaterEnabled && templaterPlugin) { + return await templaterPlugin.write_template_to_file(templateFile, file); + } else if (templatesEnabled && templatesPlugin) { + if (leaf instanceof import_obsidian2.WorkspaceLeaf) { + await leaf.openFile(file); + } + return await templatesPlugin.instance.insertTemplate(templateFile); + } + await plugin.app.vault.modify(file, templateContent); + } catch (e) { + console.error(e); + } + } +} +function getTemplatePlugins(app) { + var _a, _b, _c; + const appAsUnknown = app; + const templatesPlugin = appAsUnknown.internalPlugins.plugins.templates; + const templatesEnabled = (_a = templatesPlugin == null ? void 0 : templatesPlugin.enabled) != null ? _a : false; + const templaterPlugin = appAsUnknown.plugins.plugins["templater-obsidian"]; + const templaterEnabled = appAsUnknown.plugins.enabledPlugins.has("templater-obsidian"); + const templaterEmptyFileTemplate = templaterPlugin && ((_b = templaterPlugin.settings) == null ? void 0 : _b.empty_file_template); + const templateFolder = templatesEnabled ? templatesPlugin.instance.options.folder : (_c = templaterPlugin == null ? void 0 : templaterPlugin.settings) == null ? void 0 : _c.template_folder; + return { + templatesPlugin: templatesPlugin || null, + templatesEnabled, + templaterPlugin: (templaterPlugin == null ? void 0 : templaterPlugin.templater) || null, + templaterEnabled, + templaterEmptyFileTemplate, + templateFolder + }; +} + +// src/functions/folderNoteFunctions.ts +var import_obsidian16 = require("obsidian"); + +// src/modals/DeleteConfirmation.ts +var import_obsidian3 = require("obsidian"); +var DeleteConfirmationModal = class extends import_obsidian3.Modal { + constructor(app, plugin, file) { + super(app); + this.plugin = plugin; + this.app = app; + this.file = file; + } + onOpen() { + const { contentEl, plugin } = this; + const modalTitle = contentEl.createDiv({ cls: "fn-modal-title" }); + const modalContent = contentEl.createDiv({ cls: "fn-modal-content" }); + modalTitle.createEl("h2", { text: "Delete folder note" }); + modalContent.createEl("p", { text: `Are you sure you want to delete the folder note '${this.file.name}' ?` }); + switch (plugin.settings.deleteFilesAction) { + case "trash": + modalContent.createEl("p", { text: "It will be moved to your system trash." }); + break; + case "obsidianTrash": + modalContent.createEl("p", { text: 'It will be moved to your Obsidian trash, which is located in the ".trash" hidden folder in your vault.' }); + break; + case "delete": + modalContent.createEl("p", { text: "It will be permanently deleted." }).setCssStyles({ color: "red" }); + break; + } + const buttonContainer = contentEl.createEl("div", { cls: "modal-button-container" }); + if (!import_obsidian3.Platform.isMobile) { + const checkbox = buttonContainer.createEl("label", { cls: "mod-checkbox" }); + checkbox.tabIndex = -1; + const input = checkbox.createEl("input", { type: "checkbox" }); + checkbox.appendText("Don't ask again"); + input.addEventListener("change", (e) => { + const target = e.target; + if (target.checked) { + plugin.settings.showDeleteConfirmation = false; + } else { + plugin.settings.showDeleteConfirmation = true; + } + plugin.saveSettings(); + }); + } else { + const confirmButton = buttonContainer.createEl("button", { + text: "Delete and don't ask again", + cls: "mod-destructive" + }); + confirmButton.addEventListener("click", async () => { + plugin.settings.showDeleteConfirmation = false; + plugin.saveSettings(); + this.close(); + deleteFolderNote(plugin, this.file, false); + }); + } + const deleteButton = buttonContainer.createEl("button", { + text: "Delete", + cls: "mod-warning" + }); + deleteButton.addEventListener("click", async () => { + this.close(); + deleteFolderNote(plugin, this.file, false); + }); + deleteButton.focus(); + const cancelButton = buttonContainer.createEl("button", { + text: "Cancel", + cls: "mod-cancel" + }); + cancelButton.addEventListener("click", async () => { + this.close(); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/functions/utils.ts +var import_obsidian4 = require("obsidian"); +function isFileExplorerWorkspaceLeaf(leaf) { + var _a; + const tabHeaderEl = leaf == null ? void 0 : leaf.tabHeaderEl; + return ((_a = tabHeaderEl == null ? void 0 : tabHeaderEl.dataset) == null ? void 0 : _a.type) === "file-explorer"; +} +function getFileNameFromPathString(path) { + return path.substring(path.lastIndexOf("/") >= 0 ? path.lastIndexOf("/") + 1 : 0); +} +function getFolderNameFromPathString(path) { + const PARENT_FOLDER_INDEX = -2; + const LAST_FOLDER_INDEX = -1; + if (path.endsWith(".md") || path.endsWith(".canvas")) { + return path.split("/").slice(PARENT_FOLDER_INDEX)[0]; + } + return path.split("/").slice(LAST_FOLDER_INDEX)[0]; +} +function removeExtension(name) { + return name.replace(/\.[^/.]+$/, ""); +} +function getFolderPathFromString(path) { + const subString = path.lastIndexOf("/") >= 0 ? path.lastIndexOf("/") : 0; + const folderPath = path.substring(0, subString); + if (folderPath === "") { + return "/"; + } + return folderPath; +} +function getFileExplorer(plugin) { + var _a, _b, _c; + let leaf = plugin.app.workspace.getLeavesOfType("file-explorer")[0]; + if (!leaf) { + return void 0; + } + if (((_c = (_b = (_a = leaf.containerEl) == null ? void 0 : _a.lastChild) == null ? void 0 : _b.dataset) == null ? void 0 : _c.type) === "mk-path-view") { + plugin.app.workspace.iterateAllLeaves((x) => { + if (isFileExplorerWorkspaceLeaf(x)) { + leaf = x; + } + }); + } + return isFileExplorerWorkspaceLeaf(leaf) ? leaf : void 0; +} +function getFileExplorerActiveFolder(plugin) { + var _a, _b, _c; + const view = plugin.app.workspace.getActiveViewOfType(import_obsidian4.View); + if ((view == null ? void 0 : view.getViewType()) !== "file-explorer") + return null; + const fe = view; + const activeFileOrFolder = (_c = (_a = fe.tree.focusedItem) == null ? void 0 : _a.file) != null ? _c : (_b = fe.activeDom) == null ? void 0 : _b.file; + if (!(activeFileOrFolder instanceof import_obsidian4.TFolder)) + return null; + return activeFileOrFolder; +} +function getAttachmentFolderPath(plugin) { + const attachmentFolderPath = plugin.app.vault.getConfig("attachmentFolderPath"); + const cleanAttachmentFolderPath = (attachmentFolderPath == null ? void 0 : attachmentFolderPath.replace("./", "")) || ""; + return cleanAttachmentFolderPath; +} +function isFileInAttachmentFolder(plugin, file) { + const attachmentFolderPath = getAttachmentFolderPath(plugin); + if (!attachmentFolderPath) + return false; + return file.path.startsWith(attachmentFolderPath + "/") || file.path.includes("/" + attachmentFolderPath + "/"); +} + +// src/ExcludeFolders/ExcludePattern.ts +var ExcludePattern = class { + constructor(pattern, position, id, plugin) { + this.detached = false; + this.type = "pattern"; + this.id = id || crypto.randomUUID(); + this.string = pattern; + this.position = position; + this.subFolders = plugin.settings.excludePatternDefaultSettings.subFolders; + this.disableSync = plugin.settings.excludePatternDefaultSettings.disableSync; + this.disableAutoCreate = plugin.settings.excludePatternDefaultSettings.disableAutoCreate; + this.disableFolderNote = plugin.settings.excludePatternDefaultSettings.disableFolderNote; + this.enableCollapsing = plugin.settings.excludePatternDefaultSettings.enableCollapsing; + this.excludeFromFolderOverview = plugin.settings.excludePatternDefaultSettings.excludeFromFolderOverview; + this.path = ""; + this.hideInSettings = false; + this.showFolderNote = plugin.settings.excludePatternDefaultSettings.showFolderNote; + } +}; + +// src/ExcludeFolders/functions/folderFunctions.ts +var import_obsidian14 = require("obsidian"); + +// src/suggesters/FolderSuggester.ts +var import_obsidian5 = require("obsidian"); +var FolderSuggest = class extends import_obsidian5.AbstractInputSuggest { + constructor(inputEl, plugin, whitelistSuggester, folder) { + super(plugin.app, inputEl); + this.inputEl = inputEl; + this.whitelistSuggester = whitelistSuggester; + this.folder = folder; + this.plugin = plugin; + } + get_error_msg(mode) { + switch (mode) { + case 0 /* TemplateFiles */: + return "Templates folder doesn't exist"; + case 1 /* ScriptFiles */: + return "User Scripts folder doesn't exist"; + } + } + getSuggestions(input_str) { + const folders = []; + const lower_input_str = input_str.toLowerCase(); + let files = []; + if (this.folder) { + files = this.folder.children; + } else { + const MAX_FILE_SUGGESTIONS = 100; + files = this.plugin.app.vault.getAllLoadedFiles().slice(0, MAX_FILE_SUGGESTIONS); + } + files.forEach((folder) => { + if (folder instanceof import_obsidian5.TFolder && folder.path.toLowerCase().contains(lower_input_str) && (!this.plugin.settings.excludeFolders.find((f) => f.path === folder.path) || this.whitelistSuggester)) { + folders.push(folder); + } + }); + return folders; + } + renderSuggestion(folder, el) { + el.setText(folder.path); + } + selectSuggestion(folder) { + this.inputEl.value = folder.path; + this.inputEl.trigger("input"); + this.close(); + } +}; + +// src/ExcludeFolders/modals/ExcludeFolderSettings.ts +var import_obsidian7 = require("obsidian"); + +// src/functions/styleFunctions.ts +var import_obsidian6 = require("obsidian"); + +// src/ExcludeFolders/ExcludeFolder.ts +var ExcludedFolder = class { + constructor(path, position, id, plugin) { + this.detached = false; + this.type = "folder"; + this.id = id || crypto.randomUUID(); + this.path = path; + this.subFolders = plugin.settings.excludeFolderDefaultSettings.subFolders; + this.disableSync = plugin.settings.excludeFolderDefaultSettings.disableSync; + this.disableAutoCreate = plugin.settings.excludeFolderDefaultSettings.disableAutoCreate; + this.disableFolderNote = plugin.settings.excludeFolderDefaultSettings.disableFolderNote; + this.enableCollapsing = plugin.settings.excludeFolderDefaultSettings.enableCollapsing; + this.position = position; + this.excludeFromFolderOverview = plugin.settings.excludeFolderDefaultSettings.excludeFromFolderOverview; + this.string = ""; + this.hideInSettings = false; + this.showFolderNote = plugin.settings.excludeFolderDefaultSettings.showFolderNote; + } +}; + +// src/functions/styleFunctions.ts +function refreshAllFolderStyles(forceReload = false, plugin) { + const fileExplorer = getFileExplorer(plugin); + if (!fileExplorer) { + return; + } + if (plugin.activeFileExplorer === fileExplorer && !forceReload) { + return; + } + plugin.activeFileExplorer = fileExplorer; + plugin.app.vault.getAllLoadedFiles().forEach(async (file) => { + if (file instanceof import_obsidian6.TFolder) { + await updateCSSClassesForFolder(file.path, plugin); + } + }); +} +async function updateCSSClassesForFolder(folderPath, plugin) { + const folder = plugin.app.vault.getAbstractFileByPath(folderPath); + if (!folder || !(folder instanceof import_obsidian6.TFolder)) { + return; + } + const folderNote = getFolderNote(plugin, folder.path); + const detachedFolderNote = getDetachedFolder(plugin, folder.path); + if (folder.children.length === 0) { + addCSSClassToFileExplorerEl(folder.path, "fn-empty-folder", false, plugin); + } + if (!folderNote || detachedFolderNote) { + removeCSSClassFromFileExplorerEL(folder == null ? void 0 : folder.path, "has-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(folder == null ? void 0 : folder.path, "only-has-folder-note", true, plugin); + removeCSSClassFromFileExplorerEL(folderNote == null ? void 0 : folderNote.path, "is-folder-note", false, plugin); + return; + } + const excludedFolder = getExcludedFolder(plugin, folder.path, true); + if (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) { + removeCSSClassFromFileExplorerEL(folderNote.path, "is-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(folder.path, "has-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(folder == null ? void 0 : folder.path, "only-has-folder-note", true, plugin); + } else { + markFolderWithFolderNoteClasses(folder, plugin); + if (excludedFolder == null ? void 0 : excludedFolder.showFolderNote) { + addCSSClassToFileExplorerEl(folder.path, "show-folder-note-in-explorer", true, plugin); + unmarkFileAsFolderNote(folderNote, plugin); + return; + } + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, "only-has-folder-note", true, plugin); + } else { + removeCSSClassFromFileExplorerEL(folder.path, "only-has-folder-note", true, plugin); + } + } + markFolderAndNoteWithClasses(folderNote, folder, plugin); +} +async function updateCSSClassesForFolderNote(filePath, plugin) { + const file = plugin.app.vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof import_obsidian6.TFile)) { + return; + } + const folder = getFolder(plugin, file); + if (!folder || !(folder instanceof import_obsidian6.TFolder)) { + return; + } + updateCSSClassesForFolder(folder.path, plugin); +} +function markFolderAndNoteWithClasses(file, folder, plugin) { + markFileAsFolderNote(file, plugin); + markFolderWithFolderNoteClasses(folder, plugin); +} +function markFolderWithFolderNoteClasses(folder, plugin) { + addCSSClassToFileExplorerEl(folder.path, "has-folder-note", false, plugin); + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, "only-has-folder-note", true, plugin); + } else { + removeCSSClassFromFileExplorerEL(folder.path, "only-has-folder-note", true, plugin); + } +} +function markFileAsFolderNote(file, plugin) { + addCSSClassToFileExplorerEl(file.path, "is-folder-note", false, plugin); +} +function unmarkFileAsFolderNote(file, plugin) { + removeCSSClassFromFileExplorerEL(file.path, "is-folder-note", false, plugin); +} +function unmarkFolderAsFolderNote(folder, plugin) { + removeCSSClassFromFileExplorerEL(folder.path, "has-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(folder.path, "only-has-folder-note", true, plugin); +} +async function addCSSClassToFileExplorerEl(path, cssClass, parent = false, plugin, waitForCreate = false, count = 0) { + const fileExplorerItem = getFileExplorerElement(path, plugin); + const MAX_RETRIES = 5; + const RETRY_DELAY = 500; + if (!fileExplorerItem) { + if (waitForCreate && count < MAX_RETRIES) { + await new Promise((r) => window.setTimeout(r, RETRY_DELAY)); + addCSSClassToFileExplorerEl(path, cssClass, parent, plugin, waitForCreate, count + 1); + return; + } + return; + } + if (parent) { + const parentElement = fileExplorerItem == null ? void 0 : fileExplorerItem.parentElement; + if (parentElement) { + parentElement.addClass(cssClass); + } + } else { + fileExplorerItem.addClass(cssClass); + activeDocument.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => { + item.addClass(cssClass); + }); + } +} +function removeCSSClassFromFileExplorerEL(path, cssClass, parent, plugin) { + if (!path) + return; + const fileExplorerItem = getFileExplorerElement(path, plugin); + activeDocument.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => { + item.removeClass(cssClass); + }); + if (!fileExplorerItem) { + return; + } + if (parent) { + const parentElement = fileExplorerItem == null ? void 0 : fileExplorerItem.parentElement; + if (parentElement) { + parentElement.removeClass(cssClass); + } + return; + } + fileExplorerItem.removeClass(cssClass); +} +function getFileExplorerElement(path, plugin) { + var _a, _b, _c, _d; + const fileExplorer = getFileExplorer(plugin); + if (!((_a = fileExplorer == null ? void 0 : fileExplorer.view) == null ? void 0 : _a.fileItems)) { + return null; + } + const fileExplorerItem = (_b = fileExplorer.view.fileItems) == null ? void 0 : _b[path]; + return (_d = (_c = fileExplorerItem == null ? void 0 : fileExplorerItem.selfEl) != null ? _c : fileExplorerItem == null ? void 0 : fileExplorerItem.titleEl) != null ? _d : null; +} +function showFolderNoteInFileExplorer(path, plugin) { + const excludedFolder = new ExcludedFolder(path, plugin.settings.excludeFolders.length, void 0, plugin); + excludedFolder.subFolders = false; + excludedFolder.disableSync = false; + excludedFolder.disableAutoCreate = false; + excludedFolder.disableFolderNote = false; + excludedFolder.enableCollapsing = false; + excludedFolder.excludeFromFolderOverview = false; + excludedFolder.hideInSettings = true; + excludedFolder.showFolderNote = true; + addExcludedFolder(plugin, excludedFolder, false); + addCSSClassToFileExplorerEl(path, "show-folder-note-in-explorer", true, plugin); + updateCSSClassesForFolder(path, plugin); +} +function hideFolderNoteInFileExplorer(folderPath, plugin) { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.path !== folderPath || !folder.showFolderNote); + plugin.saveSettings(false); + removeCSSClassFromFileExplorerEL(folderPath, "show-folder-note-in-explorer", true, plugin); + updateCSSClassesForFolder(folderPath, plugin); +} +function setActiveFolder(folderPath, plugin) { + const fileExplorerItem = getFileExplorerElement(folderPath, plugin); + if (fileExplorerItem) { + fileExplorerItem.addClass("fn-is-active"); + plugin.activeFolderDom = fileExplorerItem; + } +} +function removeActiveFolder(plugin) { + var _a; + if (plugin.activeFolderDom) { + plugin.activeFolderDom.removeClass("fn-is-active"); + (_a = plugin.activeFolderDom) == null ? void 0 : _a.removeClass("has-focus"); + plugin.activeFolderDom = null; + } +} + +// src/ExcludeFolders/modals/ExcludeFolderSettings.ts +var ExcludedFolderSettings = class extends import_obsidian7.Modal { + constructor(app, plugin, excludedFolder) { + super(app); + this.plugin = plugin; + this.app = app; + this.excludedFolder = excludedFolder; + } + onOpen() { + this.display(); + } + display() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h2", { text: "Excluded folder settings" }); + new import_obsidian7.Setting(contentEl).setName("Include subfolders").setDesc("Choose if the subfolders of the folder should also be excluded").addToggle((toggle) => toggle.setValue(this.excludedFolder.subFolders).onChange(async (value) => { + this.excludedFolder.subFolders = value; + await this.plugin.saveSettings(true); + })); + new import_obsidian7.Setting(contentEl).setName("Disable folder name sync").setDesc("Choose if the folder note should be renamed when the folder name is changed").addToggle((toggle) => toggle.setValue(this.excludedFolder.disableSync).onChange(async (value) => { + this.excludedFolder.disableSync = value; + await this.plugin.saveSettings(); + })); + new import_obsidian7.Setting(contentEl).setName("Don't show folder in folder overview").setDesc("Choose if the folder should be shown in the folder overview").addToggle((toggle) => toggle.setValue(this.excludedFolder.excludeFromFolderOverview).onChange(async (value) => { + this.excludedFolder.excludeFromFolderOverview = value; + await this.plugin.saveSettings(); + })); + new import_obsidian7.Setting(contentEl).setName("Show folder note in the file explorer").setDesc("Choose if the folder note should be shown in the file explorer").addToggle((toggle) => toggle.setValue(this.excludedFolder.showFolderNote).onChange(async (value) => { + this.excludedFolder.showFolderNote = value; + updateCSSClassesForFolder(this.excludedFolder.path, this.plugin); + await this.plugin.saveSettings(); + this.display(); + })); + new import_obsidian7.Setting(contentEl).setName("Disable auto creation of folder notes in this folder").setDesc("Choose if a folder note should be created when a new folder is created").addToggle((toggle) => toggle.setValue(this.excludedFolder.disableAutoCreate).onChange(async (value) => { + this.excludedFolder.disableAutoCreate = value; + await this.plugin.saveSettings(); + })); + new import_obsidian7.Setting(contentEl).setName("Disable open folder note").setDesc("Choose if the folder note should be opened when the folder is opened").addToggle((toggle) => toggle.setValue(this.excludedFolder.disableFolderNote).onChange(async (value) => { + this.excludedFolder.disableFolderNote = value; + await this.plugin.saveSettings(true); + this.display(); + })); + if (!this.excludedFolder.disableFolderNote) { + new import_obsidian7.Setting(contentEl).setName("Collapse folder when opening folder note").setDesc("Choose if the folder should be collapsed when the folder note is opened").addToggle((toggle) => toggle.setValue(this.excludedFolder.enableCollapsing).onChange(async (value) => { + this.excludedFolder.enableCollapsing = value; + await this.plugin.saveSettings(); + })); + } + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/ExcludeFolders/functions/patternFunctions.ts +var import_obsidian9 = require("obsidian"); + +// src/ExcludeFolders/modals/PatternSettings.ts +var import_obsidian8 = require("obsidian"); +var PatternSettings = class extends import_obsidian8.Modal { + constructor(app, plugin, pattern) { + super(app); + this.plugin = plugin; + this.app = app; + this.pattern = pattern; + } + onOpen() { + this.display(); + } + display() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h2", { text: "Pattern settings" }); + new import_obsidian8.Setting(contentEl).setName("Disable folder name sync").setDesc("Choose if the folder name should be renamed when the file name has been changed").addToggle((toggle) => toggle.setValue(this.pattern.disableSync).onChange(async (value) => { + this.pattern.disableSync = value; + await this.plugin.saveSettings(); + })); + new import_obsidian8.Setting(contentEl).setName("Disable auto creation of folder notes in this folder").setDesc("Choose if a folder note should be created when a new folder is created that matches this pattern").addToggle((toggle) => toggle.setValue(this.pattern.disableAutoCreate).onChange(async (value) => { + this.pattern.disableAutoCreate = value; + await this.plugin.saveSettings(); + })); + new import_obsidian8.Setting(contentEl).setName("Don't show folder in folder overview").setDesc("Choose if the folder should be shown in the folder overview").addToggle((toggle) => toggle.setValue(this.pattern.excludeFromFolderOverview).onChange(async (value) => { + this.pattern.excludeFromFolderOverview = value; + await this.plugin.saveSettings(); + })); + new import_obsidian8.Setting(contentEl).setName("Show folder note in the file explorer").setDesc("Choose if the folder note should be shown in the file explorer").addToggle((toggle) => toggle.setValue(this.pattern.showFolderNote).onChange(async (value) => { + this.pattern.showFolderNote = value; + await this.plugin.saveSettings(); + refreshAllFolderStyles(true, this.plugin); + this.display(); + })); + new import_obsidian8.Setting(contentEl).setName("Disable open folder note").setDesc("Choose if the folder note should be opened when the folder is opened").addToggle((toggle) => toggle.setValue(this.pattern.disableFolderNote).onChange(async (value) => { + this.pattern.disableFolderNote = value; + await this.plugin.saveSettings(true); + this.display(); + })); + if (!this.pattern.disableFolderNote) { + new import_obsidian8.Setting(contentEl).setName("Collapse folder when opening folder note").setDesc("Choose if the folder should be collapsed when the folder note is opened").addToggle((toggle) => toggle.setValue(this.pattern.enableCollapsing).onChange(async (value) => { + this.pattern.enableCollapsing = value; + await this.plugin.saveSettings(); + })); + } + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/ExcludeFolders/functions/patternFunctions.ts +var REGEX_PREFIX = "{regex}"; +var STAR = "*"; +var INDEX_START = 0; +var SLICE_START_ONE = 1; +var SLICE_EXCLUDE_LAST = -1; +function matchesPatternSpec(raw, folderName) { + if (!raw) + return false; + const string = raw.trim(); + const isRegex = string.startsWith(REGEX_PREFIX); + const hasStartStar = string.startsWith(STAR); + const hasEndStar = string.endsWith(STAR); + if (!isRegex && !(hasStartStar || hasEndStar)) + return false; + if (isRegex) { + const body = string.replace(REGEX_PREFIX, "").trim(); + if (body === "") + return false; + try { + return new RegExp(body).test(folderName); + } catch (e) { + return false; + } + } + if (hasStartStar && hasEndStar) { + const inner = string.slice(SLICE_START_ONE, SLICE_EXCLUDE_LAST); + return folderName.includes(inner); + } + if (hasStartStar) { + const suffix = string.slice(SLICE_START_ONE); + return folderName.endsWith(suffix); + } + if (hasEndStar) { + const prefix = string.slice(INDEX_START, SLICE_EXCLUDE_LAST); + return folderName.startsWith(prefix); + } + return false; +} +function updatePattern(plugin, pattern, newPattern) { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== pattern.id); + addExcludedFolder(plugin, newPattern); +} +async function deletePattern(plugin, pattern) { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== pattern.id || folder.type === "folder"); + await plugin.saveSettings(true); + resyncArray(plugin); +} +function getExcludedFoldersByPattern(plugin, folderName) { + return plugin.settings.excludeFolders.filter((s) => s.type === "pattern").filter((pattern) => matchesPatternSpec(pattern.string, folderName)); +} +function addExcludePatternListItem(settings, containerEl, pattern) { + const { plugin } = settings; + const setting = new import_obsidian9.Setting(containerEl); + setting.setClass("fn-exclude-folder-list"); + setting.addSearch((cb) => { + cb.containerEl.addClass("fn-exclude-folder-path"); + cb.setPlaceholder("Pattern"); + cb.setValue(pattern.string); + cb.onChange((value) => { + pattern.string = value; + updatePattern(plugin, pattern, pattern); + }); + }); + setting.addButton((cb) => { + cb.setIcon("edit"); + cb.setTooltip("Edit pattern"); + cb.onClick(() => { + new PatternSettings(plugin.app, plugin, pattern).open(); + }); + }); + if (import_obsidian9.Platform.isDesktop || import_obsidian9.Platform.isTablet) { + setting.addButton((cb) => { + cb.setIcon("up-chevron-glyph"); + cb.setTooltip("Move up"); + cb.onClick(() => { + if (pattern.position === 0) { + return; + } + pattern.position -= 1; + updatePattern(plugin, pattern, pattern); + const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position); + if (oldPattern) { + oldPattern.position += 1; + if (oldPattern.type === "pattern") { + const pat = oldPattern; + updatePattern(plugin, pat, pat); + } else { + updateExcludedFolder(plugin, oldPattern, oldPattern); + } + } + settings.display(); + }); + }); + setting.addButton((cb) => { + cb.setIcon("down-chevron-glyph"); + cb.setTooltip("Move down"); + cb.onClick(() => { + if (pattern.position === plugin.settings.excludeFolders.length - 1) { + return; + } + pattern.position += 1; + updatePattern(plugin, pattern, pattern); + const oldPattern = plugin.settings.excludeFolders.find((folder) => folder.position === pattern.position); + if (oldPattern) { + oldPattern.position -= 1; + if (oldPattern.type === "pattern") { + const pat = oldPattern; + updatePattern(plugin, pat, pat); + } else { + updateExcludedFolder(plugin, oldPattern, oldPattern); + } + } + settings.display(); + }); + }); + } + setting.addButton((cb) => { + cb.setIcon("trash-2"); + cb.setTooltip("Delete pattern"); + cb.onClick(() => { + void deletePattern(plugin, pattern); + setting.clear(); + setting.settingEl.remove(); + }); + }); +} + +// src/ExcludeFolders/WhitelistPattern.ts +var WhitelistedPattern = class { + constructor(pattern, position, id, plugin) { + this.enableSync = false; + this.enableAutoCreate = false; + this.enableFolderNote = false; + this.disableCollapsing = false; + this.showInFolderOverview = false; + this.hideInFileExplorer = false; + this.hideInSettings = false; + this.type = "pattern"; + this.id = id || crypto.randomUUID(); + this.subFolders = plugin.settings.excludePatternDefaultSettings.subFolders; + this.position = position; + this.string = pattern; + this.path = ""; + } +}; + +// src/ExcludeFolders/functions/whitelistFolderFunctions.ts +var import_obsidian13 = require("obsidian"); + +// src/ExcludeFolders/modals/WhitelistFolderSettings.ts +var import_obsidian10 = require("obsidian"); +var WhitelistFolderSettings = class extends import_obsidian10.Modal { + constructor(app, plugin, whitelistedFolder) { + super(app); + this.plugin = plugin; + this.app = app; + this.whitelistedFolder = whitelistedFolder; + } + onOpen() { + this.display(); + } + display() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h2", { text: "Whitelisted folder settings" }); + new import_obsidian10.Setting(contentEl).setName("Include subfolders").setDesc("Choose if the subfolders of the folder should also be whitelisted").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.subFolders).onChange(async (value) => { + this.whitelistedFolder.subFolders = value; + await this.plugin.saveSettings(true); + })); + new import_obsidian10.Setting(contentEl).setName("Enable folder name sync").setDesc("Choose if the name of a folder note should be renamed when the folder name is changed").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.enableSync).onChange(async (value) => { + this.whitelistedFolder.enableSync = value; + await this.plugin.saveSettings(); + })); + new import_obsidian10.Setting(contentEl).setName("Show folder in folder overview").setDesc("Choose if the folder should be shown in the folder overview").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.showInFolderOverview).onChange(async (value) => { + this.whitelistedFolder.showInFolderOverview = value; + await this.plugin.saveSettings(); + })); + new import_obsidian10.Setting(contentEl).setName("Hide folder note in file explorer").setDesc("Choose if the folder note should be hidden in the file explorer").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.hideInFileExplorer).onChange(async (value) => { + this.whitelistedFolder.hideInFileExplorer = value; + await this.plugin.saveSettings(); + })); + new import_obsidian10.Setting(contentEl).setName("Allow auto creation of folder notes in this folder").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.enableAutoCreate).onChange(async (value) => { + this.whitelistedFolder.enableAutoCreate = value; + await this.plugin.saveSettings(); + })); + new import_obsidian10.Setting(contentEl).setName("Open folder note when clicking on the folder").setDesc("Choose if the folder note should be opened when the folder is opened").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.enableFolderNote).onChange(async (value) => { + this.whitelistedFolder.enableFolderNote = value; + await this.plugin.saveSettings(true); + this.display(); + })); + if (this.whitelistedFolder.enableFolderNote) { + new import_obsidian10.Setting(contentEl).setName("Don't collapse folder when opening folder note").setDesc("Choose if the folder should be collapsed when the folder note is opened").addToggle((toggle) => toggle.setValue(this.whitelistedFolder.disableCollapsing).onChange(async (value) => { + this.whitelistedFolder.disableCollapsing = value; + await this.plugin.saveSettings(); + })); + } + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/ExcludeFolders/functions/whitelistPatternFunctions.ts +var import_obsidian12 = require("obsidian"); + +// src/ExcludeFolders/modals/WhitelistPatternSettings.ts +var import_obsidian11 = require("obsidian"); +var WhitelistPatternSettings = class extends import_obsidian11.Modal { + constructor(app, plugin, pattern) { + super(app); + this.plugin = plugin; + this.app = app; + this.pattern = pattern; + } + onOpen() { + this.display(); + } + display() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h2", { text: "Whitelisted pattern settings" }); + new import_obsidian11.Setting(contentEl).setName("Enable folder name sync").setDesc("Choose if the name of a folder note should be renamed when the folder name is changed").addToggle((toggle) => toggle.setValue(this.pattern.enableSync).onChange(async (value) => { + this.pattern.enableSync = value; + await this.plugin.saveSettings(); + })); + new import_obsidian11.Setting(contentEl).setName("Allow auto creation of folder notes in this folder").addToggle((toggle) => toggle.setValue(this.pattern.enableAutoCreate).onChange(async (value) => { + this.pattern.enableAutoCreate = value; + await this.plugin.saveSettings(); + })); + new import_obsidian11.Setting(contentEl).setName("Show folder in folder overview").setDesc("Choose if the folder should be shown in the folder overview").addToggle((toggle) => toggle.setValue(this.pattern.showInFolderOverview).onChange(async (value) => { + this.pattern.showInFolderOverview = value; + await this.plugin.saveSettings(); + })); + new import_obsidian11.Setting(contentEl).setName("Open folder note when clicking on the folder").setDesc("Choose if the folder note should be opened when you click on the folder").addToggle((toggle) => toggle.setValue(this.pattern.enableFolderNote).onChange(async (value) => { + this.pattern.enableFolderNote = value; + await this.plugin.saveSettings(true); + this.display(); + })); + if (this.pattern.enableFolderNote) { + new import_obsidian11.Setting(contentEl).setName("Don't collapse folder when opening folder note").setDesc("Choose if the folder should be collapsed when the folder note is opened").addToggle((toggle) => toggle.setValue(this.pattern.disableCollapsing).onChange(async (value) => { + this.pattern.disableCollapsing = value; + await this.plugin.saveSettings(); + })); + } + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/ExcludeFolders/functions/whitelistPatternFunctions.ts +var REGEX_PREFIX2 = "{regex}"; +var STAR2 = "*"; +var SLICE_START_ONE2 = 1; +var SLICE_EXCLUDE_LAST2 = -1; +function matchesPatternSpec2(raw, folderName) { + if (!raw) + return false; + const string = raw.trim(); + const isRegex = string.startsWith(REGEX_PREFIX2); + const hasStartStar = string.startsWith(STAR2); + const hasEndStar = string.endsWith(STAR2); + if (!isRegex && !(hasStartStar || hasEndStar)) + return false; + if (isRegex) { + const body = string.replace(REGEX_PREFIX2, "").trim(); + if (body === "") + return false; + try { + return new RegExp(body).test(folderName); + } catch (e) { + return false; + } + } + if (hasStartStar && hasEndStar) { + const inner = string.slice(SLICE_START_ONE2, SLICE_EXCLUDE_LAST2); + return folderName.includes(inner); + } + if (hasStartStar) { + const suffix = string.slice(SLICE_START_ONE2); + return folderName.endsWith(suffix); + } + if (hasEndStar) { + const prefix = string.slice(0, SLICE_EXCLUDE_LAST2); + return folderName.startsWith(prefix); + } + return false; +} +function updateWhitelistedPattern(plugin, pattern, newPattern) { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== pattern.id); + addWhitelistedFolder(plugin, newPattern); +} +async function deletePattern2(plugin, pattern) { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== pattern.id || folder.type === "folder"); + await plugin.saveSettings(true); + resyncArray(plugin); +} +function getWhitelistedFoldersByPattern(plugin, folderName) { + return plugin.settings.whitelistFolders.filter((s) => s.type === "pattern").filter((pattern) => matchesPatternSpec2(pattern.string, folderName)); +} +function addWhitelistedPatternListItem(settings, containerEl, pattern) { + const { plugin } = settings; + const setting = new import_obsidian12.Setting(containerEl); + setting.setClass("fn-exclude-folder-list"); + setting.addSearch((cb) => { + cb.containerEl.addClass("fn-exclude-folder-path"); + cb.setPlaceholder("Pattern"); + cb.setValue(pattern.string); + cb.onChange((value) => { + const exists = plugin.settings.whitelistFolders.some((folder) => folder.string === value); + if (exists) { + return; + } + pattern.string = value; + updateWhitelistedPattern(plugin, pattern, pattern); + }); + }); + setting.addButton((cb) => { + cb.setIcon("edit"); + cb.setTooltip("Edit pattern"); + cb.onClick(() => { + new WhitelistPatternSettings(plugin.app, plugin, pattern).open(); + }); + }); + setting.addButton((cb) => { + cb.setIcon("up-chevron-glyph"); + cb.setTooltip("Move up"); + cb.onClick(() => { + if (pattern.position === 0) { + return; + } + pattern.position -= 1; + updateWhitelistedPattern(plugin, pattern, pattern); + const oldPattern = plugin.settings.whitelistFolders.find((folder) => folder.position === pattern.position); + if (oldPattern) { + oldPattern.position += 1; + if (oldPattern.type === "pattern") { + updateWhitelistedPattern(plugin, oldPattern, oldPattern); + } else { + updateWhitelistedFolder(plugin, oldPattern, oldPattern); + } + } + settings.display(); + }); + }); + setting.addButton((cb) => { + cb.setIcon("down-chevron-glyph"); + cb.setTooltip("Move down"); + cb.onClick(() => { + if (pattern.position === plugin.settings.whitelistFolders.length - 1) { + return; + } + pattern.position += 1; + updateWhitelistedPattern(plugin, pattern, pattern); + const oldPattern = plugin.settings.whitelistFolders.find((folder) => folder.position === pattern.position); + if (oldPattern) { + oldPattern.position -= 1; + if (oldPattern.type === "pattern") { + updateWhitelistedPattern(plugin, oldPattern, oldPattern); + } else { + updateWhitelistedFolder(plugin, oldPattern, oldPattern); + } + } + settings.display(); + }); + }); + setting.addButton((cb) => { + cb.setIcon("trash-2"); + cb.setTooltip("Delete pattern"); + cb.onClick(() => { + void deletePattern2(plugin, pattern); + setting.clear(); + setting.settingEl.remove(); + }); + }); +} + +// src/ExcludeFolders/functions/whitelistFolderFunctions.ts +function getWhitelistedFolder(plugin, path) { + let whitelistedFolder = {}; + const folderName = getFolderNameFromPathString(path); + const matchedPatterns = getWhitelistedFoldersByPattern(plugin, folderName); + const whitelistedFolders = getWhitelistedFoldersByPath(plugin, path); + const combinedWhitelistedFolders = [...matchedPatterns, ...whitelistedFolders]; + const propertiesToCopy = [ + "enableAutoCreate", + "enableFolderNote", + "enableSync", + "showInFolderOverview" + ]; + if (combinedWhitelistedFolders.length > 0) { + for (const matchedFolder of combinedWhitelistedFolders) { + propertiesToCopy.forEach((property) => { + const value = matchedFolder[property]; + if (value === true) { + whitelistedFolder[property] = true; + } else if (!value) { + whitelistedFolder[property] = false; + } + }); + } + } + if (whitelistedFolder && Object.keys(whitelistedFolder).length === 0) { + whitelistedFolder = void 0; + } + return whitelistedFolder; +} +function getWhitelistedFoldersByPath(plugin, path) { + return plugin.settings.whitelistFolders.filter((whitelistedFolder) => { + if (whitelistedFolder.path === path) { + return true; + } + if (!whitelistedFolder.subFolders) { + return false; + } + return getFolderPathFromString(path).startsWith(whitelistedFolder.path); + }); +} +function addWhitelistedFolder(plugin, whitelistedFolder) { + plugin.settings.whitelistFolders.push(whitelistedFolder); + void plugin.saveSettings(true); +} +async function deleteWhitelistedFolder(plugin, whitelistedFolder) { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== whitelistedFolder.id || folder.type === "pattern"); + await plugin.saveSettings(true); + resyncArray2(plugin); +} +function updateWhitelistedFolder(plugin, whitelistedFolder, newWhitelistFolder) { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.filter((folder) => folder.id !== whitelistedFolder.id); + addWhitelistedFolder(plugin, newWhitelistFolder); +} +function resyncArray2(plugin) { + plugin.settings.whitelistFolders = plugin.settings.whitelistFolders.sort((a, b) => a.position - b.position); + plugin.settings.whitelistFolders.forEach((folder, index) => { + folder.position = index; + }); + void plugin.saveSettings(); +} +function addWhitelistFolderListItem(settings, containerEl, whitelistedFolder) { + const { plugin } = settings; + const setting = new import_obsidian13.Setting(containerEl); + setting.setClass("fn-exclude-folder-list"); + const inputContainer = setting.settingEl.createDiv({ + cls: "fn-whitelist-folder-input-container" + }); + const SearchComponent = new import_obsidian13.Setting(inputContainer); + SearchComponent.addSearch((cb) => { + new FolderSuggest(cb.inputEl, plugin, true); + cb.containerEl.addClass("fn-exclude-folder-path"); + cb.setPlaceholder("Folder path"); + cb.setValue(whitelistedFolder.path); + cb.onChange((value) => { + if (value.startsWith("{regex}") || value.includes("*")) { + void deleteWhitelistedFolder(plugin, whitelistedFolder); + const pattern = new WhitelistedPattern(value, plugin.settings.whitelistFolders.length, void 0, plugin); + addWhitelistedFolder(plugin, pattern); + addWhitelistedPatternListItem(settings, containerEl, pattern); + setting.clear(); + setting.settingEl.remove(); + } + if (!plugin.app.vault.getAbstractFileByPath(value)) + return; + whitelistedFolder.path = value; + updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder); + }); + }); + const buttonContainer = setting.settingEl.createDiv({ cls: "fn-whitelist-folder-buttons" }); + new import_obsidian13.ButtonComponent(buttonContainer).setIcon("edit").setTooltip("Edit folder note").onClick(() => { + new WhitelistFolderSettings(plugin.app, plugin, whitelistedFolder).open(); + }); + new import_obsidian13.ButtonComponent(buttonContainer).setIcon("up-chevron-glyph").setTooltip("Move up").onClick(() => { + if (whitelistedFolder.position === 0) { + return; + } + whitelistedFolder.position -= 1; + updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder); + const oldWhitelistedFolder = plugin.settings.whitelistFolders.find((folder) => folder.position === whitelistedFolder.position); + if (oldWhitelistedFolder) { + oldWhitelistedFolder.position += 1; + if (oldWhitelistedFolder.type === "pattern") { + updateWhitelistedPattern(plugin, oldWhitelistedFolder, oldWhitelistedFolder); + } else { + updateWhitelistedFolder(plugin, oldWhitelistedFolder, oldWhitelistedFolder); + } + } + settings.display(); + }); + new import_obsidian13.ButtonComponent(buttonContainer).setIcon("down-chevron-glyph").setTooltip("Move down").onClick(() => { + if (whitelistedFolder.position === plugin.settings.whitelistFolders.length - 1) { + return; + } + whitelistedFolder.position += 1; + updateWhitelistedFolder(plugin, whitelistedFolder, whitelistedFolder); + const oldWhitelistedFolder = plugin.settings.whitelistFolders.find((folder) => folder.position === whitelistedFolder.position); + if (oldWhitelistedFolder) { + oldWhitelistedFolder.position -= 1; + if (oldWhitelistedFolder.type === "pattern") { + updateWhitelistedPattern(plugin, oldWhitelistedFolder, oldWhitelistedFolder); + } else { + updateWhitelistedFolder(plugin, oldWhitelistedFolder, oldWhitelistedFolder); + } + } + settings.display(); + }); + new import_obsidian13.ButtonComponent(buttonContainer).setIcon("trash-2").setTooltip("Delete excluded folder").onClick(() => { + void deleteWhitelistedFolder(plugin, whitelistedFolder); + setting.clear(); + setting.settingEl.remove(); + }); +} + +// src/ExcludeFolders/functions/folderFunctions.ts +function combineExcluded(plugin, path, includeDetached, pathOnly) { + const folderName = getFolderNameFromPathString(path); + const matchedPatterns = pathOnly ? [] : getExcludedFoldersByPattern(plugin, folderName); + const excludedByPath = getExcludedFoldersByPath(plugin, path); + let combined = [...matchedPatterns, ...excludedByPath]; + if (!includeDetached) + combined = combined.filter((f) => !f.detached); + return combined; +} +function aggregateFlags(combinedExcludedFolders) { + if (combinedExcludedFolders.length === 0) + return void 0; + const result = {}; + const propertiesToCopy = [ + "disableAutoCreate", + "disableFolderNote", + "disableSync", + "enableCollapsing", + "excludeFromFolderOverview", + "detached", + "hideInSettings", + "id", + "showFolderNote" + ]; + for (const matchedFolder of combinedExcludedFolders) { + for (const property of propertiesToCopy) { + const value = matchedFolder[property]; + if (value === true) { + result[property] = true; + } else if (!value) { + result[property] = false; + } + } + } + return result; +} +function applyWhitelistOverrides(excluded, whitelisted) { + const out = { ...excluded }; + if (out.disableAutoCreate !== void 0) { + out.disableAutoCreate = !whitelisted.enableAutoCreate; + } + if (out.disableFolderNote !== void 0) { + out.disableFolderNote = !whitelisted.enableFolderNote; + } + if (out.disableSync !== void 0) { + out.disableSync = !whitelisted.enableSync; + } + out.enableCollapsing = !whitelisted.disableCollapsing; + if (out.excludeFromFolderOverview !== void 0) { + out.excludeFromFolderOverview = !whitelisted.showInFolderOverview; + } + out.showFolderNote = !whitelisted.hideInFileExplorer; + return out; +} +function defaultExcludedIfEmpty(value) { + if (value && Object.keys(value).length === 0) { + return { + type: "folder", + id: "", + path: "", + string: "", + subFolders: false, + disableSync: false, + disableAutoCreate: false, + disableFolderNote: false, + enableCollapsing: false, + position: 0, + excludeFromFolderOverview: false, + hideInSettings: false, + detached: false, + showFolderNote: false + }; + } + return value; +} +function getExcludedFolder(plugin, path, includeDetached, pathOnly, ignoreWhitelist) { + const combined = combineExcluded(plugin, path, includeDetached, pathOnly); + let excluded = aggregateFlags(combined); + const whitelist = getWhitelistedFolder(plugin, path); + let skipWhitelist = ignoreWhitelist != null ? ignoreWhitelist : false; + if (excluded == null ? void 0 : excluded.detached) + skipWhitelist = true; + if (whitelist && excluded && !skipWhitelist) { + excluded = applyWhitelistOverrides(excluded, whitelist); + } + return defaultExcludedIfEmpty(excluded); +} +function getDetachedFolder(plugin, path) { + return plugin.settings.excludeFolders.find((f) => f.path === path && f.detached); +} +function getExcludedFoldersByPath(plugin, path) { + return plugin.settings.excludeFolders.filter((excludedFolder) => { + if (path.trim() === "" || !excludedFolder.path) { + return false; + } + if (excludedFolder.path === path) { + return true; + } + if (!excludedFolder.subFolders) { + return false; + } + const excludedFolderPath = excludedFolder.path.includes("/") ? excludedFolder.path : `${excludedFolder.path}/`; + let folderPath = getFolderPathFromString(path); + folderPath = folderPath.includes("/") ? folderPath : `${folderPath}/`; + if (folderPath.includes("/") || folderPath.includes("\\")) { + return folderPath.startsWith(excludedFolderPath) || folderPath === excludedFolderPath; + } + return folderPath === excludedFolderPath; + }); +} +function addExcludedFolder(plugin, excludedFolder, reloadStyles = true) { + plugin.settings.excludeFolders.push(excludedFolder); + void plugin.saveSettings(reloadStyles); +} +async function deleteExcludedFolder(plugin, excludedFolder) { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id || folder.type === "pattern"); + await plugin.saveSettings(true); + resyncArray(plugin); +} +function updateExcludedFolder(plugin, excludedFolder, newExcludeFolder) { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((folder) => folder.id !== excludedFolder.id); + addExcludedFolder(plugin, newExcludeFolder); +} +function resyncArray(plugin) { + plugin.settings.excludeFolders = plugin.settings.excludeFolders.sort((a, b) => a.position - b.position); + plugin.settings.excludeFolders.forEach((folder, index) => { + folder.position = index; + }); + void plugin.saveSettings(); +} +function addExcludeFolderListItem(settings, containerEl, excludedFolder) { + const { plugin } = settings; + const setting = new import_obsidian14.Setting(containerEl); + setting.setClass("fn-exclude-folder-list"); + setting.addSearch((cb) => { + new FolderSuggest(cb.inputEl, plugin, false); + cb.containerEl.addClass("fn-exclude-folder-path"); + cb.setPlaceholder("Folder path"); + cb.setValue(excludedFolder.path || ""); + cb.onChange((value) => { + if (value.startsWith("{regex}") || value.includes("*")) { + deleteExcludedFolder(plugin, excludedFolder); + const pattern = new ExcludePattern(value, plugin.settings.excludeFolders.length, void 0, plugin); + addExcludedFolder(plugin, pattern); + addExcludePatternListItem(settings, containerEl, pattern); + setting.clear(); + setting.settingEl.remove(); + } + if (!plugin.app.vault.getAbstractFileByPath(value)) + return; + excludedFolder.path = value; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + }); + }); + setting.addButton((cb) => { + cb.setIcon("edit"); + cb.setTooltip("Edit folder note"); + cb.onClick(() => { + new ExcludedFolderSettings(plugin.app, plugin, excludedFolder).open(); + }); + }); + if (import_obsidian14.Platform.isDesktop || import_obsidian14.Platform.isTablet) { + setting.addButton((cb) => { + cb.setIcon("up-chevron-glyph"); + cb.setTooltip("Move up"); + cb.onClick(() => { + if (excludedFolder.position === 0) { + return; + } + excludedFolder.position -= 1; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position); + if (oldExcludedFolder) { + oldExcludedFolder.position += 1; + if (oldExcludedFolder.type === "pattern") { + updatePattern(plugin, oldExcludedFolder, oldExcludedFolder); + } else { + updateExcludedFolder(plugin, oldExcludedFolder, oldExcludedFolder); + } + } + settings.display(); + }); + }); + setting.addButton((cb) => { + cb.setIcon("down-chevron-glyph"); + cb.setTooltip("Move down"); + cb.onClick(() => { + if (excludedFolder.position === plugin.settings.excludeFolders.length - 1) { + return; + } + excludedFolder.position += 1; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + const oldExcludedFolder = plugin.settings.excludeFolders.find((folder) => folder.position === excludedFolder.position); + if (oldExcludedFolder) { + oldExcludedFolder.position -= 1; + if (oldExcludedFolder.type === "pattern") { + updatePattern(plugin, oldExcludedFolder, oldExcludedFolder); + } else { + updateExcludedFolder(plugin, oldExcludedFolder, oldExcludedFolder); + } + } + settings.display(); + }); + }); + } + setting.addButton((cb) => { + cb.setIcon("trash-2"); + cb.setTooltip("Delete excluded folder"); + cb.onClick(() => { + deleteExcludedFolder(plugin, excludedFolder); + setting.clear(); + setting.settingEl.remove(); + }); + }); +} + +// src/functions/excalidraw.ts +async function openExcalidrawView(app, file) { + const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(app); + if (excalidrawEnabled && excalidraw) { + excalidraw.openDrawing(file); + } +} +async function getDefaultTemplate(app) { + const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(app); + if (excalidrawEnabled && excalidraw) { + return excalidraw.getBlankDrawing(); + } + return ""; +} +async function getExcalidrawPlugin(app) { + const { plugins: pluginManager } = app; + const excalidraw = pluginManager.plugins["obsidian-excalidraw-plugin"]; + const excalidrawEnabled = pluginManager.enabledPlugins.has("obsidian-excalidraw-plugin"); + return { + excalidraw: excalidraw != null ? excalidraw : null, + excalidrawEnabled + }; +} + +// src/modals/AskForExtension.ts +var import_obsidian15 = require("obsidian"); +var AskForExtensionModal = class extends import_obsidian15.FuzzySuggestModal { + constructor(plugin, folderPath, openFile, extension, useModal, existingNote) { + super(plugin.app); + this.plugin = plugin; + this.folderPath = folderPath; + this.extension = extension; + this.openFile = openFile; + this.useModal = useModal; + this.existingNote = existingNote; + plugin.askModalCurrentlyOpen = true; + } + getItems() { + return this.plugin.settings.supportedFileTypes.filter((item) => item.toLowerCase() !== ".ask"); + } + getItemText(item) { + return item; + } + onChooseItem(item, _evt) { + this.plugin.askModalCurrentlyOpen = false; + this.extension = "." + item; + void createFolderNote(this.plugin, this.folderPath, this.openFile, this.extension, this.useModal, this.existingNote); + this.close(); + } +}; + +// src/functions/folderNoteFunctions.ts +async function createFolderNote(plugin, folderPath, openFile, extension, displayModal, preexistingNote) { + var _a; + let { + leaf, + fileName, + folderNote, + folderNoteType, + detachedFolder, + path + } = getArgs(plugin, folderPath, extension, preexistingNote); + if (folderNoteType === ".excalidraw") { + folderNoteType = ".md"; + extension = ".excalidraw"; + } else if (folderNoteType === ".ask") { + if (plugin.askModalCurrentlyOpen) + return; + return new AskForExtensionModal(plugin, folderPath, openFile, folderNoteType, displayModal, preexistingNote).open(); + } + if (plugin.settings.storageLocation === "parentFolder") { + const parentFolderPath = getFolderPathFromString(folderPath); + if (parentFolderPath.trim() === "") { + path = `${fileName}${folderNoteType}`; + } else { + path = `${parentFolderPath}/${fileName}${folderNoteType}`; + } + } else if (plugin.settings.storageLocation === "vaultFolder") { + path = `${fileName}${folderNoteType}`; + } else { + path = `${folderPath}/${fileName}${folderNoteType}`; + } + if (detachedFolder && (folderNote == null ? void 0 : folderNote.extension) !== extension && folderNote) { + await handleTurnNoteIntoFolderNote(plugin, folderNote, detachedFolder, folderPath, fileName); + } + if (!extension) { + extension = folderNoteType; + } + if (!folderNote) { + folderNote = await handleCreateFolderNote(plugin, folderNoteType, openFile, leaf, folderNote, path, extension); + } else { + await plugin.app.fileManager.renameFile(folderNote, path); + } + if (openFile) { + if (((_a = plugin.app.workspace.getActiveFile()) == null ? void 0 : _a.path) === path) { + removeActiveFolder(plugin); + const folder2 = getFolder(plugin, folderNote); + if (!folder2) { + return; + } + setActiveFolder(folder2.path, plugin); + } + await leaf.openFile(folderNote); + if (plugin.settings.folderNoteType === ".excalidraw" || extension === ".excalidraw") { + openExcalidrawView(plugin.app, folderNote); + } + } + const matchingExtension = (extension == null ? void 0 : extension.split(".").pop()) === plugin.settings.templatePath.split(".").pop(); + if (folderNote && matchingExtension && plugin.settings.folderNoteType !== ".excalidraw") { + applyTemplate(plugin, folderNote, leaf, plugin.settings.templatePath); + } + const folder = plugin.app.vault.getAbstractFileByPath(folderPath); + if (!(folder instanceof import_obsidian16.TFolder)) + return; + addCSSClassToFileExplorerEl(path, "is-folder-note", false, plugin, true); + addCSSClassToFileExplorerEl(folder.path, "has-folder-note", false, plugin); +} +function getArgs(plugin, folderPath, extension, preexistingNote) { + const leaf = plugin.app.workspace.getLeaf(false); + const folderName = getFolderNameFromPathString(folderPath); + const fileName = plugin.settings.folderNoteName.replace("{{folder_name}}", folderName); + let folderNote = getFolderNote(plugin, folderPath); + if (preexistingNote) { + folderNote = preexistingNote; + } + let folderNoteType = extension != null ? extension : plugin.settings.folderNoteType; + const detachedFolder = getDetachedFolder(plugin, folderPath); + let path = ""; + return { + leaf, + fileName, + folderNote, + folderNoteType, + detachedFolder, + path + }; +} +async function handleCreateFolderNote(plugin, folderNoteType, openFile, leaf, folderNote, path, extension) { + let content = ""; + if (extension !== ".md" && extension) { + if (plugin.settings.templatePath && folderNoteType.split(".").pop() === plugin.settings.templatePath.split(".").pop()) { + const templateFile = plugin.app.vault.getAbstractFileByPath(plugin.settings.templatePath); + if (templateFile instanceof import_obsidian16.TFile) { + if (["md", "canvas", "txt"].includes(templateFile.extension)) { + content = await plugin.app.vault.read(templateFile); + if (extension === ".excalidraw" && !content.includes("==\u26A0 Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this activeDocument. \u26A0==")) { + content = await getDefaultTemplate(plugin.app); + } + } else { + plugin.app.vault.readBinary(templateFile).then(async (data) => { + folderNote = await plugin.app.vault.createBinary(path, data); + if (openFile) { + await leaf.openFile(folderNote); + } + return folderNote; + }); + } + } + } else if (plugin.settings.folderNoteType === ".excalidraw" || extension === ".excalidraw") { + content = await getDefaultTemplate(plugin.app); + } else if (plugin.settings.folderNoteType === ".canvas") { + content = "{}"; + } + } + folderNote = await plugin.app.vault.create(path, content); + return folderNote; +} +async function handleTurnNoteIntoFolderNote(plugin, folderNote, detachedFolder, folderPath, fileName) { + deleteExcludedFolder(plugin, detachedFolder); + removeCSSClassFromFileExplorerEL(folderNote == null ? void 0 : folderNote.path, "is-folder-note", false, plugin); + const folder = plugin.app.vault.getAbstractFileByPath(folderPath); + if (!(folder instanceof import_obsidian16.TFolder)) + return; + if (!folderNote || folderNote.basename !== fileName) + return; + let count = 1; + const baseName = removeExtension(folderNote.path); + const ext = folderNote.path.split(".").pop(); + let newName = `${baseName} (${count}).${ext}`; + const MAX_FOLDER_NOTE_RENAME_ATTEMPTS = 100; + while (count < MAX_FOLDER_NOTE_RENAME_ATTEMPTS && plugin.app.vault.getAbstractFileByPath(newName)) { + count++; + newName = `${baseName} (${count}).${ext}`; + } + const [ + excludedFolder, + excludedFolderExisted, + disabledSync + ] = await tempDisableSync(plugin, folder); + await plugin.app.fileManager.renameFile(folderNote, newName).then(() => { + if (!excludedFolder) + return; + if (!excludedFolderExisted) { + deleteExcludedFolder(plugin, excludedFolder); + } else if (!disabledSync) { + excludedFolder.disableSync = false; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + }); +} +async function turnIntoFolderNote(plugin, file, folder, folderNote, skipConfirmation) { + var _a; + const { extension } = file; + const detachedExcludedFolder = getDetachedFolder(plugin, folder.path); + if (folderNote) { + if (plugin.settings.showRenameConfirmation && !skipConfirmation && !detachedExcludedFolder) { + return new ExistingFolderNoteModal(plugin.app, plugin, file, folder, folderNote).open(); + } + removeCSSClassFromFileExplorerEL(folderNote.path, "is-folder-note", false, plugin); + const [ + excludedFolder, + excludedFolderExisted, + disabledSync + ] = await tempDisableSync(plugin, folder); + const CTIME_SLICE_START = 10; + const RANDOM_SUFFIX_MAX = 1e3; + const randomSuffix = Math.floor(Math.random() * RANDOM_SUFFIX_MAX); + const ctimeSuffix = file.stat.ctime.toString().slice(CTIME_SLICE_START); + const newPath = `${folder.path}/${folder.name} (${ctimeSuffix}${randomSuffix}).${extension}`; + plugin.app.fileManager.renameFile(folderNote, newPath).then(() => { + if (!excludedFolder) + return; + if (!excludedFolderExisted) { + deleteExcludedFolder(plugin, excludedFolder); + } else if (!disabledSync) { + excludedFolder.disableSync = false; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + }); + } + const folderName = folder.name; + const fileName = plugin.settings.folderNoteName.replace("{{folder_name}}", folderName); + let path = `${folder.path}/${fileName}.${extension}`; + if (plugin.settings.storageLocation === "parentFolder") { + const parentFolderPath = (_a = folder.parent) == null ? void 0 : _a.path; + if (!parentFolderPath) + return; + if (parentFolderPath.trim() === "") { + path = `${fileName}.${extension}`; + } else { + path = `${parentFolderPath}/${fileName}.${extension}`; + } + } + if (detachedExcludedFolder) { + void deleteExcludedFolder(plugin, detachedExcludedFolder); + } + await plugin.app.fileManager.renameFile(file, path); + void addCSSClassToFileExplorerEl(path, "is-folder-note", false, plugin, true); + void addCSSClassToFileExplorerEl(folder.path, "has-folder-note", false, plugin); + removeActiveFolder(plugin); + setActiveFolder(folder.path, plugin); +} +async function tempDisableSync(plugin, folder) { + let excludedFolder = getExcludedFolder(plugin, folder.path, false); + let excludedFolderExisted = true; + let disabledSync = false; + if (!excludedFolder) { + excludedFolderExisted = false; + excludedFolder = new ExcludedFolder(folder.path, plugin.settings.excludeFolders.length, void 0, plugin); + excludedFolder.disableSync = true; + addExcludedFolder(plugin, excludedFolder); + } else if (!excludedFolder.disableSync) { + disabledSync = false; + excludedFolder.disableSync = true; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + return [excludedFolder, excludedFolderExisted, disabledSync]; +} +async function openFolderNote(plugin, file, evt) { + var _a; + const { path } = file; + const focusExistingTab = plugin.settings.focusExistingTab && plugin.settings.openInNewTab; + const activeFilePath = (_a = plugin.app.workspace.getActiveFile()) == null ? void 0 : _a.path; + if (activeFilePath === path && !(import_obsidian16.Keymap.isModEvent(evt) === "tab")) { + return; + } + let foundLeaf = null; + if (focusExistingTab && file instanceof import_obsidian16.TFile) { + plugin.app.workspace.iterateAllLeaves((leaf) => { + var _a2; + if (leaf.getViewState().type === "markdown" && ((_a2 = leaf.view.file) == null ? void 0 : _a2.path) === path) { + foundLeaf = leaf; + } + }); + } + if (foundLeaf) { + plugin.app.workspace.setActiveLeaf(foundLeaf, { focus: true }); + } else { + const shouldOpenInNewTab = import_obsidian16.Keymap.isModEvent(evt) || plugin.settings.openInNewTab; + const leaf = plugin.app.workspace.getLeaf(shouldOpenInNewTab); + if (file instanceof import_obsidian16.TFile) { + await leaf.openFile(file); + } + } +} +async function deleteFolderNote(plugin, file, displayModal) { + if (plugin.settings.showDeleteConfirmation && displayModal) { + return new DeleteConfirmationModal(plugin.app, plugin, file).open(); + } + const folder = getFolder(plugin, file); + if (!folder) + return; + plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter((excludedFolder) => excludedFolder.path !== folder.path || !excludedFolder.showFolderNote); + plugin.saveSettings(false); + removeCSSClassFromFileExplorerEL(folder.path, "has-folder-note", false, plugin); + switch (plugin.settings.deleteFilesAction) { + case "trash": + await plugin.app.vault.trash(file, true); + break; + case "obsidianTrash": + await plugin.app.vault.trash(file, false); + break; + case "delete": + await plugin.app.vault.delete(file); + break; + } +} +function extractFolderName(template, changedFileName) { + const [prefix, suffix] = template.split("{{folder_name}}"); + if (prefix.trim() === "" && suffix.trim() === "") { + return changedFileName; + } + if (!changedFileName.startsWith(prefix) || !changedFileName.endsWith(suffix)) { + return null; + } + if (changedFileName.startsWith(prefix) && prefix.trim() !== "") { + return changedFileName.slice(prefix.length).replace(suffix, ""); + } else if (changedFileName.endsWith(suffix) && suffix.trim() !== "") { + return changedFileName.slice(0, -suffix.length); + } + return null; +} +function findFolderNoteFile(plugin, path, primaryType) { + let folderNote = plugin.app.vault.getAbstractFileByPath(path + primaryType); + if (folderNote instanceof import_obsidian16.TFile && plugin.settings.supportedFileTypes.includes(primaryType.replace(".", ""))) { + return folderNote; + } + const supportedFileTypes = plugin.settings.supportedFileTypes.filter((type) => type !== primaryType.replace(".", "")); + for (let type of supportedFileTypes) { + if (type === "excalidraw" || type === ".excalidraw") { + type = ".md"; + } + if (!type.startsWith(".")) { + type = "." + type; + } + folderNote = plugin.app.vault.getAbstractFileByPath(path + type); + if (folderNote instanceof import_obsidian16.TFile) { + return folderNote; + } + } + return null; +} +function getFolderNote(plugin, folderPath, storageLocation, file, oldFolderNoteName) { + const folder = getFolderInfo(folderPath); + if (!folder) + return null; + let fileName = resolveFileName(plugin, folder, file, oldFolderNoteName); + if (!fileName) + return null; + adjustFolderPathForStorage(folder, folderPath, plugin, storageLocation); + const path = buildFullPath(folder, fileName); + const primaryType = normalizeFolderNoteType(plugin.settings.folderNoteType); + return findFolderNoteFile(plugin, path, primaryType); +} +function detachFolderNote(plugin, file) { + const folder = getFolder(plugin, file); + if (!folder) + return; + const excludedFolder = new ExcludedFolder(folder.path, plugin.settings.excludeFolders.length, void 0, plugin); + excludedFolder.showFolderNote = true; + excludedFolder.hideInSettings = true; + excludedFolder.disableFolderNote = true; + excludedFolder.disableSync = true; + excludedFolder.subFolders = false; + excludedFolder.excludeFromFolderOverview = false; + excludedFolder.detached = true; + excludedFolder.detachedFilePath = file.path; + addExcludedFolder(plugin, excludedFolder); + void updateCSSClassesForFolderNote(file.path, plugin); +} +function getFolder(plugin, file, storageLocation) { + var _a, _b; + if (!file) + return null; + let folderName = extractFolderName(plugin.settings.folderNoteName, file.basename); + if (plugin.settings.folderNoteName === file.basename && plugin.settings.storageLocation === "insideFolder") { + folderName = (_b = (_a = file.parent) == null ? void 0 : _a.name) != null ? _b : ""; + } + if (!folderName) + return null; + let folderPath = getFolderPathFromString(file.path); + let folder = null; + if ((plugin.settings.storageLocation === "parentFolder" || storageLocation === "parentFolder") && storageLocation !== "insideFolder") { + if (folderPath.trim() === "" || folderPath === "/") { + folderPath = folderName; + } else { + folderPath = `${folderPath}/${folderName}`; + } + folder = plugin.app.vault.getAbstractFileByPath(folderPath); + } else { + folder = plugin.app.vault.getAbstractFileByPath(folderPath); + } + if (!folder) { + return null; + } + return folder; +} +function getFolderNoteFolder(plugin, folderNote, fileName) { + if (!folderNote) + return null; + let filePath = ""; + if (typeof folderNote === "string") { + filePath = folderNote; + } else { + fileName = folderNote.basename; + filePath = folderNote.path; + } + const folderName = extractFolderName(plugin.settings.folderNoteName, fileName); + if (!plugin.settings.folderNoteName.includes("{{folder_name}}") && plugin.settings.storageLocation === "insideFolder") { + if (folderNote instanceof import_obsidian16.TFile) { + return folderNote.parent; + } + const file = plugin.app.vault.getAbstractFileByPath(filePath); + return file instanceof import_obsidian16.TFile ? file.parent : null; + } + if (!folderName) + return null; + let folderPath = getFolderPathFromString(filePath); + if (plugin.settings.storageLocation === "parentFolder") { + if (folderPath.trim() === "") { + folderPath = folderName; + } else { + folderPath = `${folderPath}/${folderName}`; + } + } else { + folderPath = getFolderPathFromString(filePath); + } + const folder = plugin.app.vault.getAbstractFileByPath(folderPath); + if (!folder) { + return null; + } + return folder; +} +function getFolderInfo(folderPath) { + if (!folderPath) + return null; + return { + path: folderPath, + name: getFolderNameFromPathString(folderPath) + }; +} +function resolveFileName(plugin, folder, file, oldFolderNoteName) { + const templateName = oldFolderNoteName != null ? oldFolderNoteName : plugin.settings.folderNoteName; + if (!templateName) + return null; + const nameSource = file ? file.basename : folder.name; + return templateName.replace("{{folder_name}}", nameSource); +} +function adjustFolderPathForStorage(folder, folderPath, plugin, storageLocation) { + if ((plugin.settings.storageLocation === "parentFolder" || storageLocation === "parentFolder") && storageLocation !== "insideFolder") { + folder.path = getFolderPathFromString(folderPath); + } +} +function buildFullPath(folder, fileName) { + return folder.path === "/" ? fileName : `${folder.path}/${fileName}`; +} +function normalizeFolderNoteType(type) { + return type === ".excalidraw" ? ".md" : type; +} + +// src/settings/GeneralSettings.ts +var import_obsidian23 = require("obsidian"); + +// src/events/EventEmitter.ts +var CustomEventEmitter = class { + constructor() { + this.events = {}; + } + on(event, listener) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(listener); + } + off(event, listener) { + if (!this.events[event]) + return; + this.events[event] = this.events[event].filter((l) => l !== listener); + } + emit(event, data) { + if (!this.events[event]) + return; + this.events[event].forEach((listener) => listener(data)); + } +}; + +// src/functions/ListComponent.ts +var ListComponent = class { + constructor(containerEl, values = [], defaultValues = []) { + this.emitter = new CustomEventEmitter(); + this.containerEl = containerEl; + this.controlEl = containerEl.querySelector(".setting-item-control") || containerEl; + this.listEl = this.controlEl.createDiv("setting-command-hotkeys"); + this.addResetButton(); + this.setValues(values); + this.defaultValues = defaultValues; + } + on(event, listener) { + this.emitter.on(event, listener); + } + off(event, listener) { + this.emitter.off(event, listener); + } + emit(event, data) { + this.emitter.emit(event, data); + } + setValues(values) { + this.removeElements(); + this.values = values; + if (values.length !== 0) { + values.forEach((value) => { + this.addElement(value); + }); + } + this.emit("update", this.values); + } + removeElements() { + this.listEl.empty(); + } + addElement(value) { + this.listEl.createSpan("setting-hotkey", (span) => { + if (value.toLocaleLowerCase() === "md") { + const markdown = "markdown"; + span.innerText = markdown; + } else { + span.innerText = value; + } + span.setAttribute("extension", value); + const removeSpan = span.createEl("span", { cls: "ofn-list-item-remove setting-hotkey-icon" }); + const svg = ''; + const svgElement = removeSpan.createEl("span", { cls: "ofn-list-item-remove-icon" }); + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(svg, "image/svg+xml"); + const svgNode = svgDoc.documentElement; + if (svgNode) { + svgElement.appendChild(activeDocument.importNode(svgNode, true)); + } + removeSpan.onClickEvent(() => { + this.removeValue(value); + span.remove(); + }); + }); + } + async addValue(value) { + this.values.push(value); + this.addElement(value); + this.emit("add", value); + this.emit("update", this.values); + } + addResetButton() { + const resetButton = this.controlEl.createEl("span", { cls: "clickable-icon setting-restore-hotkey-button" }); + const svg = ''; + const svgElement = resetButton.createEl("span", { cls: "ofn-list-item-remove-icon" }); + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(svg, "image/svg+xml"); + const svgNode = svgDoc.documentElement; + if (svgNode) { + svgElement.appendChild(activeDocument.importNode(svgNode, true)); + } + resetButton.onClickEvent(() => { + this.setValues(this.defaultValues); + }); + return this; + } + removeValue(value) { + this.values = this.values.filter((v) => v !== value); + this.listEl.find(`[extension='${value}']`).remove(); + this.emit("remove", value); + this.emit("update", this.values); + } +}; + +// src/modals/AddSupportedFileType.ts +var import_obsidian17 = require("obsidian"); +var AddSupportedFileModal = class extends import_obsidian17.Modal { + constructor(app, plugin, settingsTab, list) { + super(app); + this.plugin = plugin; + this.app = app; + this.name = ""; + this.list = list; + this.settingsTab = settingsTab; + } + onOpen() { + const { contentEl } = this; + contentEl.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + this.close(); + } + }); + contentEl.createEl("h2", { text: "Extension name" }); + new import_obsidian17.Setting(contentEl).setName('Enter the name of the extension (only the short form, e.g. "md")').addText((text) => text.setValue("").onChange(async (value) => { + if (value.trim() !== "") { + this.name = value.trim(); + } + })); + } + onClose() { + if (this.name.toLocaleLowerCase() === "markdown") { + this.name = "md"; + } + const { contentEl } = this; + if (this.name === "") { + contentEl.empty(); + this.settingsTab.display(); + } else if (this.plugin.settings.supportedFileTypes.includes(this.name.toLowerCase())) { + new import_obsidian17.Notice("This extension is already supported"); + return; + } else { + void (async () => { + await this.list.addValue(this.name.toLowerCase()); + this.settingsTab.display(); + await this.plugin.saveSettings(); + contentEl.empty(); + })(); + } + } +}; + +// src/events/FrontMatterTitle.ts +var import_front_matter_plugin_api_provider = __toESM(require_lib()); +var import_obsidian18 = require("obsidian"); +var FrontMatterTitlePluginHandler = class { + constructor(plugin) { + this.api = null; + this.deffer = null; + this.modifiedFolders = /* @__PURE__ */ new Map(); + this.eventRef = null; + this.dispatcher = null; + this.plugin = plugin; + this.app = plugin.app; + (async () => { + var _a; + this.deffer = (0, import_front_matter_plugin_api_provider.getDefer)(this.app); + if (this.deffer.isPluginReady()) { + this.api = this.deffer.getApi(); + } else { + await this.deffer.awaitPlugin(); + this.api = this.deffer.getApi(); + if (!this.deffer.isFeaturesReady()) { + await this.deffer.awaitFeatures(); + } + } + if (plugin.settings.frontMatterTitle.enabled) { + const dispatcher = (_a = this.api) == null ? void 0 : _a.getEventDispatcher(); + if (dispatcher) { + this.dispatcher = dispatcher; + } + const event = { + name: "manager:update", + cb: (data) => { + this.fmptUpdateFileName(data, true); + } + }; + const ref = dispatcher == null ? void 0 : dispatcher.addListener(event); + if (ref) { + this.eventRef = ref; + } + plugin.updateAllBreadcrumbs(); + } + })(); + } + deleteEvent() { + if (this.eventRef && this.dispatcher) { + this.dispatcher.removeListener(this.eventRef); + } + } + async fmptUpdateFileName(data, isEvent) { + var _a, _b, _c; + const hasNestedData = "data" in data; + const actualData = hasNestedData ? data.data : data; + const file = this.app.vault.getAbstractFileByPath(actualData.path); + if (!(file instanceof import_obsidian18.TFile)) { + return; + } + const resolver = (_b = (_a = this.api) == null ? void 0 : _a.getResolverFactory()) == null ? void 0 : _b.createResolver("#feature-id#"); + const newName = resolver == null ? void 0 : resolver.resolve((_c = file == null ? void 0 : file.path) != null ? _c : ""); + const folder = getFolder(this.plugin, file); + if (!(folder instanceof import_obsidian18.TFolder)) { + return; + } + const folderNote = getFolderNote(this.plugin, folder.path); + if (!folderNote) { + return; + } + if (folderNote !== file) { + return; + } + if (!actualData.pathOnly) { + this.plugin.changeFolderNameInExplorer(folder, newName); + } + const { breadcrumb } = actualData; + if (breadcrumb) { + this.plugin.changeFolderNameInPath(folder, newName, breadcrumb); + } + if (isEvent) { + this.plugin.updateAllBreadcrumbs(); + } + if (newName) { + folder.newName = newName; + this.modifiedFolders.set(folder.path, folder); + } else { + folder.newName = null; + this.modifiedFolders.delete(folder.path); + } + } + async fmptUpdateFolderName(data, _replacePath) { + var _a, _b, _c; + const hasNestedData = "data" in data; + const actualData = hasNestedData ? data.data : data; + const folder = this.app.vault.getAbstractFileByPath(actualData.path); + if (!(folder instanceof import_obsidian18.TFolder)) { + return; + } + const folderNote = getFolderNote(this.plugin, folder.path); + if (!folderNote) { + return; + } + const resolver = (_b = (_a = this.api) == null ? void 0 : _a.getResolverFactory()) == null ? void 0 : _b.createResolver("#feature-id#"); + const newName = resolver == null ? void 0 : resolver.resolve((_c = folderNote == null ? void 0 : folderNote.path) != null ? _c : ""); + if (!newName) + return; + if (!actualData.pathOnly) { + this.plugin.changeFolderNameInExplorer(folder, newName); + } + const { breadcrumb } = actualData; + if (breadcrumb) { + this.plugin.changeFolderNameInPath(folder, newName, breadcrumb); + } + folder.newName = newName; + this.modifiedFolders.set(folder.path, folder); + } + async getNewFolderName(folder) { + var _a, _b, _c, _d; + if (this.modifiedFolders.has(folder.path)) { + const modifiedFolder = this.modifiedFolders.get(folder.path); + if (modifiedFolder) { + return modifiedFolder.newName; + } + } + const folderNote = getFolderNote(this.plugin, folder.path); + if (!folderNote) + return null; + const resolver = (_b = (_a = this.api) == null ? void 0 : _a.getResolverFactory()) == null ? void 0 : _b.createResolver("#feature-id#"); + return (_d = resolver == null ? void 0 : resolver.resolve((_c = folderNote == null ? void 0 : folderNote.path) != null ? _c : "")) != null ? _d : null; + } + async getNewFileName(file) { + var _a, _b, _c; + const resolver = (_b = (_a = this.api) == null ? void 0 : _a.getResolverFactory()) == null ? void 0 : _b.createResolver("#feature-id#"); + const changedName = resolver == null ? void 0 : resolver.resolve((_c = file == null ? void 0 : file.path) != null ? _c : ""); + return changedName != null ? changedName : null; + } +}; + +// src/settings/modals/CreateFnForEveryFolder.ts +var import_obsidian19 = require("obsidian"); +var ConfirmationModal = class extends import_obsidian19.Modal { + constructor(app, plugin) { + super(app); + this.plugin = plugin; + this.app = app; + this.extension = plugin.settings.folderNoteType; + } + onOpen() { + var _a, _b, _c; + this.modalEl.addClass("fn-confirmation-modal"); + let templateFolderPath; + const { templateFolder, templaterPlugin } = getTemplatePlugins(this.plugin.app); + if ((!templateFolder || (templateFolder == null ? void 0 : templateFolder.trim()) === "") && !templaterPlugin) { + templateFolderPath = ""; + } + if (templaterPlugin) { + templateFolderPath = (_b = (_a = templaterPlugin.plugin) == null ? void 0 : _a.settings) == null ? void 0 : _b.templates_folder; + } else if (templateFolder) { + templateFolderPath = templateFolder; + } + const { contentEl } = this; + contentEl.createEl("h2", { text: "Create folder note for every folder" }); + const setting = new import_obsidian19.Setting(contentEl); + setting.infoEl.createEl("p", { text: "Make sure to backup your vault before using this feature." }).addClass("fn-warning-text"); + setting.infoEl.createEl("p", { text: "This feature will create a folder note for every folder in your vault." }); + setting.infoEl.createEl("p", { text: "Every folder that already has a folder note will be ignored." }); + setting.infoEl.createEl("p", { text: "Every excluded folder will be ignored." }); + if (!this.plugin.settings.templatePath || ((_c = this.plugin.settings.templatePath) == null ? void 0 : _c.trim()) === "") { + new import_obsidian19.Setting(contentEl).setName("Folder note file extension").setDesc("Choose the file extension for the folder notes.").addDropdown((cb) => { + this.plugin.settings.supportedFileTypes.forEach((extension) => { + cb.addOption("." + extension, extension); + }); + cb.setValue(this.extension); + cb.onChange(async (value) => { + this.extension = value; + }); + }); + } + new import_obsidian19.Setting(contentEl).addButton((cb) => { + cb.setButtonText("Create"); + cb.setCta(); + cb.buttonEl.focus(); + cb.onClick(async () => { + if (this.plugin.settings.templatePath && this.plugin.settings.templatePath.trim() !== "") { + this.extension = "." + this.plugin.settings.templatePath.split(".").pop(); + } + if (this.extension === ".ask") { + return new import_obsidian19.Notice("Please choose a file extension"); + } + this.close(); + const folders = this.app.vault.getAllLoadedFiles().filter((file) => file.parent instanceof import_obsidian19.TFolder); + for (const folder of folders) { + if (folder instanceof import_obsidian19.TFolder) { + const excludedFolder = getExcludedFolder(this.plugin, folder.path, true); + if (excludedFolder) + continue; + if (folder.path === templateFolderPath) + continue; + const folderNote = getFolderNote(this.plugin, folder.path); + if (folderNote) + continue; + await createFolderNote(this.plugin, folder.path, false, this.extension); + } + } + }); + }).addButton((cb) => { + cb.setButtonText("Cancel"); + cb.onClick(async () => { + this.close(); + }); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/suggesters/TemplateSuggester.ts +var import_obsidian20 = require("obsidian"); +var TemplateSuggest = class extends import_obsidian20.AbstractInputSuggest { + constructor(inputEl, plugin) { + super(plugin.app, inputEl); + this.inputEl = inputEl; + this.plugin = plugin; + } + get_error_msg(mode) { + switch (mode) { + case 0 /* TemplateFiles */: + return "Templates folder doesn't exist"; + case 1 /* ScriptFiles */: + return "User Scripts folder doesn't exist"; + } + } + getSuggestions(input_str) { + var _a, _b; + const { templateFolder, templaterPlugin } = getTemplatePlugins(this.app); + let files = []; + const lower_input_str = input_str.toLowerCase(); + if ((!templateFolder || templateFolder.trim() === "") && !templaterPlugin) { + files = this.plugin.app.vault.getFiles().filter((file) => file.path.toLowerCase().includes(lower_input_str)); + } else { + let folder = null; + if (templaterPlugin) { + folder = this.plugin.app.vault.getAbstractFileByPath((_b = (_a = templaterPlugin.plugin) == null ? void 0 : _a.settings) == null ? void 0 : _b.templates_folder); + if (!(folder instanceof import_obsidian20.TFolder)) { + return [ + { + path: "", + name: "You need to set the Templates folder in the Templater settings first." + } + ]; + } + } else if (templateFolder) { + folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder); + } + if (!(folder instanceof import_obsidian20.TFolder)) { + return []; + } + import_obsidian20.Vault.recurseChildren(folder, (file) => { + if (file instanceof import_obsidian20.TFile && file.path.toLowerCase().includes(lower_input_str)) { + files.push(file); + } + }); + } + return files; + } + renderSuggestion(file, el) { + var _a, _b; + const { templateFolder, templaterPlugin } = getTemplatePlugins(this.app); + if ((!templateFolder || templateFolder.trim() === "") && !templaterPlugin) { + el.setText(`${((_a = file.parent) == null ? void 0 : _a.path) !== "/" ? ((_b = file.parent) == null ? void 0 : _b.path) + "/" : ""}${file.name}`); + } else { + el.setText(file.name); + } + } + selectSuggestion(file) { + this.inputEl.value = file.name.replace(".md", ""); + this.inputEl.trigger("input"); + this.plugin.settings.templatePath = file.path; + void this.plugin.saveSettings(); + this.close(); + } +}; + +// src/settings/modals/BackupWarning.ts +var import_obsidian21 = require("obsidian"); +var BackupWarningModal = class extends import_obsidian21.Modal { + constructor(plugin, title, description, callback, args) { + super(plugin.app); + this.plugin = plugin; + this.title = title; + this.callback = callback; + this.args = args; + this.desc = description; + } + onOpen() { + this.modalEl.addClass("fn-backup-warning-modal"); + const { contentEl } = this; + contentEl.createEl("h2", { text: this.title }); + contentEl.createEl("p", { text: this.desc }); + contentEl.createEl("p", { text: "Make sure to backup your vault before using this feature." }).addClass("fn-warning-text"); + const buttonContainer = contentEl.createDiv({ cls: "fn-modal-button-container" }); + const confirmButton = new import_obsidian21.ButtonComponent(buttonContainer); + confirmButton.setButtonText("Confirm").setCta().onClick(() => { + this.callback(...this.args); + this.close(); + }); + const cancelButton = new import_obsidian21.ButtonComponent(buttonContainer); + cancelButton.setButtonText("Cancel").onClick(() => { + this.close(); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/settings/modals/RenameFns.ts +var import_obsidian22 = require("obsidian"); +var RenameFolderNotesModal = class extends BackupWarningModal { + constructor(plugin, title, description, callback, args) { + super(plugin, title, description, callback, args); + } + insertCustomHtml() { + const { contentEl } = this; + new import_obsidian22.Setting(contentEl).setName("Old folder note name").setDesc("Every folder note that matches this name will be renamed to the new folder note name.").addText((text) => text.setPlaceholder("Enter the old folder note name").setValue(this.plugin.settings.oldFolderNoteName || "").onChange(async (value) => { + this.plugin.settings.oldFolderNoteName = value; + })); + new import_obsidian22.Setting(contentEl).setName("New folder note name").setDesc("Every folder note that matches the old folder note name will be renamed to this name.").addText((text) => text.setPlaceholder("Enter the new folder note name").setValue(this.plugin.settings.folderNoteName || "").onChange(async (value) => { + this.plugin.settings.folderNoteName = value; + this.plugin.settingsTab.display(); + })); + } +}; + +// src/settings/GeneralSettings.ts +var debounceTimer; +async function renderGeneral(settingsTab) { + const containerEl = settingsTab.settingsPage; + const nameSetting = new import_obsidian23.Setting(containerEl).setName("Folder note name template").setDesc("All folder notes will use this name. Use {{folder_name}} to insert the folder\u2019s name. Existing notes won\u2019t update automatically; click on the button to apply the new name.").addText((text) => text.setValue(settingsTab.plugin.settings.folderNoteName).onChange(async (value) => { + if (value.trim() === "") { + return; + } + settingsTab.plugin.settings.folderNoteName = value; + await settingsTab.plugin.saveSettings(); + window.clearTimeout(debounceTimer); + const FOLDER_NOTE_NAME_DEBOUNCE_MS = 2e3; + debounceTimer = window.setTimeout(() => { + if (!value.includes("{{folder_name}}")) { + if (!settingsTab.showFolderNameInTabTitleSetting) { + settingsTab.display(); + settingsTab.showFolderNameInTabTitleSetting = true; + } + } else { + if (settingsTab.showFolderNameInTabTitleSetting) { + settingsTab.display(); + settingsTab.showFolderNameInTabTitleSetting = false; + } + } + }, FOLDER_NOTE_NAME_DEBOUNCE_MS); + })).addButton((button) => button.setButtonText("Rename existing folder notes").setCta().onClick(async () => { + var _a; + new RenameFolderNotesModal(settingsTab.plugin, "Rename all existing folder notes", 'When you click on "Confirm" all existing folder notes will be renamed to the new folder note name.', settingsTab.renameFolderNotes, [(_a = settingsTab.plugin.settings.oldFolderNoteName) != null ? _a : "{{folder_name}}"]).open(); + })); + nameSetting.infoEl.appendText("Requires a restart to take effect"); + nameSetting.infoEl.style.color = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + if (!settingsTab.plugin.settings.folderNoteName.includes("{{folder_name}}")) { + new import_obsidian23.Setting(containerEl).setName("Display folder name in tab title").setDesc('Use the actual folder name in the tab title instead of the custom folder note name (e.g., "folder note").').addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.tabManagerEnabled).onChange(async (value) => { + if (!value) { + settingsTab.plugin.tabManager.resetTabs(); + } else { + settingsTab.plugin.settings.tabManagerEnabled = value; + settingsTab.plugin.tabManager.updateTabs(); + } + settingsTab.plugin.settings.tabManagerEnabled = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + } + new import_obsidian23.Setting(containerEl).setName("Default file type for new folder notes").setDesc("Choose the default file type (canvas, Markdown, ...) used when creating new folder notes.").addDropdown((dropdown) => { + dropdown.addOption(".ask", "Ask for file type"); + settingsTab.plugin.settings.supportedFileTypes.forEach((type) => { + if (type === ".md" || type === "md") { + dropdown.addOption(".md", "Markdown"); + } else { + dropdown.addOption("." + type, type); + } + }); + if (!settingsTab.plugin.settings.supportedFileTypes.includes(settingsTab.plugin.settings.folderNoteType.replace(".", "")) && settingsTab.plugin.settings.folderNoteType !== ".ask") { + settingsTab.plugin.settings.folderNoteType = ".md"; + void settingsTab.plugin.saveSettings(); + } + let defaultType = settingsTab.plugin.settings.folderNoteType.startsWith(".") ? settingsTab.plugin.settings.folderNoteType : "." + settingsTab.plugin.settings.folderNoteType; + if (!settingsTab.plugin.settings.supportedFileTypes.includes(defaultType.replace(".", ""))) { + defaultType = ".ask"; + settingsTab.plugin.settings.folderNoteType = defaultType; + } + dropdown.setValue(defaultType).onChange(async (value) => { + settingsTab.plugin.settings.folderNoteType = value; + void settingsTab.plugin.saveSettings(); + void settingsTab.display(); + }); + }); + const setting0 = new import_obsidian23.Setting(containerEl); + setting0.setName("Supported file types"); + const desc0 = document.createDocumentFragment(); + desc0.append("Specify which file types are allowed as folder notes. Applies to both new and existing folders. Adding many types may affect performance."); + setting0.setDesc(desc0); + const list = new ListComponent(setting0.settingEl, settingsTab.plugin.settings.supportedFileTypes || [], ["md", "canvas"]); + list.on("update", (values) => { + settingsTab.plugin.settings.supportedFileTypes = values; + void settingsTab.plugin.saveSettings(); + void settingsTab.display(); + }); + if (!settingsTab.plugin.settings.supportedFileTypes.includes("md") || !settingsTab.plugin.settings.supportedFileTypes.includes("canvas") || !settingsTab.plugin.settings.supportedFileTypes.includes("excalidraw")) { + setting0.addDropdown((dropdown) => { + const options = [ + { value: "md", label: "Markdown" }, + { value: "canvas", label: "Canvas" }, + { value: "base", label: "Bases" }, + { value: "excalidraw", label: "Excalidraw" }, + { value: "custom", label: "Custom extension" } + ]; + options.forEach((option) => { + var _a; + if (!((_a = settingsTab.plugin.settings.supportedFileTypes) == null ? void 0 : _a.includes(option.value))) { + dropdown.addOption(option.value, option.label); + } + }); + dropdown.addOption("+", "+"); + dropdown.setValue("+"); + dropdown.onChange(async (value) => { + if (value === "custom") { + return new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list).open(); + } + await list.addValue(value.toLowerCase()); + void settingsTab.display(); + void settingsTab.plugin.saveSettings(); + }); + }); + } else { + setting0.addButton((button) => button.setButtonText("Add custom file type").setCta().onClick(async () => { + new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list).open(); + })); + } + const templateSetting = new import_obsidian23.Setting(containerEl).setDesc("Can be used with templater/templates plugin. If you add the location of the templates there.").setName("Template path").addSearch((cb) => { + new TemplateSuggest(cb.inputEl, settingsTab.plugin); + cb.setPlaceholder("Template path"); + const templateFile = settingsTab.plugin.app.vault.getAbstractFileByPath(settingsTab.plugin.settings.templatePath); + const templateName = (templateFile == null ? void 0 : templateFile.name.replace(".md", "")) || ""; + cb.setValue(templateName); + cb.onChange(async (value) => { + if (value.trim() === "") { + settingsTab.plugin.settings.templatePath = ""; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + return; + } + }); + }); + templateSetting.infoEl.appendText("Requires a restart to take effect"); + templateSetting.infoEl.style.color = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + const storageLocation = new import_obsidian23.Setting(containerEl).setName("Storage location").setDesc("Choose where to store the folder notes").addDropdown((dropdown) => dropdown.addOption("insideFolder", "Inside the folder").addOption("parentFolder", "In the parent folder").setValue(settingsTab.plugin.settings.storageLocation).onChange(async (value) => { + if (value !== "insideFolder" && value !== "parentFolder" && value !== "vaultFolder") { + return; + } + settingsTab.plugin.settings.storageLocation = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + refreshAllFolderStyles(void 0, settingsTab.plugin); + })).addButton((button) => button.setButtonText("Switch").setCta().onClick(async () => { + let oldStorageLocation = settingsTab.plugin.settings.storageLocation; + if (settingsTab.plugin.settings.storageLocation === "parentFolder") { + oldStorageLocation = "insideFolder"; + } else if (settingsTab.plugin.settings.storageLocation === "insideFolder") { + oldStorageLocation = "parentFolder"; + } + new BackupWarningModal(settingsTab.plugin, "Switch storage location", 'When you click on "Confirm" all folder notes will be moved to the new storage location.', settingsTab.switchStorageLocation, [oldStorageLocation]).open(); + })); + storageLocation.infoEl.appendText("Requires a restart to take effect"); + storageLocation.infoEl.style.color = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + if (settingsTab.plugin.settings.storageLocation === "parentFolder") { + new import_obsidian23.Setting(containerEl).setName("Delete folder notes when deleting the folder").setDesc("Delete the folder note when deleting the folder").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.syncDelete).onChange(async (value) => { + settingsTab.plugin.settings.syncDelete = value; + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian23.Setting(containerEl).setName("Move folder notes when moving the folder").setDesc("Move the folder note file along with the folder when it is moved").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.syncMove).onChange(async (value) => { + settingsTab.plugin.settings.syncMove = value; + await settingsTab.plugin.saveSettings(); + })); + } + if (import_obsidian23.Platform.isDesktopApp) { + settingsTab.settingsPage.createEl("h3", { text: "Keyboard shortcuts" }); + new import_obsidian23.Setting(containerEl).setName("Key for creating folder note").setDesc("The key combination to create a folder note").addDropdown((dropdown) => { + if (!import_obsidian23.Platform.isMacOS) { + dropdown.addOption("ctrl", "Ctrl + Click"); + dropdown.addOption("alt", "Alt + Click"); + } else { + dropdown.addOption("ctrl", "Cmd + Click"); + dropdown.addOption("alt", "Option + Click"); + } + dropdown.setValue(settingsTab.plugin.settings.ctrlKey ? "ctrl" : "alt"); + dropdown.onChange(async (value) => { + settingsTab.plugin.settings.ctrlKey = value === "ctrl"; + settingsTab.plugin.settings.altKey = value === "alt"; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + }); + }); + new import_obsidian23.Setting(containerEl).setName("Key for opening folder note").setDesc("Select the combination to open a folder note").addDropdown((dropdown) => { + dropdown.addOption("click", "Mouse click"); + if (!import_obsidian23.Platform.isMacOS) { + dropdown.addOption("ctrl", "Ctrl + Click"); + dropdown.addOption("alt", "Alt + Click"); + } else { + dropdown.addOption("ctrl", "Cmd + Click"); + dropdown.addOption("alt", "Option + Click"); + } + if (settingsTab.plugin.settings.openByClick) { + dropdown.setValue("click"); + } else if (settingsTab.plugin.settings.openWithCtrl) { + dropdown.setValue("ctrl"); + } else { + dropdown.setValue("alt"); + } + dropdown.onChange(async (value) => { + settingsTab.plugin.settings.openByClick = value === "click"; + settingsTab.plugin.settings.openWithCtrl = value === "ctrl"; + settingsTab.plugin.settings.openWithAlt = value === "alt"; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + }); + }); + } + settingsTab.settingsPage.createEl("h3", { text: "Folder note behavior" }); + new import_obsidian23.Setting(containerEl).setName("Confirm folder note deletion").setDesc("Ask for confirmation before deleting a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.showDeleteConfirmation).onChange(async (value) => { + settingsTab.plugin.settings.showDeleteConfirmation = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + new import_obsidian23.Setting(containerEl).setName("Deleted folder notes").setDesc("What happens to the folder note after you delete it").addDropdown((dropdown) => { + dropdown.addOption("trash", "Move to system trash"); + dropdown.addOption("obsidianTrash", "Move to Obsidian trash (.trash folder)"); + dropdown.addOption("delete", "Delete permanently"); + dropdown.setValue(settingsTab.plugin.settings.deleteFilesAction); + dropdown.onChange(async (value) => { + const v = value; + settingsTab.plugin.settings.deleteFilesAction = v; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + }); + }); + if (import_obsidian23.Platform.isDesktop) { + const setting3 = new import_obsidian23.Setting(containerEl); + setting3.setName("Open folder note in a new tab by default"); + setting3.setDesc("Always open folder notes in a new tab unless the note is already open in the current tab."); + setting3.addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.openInNewTab).onChange(async (value) => { + settingsTab.plugin.settings.openInNewTab = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + setting3.infoEl.appendText("Requires a restart to take effect"); + setting3.infoEl.style.color = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + } + if (settingsTab.plugin.settings.openInNewTab) { + new import_obsidian23.Setting(containerEl).setName("Focus existing tab instead of creating a new one").setDesc("If a folder note is already open in a tab, focus that tab instead of creating a new one.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.focusExistingTab).onChange(async (value) => { + settingsTab.plugin.settings.focusExistingTab = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + } + new import_obsidian23.Setting(containerEl).setName("Sync folder name").setDesc("Automatically rename the folder note when the folder name is changed").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.syncFolderName).onChange(async (value) => { + settingsTab.plugin.settings.syncFolderName = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + settingsTab.settingsPage.createEl("h4", { text: "Automation settings" }); + new import_obsidian23.Setting(containerEl).setName("Create folder notes for all folders").setDesc("Generate folder notes for every folder in the vault.").addButton((cb) => { + cb.setIcon("plus"); + cb.setTooltip("Create folder notes"); + cb.onClick(async () => { + new ConfirmationModal(settingsTab.app, settingsTab.plugin).open(); + }); + }); + new import_obsidian23.Setting(containerEl).setName("Auto-create on folder creation").setDesc("Automatically create a folder note whenever a new folder is added.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.autoCreate).onChange(async (value) => { + settingsTab.plugin.settings.autoCreate = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + if (settingsTab.plugin.settings.autoCreate) { + new import_obsidian23.Setting(containerEl).setName("Auto-open after creation").setDesc("Open the folder note immediately after it\u2019s created automatically.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.autoCreateFocusFiles).onChange(async (value) => { + settingsTab.plugin.settings.autoCreateFocusFiles = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + new import_obsidian23.Setting(containerEl).setName("Auto-create for attachment folders").setDesc('Also automatically create folder notes for attachment folders (e.g., "attachments", "media", etc.).').addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.autoCreateForAttachmentFolder).onChange(async (value) => { + settingsTab.plugin.settings.autoCreateForAttachmentFolder = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + } + new import_obsidian23.Setting(containerEl).setName("Auto-create when creating notes").setDesc("Automatically create a folder note when a regular note is created inside a folder. Works for supported file types only.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.autoCreateForFiles).onChange(async (value) => { + settingsTab.plugin.settings.autoCreateForFiles = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + settingsTab.settingsPage.createEl("h3", { text: "Integration & compatibility" }); + const desc1 = document.createDocumentFragment(); + const link = activeDocument.createElement("a"); + link.href = "https://github.com/snezhig/obsidian-front-matter-title"; + link.textContent = "Front matter title plugin"; + link.target = "_blank"; + desc1.append("Allows you to use the ", link, " with folder notes. It allows you to set the folder name to some name you set in the front matter."); + const fmtpSetting = new import_obsidian23.Setting(containerEl).setName("Enable front matter title plugin integration").setDesc(desc1).addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.frontMatterTitle.enabled).onChange(async (value) => { + var _a; + settingsTab.plugin.settings.frontMatterTitle.enabled = value; + await settingsTab.plugin.saveSettings(); + if (value) { + settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin); + } else { + if (settingsTab.plugin.fmtpHandler) { + settingsTab.plugin.updateAllBreadcrumbs(true); + } + settingsTab.plugin.app.vault.getFiles().forEach((file) => { + var _a2; + void ((_a2 = settingsTab.plugin.fmtpHandler) == null ? void 0 : _a2.fmptUpdateFileName({ + id: "", + result: false, + path: file.path, + pathOnly: false + }, false)); + }); + (_a = settingsTab.plugin.fmtpHandler) == null ? void 0 : _a.deleteEvent(); + settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin); + } + settingsTab.display(); + })); + fmtpSetting.infoEl.appendText("Requires a restart to take effect"); + fmtpSetting.infoEl.style.color = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + settingsTab.settingsPage.createEl("h3", { text: "Session & persistence" }); + new import_obsidian23.Setting(containerEl).setName("Persist tab after restart").setDesc("Restore the same settings tab after restarting Obsidian.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.persistentSettingsTab.afterRestart).onChange(async (value) => { + settingsTab.plugin.settings.persistentSettingsTab.afterRestart = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + new import_obsidian23.Setting(containerEl).setName("Persist tab during session only").setDesc("Keep the current settings tab open during the session, but reset it after a restart or reload.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.persistentSettingsTab.afterChangingTab).onChange(async (value) => { + settingsTab.plugin.settings.persistentSettingsTab.afterChangingTab = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); +} + +// src/settings/FileExplorerSettings.ts +var import_obsidian24 = require("obsidian"); +async function renderFileExplorer(settingsTab) { + const containerEl = settingsTab.settingsPage; + new import_obsidian24.Setting(containerEl).setName("Hide folder note").setDesc("Hide the folder note file from appearing in the file explorer").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.hideFolderNote).onChange(async (value) => { + settingsTab.plugin.settings.hideFolderNote = value; + await settingsTab.plugin.saveSettings(); + if (value) { + activeDocument.body.classList.add("hide-folder-note"); + } else { + activeDocument.body.classList.remove("hide-folder-note"); + } + settingsTab.display(); + })); + const setting2 = new import_obsidian24.Setting(containerEl).setName("Disable click-to-open folder note on mobile").setDesc("Prevents folder notes from opening when tapping the folder name or surrounding area on mobile devices. They can now only be opened via the context menu or a command.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.disableOpenFolderNoteOnClick).onChange(async (value) => { + settingsTab.plugin.settings.disableOpenFolderNoteOnClick = value; + await settingsTab.plugin.saveSettings(); + })); + setting2.infoEl.appendText("Requires a restart to take effect"); + const setting2AccentColor = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + setting2.infoEl.style.color = setting2AccentColor; + new import_obsidian24.Setting(containerEl).setName("Open folder notes by only clicking directly on the folder name").setDesc("Only allow folder notes to open when clicking directly on the folder name in the file explorer").addToggle((toggle) => toggle.setValue(!settingsTab.plugin.settings.stopWhitespaceCollapsing).onChange(async (value) => { + if (!value) { + activeDocument.body.classList.add("fn-whitespace-stop-collapsing"); + } else { + activeDocument.body.classList.remove("fn-whitespace-stop-collapsing"); + } + settingsTab.plugin.settings.stopWhitespaceCollapsing = !value; + await settingsTab.plugin.saveSettings(); + })); + const disableSetting = new import_obsidian24.Setting(containerEl); + disableSetting.setName("Disable folder collapsing"); + disableSetting.setDesc("When enabled, folders in the file explorer will only collapse when clicking the collapse icon next to the folder name, not when clicking near a folder name when it has a folder note."); + disableSetting.addToggle((toggle) => toggle.setValue(!settingsTab.plugin.settings.enableCollapsing).onChange(async (value) => { + settingsTab.plugin.settings.enableCollapsing = !value; + await settingsTab.plugin.saveSettings(); + })); + disableSetting.infoEl.appendText("Requires a restart to take effect"); + const accentColor = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + disableSetting.infoEl.style.color = accentColor; + new import_obsidian24.Setting(containerEl).setName("Use submenus").setDesc("Use submenus for file/folder commands").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.useSubmenus).onChange(async (value) => { + settingsTab.plugin.settings.useSubmenus = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + if (settingsTab.plugin.settings.frontMatterTitle.enabled) { + new import_obsidian24.Setting(containerEl).setName("Auto update folder name in the file explorer (front matter title plugin only)").setDesc("Automatically update the folder name in the file explorer when the front matter title plugin is enabled and the title for a folder note is changed in the front matter. This will not change the file name, only the displayed name in the file explorer.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.frontMatterTitle.explorer).onChange(async (value) => { + settingsTab.plugin.settings.frontMatterTitle.explorer = value; + await settingsTab.plugin.saveSettings(); + settingsTab.plugin.app.vault.getFiles().forEach((file) => { + var _a; + (_a = settingsTab.plugin.fmtpHandler) == null ? void 0 : _a.fmptUpdateFileName({ + id: "", + result: false, + path: file.path, + pathOnly: false + }, false); + }); + })); + } + settingsTab.settingsPage.createEl("h3", { text: "Style settings" }); + new import_obsidian24.Setting(containerEl).setName("Highlight folder in the file explorer").setDesc("Highlight the folder in the file explorer when it has a folder note and the folder note is open in the editor").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.highlightFolder).onChange(async (value) => { + settingsTab.plugin.settings.highlightFolder = value; + if (!value) { + activeDocument.body.classList.add("disable-folder-highlight"); + } else { + activeDocument.body.classList.remove("disable-folder-highlight"); + } + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian24.Setting(containerEl).setName("Hide collapse icon").setDesc("Hide the collapse icon in the file explorer next to the name of a folder when a folder only contains a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.hideCollapsingIcon).onChange(async (value) => { + settingsTab.plugin.settings.hideCollapsingIcon = value; + if (value) { + activeDocument.body.classList.add("fn-hide-collapse-icon"); + } else { + activeDocument.body.classList.remove("fn-hide-collapse-icon"); + } + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + new import_obsidian24.Setting(containerEl).setName("Hide collapse icon for every empty folder").setDesc("Hide the collapse icon in the file explorer next to the name of a folder when a folder is empty").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.hideCollapsingIconForEmptyFolders).onChange(async (value) => { + settingsTab.plugin.settings.hideCollapsingIconForEmptyFolders = value; + await settingsTab.plugin.saveSettings(); + if (value) { + activeDocument.body.classList.add("fn-hide-empty-collapse-icon"); + } else { + activeDocument.body.classList.remove("fn-hide-empty-collapse-icon"); + } + settingsTab.display(); + })); + if (settingsTab.plugin.settings.hideCollapsingIcon) { + new import_obsidian24.Setting(containerEl).setName("Hide collapse icon also when only the attachment folder is in the same folder").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.ignoreAttachmentFolder).onChange(async (value) => { + if (value) { + activeDocument.body.classList.add("fn-ignore-attachment-folder"); + } else { + activeDocument.body.classList.remove("fn-ignore-attachment-folder"); + } + settingsTab.plugin.settings.ignoreAttachmentFolder = value; + await settingsTab.plugin.saveSettings(); + })); + } + new import_obsidian24.Setting(containerEl).setName("Underline the name of folder notes").setDesc("Add an underline to folders that have a folder note in the file explorer").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.underlineFolder).onChange(async (value) => { + settingsTab.plugin.settings.underlineFolder = value; + if (value) { + activeDocument.body.classList.add("folder-note-underline"); + } else { + activeDocument.body.classList.remove("folder-note-underline"); + } + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian24.Setting(containerEl).setName("Bold the name of folder notes").setDesc("Make the folder name bold in the file explorer when it has a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.boldName).onChange(async (value) => { + settingsTab.plugin.settings.boldName = value; + if (value) { + activeDocument.body.classList.add("folder-note-bold"); + } else { + activeDocument.body.classList.remove("folder-note-bold"); + } + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian24.Setting(containerEl).setName("Cursive the name of folder notes").setDesc("Make the folder name cursive in the file explorer when it has a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.cursiveName).onChange(async (value) => { + settingsTab.plugin.settings.cursiveName = value; + if (value) { + activeDocument.body.classList.add("folder-note-cursive"); + } else { + activeDocument.body.classList.remove("folder-note-cursive"); + } + await settingsTab.plugin.saveSettings(); + })); +} + +// src/settings/PathSettings.ts +var import_obsidian25 = require("obsidian"); +async function renderPath(settingsTab) { + const containerEl = settingsTab.settingsPage; + new import_obsidian25.Setting(containerEl).setName("Open folder note through path").setDesc("Open a folder note when clicking on a folder name in the path if it is a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.openFolderNoteOnClickInPath).onChange(async (value) => { + settingsTab.plugin.settings.openFolderNoteOnClickInPath = value; + await settingsTab.plugin.saveSettings(); + settingsTab.display(); + })); + if (settingsTab.plugin.settings.openFolderNoteOnClickInPath) { + new import_obsidian25.Setting(containerEl).setName("Open sidebar when opening a folder note through path (mobile only)").setDesc("Open the sidebar when opening a folder note through the path on mobile").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.openSidebar.mobile).onChange(async (value) => { + settingsTab.plugin.settings.openSidebar.mobile = value; + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian25.Setting(containerEl).setName("Open sidebar when opening a folder note through path (desktop only)").setDesc("Open the sidebar when opening a folder note through the path on desktop").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.openSidebar.desktop).onChange(async (value) => { + settingsTab.plugin.settings.openSidebar.desktop = value; + await settingsTab.plugin.saveSettings(); + })); + } + if (settingsTab.plugin.settings.frontMatterTitle.enabled) { + new import_obsidian25.Setting(containerEl).setName("Auto update folder name in the path (front matter title plugin only)").setDesc("Automatically update the folder name in the path when the front matter title plugin is enabled and the title for a folder note is changed in the front matter. This will not change the file name, only the displayed name in the path.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.frontMatterTitle.path).onChange(async (value) => { + settingsTab.plugin.settings.frontMatterTitle.path = value; + await settingsTab.plugin.saveSettings(); + if (value) { + settingsTab.plugin.updateAllBreadcrumbs(); + } else { + settingsTab.plugin.updateAllBreadcrumbs(true); + } + })); + } + settingsTab.settingsPage.createEl("h3", { text: "Style settings" }); + new import_obsidian25.Setting(containerEl).setName("Underline folders in the path").setDesc("Add an underline to folders that have a folder note in the path above a note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.underlineFolderInPath).onChange(async (value) => { + settingsTab.plugin.settings.underlineFolderInPath = value; + if (value) { + activeDocument.body.classList.add("folder-note-underline-path"); + } else { + activeDocument.body.classList.remove("folder-note-underline-path"); + } + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian25.Setting(containerEl).setName("Bold folders in the path").setDesc("Make the folder name bold in the path above a note when it has a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.boldNameInPath).onChange(async (value) => { + settingsTab.plugin.settings.boldNameInPath = value; + if (value) { + activeDocument.body.classList.add("folder-note-bold-path"); + } else { + activeDocument.body.classList.remove("folder-note-bold-path"); + } + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian25.Setting(containerEl).setName("Cursive the name of folder notes in the path").setDesc("Make the folder name cursive in the path above a note when it has a folder note").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.cursiveNameInPath).onChange(async (value) => { + settingsTab.plugin.settings.cursiveNameInPath = value; + if (value) { + activeDocument.body.classList.add("folder-note-cursive-path"); + } else { + activeDocument.body.classList.remove("folder-note-cursive-path"); + } + await settingsTab.plugin.saveSettings(); + })); + new import_obsidian25.Setting(containerEl).setName("Hide folder note name in the path").setDesc("Only show the folder name in the path and hide the folder note name.").addToggle((toggle) => toggle.setValue(settingsTab.plugin.settings.hideFolderNoteNameInPath).onChange(async (value) => { + activeDocument.body.classList.toggle("folder-note-hide-name-path", value); + settingsTab.plugin.settings.hideFolderNoteNameInPath = value; + await settingsTab.plugin.saveSettings(); + })); +} + +// src/settings/FolderOverviewSettings.ts +var import_obsidian36 = require("obsidian"); + +// src/obsidian-folder-overview/src/settings.ts +var import_obsidian35 = require("obsidian"); + +// src/obsidian-folder-overview/src/FolderOverview.ts +var import_obsidian32 = require("obsidian"); + +// src/obsidian-folder-overview/src/modals/Settings.ts +var import_obsidian28 = require("obsidian"); + +// src/obsidian-folder-overview/src/utils/LinkList.ts +var import_obsidian26 = require("obsidian"); +function buildLinkListBlock(id, calloutFlag) { + if (calloutFlag) { + return '\n> \n> '; + } + return '\n\n'; +} +function updateLinkList(files = [], plugin, yaml, pathBlacklist, sourceFile) { + buildLinkList(files, plugin, yaml, pathBlacklist, sourceFile).then((fileLinks) => { + plugin.app.vault.process(sourceFile, (text) => { + const lines = text.split("\n"); + const linkListStart = `${yaml.isInCallout ? "> " : ""}`; + const linkListEnd = `${yaml.isInCallout ? "> " : ""}`; + const startIdx = lines.findIndex((l) => l.trim() === linkListStart); + const endIdx = lines.findIndex((l) => l.trim() === linkListEnd); + const NOT_FOUND = -1; + const linkListExists = startIdx !== NOT_FOUND && endIdx !== NOT_FOUND; + const isInvalidLinkList = endIdx < startIdx; + if (!linkListExists || isInvalidLinkList) { + return text; + } + lines.splice(startIdx, endIdx - startIdx + 1); + const newBlock = [ + linkListStart, + ...fileLinks, + linkListEnd + ]; + lines.splice(startIdx, 0, ...newBlock); + return lines.join("\n"); + }); + }); +} +async function buildLinkList(items, plugin, yaml, pathBlacklist, sourceFile, indent = 0) { + const result = []; + const filtered = (await filterFiles(items, plugin, yaml.folderPath, yaml.depth, pathBlacklist, yaml, sourceFile)).filter((file) => file !== null); + const sorted = sortFiles(filtered, yaml, plugin); + for (const item of sorted) { + const indentStr = " ".repeat(indent); + if (item instanceof import_obsidian26.TFile) { + result.push(buildFileLinkListLine(item, yaml, indentStr)); + } else if (item instanceof import_obsidian26.TFolder) { + const folderLines = await buildFolderLinkListLines(item, plugin, yaml, pathBlacklist, sourceFile, indentStr, indent); + result.push(...folderLines); + } + } + return result; +} +function buildFileLinkListLine(item, yaml, indentStr) { + const prefix = yaml.isInCallout ? "> " : ""; + let base; + if (yaml.useWikilinks) { + base = `${prefix}${indentStr}- [[${item.path}|${item.basename}]]`; + } else { + base = `${prefix}${indentStr}- [${item.basename}](${encodeURI(item.path)})`; + } + if (yaml.hideLinkList) { + return base + ' '; + } + return base; +} +async function buildFolderLinkListLines(item, plugin, yaml, pathBlacklist, sourceFile, indentStr, indent) { + const lines = []; + const prefix = yaml.isInCallout ? "> " : ""; + let line = `${prefix}${indentStr}- ${item.name}`; + let folderNote = null; + if (plugin instanceof FolderNotesPlugin) { + folderNote = getFolderNote(plugin, item.path); + } + if (folderNote) { + if (yaml.useWikilinks) { + line = `${prefix}${indentStr}- [[${folderNote.path}|${item.name}]]`; + } else { + line = `${prefix}${indentStr}- [${item.name}](${encodeURI(folderNote.path)})`; + } + } + if (yaml.hideLinkList) { + line += ' '; + } + lines.push(line); + const children = item.children.filter((child) => !(child instanceof import_obsidian26.TFile && folderNote && child.path === folderNote.path)); + if (children.length > 0) { + const childLinks = await buildLinkList(children, plugin, yaml, pathBlacklist, sourceFile, indent + 1); + lines.push(...childLinks); + } + return lines; +} +function removeLinkList(plugin, sourceFile, yaml) { + if (sourceFile) { + plugin.app.vault.process(sourceFile, (text) => { + const lines = text.split("\n"); + const linkListStart = `${yaml.isInCallout ? "> " : ""}`; + const linkListEnd = `${yaml.isInCallout ? "> " : ""}`; + const startIdx = lines.findIndex((l) => l.trim() === linkListStart); + const endIdx = lines.findIndex((l) => l.trim() === linkListEnd); + const NOT_FOUND = -1; + const linkListExists = startIdx !== NOT_FOUND && endIdx !== NOT_FOUND; + const isInvalidLinkList = endIdx < startIdx; + if (!linkListExists || isInvalidLinkList) { + return text; + } + lines.splice(startIdx, endIdx - startIdx + 1); + return lines.join("\n"); + }); + } +} + +// src/obsidian-folder-overview/src/utils/functions.ts +var import_obsidian27 = require("obsidian"); +function getFolderPathFromString2(path) { + const subString = path.lastIndexOf("/") >= 0 ? path.lastIndexOf("/") : 0; + const folderPath = path.substring(0, subString); + if (folderPath === "") { + return "/"; + } + return folderPath; +} +var CODE_BLOCK_END_NOT_FOUND = -1; +var MAX_CODE_BLOCK_SEARCH_COUNT = 50; +function getCodeBlockEndLine(text, startLine, count = 1) { + let line = startLine + 1; + const lines = text.split("\n"); + while (line < lines.length) { + if (count > MAX_CODE_BLOCK_SEARCH_COUNT) { + return CODE_BLOCK_END_NOT_FOUND; + } + if (lines[line].startsWith("```")) { + return line; + } + line++; + count++; + } + return line; +} +async function updateAllOverviews(plugin) { + const filePaths = await plugin.fvIndexDB.getAllNotes(); + if (filePaths.length === 0) + return; + filePaths.forEach(async (filePath) => { + const file = plugin.app.vault.getAbstractFileByPath(filePath); + if (!(file instanceof import_obsidian27.TFile)) { + plugin.fvIndexDB.removeNote(filePath); + return; + } + if (!hasOverviewYaml(plugin, file)) { + plugin.fvIndexDB.removeNote(file.path); + return; + } + const overviews = await getOverviews(plugin, file); + overviews.forEach(async (overview) => { + if (!overview.useActualLinks) + return; + let files = []; + let sourceFolderPath = overview.folderPath.trim(); + if (!sourceFolderPath.includes("/")) { + sourceFolderPath = "/"; + } + const sourceFolder = plugin.app.vault.getAbstractFileByPath(sourceFolderPath); + if (!(sourceFolder instanceof import_obsidian27.TFolder) && sourceFolderPath !== "/") { + return; + } + if ((sourceFolder == null ? void 0 : sourceFolder.path) === "/") { + const rootFiles = []; + plugin.app.vault.getAllLoadedFiles().filter((f) => { + var _a; + return ((_a = f.parent) == null ? void 0 : _a.path) === "/"; + }).forEach((f) => { + if (!f.path.includes("/")) { + rootFiles.push(f); + } + }); + files = rootFiles; + } else if (sourceFolder instanceof import_obsidian27.TFolder) { + files = sourceFolder.children; + } + files = getAllFiles(files, sourceFolderPath, overview.depth); + const filteredFiles = await filterFiles(files, plugin, sourceFolderPath, overview.depth, [], overview, file); + files = filteredFiles.filter((f) => f !== null); + if (!overview.includeTypes.includes("folder")) { + files = getAllFiles(files, sourceFolderPath, overview.depth); + } + files = sortFiles(files, overview, plugin); + updateLinkList(files, plugin, overview, [], file); + }); + }); +} +function buildYamlConfig(yaml, defaultSettings, ctx, includeTypesParam) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z; + return { + id: (_a = yaml == null ? void 0 : yaml.id) != null ? _a : crypto.randomUUID(), + folderPath: (_c = (_b = yaml == null ? void 0 : yaml.folderPath) == null ? void 0 : _b.trim()) != null ? _c : getFolderPathFromString2(ctx.sourcePath), + title: (_d = yaml == null ? void 0 : yaml.title) != null ? _d : defaultSettings.title, + showTitle: (_e = yaml == null ? void 0 : yaml.showTitle) != null ? _e : defaultSettings.showTitle, + depth: (_f = yaml == null ? void 0 : yaml.depth) != null ? _f : defaultSettings.depth, + style: (_g = yaml == null ? void 0 : yaml.style) != null ? _g : "list", + includeTypes: includeTypesParam.map((type) => type.toLowerCase()), + disableFileTag: (_h = yaml == null ? void 0 : yaml.disableFileTag) != null ? _h : defaultSettings.disableFileTag, + sortBy: (_i = yaml == null ? void 0 : yaml.sortBy) != null ? _i : defaultSettings.sortBy, + sortByAsc: (_j = yaml == null ? void 0 : yaml.sortByAsc) != null ? _j : defaultSettings.sortByAsc, + showEmptyFolders: (_k = yaml == null ? void 0 : yaml.showEmptyFolders) != null ? _k : defaultSettings.showEmptyFolders, + onlyIncludeSubfolders: (_l = yaml == null ? void 0 : yaml.onlyIncludeSubfolders) != null ? _l : defaultSettings.onlyIncludeSubfolders, + storeFolderCondition: (_m = yaml == null ? void 0 : yaml.storeFolderCondition) != null ? _m : defaultSettings.storeFolderCondition, + showFolderNotes: (_n = yaml == null ? void 0 : yaml.showFolderNotes) != null ? _n : defaultSettings.showFolderNotes, + disableCollapseIcon: (_o = yaml == null ? void 0 : yaml.disableCollapseIcon) != null ? _o : defaultSettings.disableCollapseIcon, + alwaysCollapse: (_p = yaml == null ? void 0 : yaml.alwaysCollapse) != null ? _p : defaultSettings.alwaysCollapse, + autoSync: (_q = yaml == null ? void 0 : yaml.autoSync) != null ? _q : defaultSettings.autoSync, + allowDragAndDrop: (_r = yaml == null ? void 0 : yaml.allowDragAndDrop) != null ? _r : defaultSettings.allowDragAndDrop, + hideLinkList: (_s = yaml == null ? void 0 : yaml.hideLinkList) != null ? _s : defaultSettings.hideLinkList, + hideFolderOverview: (_t = yaml == null ? void 0 : yaml.hideFolderOverview) != null ? _t : defaultSettings.hideFolderOverview, + useActualLinks: (_u = yaml == null ? void 0 : yaml.useActualLinks) != null ? _u : defaultSettings.useActualLinks, + fmtpIntegration: (_v = yaml == null ? void 0 : yaml.fmtpIntegration) != null ? _v : defaultSettings.fmtpIntegration, + titleSize: (_w = yaml == null ? void 0 : yaml.titleSize) != null ? _w : defaultSettings.titleSize, + isInCallout: (_x = yaml == null ? void 0 : yaml.isInCallout) != null ? _x : false, + useWikilinks: (_z = (_y = yaml == null ? void 0 : yaml.useWikilinks) != null ? _y : defaultSettings.useWikilinks) != null ? _z : true + }; +} +async function updateYamlById(plugin, overviewId, file, newYaml, addLinkList, isCallout = false) { + await plugin.app.vault.process(file, (text) => { + const yamlBlocks = getYamlBlocks(text, isCallout); + if (!yamlBlocks) + return text; + for (const block of yamlBlocks) { + const cleanedBlock = cleanYamlBlock(block, isCallout); + const yaml = (0, import_obsidian27.parseYaml)(cleanedBlock); + if (!yaml) + continue; + if (yaml.id === overviewId) { + let stringYaml = (0, import_obsidian27.stringifyYaml)(newYaml); + if (stringYaml[stringYaml.length - 1] !== "\n") { + stringYaml += "\n"; + } + let newBlock = buildNewBlock(stringYaml, isCallout); + if (addLinkList && !isCallout) { + newBlock += buildLinkListBlock(newYaml.id, false); + } else if (addLinkList && isCallout) { + newBlock += buildLinkListBlock(newYaml.id, true); + } + text = text.replace(block, newBlock); + } + } + return text; + }); + function getYamlBlocks(text, callout) { + return callout ? text.match(/^> ```folder-overview\n([\s\S]*?)```/gm) : text.match(/^(?!>).*```folder-overview\n(?:^(?!>).*[\r\n]*)*?^```$/gm); + } + function cleanYamlBlock(block, calloutFlag) { + if (calloutFlag) { + let cleaned = block.replace("> ```folder-overview\n", "").replace("```", ""); + return cleaned.replace(/^> ?/gm, ""); + } + return block.replace("```folder-overview\n", "").replace("```", ""); + } + function buildNewBlock(stringYaml, calloutFlag) { + if (calloutFlag) { + const yamlLines = stringYaml.split("\n").map((line) => `> ${line}`).join("\n"); + return "> ```folder-overview\n" + yamlLines + "\n> ```"; + } + return "```folder-overview\n" + stringYaml + "\n```"; + } +} +function parseOverviewTitle(overview, plugin, sourceFolder, sourceFolderPath, sourceFile) { + var _a, _b, _c, _d; + let { title } = overview; + const variables = { + folderName: (sourceFolder == null ? void 0 : sourceFolder.path) === "/" || sourceFolderPath === "/" ? "Vault" : (_a = sourceFolder == null ? void 0 : sourceFolder.name) != null ? _a : "", + folderPath: (_c = (_b = sourceFolder == null ? void 0 : sourceFolder.path) != null ? _b : sourceFolderPath) != null ? _c : "", + filePath: sourceFile.path, + fileName: sourceFile instanceof import_obsidian27.TFile ? sourceFile.basename : "" + }; + const fileCache = plugin.app.metadataCache.getFileCache(sourceFile); + const frontmatter = (_d = fileCache == null ? void 0 : fileCache.frontmatter) != null ? _d : {}; + title = replacePropertiesInTitle(title, frontmatter); + title = replaceVariablesInTitle(title, variables); + return title; +} +function replacePropertiesInTitle(title, frontmatter) { + const propertyRegex = /\{\{properties\.([\w-]+)\}\}/g; + return title.replace(propertyRegex, (_, prop) => { + const value = frontmatter[prop]; + return value !== void 0 ? String(value) : ""; + }); +} +function replaceVariablesInTitle(title, variables) { + return title.replace(/\{\{(\w+)\}\}/g, (_, key) => { + var _a; + return (_a = variables[key]) != null ? _a : ""; + }); +} + +// src/obsidian-folder-overview/src/modals/Settings.ts +var FolderOverviewSettings = class extends import_obsidian28.Modal { + constructor(app, plugin, yaml, ctx, el, defaultSettings) { + super(app); + this.plugin = plugin; + this.app = app; + this.defaultSettings = defaultSettings; + this.yaml = this.initializeYaml(yaml, ctx, defaultSettings); + this.ctx = ctx != null ? ctx : void 0; + this.el = el != null ? el : void 0; + updateYaml(this.plugin, this.ctx, this.el, this.yaml, false); + } + initializeYaml(yaml, ctx, defaultSettings) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y; + if (!yaml) { + return this.defaultSettings; + } + if (ctx) { + const includeTypes = (yaml == null ? void 0 : yaml.includeTypes) || defaultSettings.includeTypes || ["folder", "markdown"]; + return { + id: (_a = yaml == null ? void 0 : yaml.id) != null ? _a : crypto.randomUUID(), + folderPath: (_b = yaml == null ? void 0 : yaml.folderPath) != null ? _b : getFolderPathFromString2(ctx.sourcePath), + title: (_c = yaml == null ? void 0 : yaml.title) != null ? _c : defaultSettings.title, + showTitle: (_d = yaml == null ? void 0 : yaml.showTitle) != null ? _d : defaultSettings.showTitle, + depth: (_e = yaml == null ? void 0 : yaml.depth) != null ? _e : defaultSettings.depth, + style: (_f = yaml == null ? void 0 : yaml.style) != null ? _f : "list", + includeTypes: includeTypes.map((type) => type.toLowerCase()), + disableFileTag: (_g = yaml == null ? void 0 : yaml.disableFileTag) != null ? _g : defaultSettings.disableFileTag, + sortBy: (_h = yaml == null ? void 0 : yaml.sortBy) != null ? _h : defaultSettings.sortBy, + sortByAsc: (_i = yaml == null ? void 0 : yaml.sortByAsc) != null ? _i : defaultSettings.sortByAsc, + showEmptyFolders: (_j = yaml == null ? void 0 : yaml.showEmptyFolders) != null ? _j : defaultSettings.showEmptyFolders, + onlyIncludeSubfolders: (_k = yaml == null ? void 0 : yaml.onlyIncludeSubfolders) != null ? _k : defaultSettings.onlyIncludeSubfolders, + storeFolderCondition: (_l = yaml == null ? void 0 : yaml.storeFolderCondition) != null ? _l : defaultSettings.storeFolderCondition, + showFolderNotes: (_m = yaml == null ? void 0 : yaml.showFolderNotes) != null ? _m : defaultSettings.showFolderNotes, + disableCollapseIcon: (_n = yaml == null ? void 0 : yaml.disableCollapseIcon) != null ? _n : defaultSettings.disableCollapseIcon, + alwaysCollapse: (_o = yaml == null ? void 0 : yaml.alwaysCollapse) != null ? _o : defaultSettings.alwaysCollapse, + autoSync: (_p = yaml == null ? void 0 : yaml.autoSync) != null ? _p : defaultSettings.autoSync, + allowDragAndDrop: (_q = yaml == null ? void 0 : yaml.allowDragAndDrop) != null ? _q : defaultSettings.allowDragAndDrop, + hideLinkList: (_r = yaml == null ? void 0 : yaml.hideLinkList) != null ? _r : defaultSettings.hideLinkList, + hideFolderOverview: (_s = yaml == null ? void 0 : yaml.hideFolderOverview) != null ? _s : defaultSettings.hideFolderOverview, + useActualLinks: (_t = yaml == null ? void 0 : yaml.useActualLinks) != null ? _t : defaultSettings.useActualLinks, + fmtpIntegration: (_u = yaml == null ? void 0 : yaml.fmtpIntegration) != null ? _u : defaultSettings.fmtpIntegration, + titleSize: (_v = yaml == null ? void 0 : yaml.titleSize) != null ? _v : defaultSettings.titleSize, + isInCallout: (_w = yaml == null ? void 0 : yaml.isInCallout) != null ? _w : false, + useWikilinks: (_y = (_x = yaml == null ? void 0 : yaml.useWikilinks) != null ? _x : defaultSettings.useWikilinks) != null ? _y : true + }; + } + return yaml; + } + onOpen() { + const { contentEl } = this; + this.display(contentEl, this.yaml, this.plugin, this.defaultSettings, this.display, this.el, this.ctx); + } + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, changedSection) { + modal = this != null ? this : modal; + contentEl.empty(); + contentEl.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + if (!modal) { + return; + } + modal.close(); + } + }); + if (!modal.defaultSettings) { + contentEl.createEl("h2", { text: "Folder overview settings" }); + } else { + contentEl.createEl("h2", { text: "Default folder overview settings" }); + } + createOverviewSettings(contentEl, yaml, plugin, defaultSettings, display, el, ctx, void 0, void 0, modal, changedSection); + } + onClose() { + this.plugin.updateOverviewView(this.plugin, this.yaml); + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/obsidian-folder-overview/src/styles/FileExplorer.ts +var import_obsidian29 = require("obsidian"); +var FileExplorerOverview = class { + constructor(plugin, ctx, root, yaml, pathBlacklist, folderOverview) { + this.eventListeners = []; + this.plugin = plugin; + this.folderOverview = folderOverview; + this.pathBlacklist = pathBlacklist; + this.source = ctx.sourcePath; + this.yaml = yaml; + this.root = root; + } + disconnectListeners() { + this.eventListeners.forEach((unregister) => { + unregister(); + }); + this.eventListeners = []; + } + async renderFileExplorer() { + this.disconnectListeners(); + const { plugin, ctx, root, yaml, folderOverview } = { + plugin: this.plugin, + ctx: this.folderOverview.ctx, + root: this.folderOverview.root, + yaml: this.folderOverview.yaml, + folderOverview: this.folderOverview + }; + let folder = null; + if (plugin instanceof FolderNotesPlugin) { + folder = getFileExplorerElement(yaml.folderPath, plugin); + } + let folderElement = folder == null ? void 0 : folder.parentElement; + const overviewList = folderOverview.listEl; + overviewList == null ? void 0 : overviewList.empty(); + if (!overviewList) + return; + let tFolder = plugin.app.vault.getAbstractFileByPath(yaml.folderPath); + if (!tFolder && yaml.folderPath.trim() === "") { + if (ctx.sourcePath.includes("/")) { + const folderPath = getFolderPathFromString(ctx.sourcePath); + tFolder = plugin.app.vault.getAbstractFileByPath(folderPath); + } else { + yaml.folderPath = "/"; + tFolder = plugin.app.vault.getAbstractFileByPath("/"); + } + } + if (!folderElement && !tFolder) + return; + const sourceFolderPath = (tFolder == null ? void 0 : tFolder.path) || ""; + folderElement = document.querySelectorAll(".nav-files-container")[0]; + if (!folderElement && root) { + folderElement = root.createDiv({ + cls: "nav-files-container" + }); + } + const newFolderElement = folderElement.cloneNode(true); + newFolderElement.querySelectorAll("div.nav-folder-title").forEach((el) => { + var _a; + const folderItem = plugin.app.vault.getAbstractFileByPath(el.getAttribute("data-path") || ""); + if (!(folderItem instanceof import_obsidian29.TFolder)) + return; + if (yaml.alwaysCollapse) { + folderItem.collapsed = true; + el.classList.add("is-collapsed"); + } else { + if (yaml.storeFolderCondition) { + if (folderItem.collapsed) { + el.classList.add("is-collapsed"); + } else { + el.classList.remove("is-collapsed"); + } + } else { + if ((_a = el.parentElement) == null ? void 0 : _a.classList.contains("is-collapsed")) { + folderItem.collapsed = true; + } else { + folderItem.collapsed = false; + } + } + } + if (el.classList.contains("has-folder-note")) { + if (plugin instanceof FolderNotesPlugin) { + const folderNote = getFolderNote(plugin, folderItem.path); + if (folderNote) { + folderOverview.pathBlacklist.push(folderNote.path); + } + } + } + }); + const DEBOUNCE_DELAY_MS = 300; + const debouncedRenderFileExplorer = (0, import_obsidian29.debounce)(() => this.renderFileExplorer(), DEBOUNCE_DELAY_MS); + const handleVaultChange = () => { + debouncedRenderFileExplorer(); + }; + this.eventListeners.push(() => { + folderOverview.off("vault-change", handleVaultChange); + }); + folderOverview.on("vault-change", handleVaultChange); + if (tFolder instanceof import_obsidian29.TFolder) { + await this.addFiles(tFolder.children, overviewList, folderOverview, sourceFolderPath); + } + newFolderElement.querySelectorAll("div.tree-item-icon").forEach((el) => { + if (el instanceof HTMLElement) { + el.onclick = () => { + var _a; + const path = (_a = el.parentElement) == null ? void 0 : _a.getAttribute("data-path"); + if (!path) + return; + const targetFolder = plugin.app.vault.getAbstractFileByPath(path); + this.handleCollapseClick(el, plugin, yaml, this.pathBlacklist, sourceFolderPath, folderOverview, targetFolder); + }; + } + }); + } + async addFiles(files, childrenElement, folderOverview, sourceFolderPath) { + const { plugin } = folderOverview; + const allFiles = await filterFiles(files, plugin, sourceFolderPath, folderOverview.yaml.depth, folderOverview.pathBlacklist, folderOverview.yaml, folderOverview.sourceFile); + const sortedFiles = sortFiles((allFiles != null ? allFiles : []).filter((file) => file !== null), folderOverview.yaml, folderOverview.plugin); + const folders = sortedFiles.filter((child) => child instanceof import_obsidian29.TFolder); + const otherFiles = sortedFiles.filter((child) => child instanceof import_obsidian29.TFile); + for (const child of folders) { + if (!(child instanceof import_obsidian29.TFolder)) + continue; + await this.createFolderEL(plugin, child, folderOverview, childrenElement, sourceFolderPath); + } + for (const child of otherFiles) { + if (!(child instanceof import_obsidian29.TFile)) + continue; + await this.createFileEL(plugin, child, folderOverview, childrenElement); + } + } + async handleCollapseClick(el, plugin, yaml, pathBlacklist, sourceFolderPath, folderOverview, folder) { + var _a, _b, _c, _d; + el.classList.toggle("is-collapsed"); + if (el.classList.contains("is-collapsed")) { + if (!(folder instanceof import_obsidian29.TFolder)) + return; + folder.collapsed = true; + (_c = (_b = (_a = el.parentElement) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.childNodes[1]) == null ? void 0 : _c.remove(); + } else { + if (!(folder instanceof import_obsidian29.TFolder)) + return; + folder.collapsed = false; + const folderElement = (_d = el.parentElement) == null ? void 0 : _d.parentElement; + if (!folderElement) + return; + const childrenElement = folderElement.createDiv({ + cls: "tree-item-children nav-folder-children" + }); + const files = sortFiles(folder.children, yaml, plugin); + const filteredFilesResult = await filterFiles(files, plugin, folder.path, yaml.depth || 1, pathBlacklist, yaml, folderOverview.sourceFile); + const filteredFiles = (filteredFilesResult != null ? filteredFilesResult : []).filter((file) => file !== null); + await this.addFiles(filteredFiles, childrenElement, folderOverview, sourceFolderPath); + } + } + async createFolderEL(plugin, child, folderOverview, childrenElement, sourceFolderPath) { + const folderNote = this.getFolderNoteIfExists(plugin, child); + if (folderNote) { + folderOverview.pathBlacklist.push(folderNote.path); + } + if (this.shouldExcludeFolder(plugin, child)) { + return; + } + const { folderElement, folderTitle } = this.createFolderElements(plugin, child, folderOverview, childrenElement, folderNote); + await this.handleFolderChildren(child, folderOverview, folderElement, folderTitle, childrenElement, sourceFolderPath); + this.setupFolderStyles(folderNote, child, folderTitle, folderOverview.yaml); + this.createCollapseIcon(folderTitle, child, plugin, folderOverview, sourceFolderPath); + } + getFolderNoteIfExists(plugin, child) { + if (plugin instanceof FolderNotesPlugin) { + return getFolderNote(plugin, child.path); + } + return void 0; + } + shouldExcludeFolder(plugin, child) { + var _a; + if (plugin instanceof FolderNotesPlugin) { + const excludedFolder = getExcludedFolder(plugin, child.path, true); + return (_a = excludedFolder == null ? void 0 : excludedFolder.excludeFromFolderOverview) != null ? _a : false; + } + return false; + } + createFolderElements(plugin, child, folderOverview, childrenElement, folderNote) { + var _a; + const { yaml } = folderOverview; + if (!yaml.includeTypes.includes("folder")) { + return { folderElement: null, folderTitle: null }; + } + (_a = folderOverview.el.parentElement) == null ? void 0 : _a.classList.add("fv-remove-edit-button"); + const folderElement = childrenElement.createDiv({ + cls: "tree-item nav-folder" + }); + const folderTitle = folderElement.createDiv({ + cls: "tree-item-self is-clickable nav-folder-title", + attr: { + "data-path": child.path + } + }); + this.setupFolderTitle(plugin, child, folderOverview, folderTitle, folderNote); + return { folderElement, folderTitle }; + } + async setupFolderTitle(plugin, child, folderOverview, folderTitle, folderNote) { + var _a, _b; + const { yaml, pathBlacklist } = folderOverview; + let folderName = child.name; + if (yaml.fmtpIntegration && plugin instanceof FolderNotesPlugin && folderNote) { + folderName = (_b = await ((_a = plugin.fmtpHandler) == null ? void 0 : _a.getNewFileName(folderNote))) != null ? _b : child.name; + } + const folderTitleText = folderTitle.createDiv({ + cls: "tree-item-inner nav-folder-title-content", + text: folderName + }); + if (!folderNote) { + folderTitleText.onclick = () => { + const collapseIcon = folderTitle.querySelectorAll(".tree-item-icon")[0]; + if (collapseIcon) { + this.handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist, "", folderOverview, child); + } + }; + } + if (yaml.allowDragAndDrop) { + this.handleDragAndDrop(folderTitle, folderTitle.parentElement, child); + } + folderTitle.oncontextmenu = (e) => { + folderOverview.folderMenu(child, e); + }; + } + async handleFolderChildren(child, folderOverview, folderElement, folderTitle, childrenElement, sourceFolderPath) { + const { yaml } = folderOverview; + if (!child.collapsed || !yaml.includeTypes.includes("folder")) { + if (yaml.alwaysCollapse) { + child.collapsed = true; + } + if (yaml.includeTypes.includes("folder")) { + folderTitle == null ? void 0 : folderTitle.classList.remove("is-collapsed"); + const folderChildren = folderElement == null ? void 0 : folderElement.createDiv({ + cls: "tree-item-children nav-folder-children" + }); + if (folderChildren) { + await this.addFiles(child.children, folderChildren, folderOverview, sourceFolderPath); + } + } else { + await this.addFiles(child.children, childrenElement, folderOverview, sourceFolderPath); + } + } else { + folderTitle == null ? void 0 : folderTitle.classList.add("is-collapsed"); + } + } + setupFolderStyles(folderNote, child, folderTitle, yaml) { + if (folderNote) { + folderTitle == null ? void 0 : folderTitle.classList.add("has-folder-note"); + } + if (folderNote && child.children.length === 1 && yaml.disableCollapseIcon) { + folderTitle == null ? void 0 : folderTitle.classList.add("fn-has-no-files"); + } + } + createCollapseIcon(folderTitle, child, plugin, folderOverview, sourceFolderPath) { + var _a; + const collapseIcon = folderTitle == null ? void 0 : folderTitle.createDiv({ + cls: "tree-item-icon collapse-icon nav-folder-collapse-indicator fn-folder-overview-collapse-icon" + }); + if (child.collapsed) { + collapseIcon == null ? void 0 : collapseIcon.classList.add("is-collapsed"); + } + if (collapseIcon) { + (0, import_obsidian29.setIcon)(collapseIcon, "chevron-down"); + (_a = collapseIcon.querySelector("path")) == null ? void 0 : _a.setAttribute("d", "M3 8L12 17L21 8"); + collapseIcon.onclick = () => { + this.handleCollapseClick(collapseIcon, plugin, folderOverview.yaml, folderOverview.pathBlacklist, sourceFolderPath, folderOverview, child); + }; + } + } + handleDragAndDrop(folderTitle, folderElement, child) { + folderTitle.draggable = true; + folderTitle.addEventListener("dragstart", (e) => { + const { dragManager } = this.plugin.app; + if (dragManager && typeof dragManager.dragFolder === "function") { + const dragData = dragManager.dragFolder(e, child); + if (typeof dragManager.onDragStart === "function") { + dragManager.onDragStart(e, dragData); + } + folderTitle == null ? void 0 : folderTitle.classList.add("is-being-dragged"); + } + }); + folderTitle.addEventListener("dragend", () => { + folderTitle == null ? void 0 : folderTitle.classList.remove("is-being-dragged"); + }); + folderTitle.addEventListener("dragover", (e) => { + e.preventDefault(); + const { draggable } = this.plugin.app.dragManager; + if (draggable) { + folderElement == null ? void 0 : folderElement.classList.add("is-being-dragged-over"); + this.plugin.app.dragManager.setAction(window.i18next.t("interface.drag-and-drop.move-into-folder", { folder: child.name })); + } + }); + folderTitle.addEventListener("dragleave", () => { + folderElement == null ? void 0 : folderElement.classList.remove("is-being-dragged-over"); + }); + folderTitle.addEventListener("drop", () => { + const { draggable } = this.plugin.app.dragManager; + if (draggable && draggable.file) { + const newPath = child.path + "/" + draggable.file.name; + this.plugin.app.fileManager.renameFile(draggable.file, newPath); + } + }); + } + async createFileEL(plugin, child, folderOverview, childrenElement) { + var _a, _b, _c; + const { yaml } = folderOverview; + const { pathBlacklist } = folderOverview; + if (pathBlacklist.includes(child.path) && !yaml.showFolderNotes) { + return; + } + (_a = folderOverview.el.parentElement) == null ? void 0 : _a.classList.add("fv-remove-edit-button"); + const fileElement = childrenElement.createDiv({ + cls: "tree-item nav-file" + }); + const fileTitle = fileElement.createDiv({ + cls: "tree-item-self is-clickable nav-file-title pointer-cursor", + attr: { + "data-path": child.path + } + }); + if (yaml.allowDragAndDrop) { + fileTitle.draggable = true; + fileTitle.addEventListener("dragstart", (e) => { + const { dragManager } = plugin.app; + if (dragManager && typeof dragManager.dragFile === "function") { + const dragData = dragManager.dragFile(e, child); + if (typeof dragManager.onDragStart === "function") { + dragManager.onDragStart(e, dragData); + } + fileTitle.classList.add("is-being-dragged"); + } + }); + fileTitle.addEventListener("dragend", () => { + fileTitle.classList.remove("is-being-dragged"); + }); + fileTitle.addEventListener("dragover", (e) => { + var _a2, _b2, _c2; + e.preventDefault(); + const { draggable } = plugin.app.dragManager; + if (draggable) { + const folderName = ((_a2 = child.parent) == null ? void 0 : _a2.name) || plugin.app.vault.getName(); + plugin.app.dragManager.setAction(window.i18next.t("interface.drag-and-drop.move-into-folder", { folder: folderName })); + (_c2 = (_b2 = fileElement.parentElement) == null ? void 0 : _b2.parentElement) == null ? void 0 : _c2.classList.add("is-being-dragged-over"); + } + }); + fileTitle.addEventListener("dragleave", () => { + var _a2, _b2; + (_b2 = (_a2 = fileElement.parentElement) == null ? void 0 : _a2.parentElement) == null ? void 0 : _b2.classList.remove("is-being-dragged-over"); + }); + fileTitle.addEventListener("drop", (e) => { + var _a2, _b2, _c2; + e.preventDefault(); + const { draggable } = plugin.app.dragManager; + if (draggable == null ? void 0 : draggable.file) { + const targetFolder = ((_a2 = child.parent) == null ? void 0 : _a2.path) || ""; + if (targetFolder) { + const newPath = `${targetFolder}/${draggable.file.name}`; + plugin.app.fileManager.renameFile(draggable.file, newPath); + } + (_c2 = (_b2 = fileElement.parentElement) == null ? void 0 : _b2.parentElement) == null ? void 0 : _c2.classList.remove("is-being-dragged-over"); + } + }); + } + fileTitle.onclick = () => { + plugin.app.workspace.openLinkText(child.path, child.path, true); + }; + fileTitle.oncontextmenu = (e) => { + folderOverview.fileMenu(child, e); + }; + let fileName = child.basename; + if (yaml.fmtpIntegration) { + fileName = (_c = await ((_b = plugin.fmtpHandler) == null ? void 0 : _b.getNewFileName(child))) != null ? _c : child.basename; + } + fileTitle.createDiv({ + cls: "tree-item-inner nav-file-title-content", + text: fileName + }); + if (child.extension !== "md" && !yaml.disableFileTag) { + fileTitle.createDiv({ + cls: "nav-file-tag", + text: child.extension + }); + } + } +}; + +// src/obsidian-folder-overview/src/styles/List.ts +var import_obsidian30 = require("obsidian"); +async function renderListOverview(plugin, ctx, root, yaml, pathBlacklist, folderOverview) { + const overviewList = folderOverview.listEl; + const { app } = plugin; + overviewList == null ? void 0 : overviewList.empty(); + let tFolder = app.vault.getAbstractFileByPath(yaml.folderPath); + if (!tFolder && yaml.folderPath.trim() === "") { + if (ctx.sourcePath.includes("/")) { + tFolder = app.vault.getAbstractFileByPath(getFolderPathFromString(ctx.sourcePath)); + } else { + yaml.folderPath = "/"; + tFolder = app.vault.getAbstractFileByPath("/"); + } + } + if (!(tFolder instanceof import_obsidian30.TFolder)) { + return; + } + let files = tFolder.children; + if (!files) { + return; + } + const ul = folderOverview.listEl; + const sourceFolderPath = tFolder.path; + files = await filterFiles(files, plugin, sourceFolderPath, yaml.depth, folderOverview.pathBlacklist, yaml, folderOverview.sourceFile); + const folders = sortFiles(files.filter((f) => f instanceof import_obsidian30.TFolder), folderOverview.yaml, plugin); + files = sortFiles(files.filter((f) => f instanceof import_obsidian30.TFile), folderOverview.yaml, plugin); + if (!ul) { + return; + } + folders.forEach(async (file) => { + if (file instanceof import_obsidian30.TFolder) { + if (yaml.includeTypes.includes("folder")) { + const folderItem = await addFolderList(plugin, ul, folderOverview.pathBlacklist, file, folderOverview); + if (!folderItem) { + return; + } + goThroughFolders(plugin, folderItem, file, folderOverview.yaml.depth, sourceFolderPath, ctx, folderOverview.yaml, folderOverview.pathBlacklist, folderOverview.yaml.includeTypes, folderOverview.yaml.disableFileTag, folderOverview); + } else { + goThroughFolders(plugin, ul, file, folderOverview.yaml.depth, sourceFolderPath, ctx, folderOverview.yaml, folderOverview.pathBlacklist, folderOverview.yaml.includeTypes, folderOverview.yaml.disableFileTag, folderOverview); + } + } + }); + files.forEach((file) => { + if (file instanceof import_obsidian30.TFile) { + addFileList(plugin, ul, folderOverview.pathBlacklist, file, folderOverview.yaml.includeTypes, folderOverview.yaml.disableFileTag, folderOverview); + } + }); + const DEBOUNCE_DELAY = 300; + const debouncedRenderListOverview = debounce2(() => renderListOverview(plugin, ctx, root, yaml, pathBlacklist, folderOverview), DEBOUNCE_DELAY); + const handleVaultChange = () => { + debouncedRenderListOverview(); + }; + folderOverview.on("vault-change", handleVaultChange); +} +function debounce2(func, wait) { + let timeout; + return (...args) => { + clearTimeout(timeout); + timeout = window.setTimeout(() => func(...args), wait); + }; +} +async function addFolderList(plugin, list, pathBlacklist, folder, folderOverview) { + var _a, _b, _c; + (_a = folderOverview.el.parentElement) == null ? void 0 : _a.classList.add("fv-remove-edit-button"); + const folderDepth = folder.path.split("/").length; + const sourceFolderDepth = folderOverview.yaml.folderPath.split("/").length; + const isFirstLevelSub = folderDepth === sourceFolderDepth + 1; + if (!folderOverview.yaml.showEmptyFolders && folder.children.length === 0 && !folderOverview.yaml.onlyIncludeSubfolders) { + return; + } else if (folderOverview.yaml.onlyIncludeSubfolders && !isFirstLevelSub && folder.children.length === 0) { + return; + } + const folderItem = list.createEl("li", { cls: "folder-overview-list folder-list" }); + if (plugin instanceof FolderNotesPlugin) { + const folderNote = getFolderNote(plugin, folder.path); + if (folderNote instanceof import_obsidian30.TFile) { + const folderNoteLink = folderItem.createEl("a", { + cls: "folder-overview-list-item folder-name-item internal-link", + href: folderNote.path + }); + if (folderOverview.yaml.fmtpIntegration) { + folderNoteLink.innerText = (_c = await ((_b = plugin.fmtpHandler) == null ? void 0 : _b.getNewFileName(folderNote))) != null ? _c : folder.name; + } else { + folderNoteLink.innerText = folder.name; + } + pathBlacklist.push(folderNote.path); + folderNoteLink.oncontextmenu = (e) => { + e.stopImmediatePropagation(); + folderOverview.fileMenu(folderNote, e); + }; + } else { + const folderName = folderItem.createEl("span", { + cls: "folder-overview-list-item folder-name-item" + }); + folderName.innerText = folder.name; + folderName.oncontextmenu = (e) => { + folderOverview.folderMenu(folder, e); + }; + } + } else { + const folderName = folderItem.createEl("span", { + cls: "folder-overview-list-item folder-name-item" + }); + folderName.innerText = folder.name; + folderName.oncontextmenu = (e) => { + folderOverview.folderMenu(folder, e); + }; + } + return folderItem; +} +async function goThroughFolders(plugin, list, folder, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag, folderOverview) { + if (sourceFolderPath === "") { + depth--; + } + const allFiles = await filterFiles(folder.children, plugin, sourceFolderPath, depth, pathBlacklist, yaml, folderOverview.sourceFile); + const files = sortFiles(allFiles.filter((file) => !(file instanceof import_obsidian30.TFolder) && file !== null), yaml, plugin); + const folders = sortFiles(allFiles.filter((file) => file instanceof import_obsidian30.TFolder && file !== null), yaml, plugin); + const ul = list.createEl("ul", { cls: "folder-overview-list" }); + folders.forEach(async (file) => { + if (file instanceof import_obsidian30.TFolder) { + if (yaml.includeTypes.includes("folder")) { + const folderItem = await addFolderList(plugin, ul, pathBlacklist, file, folderOverview); + if (!folderItem) { + return; + } + goThroughFolders(plugin, folderItem, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag, folderOverview); + } else { + goThroughFolders(plugin, list, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag, folderOverview); + } + } + }); + files.forEach((file) => { + if (file instanceof import_obsidian30.TFile) { + if (yaml.includeTypes.includes("folder")) { + addFileList(plugin, ul, pathBlacklist, file, includeTypes, disableFileTag, folderOverview); + } else { + addFileList(plugin, list, pathBlacklist, file, includeTypes, disableFileTag, folderOverview); + } + } + }); +} +async function addFileList(plugin, list, pathBlacklist, file, includeTypes, disableFileTag, folderOverview) { + var _a, _b, _c, _d; + if (!folderOverview.yaml.showFolderNotes) { + if (pathBlacklist.includes(file.path)) + return; + if (plugin instanceof FolderNotesPlugin && extractFolderName(plugin.settings.folderNoteName, file.basename) === ((_a = file.parent) == null ? void 0 : _a.name)) { + return; + } + } + (_b = folderOverview.el.parentElement) == null ? void 0 : _b.classList.add("fv-remove-edit-button"); + const listItem = list.createEl("li", { cls: "folder-overview-list file-link" }); + listItem.oncontextmenu = (e) => { + e.stopImmediatePropagation(); + folderOverview.fileMenu(file, e); + }; + const nameItem = listItem.createEl("div", { cls: "folder-overview-list-item" }); + const link = nameItem.createEl("a", { cls: "internal-link", href: file.path }); + if (folderOverview.yaml.fmtpIntegration) { + link.innerText = (_d = await ((_c = plugin.fmtpHandler) == null ? void 0 : _c.getNewFileName(file))) != null ? _d : file.basename; + } else { + link.innerText = file.basename; + } + if (file.extension !== "md" && !disableFileTag) { + nameItem.createDiv({ cls: "nav-file-tag" }).innerText = file.extension; + } +} + +// src/modals/NewFolderName.ts +var import_obsidian31 = require("obsidian"); +var NewFolderNameModal = class extends import_obsidian31.Modal { + constructor(app, plugin, folder) { + super(app); + this.plugin = plugin; + this.app = app; + this.folder = folder; + } + onOpen() { + const { contentEl } = this; + contentEl.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + this.saveFolderName(); + this.close(); + } + }); + this.modalEl.classList.add("mod-file-rename"); + const modalTitle = this.modalEl.querySelector("div.modal-title"); + if (modalTitle) { + modalTitle.textContent = "Folder title"; + } + const textarea = contentEl.createEl("textarea", { + text: this.folder.name.replace(this.plugin.settings.folderNoteType, ""), + attr: { + placeholder: "Enter the name of the folder", + rows: "1", + spellcheck: "false", + class: "rename-textarea" + } + }); + textarea.addEventListener("focus", function() { + this.select(); + }); + textarea.focus(); + const buttonContainer = this.modalEl.createDiv({ cls: "modal-button-container" }); + const saveButton = buttonContainer.createEl("button", { text: "Save", cls: "mod-cta" }); + saveButton.addEventListener("click", async () => { + this.saveFolderName(); + this.close(); + }); + const cancelButton = buttonContainer.createEl("button", { + text: "Cancel", + cls: "mod-cancel" + }); + cancelButton.addEventListener("click", () => { + this.close(); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } + saveFolderName() { + const textarea = this.contentEl.querySelector("textarea"); + if (textarea) { + const newName = textarea.value.trim(); + if (newName.trim() !== "") { + const folderBasePath = this.folder.path.slice(0, this.folder.path.lastIndexOf("/") + 1); + const newFolderPath = folderBasePath + newName.trim(); + if (!this.app.vault.getAbstractFileByPath(newFolderPath)) { + this.plugin.app.fileManager.renameFile(this.folder, newFolderPath); + } + } + } + } +}; + +// src/obsidian-folder-overview/src/utils/EventEmitter.ts +var CustomEventEmitter2 = class { + constructor() { + this.events = {}; + } + on(event, listener) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(listener); + } + off(event, listener) { + if (!this.events[event]) + return; + this.events[event] = this.events[event].filter((l) => l !== listener); + } + emit(event, data) { + if (!this.events[event]) + return; + this.events[event].forEach((listener) => listener(data)); + } +}; + +// src/obsidian-folder-overview/src/FolderOverview.ts +var FolderOverview = class { + constructor(plugin, ctx, source, el, defaultSettings) { + this.pathBlacklist = []; + this.folders = []; + this.counter = 0; + this.LINK_LIST_UPDATE_DELAY_MS = 1e3; + this.eventListeners = []; + this.plugin = plugin; + this.emitter = new CustomEventEmitter2(); + const yaml = this.parseYamlOrUseDefault(source); + const includeTypes = (yaml == null ? void 0 : yaml.includeTypes) || defaultSettings.includeTypes || ["folder", "markdown"]; + this.ctx = ctx; + this.source = source; + this.el = el; + this.sourceFile = this.getSourceFile(ctx); + this.yaml = buildYamlConfig(yaml, defaultSettings, ctx, includeTypes); + this.setSourceFolder(); + this.defaultSettings = defaultSettings; + const customChild = new CustomMarkdownRenderChild(el, this); + ctx.addChild(customChild); + } + async create(plugin, el, ctx) { + var _a, _b, _c; + el.empty(); + if (!(this.sourceFile instanceof import_obsidian32.TFile)) + return; + (_a = el.parentElement) == null ? void 0 : _a.classList.add("folder-overview-container"); + if (this.yaml.hideFolderOverview) { + if (this.yaml.isInCallout) { + el == null ? void 0 : el.classList.add("fv-hide-overview"); + } else { + (_b = el.parentElement) == null ? void 0 : _b.classList.add("fv-hide-overview"); + } + } + (_c = el.parentElement) == null ? void 0 : _c.addEventListener("contextmenu", (e) => this.editOverviewContextMenu(e), { capture: true }); + this.root = el.createEl("div", { cls: "folder-overview" }); + const headingTag = `h${this.yaml.titleSize}`; + const titleEl = this.root.createEl(headingTag, { cls: "folder-overview-title" }); + this.listEl = this.root.createEl("ul", { cls: "folder-overview-list" }); + if (this.hasNoIncludedTypes(this.root)) + return; + let sourceFolderPath = this.yaml.folderPath.trim() || getFolderPathFromString(ctx.sourcePath) || "/"; + this.registerListeners(); + await this.renderTitle(this.sourceFolder, sourceFolderPath, this.sourceFile, titleEl); + if (!this.validateSourceFolder(this.sourceFolder, sourceFolderPath)) { + this.addEditButton(this.root); + return; + } + let files = this.getInitialFiles(plugin, this.sourceFolder); + files = await this.filterAndProcessFiles(files, plugin, sourceFolderPath, this.yaml, this.sourceFile); + if (files.length === 0) { + updateLinkList(files, this.plugin, this.yaml, this.pathBlacklist, this.sourceFile); + this.addEditButton(this.root); + return; + } + files = sortFiles(files, this.yaml, this.plugin); + this.renderOverviewStyle(plugin, ctx, this.root); + this.handleLinkList(files); + this.addEditButton(this.root); + } + parseYamlOrUseDefault(source) { + let yaml = (0, import_obsidian32.parseYaml)(source); + if (!yaml) { + yaml = {}; + } + return yaml; + } + getSourceFile(ctx) { + const sourceFile = this.plugin.app.vault.getAbstractFileByPath(ctx.sourcePath); + if (sourceFile instanceof import_obsidian32.TFile) { + return sourceFile; + } + return void 0; + } + on(event, listener) { + this.emitter.on(event, listener); + } + off(event, listener) { + this.emitter.off(event, listener); + } + emit(event, data) { + this.emitter.emit(event, data); + } + handleVaultChange(eventType) { + if (this.yaml.autoSync) { + this.emit("vault-change", eventType); + } + } + disconnectListeners() { + this.eventListeners.forEach((unregister) => unregister()); + this.eventListeners = []; + } + registerListeners() { + const { plugin } = this; + const handleRename2 = () => this.handleVaultChange("renamed"); + const handleCreate2 = () => this.handleVaultChange("created"); + const handleDelete2 = () => this.handleVaultChange("deleted"); + plugin.app.vault.on("rename", handleRename2); + plugin.app.vault.on("create", handleCreate2); + plugin.app.vault.on("delete", handleDelete2); + this.eventListeners.push(() => plugin.app.vault.off("rename", handleRename2)); + this.eventListeners.push(() => plugin.app.vault.off("create", handleCreate2)); + this.eventListeners.push(() => plugin.app.vault.off("delete", handleDelete2)); + } + setSourceFolder() { + var _a; + switch ((_a = this.yaml) == null ? void 0 : _a.folderPath.trim()) { + case "": + case "File\u2019s parent folder path": { + const folderPath = getFolderPathFromString(this.ctx.sourcePath); + const sourceFolder = this.plugin.app.vault.getAbstractFileByPath(folderPath); + if (sourceFolder instanceof import_obsidian32.TFolder) { + this.yaml.folderPath = sourceFolder.path; + this.sourceFolder = sourceFolder; + } + break; + } + case "Path of folder linked to the file": { + if (this.plugin instanceof FolderNotesPlugin && this.sourceFile instanceof import_obsidian32.TFile) { + const folderNoteFolder = getFolder(this.plugin, this.sourceFile); + if (folderNoteFolder instanceof import_obsidian32.TFolder) { + this.sourceFolder = folderNoteFolder; + this.yaml.folderPath = folderNoteFolder.path; + } else { + this.yaml.folderPath = ""; + } + } + break; + } + default: { + const sourceFolder = this.plugin.app.vault.getAbstractFileByPath(this.yaml.folderPath); + if (sourceFolder instanceof import_obsidian32.TFolder) { + this.sourceFolder = sourceFolder; + } + } + } + } + async filterAndProcessFiles(files, plugin, sourceFolderPath, yaml, sourceFile) { + let filteredFiles = await filterFiles(files, plugin, sourceFolderPath, yaml.depth, this.pathBlacklist, yaml, sourceFile); + if (!yaml.includeTypes.includes("folder")) { + filteredFiles = getAllFiles(filteredFiles, sourceFolderPath, yaml.depth); + } + return filteredFiles; + } + hasNoIncludedTypes(root) { + if (this.yaml.includeTypes.length === 0) { + this.addEditButton(root); + return true; + } + return false; + } + handleLinkList(files) { + if (this.yaml.useActualLinks) { + setTimeout(() => { + if (this.sourceFile instanceof import_obsidian32.TFile) { + updateLinkList(files, this.plugin, this.yaml, this.pathBlacklist, this.sourceFile); + } + }, this.LINK_LIST_UPDATE_DELAY_MS); + } else { + removeLinkList(this.plugin, this.sourceFile, this.yaml); + } + } + async renderTitle(sourceFolder, sourceFolderPath, sourceFile, titleEl) { + var _a, _b, _c, _d, _e, _f; + if (!this.yaml.showTitle) + return; + const variables = { + folderName: (sourceFolder == null ? void 0 : sourceFolder.path) === "/" || sourceFolderPath === "/" ? "Vault" : (_a = sourceFolder == null ? void 0 : sourceFolder.name) != null ? _a : "", + folderPath: (_c = (_b = sourceFolder == null ? void 0 : sourceFolder.path) != null ? _b : sourceFolderPath) != null ? _c : "", + filePath: sourceFile.path, + fileName: sourceFile instanceof import_obsidian32.TFile ? sourceFile.basename : "", + fmtpFileName: (_e = await ((_d = this.plugin.fmtpHandler) == null ? void 0 : _d.getNewFileName(sourceFile))) != null ? _e : "" + }; + const fileCache = this.plugin.app.metadataCache.getFileCache(sourceFile); + const frontmatter = (_f = fileCache == null ? void 0 : fileCache.frontmatter) != null ? _f : {}; + let { title } = this.yaml; + title = replacePropertiesInTitle(title, frontmatter); + title = replaceVariablesInTitle(title, variables); + titleEl.innerText = title; + } + validateSourceFolder(sourceFolder, sourceFolderPath) { + if (!sourceFolder && sourceFolderPath !== "/" && sourceFolderPath !== "") { + new import_obsidian32.Notice("Folder overview: Couldn't find the folder"); + return false; + } + if (!sourceFolder && sourceFolderPath === "") { + sourceFolderPath = "/"; + } + if (!(sourceFolder instanceof import_obsidian32.TFolder) && sourceFolderPath !== "/") { + return false; + } + return true; + } + getInitialFiles(plugin, sourceFolder) { + if ((sourceFolder == null ? void 0 : sourceFolder.path) === "/") { + const rootFiles = []; + plugin.app.vault.getAllLoadedFiles().filter((f) => { + var _a; + return ((_a = f.parent) == null ? void 0 : _a.path) === "/"; + }).forEach((file) => { + if (!file.path.includes("/")) { + rootFiles.push(file); + } + }); + return rootFiles; + } else if (sourceFolder instanceof import_obsidian32.TFolder) { + return sourceFolder.children; + } + return []; + } + renderOverviewStyle(plugin, ctx, root) { + if (this.yaml.style === "grid") { + } else if (this.yaml.style === "list") { + renderListOverview(plugin, ctx, root, this.yaml, this.pathBlacklist, this); + } else if (this.yaml.style === "explorer") { + const fileExplorerOverview = new FileExplorerOverview(plugin, ctx, root, this.yaml, this.pathBlacklist, this); + this.plugin.app.workspace.onLayoutReady(async () => { + await fileExplorerOverview.renderFileExplorer(); + }); + } + } + addEditButton(root) { + const editButton = root.createEl("button", { cls: "folder-overview-edit-button" }); + editButton.innerText = "Edit overview"; + editButton.addEventListener("click", (e) => { + e.stopImmediatePropagation(); + e.preventDefault(); + e.stopPropagation(); + new FolderOverviewSettings(this.plugin.app, this.plugin, this.yaml, this.ctx, this.el, this.plugin instanceof FolderNotesPlugin ? this.plugin.settings.defaultOverview : this.plugin.settings.defaultOverviewSettings).open(); + }, { capture: true }); + } + fileMenu(file, e) { + const { plugin } = this; + const fileMenu = new import_obsidian32.Menu(); + fileMenu.addItem((item) => { + item.setTitle("Edit folder overview"); + item.setIcon("pencil"); + item.onClick(async () => { + new FolderOverviewSettings(plugin.app, plugin, this.yaml, this.ctx, this.el, plugin instanceof FolderNotesPlugin ? plugin.settings.defaultOverview : plugin.settings.defaultOverviewSettings).open(); + }); + }); + fileMenu.addSeparator(); + fileMenu.addItem((item) => { + item.setTitle(window.i18next.t("plugins.file-explorer.menu-opt-rename")); + item.setIcon("pencil"); + item.onClick(async () => { + plugin.app.fileManager.promptForFileRename(file); + }); + }); + fileMenu.addItem((item) => { + item.setTitle(window.i18next.t("plugins.file-explorer.menu-opt-delete")); + item.setIcon("trash"); + item.dom.addClass("is-warning"); + item.dom.setAttribute("data-section", "danger"); + item.onClick(() => { + plugin.app.fileManager.promptForDeletion(file); + }); + }); + fileMenu.addSeparator(); + plugin.app.workspace.trigger("file-menu", fileMenu, file, "folder-overview-file-context-menu", null); + fileMenu.showAtPosition({ x: e.pageX, y: e.pageY }); + } + folderMenu(folder, e) { + const { plugin } = this; + const folderMenu = new import_obsidian32.Menu(); + folderMenu.addItem((item) => { + item.setTitle("Edit folder overview"); + item.setIcon("pencil"); + item.onClick(async () => { + new FolderOverviewSettings(plugin.app, plugin, this.yaml, this.ctx, this.el, plugin instanceof FolderNotesPlugin ? plugin.settings.defaultOverview : plugin.settings.defaultOverviewSettings).open(); + }); + }); + folderMenu.addSeparator(); + folderMenu.addItem((item) => { + item.setTitle("Rename"); + item.setIcon("pencil"); + item.onClick(async () => { + if (plugin instanceof FolderNotesPlugin) { + new NewFolderNameModal(plugin.app, plugin, folder).open(); + } + }); + }); + folderMenu.addItem((item) => { + item.setTitle("Delete"); + item.setIcon("trash"); + item.dom.addClass("is-warning"); + item.dom.setAttribute("data-section", "danger"); + item.onClick(() => { + plugin.app.fileManager.promptForFolderDeletion(folder); + }); + }); + folderMenu.addSeparator(); + plugin.app.workspace.trigger("file-menu", folderMenu, folder, "folder-overview-folder-context-menu", null); + folderMenu.showAtPosition({ x: e.pageX, y: e.pageY }); + } + editOverviewContextMenu(e) { + const { plugin } = this; + const menu = new import_obsidian32.Menu(); + menu.addItem((item) => { + item.setTitle("Edit folder overview"); + item.setIcon("pencil"); + item.onClick(async () => { + new FolderOverviewSettings(plugin.app, plugin, this.yaml, this.ctx, this.el, plugin instanceof FolderNotesPlugin ? plugin.settings.defaultOverview : plugin.settings.defaultOverviewSettings).open(); + }); + }); + menu.showAtPosition({ x: e.pageX, y: e.pageY }); + } + getElFromOverview(path) { + const selector = `[data-path='${CSS.escape(path)}']`; + if (!this.listEl) + return null; + const el = this.listEl.querySelector(selector); + return el; + } +}; +async function updateYaml(plugin, ctx, el, yaml, addLinkList) { + const NO_CODEBLOCK_END = -1; + const file = plugin.app.vault.getAbstractFileByPath(ctx.sourcePath); + if (!(file instanceof import_obsidian32.TFile)) + return; + let stringYaml = (0, import_obsidian32.stringifyYaml)(yaml); + plugin.app.vault.process(file, (text) => { + const info = ctx.getSectionInfo(el); + if (stringYaml[stringYaml.length - 1] !== "\n") { + stringYaml += "\n"; + } + if (info) { + const { lineStart } = info; + const lineEnd = getCodeBlockEndLine(text, lineStart); + if (lineEnd === NO_CODEBLOCK_END || !lineEnd) + return text; + const lineLength = lineEnd - lineStart; + const lines = text.split("\n"); + let overviewBlock = `\`\`\`folder-overview +${stringYaml}\`\`\``; + overviewBlock += addLinkList ? ` + +` : ""; + lines.splice(lineStart, lineLength + 1, overviewBlock); + return lines.join("\n"); + } + getOverviews(plugin, file).then((overviews) => { + overviews.forEach((overview) => { + if (overview.id !== yaml.id) + return; + const isInCallout = typeof overview.isInCallout === "boolean" ? overview.isInCallout : false; + updateYamlById(plugin, yaml.id, file, yaml, addLinkList, isInCallout); + }); + }); + return text; + }); +} +async function getOverviews(plugin, file) { + if (!file) + return []; + const overviews = []; + const content = await plugin.app.vault.read(file); + if (!content) + return overviews; + const yamlBlocks = content.match(/^(?!>).*```folder-overview\n(?:^(?!>).*[\r\n]*)*?^```$/gm); + const calloutYamlBlocks = content.match(/^> ```folder-overview\n([\s\S]*?)```/gm); + if (calloutYamlBlocks) { + for (const block of calloutYamlBlocks) { + const cleanedBlock = block.replace(/^> ```folder-overview\n/, "").replace(/```$/, "").replace(/^> ?/gm, ""); + const yaml = (0, import_obsidian32.parseYaml)(cleanedBlock); + if (yaml) { + yaml.isInCallout = true; + overviews.push(yaml); + } + } + } + if (!yamlBlocks) + return overviews; + for (const block of yamlBlocks) { + const yaml = (0, import_obsidian32.parseYaml)(block.replace("```folder-overview\n", "").replace("```", "")); + if (!yaml) + continue; + overviews.push(yaml); + } + return overviews; +} +async function hasOverviewYaml(plugin, file) { + const content = await plugin.app.vault.read(file); + if (!content) + return false; + const yamlBlocks = content.match(/```folder-overview\n([\s\S]*?)```/g); + return !!yamlBlocks; +} +var CustomMarkdownRenderChild = class extends import_obsidian32.MarkdownRenderChild { + constructor(el, folderOverview) { + super(el); + this.folderOverview = folderOverview; + } + onunload() { + this.folderOverview.disconnectListeners(); + } +}; +function sortFiles(files, yaml, plugin) { + var _a, _b; + if (!(yaml == null ? void 0 : yaml.sortBy)) { + const defaultSettings = plugin instanceof FolderNotesPlugin ? plugin.settings.defaultOverview : plugin.settings.defaultOverviewSettings; + yaml.sortBy = (_a = defaultSettings.sortBy) != null ? _a : "name"; + yaml.sortByAsc = (_b = defaultSettings.sortByAsc) != null ? _b : false; + } + const collator = new Intl.Collator(void 0, { numeric: true, sensitivity: "base" }); + const FOLDER_FIRST = -1; + const FILE_FIRST = 1; + const EQUAL = 0; + function compareFilesAndFolders(a, b) { + const a_IsFolder = a instanceof import_obsidian32.TFolder; + const b_IsFolder = b instanceof import_obsidian32.TFolder; + const a_IsFile = a instanceof import_obsidian32.TFile; + const b_IsFile = b instanceof import_obsidian32.TFile; + if (a_IsFolder && !b_IsFolder) { + return FOLDER_FIRST; + } + if (!a_IsFolder && b_IsFolder) { + return FILE_FIRST; + } + if (a_IsFolder && b_IsFolder) { + return yaml.sortByAsc ? collator.compare(a.name, b.name) : collator.compare(b.name, a.name); + } + if (a_IsFile && b_IsFile) { + return compareFiles(a, b); + } + return EQUAL; + } + function compareFiles(a, b) { + if (yaml.sortBy === "created") { + return yaml.sortByAsc ? a.stat.ctime - b.stat.ctime : b.stat.ctime - a.stat.ctime; + } else if (yaml.sortBy === "modified") { + return yaml.sortByAsc ? a.stat.mtime - b.stat.mtime : b.stat.mtime - a.stat.mtime; + } else if (yaml.sortBy === "name") { + return yaml.sortByAsc ? collator.compare(a.basename, b.basename) : collator.compare(b.basename, a.basename); + } + return 0; + } + files.sort(compareFilesAndFolders); + return files; +} +async function filterFiles(files, plugin, sourceFolderPath, depth, pathBlacklist, yaml, sourceFile) { + const filteredFiles = await Promise.all(files.map(async (file) => filterSingleFile(file, plugin, sourceFolderPath, depth, pathBlacklist, yaml, sourceFile))); + return filteredFiles.filter((file) => file !== null); +} +async function filterSingleFile(file, plugin, sourceFolderPath, depth, pathBlacklist, yaml, sourceFile) { + const folderPath = getFolderPathFromString(file.path); + const dontShowFolderNote = pathBlacklist.includes(file.path); + const isSubfolder = isFileInSubfolder(sourceFolderPath, folderPath); + const isSourceFile = sourceFile ? file.path === sourceFile.path : false; + const isFile = file instanceof import_obsidian32.TFile; + const includeTypes = yaml.includeTypes || []; + const extension = isFile ? file.extension.toLowerCase() : ""; + const imageTypes = ["jpg", "jpeg", "png", "gif", "svg", "webp"]; + const videoTypes = ["mp4", "webm", "ogv", "mov", "mkv"]; + const audioTypes = [ + "mp3", + "wav", + "m4a", + "3gp", + "flac", + "ogg", + "oga", + "opus" + ]; + if (isFile && !isFileTypeIncluded(extension, includeTypes, imageTypes, videoTypes, audioTypes)) { + return null; + } + const isExcludedFromOverview = await getIsExcludedFromOverview(plugin, file); + if (shouldExcludeFile(dontShowFolderNote, yaml.showFolderNotes, isSubfolder, isSourceFile, isExcludedFromOverview)) { + return null; + } + const fileDepth = getFileDepth(file.path, sourceFolderPath); + return fileDepth <= depth ? file : null; +} +function isFileTypeIncluded(extension, includeTypes, imageTypes, videoTypes, audioTypes) { + if (includeTypes.length === 0 || includeTypes.includes("all")) + return true; + if ((extension === "md" || extension === "markdown") && includeTypes.includes("markdown")) + return true; + if (extension === "canvas" && includeTypes.includes("canvas")) + return true; + if (extension === "pdf" && includeTypes.includes("pdf")) + return true; + if (imageTypes.includes(extension) && includeTypes.includes("image")) + return true; + if (videoTypes.includes(extension) && includeTypes.includes("video")) + return true; + if (audioTypes.includes(extension) && includeTypes.includes("audio")) + return true; + return false; +} +function isFileInSubfolder(sourceFolderPath, folderPath) { + return sourceFolderPath === "/" || folderPath.startsWith(sourceFolderPath); +} +async function getIsExcludedFromOverview(plugin, file) { + var _a; + if (plugin instanceof FolderNotesPlugin) { + const excluded = getExcludedFolder(plugin, file.path, true); + return (_a = excluded == null ? void 0 : excluded.excludeFromFolderOverview) != null ? _a : false; + } + return false; +} +function shouldExcludeFile(dontShowFolderNote, showFolderNotes, isSubfolder, isSourceFile, isExcludedFromOverview) { + return dontShowFolderNote && !showFolderNotes || !isSubfolder || isSourceFile || isExcludedFromOverview; +} +function getFileDepth(filePath, sourceFolderPath) { + return filePath.split("/").length - (sourceFolderPath === "/" ? 0 : sourceFolderPath.split("/").length); +} +function getAllFiles(files, sourceFolderPath, depth) { + const allFiles = []; + const getDepth = (filePath) => { + return filePath.split("/").length - sourceFolderPath.split("/").length; + }; + files.forEach((file) => { + const fileDepth = getDepth(file.path); + if (file instanceof import_obsidian32.TFolder) { + if (fileDepth < depth) { + allFiles.push(...getAllFiles(file.children, sourceFolderPath, depth)); + } + } else { + allFiles.push(file); + } + }); + return allFiles; +} + +// src/obsidian-folder-overview/src/suggesters/FolderSuggester.ts +var import_obsidian33 = require("obsidian"); +var MAX_LOADED_FILES = 100; +var FolderSuggest2 = class extends import_obsidian33.AbstractInputSuggest { + constructor(inputEl, plugin, whitelistSuggester, folder) { + super(plugin.app, inputEl); + this.inputEl = inputEl; + this.whitelistSuggester = whitelistSuggester; + this.folder = folder; + this.plugin = plugin; + } + getSuggestions(input_str) { + const folders = []; + const lower_input_str = input_str.toLowerCase(); + let files = []; + if (this.folder) { + files = this.folder.children.slice(0, MAX_LOADED_FILES); + } else { + files = this.plugin.app.vault.getAllLoadedFiles().slice(0, MAX_LOADED_FILES); + } + folders.push({ path: "File\u2019s parent folder path" }); + if (this.plugin instanceof FolderNotesPlugin) { + folders.push({ path: "Path of folder linked to the file" }); + } + files.forEach((folder) => { + if (folder instanceof import_obsidian33.TFolder && folder.path.toLowerCase().contains(lower_input_str) && (this.plugin instanceof FolderNotesPlugin ? !this.plugin.settings.excludeFolders.find((f) => f.path === folder.path) || this.whitelistSuggester : true)) { + folders.push(folder); + } + }); + return folders; + } + renderSuggestion(folder, el) { + el.setText(folder.path); + } + selectSuggestion(folder) { + this.inputEl.value = folder.path; + this.inputEl.trigger("input"); + this.close(); + } +}; + +// src/obsidian-folder-overview/src/utils/ListComponent.ts +var import_obsidian34 = require("obsidian"); +var ListComponent2 = class { + constructor(containerEl, values = [], defaultValues = []) { + this.values = []; + this.emitter = new CustomEventEmitter2(); + this.containerEl = containerEl; + this.controlEl = containerEl.querySelector(".setting-item-control") || containerEl; + this.listEl = this.controlEl.createDiv("setting-command-hotkeys"); + this.addResetButton(); + this.setValues(values); + this.defaultValues = defaultValues; + } + on(event, listener) { + this.emitter.on(event, listener); + } + off(event, listener) { + this.emitter.off(event, listener); + } + emit(event, data) { + this.emitter.emit(event, data); + } + setValues(values) { + this.removeElements(); + this.values = values; + if (values.length !== 0) { + values.forEach((value) => { + this.addElement(value); + }); + } + this.emit("update", this.values); + } + removeElements() { + this.listEl.empty(); + } + addElement(value) { + this.listEl.createSpan("setting-hotkey", (span) => { + if (value.toLocaleLowerCase() === "md") { + span.innerText = "markdown"; + } else { + span.innerText = value; + } + span.setAttribute("extension", value); + const removeSpan = span.createEl("span", { + cls: "ofn-list-item-remove setting-hotkey-icon" + }); + const svgElement = removeSpan.createEl("span", { cls: "ofn-list-item-remove-icon" }); + (0, import_obsidian34.setIcon)(svgElement, "x"); + removeSpan.onClickEvent(() => { + this.removeValue(value); + span.remove(); + }); + }); + } + async addValue(value) { + this.values.push(value); + this.addElement(value); + this.emit("add", value); + this.emit("update", this.values); + } + addResetButton() { + const resetButton = this.controlEl.createEl("span", { + cls: "clickable-icon setting-restore-hotkey-button" + }); + (0, import_obsidian34.setIcon)(resetButton, "rotate-ccw"); + resetButton.onClickEvent(() => { + this.setValues(this.defaultValues); + }); + return this; + } + removeValue(value) { + this.values = this.values.filter((v) => v !== value); + this.listEl.find(`[extension='${value}']`).remove(); + this.emit("remove", value); + this.emit("update", this.values); + } +}; + +// src/obsidian-folder-overview/src/settings.ts +var OVERVIEW_SETTINGS = { + id: "", + folderPath: "", + title: "{{folderName}} overview", + showTitle: false, + depth: 3, + includeTypes: ["folder", "markdown"], + style: "list", + disableFileTag: false, + sortBy: "name", + sortByAsc: true, + showEmptyFolders: false, + onlyIncludeSubfolders: false, + storeFolderCondition: true, + showFolderNotes: false, + disableCollapseIcon: true, + alwaysCollapse: false, + autoSync: true, + allowDragAndDrop: true, + hideLinkList: true, + hideFolderOverview: false, + useActualLinks: false, + fmtpIntegration: false, + titleSize: 1, + isInCallout: false, + useWikilinks: true +}; +var GLOBAL_SETTINGS = { + autoUpdateLinks: false +}; +var DEFAULT_SETTINGS = { + defaultOverviewSettings: OVERVIEW_SETTINGS, + globalSettings: GLOBAL_SETTINGS, + firstTimeInsertOverview: false +}; +var MAX_INCLUDE_TYPES_FOR_DROPDOWN = 8; +var SettingsTab = class extends import_obsidian35.PluginSettingTab { + constructor(plugin) { + super(plugin.app, plugin); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.createEl("h3", { text: "Global settings" }); + new import_obsidian35.Setting(containerEl).setName("Auto-update links without opening the overview").setDesc("If enabled, the links that appear in the graph view will be updated even when you don't have the overview open somewhere.").addToggle((toggle) => toggle.setValue(this.plugin.settings.globalSettings.autoUpdateLinks).onChange(async (value) => { + this.plugin.settings.globalSettings.autoUpdateLinks = value; + await this.plugin.saveSettings(); + if (value) { + this.plugin.fvIndexDB.init(true); + } else { + this.plugin.fvIndexDB.active = false; + } + })); + containerEl.createEl("h3", { text: "Overviews default settings" }); + const pEl = containerEl.createEl("p", { + text: "Edit the default settings for new folder overviews, ", + cls: "setting-item-description" + }); + const span = createSpan({ + text: "this won't apply to already existing overviews.", + cls: "" + }); + const accentColor = this.plugin.app.vault.getConfig("accentColor") || "#7d5bed"; + span.setAttr("style", `color: ${accentColor};`); + pEl.appendChild(span); + this.display = this.display.bind(this); + createOverviewSettings(containerEl, this.plugin.settings.defaultOverviewSettings, this.plugin, this.plugin.settings.defaultOverviewSettings, this.display, void 0, void 0, void 0, this); + } +}; +var createOrReplaceSetting = (container, section, changedSection, renderSetting) => { + let sectionContainer = container.querySelector(`.setting-${section}`); + if (sectionContainer) { + if (changedSection === section || changedSection === "all") { + sectionContainer.empty(); + renderSetting(sectionContainer); + return; + } + return; + } + sectionContainer = container.createDiv({ + cls: `setting-${section} overview-setting-item-fv` + }); + renderSetting(sectionContainer); +}; +async function createOverviewSettings(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, changedSection) { + changedSection = changedSection != null ? changedSection : null; + createOrReplaceSetting(contentEl, "auto-sync", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Auto sync").setDesc("Choose if the overview should automatically update when you delete, create or rename a file/folder").addToggle((toggle) => toggle.setValue(yaml.autoSync).onChange(async (value) => { + yaml.autoSync = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "allow-drag-and-drop", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Allow drag and drop").setDesc("Choose if you want to allow drag and drop in the overview").addToggle((toggle) => toggle.setValue(yaml.allowDragAndDrop).onChange(async (value) => { + yaml.allowDragAndDrop = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "showTitle", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Show the title").setDesc("Choose if the title above the folder overview should be shown").addToggle((toggle) => toggle.setValue(yaml.showTitle).onChange(async (value) => { + yaml.showTitle = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "title-container-fn", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Title").setDesc(createFragment((frag) => { + const link = frag.createEl("a", { + text: "Find more information about the title in the documentation. There is also a list of variables you can use", + href: "https://lostpaul.github.io/obsidian-folder-notes/Folder%20overview/#title" + }); + link.target = "_blank"; + })).addText((text) => text.setValue((yaml == null ? void 0 : yaml.title) || "{{folderName}} overview").onChange(async (value) => { + yaml.title = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + })); + }); + createOrReplaceSetting(contentEl, "title-size", changedSection, (settingEl) => { + const MIN_TITLE_SIZE = 1; + const MAX_TITLE_SIZE = 6; + new import_obsidian35.Setting(settingEl).setName("Title size").setDesc("The larger the number, the smaller the title will be displayed.").addSlider((slider) => slider.setValue(yaml.titleSize).setLimits(MIN_TITLE_SIZE, MAX_TITLE_SIZE, 1).setDynamicTooltip().onChange(async (value) => { + yaml.titleSize = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "folder-path", changedSection, (settingEl) => { + const folderPathSetting = new import_obsidian35.Setting(settingEl).setName("Folder path for the overview").setDesc(createFragment((frag) => { + frag.appendText("The overview will show the subfolders and files of the folder you choose here. "); + const link = frag.createEl("a", { + text: "Find more information about this setting in the documentation.", + href: "https://lostpaul.github.io/obsidian-folder-notes/Folder%20overview/#folder-path" + }); + link.target = "_blank"; + })).addSearch((search) => { + new FolderSuggest2(search.inputEl, plugin, false); + search.setPlaceholder("Folder path").setValue((yaml == null ? void 0 : yaml.folderPath) || "").onChange(async (value) => { + const whiteList = [ + "File's parent folder path", + "Path of folder linked to the file" + ]; + if (value.trim() !== "" && !whiteList.includes(value.trim())) { + value = (0, import_obsidian35.normalizePath)(value); + } + if (!whiteList.includes(value.trim())) { + const abstractFile = plugin.app.vault.getAbstractFileByPath(value); + const isFolder = abstractFile instanceof import_obsidian35.TFolder; + if (!isFolder && value !== "") { + return; + } + } + yaml.folderPath = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + }); + }); + folderPathSetting.settingEl.classList.add("fn-overview-folder-path"); + }); + createOrReplaceSetting(contentEl, "use-actual-links", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Use actual links").setDesc("Choose if the links in the overview should be showed in the graph view. This requires a second list under the actual overview and which is hidden by default.").addToggle((toggle) => toggle.setValue(yaml.useActualLinks).onChange(async (value) => { + yaml.useActualLinks = value; + updateSettings(contentEl, yaml, plugin, yaml.useActualLinks, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "use-wikilinks", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Use wikilinks").setDesc("Choose if the links in the link list should be in wikilink format or markdown link format (e.g., [[link]] vs [link](url)).").addToggle((toggle) => toggle.setValue(yaml.useWikilinks).onChange(async (value) => { + yaml.useWikilinks = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "hide-folder-overview", changedSection, (settingEl) => { + const hideOverviewSeting = new import_obsidian35.Setting(settingEl).setName("Hide folder overview").setDesc("Choose if the folder overview should be hidden and instead only the link list should be shown").addToggle((toggle) => toggle.setValue(yaml.hideFolderOverview).onChange(async (value) => { + yaml.hideFolderOverview = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + hideOverviewSeting.settingEl.classList.add("fn-hide-overview-setting"); + }); + createOrReplaceSetting(contentEl, "hide-link-list", changedSection, (settingEl) => { + const hideLinkListSetting = new import_obsidian35.Setting(settingEl).setName("Hide link list").setDesc("Choose if only link list under the folder overview should be shown").addToggle((toggle) => toggle.setValue(yaml.hideLinkList).onChange(async (value) => { + yaml.hideLinkList = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + hideLinkListSetting.settingEl.classList.add("fn-hide-link-list-setting"); + }); + createOrReplaceSetting(contentEl, "overview-style", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Overview style").setDesc("Choose the style of the overview (grid style soon)").addDropdown((dropdown) => dropdown.addOption("list", "List").addOption("explorer", "Explorer").setValue((yaml == null ? void 0 : yaml.style) || "list").onChange((value) => { + yaml.style = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + })); + }); + createOrReplaceSetting(contentEl, "include-types", changedSection, (settingEl) => { + var _a, _b; + const setting = new import_obsidian35.Setting(settingEl); + setting.setName("Include types"); + const list = new ListComponent2(setting.settingEl, yaml.includeTypes || [], ["markdown", "folder"]); + list.on("update", (values) => { + yaml.includeTypes = values; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, "include-types"); + }); + if ((((_a = yaml == null ? void 0 : yaml.includeTypes) == null ? void 0 : _a.length) || 0) < MAX_INCLUDE_TYPES_FOR_DROPDOWN && !((_b = yaml.includeTypes) == null ? void 0 : _b.includes("all"))) { + setting.addDropdown((dropdown) => { + if (!yaml.includeTypes) { + yaml.includeTypes = plugin instanceof FolderNotesPlugin ? plugin.settings.defaultOverview.includeTypes : plugin.settings.defaultOverviewSettings.includeTypes || []; + } + yaml.includeTypes = yaml.includeTypes.map((type) => type.toLowerCase()); + const options = [ + { value: "markdown", label: "Markdown" }, + { value: "folder", label: "Folder" }, + { value: "canvas", label: "Canvas" }, + { value: "pdf", label: "PDF" }, + { value: "image", label: "Image" }, + { value: "audio", label: "Audio" }, + { value: "video", label: "Video" }, + { value: "other", label: "All other file types" }, + { value: "all", label: "All file types" } + ]; + options.forEach((option) => { + var _a2; + if (!((_a2 = yaml.includeTypes) == null ? void 0 : _a2.includes(option.value))) { + dropdown.addOption(option.value, option.label); + } + }); + dropdown.addOption("+", "+"); + dropdown.setValue("+"); + dropdown.onChange(async (value) => { + var _a2; + if (value === "all") { + yaml.includeTypes = (_a2 = yaml.includeTypes) == null ? void 0 : _a2.filter((type) => type === "folder"); + list.setValues(yaml.includeTypes); + } + await list.addValue(value.toLowerCase()); + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, "include-types"); + }); + }); + } + }); + createOrReplaceSetting(contentEl, "file-tag", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Disable file tag").setDesc("Choose if the file tag should be shown after the file name").addToggle((toggle) => { + toggle.setValue(yaml.disableFileTag).onChange(async (value) => { + yaml.disableFileTag = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + }); + }); + }); + createOrReplaceSetting(contentEl, "show-folder-notes", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Show folder notes").setDesc("Choose if folder notes (the note itself and not the folder name) should be shown in the overview").addToggle((toggle) => toggle.setValue(yaml.showFolderNotes).onChange(async (value) => { + yaml.showFolderNotes = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + })); + }); + createOrReplaceSetting(contentEl, "file-depth", changedSection, (settingEl) => { + const DEFAULT_DEPTH = 2; + const MAX_DEPTH = 10; + new import_obsidian35.Setting(settingEl).setName("File depth").setDesc("File & folder = +1 depth").addSlider((slider) => slider.setValue((yaml == null ? void 0 : yaml.depth) || DEFAULT_DEPTH).setLimits(1, MAX_DEPTH, 1).setDynamicTooltip().onChange(async (value) => { + yaml.depth = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + })); + }); + createOrReplaceSetting(contentEl, "sort-files", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Sort files by").setDesc("Choose how the files should be sorted").addDropdown((dropdown) => dropdown.addOption("name", "Name").addOption("created", "Created").addOption("modified", "Modified").setValue((yaml == null ? void 0 : yaml.sortBy) || "name").onChange(async (value) => { + yaml.sortBy = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + })).addDropdown((dropdown) => { + dropdown.addOption("desc", "Descending").addOption("asc", "Ascending"); + if (yaml.sortByAsc) { + dropdown.setValue("asc"); + } else { + dropdown.setValue("desc"); + } + dropdown.onChange(async (value) => { + yaml.sortByAsc = value === "asc"; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + }); + }); + }); + createOrReplaceSetting(contentEl, "show-empty-folders", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Show folder names of folders that appear empty in the folder overview").setDesc("Show the names of folders that appear to have no files/folders in the folder overview. That's mostly the case when you set the file depth to 1.").addToggle((toggle) => { + toggle.setValue(yaml.showEmptyFolders).onChange(async (value) => { + yaml.showEmptyFolders = value; + yaml.onlyIncludeSubfolders = false; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal); + }); + }); + }); + createOrReplaceSetting(contentEl, "show-empty-folders-only-first-level", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Only show empty folders which are on the first level of the folder overview").addToggle((toggle) => { + toggle.setValue(yaml.onlyIncludeSubfolders).onChange(async (value) => { + yaml.onlyIncludeSubfolders = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + }); + }); + }); + createOrReplaceSetting(contentEl, "disable-collapse-icon", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Disable collapse icon for folder notes").setDesc("Remove the collapse icon next to the folder name for folder notes when they only contain the folder note itself").addToggle((toggle) => { + toggle.setValue(yaml.disableCollapseIcon).onChange(async (value) => { + yaml.disableCollapseIcon = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + }); + }); + }); + createOrReplaceSetting(contentEl, "store-collapse-condition", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Store collapsed condition").setDesc("Choose if the collapsed condition should be stored until you restart Obsidian").addToggle((toggle) => toggle.setValue(yaml.storeFolderCondition).onChange(async (value) => { + yaml.storeFolderCondition = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + })); + }); + createOrReplaceSetting(contentEl, "collapse-all-by-default", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Collapse all in the tree by default").setDesc("Collapse every folder in the file explorer in the overview by default").addToggle((toggle) => { + toggle.setValue(yaml.alwaysCollapse).onChange(async (value) => { + yaml.alwaysCollapse = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + }); + }); + }); + createOrReplaceSetting(contentEl, "fmtp-integration", changedSection, (settingEl) => { + new import_obsidian35.Setting(settingEl).setName("Front Matter Title Plugin integration").setDesc("Replace the folder/file name with the title from the Front Matter Title Plugin. This requires the plugin to be installed and enabled.").addToggle((toggle) => toggle.setValue(yaml.fmtpIntegration).onChange(async (value) => { + yaml.fmtpIntegration = value; + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); + })); + }); + updateSettings(contentEl, yaml, plugin, false, defaultSettings, el, ctx, file); +} +function determineVisibleSections(yaml, plugin) { + var _a; + let showDisableFileTag = false; + (_a = yaml.includeTypes) == null ? void 0 : _a.forEach((type) => { + if (type !== "markdown" && type !== "folder") { + showDisableFileTag = true; + } + }); + if (yaml.includeTypes.length === 0) { + showDisableFileTag = false; + } + return { + "setting-title-container-fn": yaml.showTitle, + "setting-title-size": yaml.showTitle, + "setting-store-collapse-condition": yaml.style === "explorer", + "setting-file-tag": showDisableFileTag, + "setting-show-empty-folders": yaml.style === "list", + "setting-show-empty-folders-only-first-level": yaml.showEmptyFolders && yaml.style === "list", + "setting-disable-collapse-icon": yaml.style === "explorer", + "setting-collapse-all-by-default": yaml.style === "explorer", + "setting-allow-drag-and-drop": yaml.style === "explorer", + "setting-hide-folder-overview": !yaml.hideLinkList && yaml.useActualLinks, + "setting-hide-link-list": !yaml.hideFolderOverview && yaml.useActualLinks, + "setting-use-wikilinks": yaml.useActualLinks, + "setting-fmtp-integration": !!plugin.app.plugins.getPlugin("obsidian-front-matter-title-plugin") + }; +} +async function updateSettings(contentEl, yaml, plugin, addLinkList, defaultSettings, el, ctx, file) { + const visibleSections = determineVisibleSections(yaml, plugin); + toggleSections(contentEl, visibleSections); + if (!yaml.id) { + plugin.saveSettings(); + if (file === void 0) { + plugin.updateOverviewView(plugin); + } + return; + } + if (el && ctx) { + await updateYaml(plugin, ctx, el, yaml, addLinkList); + } + if (file) { + await updateYamlById(plugin, yaml.id, file, yaml, addLinkList, yaml.isInCallout); + } +} +function refresh(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, changedSection) { + if (file) { + contentEl = contentEl.parentElement; + } + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, changedSection); +} +function toggleSections(contentEl, sections) { + Object.entries(sections).forEach(([sectionClass, shouldShow]) => { + const sectionElements = contentEl.querySelectorAll(`.${sectionClass}`); + sectionElements.forEach((section) => { + if (shouldShow && section) { + section.classList.remove("hide"); + } else { + section == null ? void 0 : section.classList.add("hide"); + } + }); + }); +} + +// src/settings/FolderOverviewSettings.ts +async function renderFolderOverview(settingsTab) { + const { plugin } = settingsTab; + const defaultOverviewSettings = plugin.settings.defaultOverview; + const containerEl = settingsTab.settingsPage; + containerEl.createEl("h3", { text: "Global settings" }); + new import_obsidian36.Setting(containerEl).setName("Auto-update links without opening the overview").setDesc("If enabled, the links that appear in the graph view will be updated even when you don't have the overview open somewhere.").addToggle((toggle) => toggle.setValue(plugin.settings.fvGlobalSettings.autoUpdateLinks).onChange(async (value) => { + plugin.settings.fvGlobalSettings.autoUpdateLinks = value; + await plugin.saveSettings(); + if (value) { + plugin.fvIndexDB.init(true); + } else { + plugin.fvIndexDB.active = false; + } + })); + containerEl.createEl("h3", { text: "Overviews default settings" }); + const pEl = containerEl.createEl("p", { + text: "Edit the default settings for new folder overviews, ", + cls: "setting-item-description" + }); + const span = createSpan({ text: "this won't apply to already existing overviews.", cls: "" }); + const accentColor = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + span.setAttr("style", `color: ${accentColor};`); + pEl.appendChild(span); + void createOverviewSettings(containerEl, defaultOverviewSettings, plugin, plugin.settings.defaultOverview, settingsTab.display, void 0, void 0, void 0, settingsTab); +} + +// src/settings/ExcludedFoldersSettings.ts +var import_obsidian38 = require("obsidian"); + +// src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts +var import_obsidian37 = require("obsidian"); + +// src/ExcludeFolders/WhitelistFolder.ts +var WhitelistedFolder = class { + constructor(path, position, id, plugin) { + this.enableSync = false; + this.enableAutoCreate = false; + this.enableFolderNote = false; + this.disableCollapsing = false; + this.showInFolderOverview = false; + this.hideInFileExplorer = false; + this.hideInSettings = false; + this.type = "folder"; + this.id = id || crypto.randomUUID(); + this.path = path; + this.subFolders = plugin.settings.excludeFolderDefaultSettings.subFolders; + this.position = position; + this.string = ""; + } +}; + +// src/ExcludeFolders/modals/WhitelistedFoldersSettings.ts +var WhitelistedFoldersSettings = class extends import_obsidian37.Modal { + constructor(settingsTab) { + super(settingsTab.app); + this.plugin = settingsTab.plugin; + this.settingsTab = settingsTab; + this.app = settingsTab.app; + } + onOpen() { + const { contentEl } = this; + contentEl.createEl("h2", { text: "Manage whitelisted folders" }); + new import_obsidian37.Setting(contentEl).setName("Add whitelisted folder").setClass("add-exclude-folder-item").addButton((cb) => { + cb.setIcon("plus"); + cb.setClass("add-exclude-folder"); + cb.setTooltip("Add whitelisted folder"); + cb.onClick(() => { + const whitelistedFolder = new WhitelistedFolder("", this.plugin.settings.whitelistFolders.length, void 0, this.plugin); + addWhitelistFolderListItem(this.plugin.settingsTab, contentEl, whitelistedFolder); + addWhitelistedFolder(this.plugin, whitelistedFolder); + this.settingsTab.renderSettingsPage(this.settingsTab.plugin.settings.settingsTab); + }); + }); + this.plugin.settings.whitelistFolders.sort((a, b) => a.position - b.position).forEach((whitelistedFolder) => { + var _a, _b; + if (((_a = whitelistedFolder.string) == null ? void 0 : _a.trim()) !== "" && ((_b = whitelistedFolder.path) == null ? void 0 : _b.trim()) === "") { + addWhitelistedPatternListItem(this.settingsTab, contentEl, whitelistedFolder); + } else { + addWhitelistFolderListItem(this.settingsTab, contentEl, whitelistedFolder); + } + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/settings/ExcludedFoldersSettings.ts +async function renderExcludeFolders(settingsTab) { + const containerEl = settingsTab.settingsPage; + const manageExcluded = new import_obsidian38.Setting(containerEl).setHeading().setClass("fn-excluded-folder-heading").setName("Manage excluded folders"); + const desc3 = document.createDocumentFragment(); + desc3.append("Add {regex} at the beginning of the folder name to use a regex pattern.", desc3.createEl("br"), "Use * before and after to exclude folders that include the name between the *s.", desc3.createEl("br"), "Use * before the folder name to exclude folders that end with the folder name.", desc3.createEl("br"), "Use * after the folder name to exclude folders that start with the folder name."); + manageExcluded.setDesc(desc3); + manageExcluded.infoEl.appendText("The regexes and wildcards are only for the folder name, not the path."); + manageExcluded.infoEl.createEl("br"); + manageExcluded.infoEl.appendText("If you want to switch to a folder path delete the pattern first."); + manageExcluded.infoEl.style.color = settingsTab.app.vault.getConfig("accentColor") || "#7d5bed"; + new import_obsidian38.Setting(containerEl).setName("Whitelisted folders").setDesc("Folders that override the excluded folders/patterns").addButton((cb) => { + cb.setButtonText("Manage"); + cb.setCta(); + cb.onClick(async () => { + new WhitelistedFoldersSettings(settingsTab).open(); + }); + }); + new import_obsidian38.Setting(containerEl).setName("Exclude folder default settings").addButton((cb) => { + cb.setButtonText("Manage"); + cb.setCta(); + cb.onClick(async () => { + new ExcludedFolderSettings(settingsTab.app, settingsTab.plugin, settingsTab.plugin.settings.excludeFolderDefaultSettings).open(); + }); + }); + new import_obsidian38.Setting(containerEl).setName("Exclude pattern default settings").addButton((cb) => { + cb.setButtonText("Manage"); + cb.setCta(); + cb.onClick(async () => { + new PatternSettings(settingsTab.app, settingsTab.plugin, settingsTab.plugin.settings.excludePatternDefaultSettings).open(); + }); + }); + new import_obsidian38.Setting(containerEl).setName("Add excluded folder").setClass("add-exclude-folder-item").addButton((cb) => { + cb.setIcon("plus"); + cb.setClass("add-exclude-folder"); + cb.setTooltip("Add excluded folder"); + cb.onClick(() => { + const excludedFolder = new ExcludedFolder("", settingsTab.plugin.settings.excludeFolders.length, void 0, settingsTab.plugin); + addExcludeFolderListItem(settingsTab, containerEl, excludedFolder); + addExcludedFolder(settingsTab.plugin, excludedFolder); + settingsTab.renderSettingsPage(settingsTab.plugin.settings.settingsTab); + }); + }); + settingsTab.plugin.settings.excludeFolders.filter((folder) => !folder.hideInSettings).sort((a, b) => a.position - b.position).forEach((excludedFolder) => { + var _a, _b; + if (((_a = excludedFolder.string) == null ? void 0 : _a.trim()) !== "" && ((_b = excludedFolder.path) == null ? void 0 : _b.trim()) === "") { + addExcludePatternListItem(settingsTab, containerEl, excludedFolder); + } else { + addExcludeFolderListItem(settingsTab, containerEl, excludedFolder); + } + }); +} + +// src/settings/SettingsTab.ts +var DEFAULT_SETTINGS2 = { + syncFolderName: true, + ctrlKey: true, + altKey: false, + hideFolderNote: true, + templatePath: "", + autoCreate: false, + autoCreateFocusFiles: true, + autoCreateForAttachmentFolder: false, + autoCreateForFiles: false, + enableCollapsing: false, + excludeFolders: [], + whitelistFolders: [], + showDeleteConfirmation: true, + underlineFolder: true, + stopWhitespaceCollapsing: true, + underlineFolderInPath: true, + openFolderNoteOnClickInPath: true, + openInNewTab: false, + focusExistingTab: false, + oldFolderNoteName: void 0, + folderNoteName: "{{folder_name}}", + folderNoteType: ".md", + disableFolderHighlighting: false, + newFolderNoteName: "{{folder_name}}", + storageLocation: "insideFolder", + syncDelete: false, + showRenameConfirmation: true, + defaultOverview: { + id: "", + folderPath: "", + title: "{{folderName}} overview", + showTitle: false, + depth: 3, + includeTypes: ["folder", "markdown"], + style: "list", + disableFileTag: false, + sortBy: "name", + sortByAsc: true, + showEmptyFolders: false, + onlyIncludeSubfolders: false, + storeFolderCondition: true, + showFolderNotes: false, + disableCollapseIcon: true, + alwaysCollapse: false, + autoSync: true, + allowDragAndDrop: true, + hideLinkList: true, + hideFolderOverview: false, + useActualLinks: false, + fmtpIntegration: false, + titleSize: 1, + isInCallout: false, + useWikilinks: true + }, + useSubmenus: true, + syncMove: true, + frontMatterTitle: { + enabled: false, + explorer: true, + path: true + }, + settingsTab: "general", + supportedFileTypes: ["md", "canvas", "base"], + boldName: false, + boldNameInPath: false, + cursiveName: false, + cursiveNameInPath: false, + disableOpenFolderNoteOnClick: false, + openByClick: true, + openWithCtrl: false, + openWithAlt: false, + excludeFolderDefaultSettings: { + type: "folder", + path: "", + id: crypto.randomUUID(), + subFolders: true, + disableSync: true, + disableAutoCreate: true, + disableFolderNote: false, + enableCollapsing: false, + position: 0, + excludeFromFolderOverview: false, + string: "", + hideInSettings: false, + detached: false, + showFolderNote: false + }, + excludePatternDefaultSettings: { + type: "pattern", + path: "", + id: crypto.randomUUID(), + subFolders: true, + disableSync: true, + disableAutoCreate: true, + disableFolderNote: false, + enableCollapsing: false, + position: 0, + excludeFromFolderOverview: false, + string: "", + hideInSettings: false, + detached: false, + showFolderNote: false + }, + hideCollapsingIcon: false, + hideCollapsingIconForEmptyFolders: false, + tabManagerEnabled: true, + ignoreAttachmentFolder: true, + deleteFilesAction: "trash", + openSidebar: { + mobile: false, + desktop: true + }, + highlightFolder: true, + persistentSettingsTab: { + afterRestart: true, + afterChangingTab: true + }, + firstTimeInsertOverview: true, + fvGlobalSettings: { + autoUpdateLinks: false + }, + hideFolderNoteNameInPath: false +}; +var SettingsTab2 = class extends import_obsidian39.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.TABS = { + GENERAL: { + name: "General", + id: "general" + }, + FOLDER_OVERVIEW: { + name: "Folder overview", + id: "folder_overview" + }, + EXCLUDE_FOLDERS: { + name: "Exclude folders", + id: "exclude_folders" + }, + FILE_EXPLORER: { + name: "File explorer", + id: "file_explorer" + }, + PATH: { + name: "Path", + id: "path" + } + }; + } + renderSettingsPage(tabId) { + this.settingsPage.empty(); + switch (tabId.toLocaleLowerCase()) { + case this.TABS.GENERAL.id: + void renderGeneral(this); + break; + case this.TABS.FOLDER_OVERVIEW.id: + void renderFolderOverview(this); + break; + case this.TABS.EXCLUDE_FOLDERS.id: + void renderExcludeFolders(this); + break; + case this.TABS.FILE_EXPLORER.id: + void renderFileExplorer(this); + break; + case this.TABS.PATH.id: + void renderPath(this); + break; + } + } + display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab) { + var _a; + plugin = (_a = this == null ? void 0 : this.plugin) != null ? _a : plugin; + if (plugin) { + plugin.settingsOpened = true; + } + settingsTab = this != null ? this : settingsTab; + const { containerEl } = settingsTab; + if (plugin && !plugin.settings.persistentSettingsTab.afterChangingTab) { + plugin.settings.settingsTab = this.TABS.GENERAL.id; + } + containerEl.empty(); + const tabBar = containerEl.createEl("nav", { cls: "fn-settings-tab-bar" }); + for (const [tabId, tabInfo] of Object.entries(settingsTab.TABS)) { + const tabEl = tabBar.createEl("div", { cls: "fn-settings-tab" }); + tabEl.createEl("div", { cls: "fn-settings-tab-name", text: tabInfo.name }); + if (plugin && plugin.settings.settingsTab.toLocaleLowerCase() === tabId.toLocaleLowerCase()) { + tabEl.addClass("fn-settings-tab-active"); + } + tabEl.addEventListener("click", () => { + for (const child of Array.from(tabBar.children)) { + child.classList.remove("fn-settings-tab-active"); + if (!plugin) { + return; + } + plugin.settings.settingsTab = tabId.toLocaleLowerCase(); + void plugin.saveSettings(); + } + tabEl.addClass("fn-settings-tab-active"); + if (!settingsTab) { + return; + } + settingsTab.renderSettingsPage(tabId); + }); + } + settingsTab.settingsPage = containerEl.createDiv({ cls: "fn-settings-page" }); + if (plugin) { + if (plugin.settings.persistentSettingsTab) { + settingsTab.renderSettingsPage(plugin.settings.settingsTab); + } else { + settingsTab.renderSettingsPage(this.TABS.GENERAL.id); + } + } + } + renameFolderNotes() { + var _a, _b, _c; + new import_obsidian39.Notice("Starting to update folder notes..."); + const oldTemplate = (_a = this.plugin.settings.oldFolderNoteName) != null ? _a : "{{folder_name}}"; + for (const folder of this.app.vault.getAllLoadedFiles()) { + if (folder instanceof import_obsidian39.TFolder) { + const folderNote = getFolderNote(this.plugin, folder.path, void 0, void 0, oldTemplate); + if (!(folderNote instanceof import_obsidian39.TFile)) { + continue; + } + const folderName = (_b = extractFolderName(oldTemplate, folderNote.basename)) != null ? _b : ""; + const newFolderNoteName = this.plugin.settings.folderNoteName.replace("{{folder_name}}", folderName); + let newPath = ""; + if (this.plugin.settings.storageLocation === "parentFolder") { + if (getFolderPathFromString(folder.path).trim() === "/") { + newPath = `${newFolderNoteName}.${folderNote.extension}`; + } else { + newPath = `${(_c = folderNote.parent) == null ? void 0 : _c.path}/${newFolderNoteName}.${folderNote.extension}`; + } + } else if (this.plugin.settings.storageLocation === "insideFolder") { + newPath = `${folder.path}/${newFolderNoteName}.${folderNote.extension}`; + } + void this.app.fileManager.renameFile(folderNote, newPath); + } + } + this.plugin.settings.oldFolderNoteName = this.plugin.settings.folderNoteName; + void this.plugin.saveSettings(); + new import_obsidian39.Notice("Finished updating folder notes"); + } + switchStorageLocation(oldMethod) { + new import_obsidian39.Notice("Starting to switch storage location..."); + this.app.vault.getAllLoadedFiles().forEach((file) => { + if (file instanceof import_obsidian39.TFolder) { + const folderNote = getFolderNote(this.plugin, file.path, oldMethod); + if (folderNote instanceof import_obsidian39.TFile) { + if (this.plugin.settings.storageLocation === "parentFolder") { + let newPath = ""; + if (getFolderPathFromString(file.path).trim() === "") { + newPath = `${folderNote.name}`; + } else { + newPath = `${getFolderPathFromString(file.path)}/${folderNote.name}`; + } + void this.plugin.app.fileManager.renameFile(folderNote, newPath); + } else if (this.plugin.settings.storageLocation === "insideFolder") { + if (getFolderPathFromString(folderNote.path) === file.path) { + return; + } + const newPath = `${file.path}/${folderNote.name}`; + void this.plugin.app.fileManager.renameFile(folderNote, newPath); + } + } + } + }); + new import_obsidian39.Notice("Finished switching storage location"); + } + onClose() { + this.plugin.settingsOpened = false; + } +}; + +// src/Commands.ts +var import_obsidian40 = require("obsidian"); +var Commands = class { + constructor(app, plugin) { + this.plugin = plugin; + this.app = app; + } + registerCommands() { + this.editorCommands(); + this.fileCommands(); + this.regularCommands(); + } + regularCommands() { + this.plugin.addCommand({ + id: "turn-into-folder-note", + name: "Use this file as the folder note for its parent folder", + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!(file instanceof import_obsidian40.TFile)) + return false; + const folder = file.parent; + if (!folder || !(folder instanceof import_obsidian40.TFolder)) + return false; + if (folder.path === "" || folder.path === "/") + return false; + const folderNote = getFolderNote(this.plugin, folder.path); + if (folderNote instanceof import_obsidian40.TFile && folderNote === file) + return false; + if (checking) + return true; + void turnIntoFolderNote(this.plugin, file, folder, folderNote); + } + }); + this.plugin.addCommand({ + id: "create-folder-note", + name: "Make a folder with this file as its folder note", + callback: async () => { + var _a, _b, _c; + const file = this.app.workspace.getActiveFile(); + if (!(file instanceof import_obsidian40.TFile)) + return; + let newPath = ((_a = file.parent) == null ? void 0 : _a.path) + "/" + file.basename; + if (((_b = file.parent) == null ? void 0 : _b.path) === "" || ((_c = file.parent) == null ? void 0 : _c.path) === "/") { + newPath = file.basename; + } + if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { + return new import_obsidian40.Notice("Folder already exists"); + } + const automaticallyCreateFolderNote = this.plugin.settings.autoCreate; + this.plugin.settings.autoCreate = false; + void this.plugin.saveSettings(); + await this.plugin.app.vault.createFolder(newPath); + const folder = this.plugin.app.vault.getAbstractFileByPath(newPath); + if (!(folder instanceof import_obsidian40.TFolder)) + return; + await createFolderNote(this.plugin, folder.path, true, "." + file.extension, false, file); + this.plugin.settings.autoCreate = automaticallyCreateFolderNote; + void this.plugin.saveSettings(); + } + }); + this.plugin.addCommand({ + id: "create-folder-note-for-current-folder", + name: "Create Markdown folder note for this folder", + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!(file instanceof import_obsidian40.TFile)) + return false; + const folder = file.parent; + if (!(folder instanceof import_obsidian40.TFolder)) + return false; + if (folder.path === "" || folder.path === "/") + return false; + if (checking) + return true; + void createFolderNote(this.plugin, folder.path, true, ".md", false); + } + }); + this.plugin.settings.supportedFileTypes.forEach((fileType) => { + if (fileType === "md") + return; + this.plugin.addCommand({ + id: `create-${fileType}-folder-note-for-current-folder`, + name: `Create ${fileType} folder note for this folder`, + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!(file instanceof import_obsidian40.TFile)) + return false; + const folder = file.parent; + if (!(folder instanceof import_obsidian40.TFolder)) + return false; + if (folder.path === "" || folder.path === "/") + return false; + if (checking) + return true; + void createFolderNote(this.plugin, folder.path, true, "." + fileType, false); + } + }); + }); + this.plugin.settings.supportedFileTypes.forEach((fileType) => { + const type = fileType === "md" ? "markdown" : fileType; + this.plugin.addCommand({ + id: `create-${type}-folder-note-for-active-file-explorer-folder`, + name: `Create ${type} folder note for current active folder in file explorer`, + checkCallback: (checking) => { + const folder = getFileExplorerActiveFolder(this.plugin); + if (!folder) + return false; + const folderNote = getFolderNote(this.plugin, folder.path); + if (folderNote instanceof import_obsidian40.TFile) + return false; + if (checking) + return true; + const ext = "." + fileType; + const { path } = folder; + void createFolderNote(this.plugin, path, true, ext, false); + } + }); + }); + this.plugin.addCommand({ + id: "delete-folder-note-for-current-folder", + name: "Delete this folder's linked note", + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!(file instanceof import_obsidian40.TFile)) + return false; + const folder = file.parent; + if (!(folder instanceof import_obsidian40.TFolder)) + return false; + const folderNote = getFolderNote(this.plugin, folder.path); + if (!(folderNote instanceof import_obsidian40.TFile)) + return false; + if (checking) + return true; + void deleteFolderNote(this.plugin, folderNote, true); + } + }); + this.plugin.addCommand({ + id: "delete-folder-note-of-active-file-explorer-folder", + name: "Delete folder note of current active folder in file explorer", + checkCallback: (checking) => { + const folder = getFileExplorerActiveFolder(this.plugin); + if (!folder) + return false; + const folderNote = getFolderNote(this.plugin, folder.path); + if (!(folderNote instanceof import_obsidian40.TFile)) + return false; + if (checking) + return true; + void deleteFolderNote(this.plugin, folderNote, true); + } + }); + this.plugin.addCommand({ + id: "open-folder-note-for-current-folder", + name: "Open this folder's linked note", + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!(file instanceof import_obsidian40.TFile)) + return false; + const folder = file.parent; + if (!(folder instanceof import_obsidian40.TFolder)) + return false; + const folderNote = getFolderNote(this.plugin, folder.path); + if (!(folderNote instanceof import_obsidian40.TFile)) + return false; + if (checking) + return true; + void openFolderNote(this.plugin, folderNote); + } + }); + this.plugin.addCommand({ + id: "open-folder-note-of-active-file-explorer-folder", + name: "Open folder note of current active folder in file explorer", + checkCallback: (checking) => { + const folder = getFileExplorerActiveFolder(this.plugin); + if (!folder) + return false; + const folderNote = getFolderNote(this.plugin, folder.path); + if (!(folderNote instanceof import_obsidian40.TFile)) + return false; + if (checking) + return true; + void openFolderNote(this.plugin, folderNote); + } + }); + this.plugin.addCommand({ + id: "create-folder-note-from-selected-text", + name: "Create folder note from selection", + editorCheckCallback: (checking, editor, view) => { + const text = editor.getSelection().trim(); + const { file } = view; + if (!(file instanceof import_obsidian40.TFile)) + return false; + if (text && text.trim() !== "") { + if (checking) { + return true; + } + const blacklist = ["*", "\\", '"', "/", "<", ">", "?", "|", ":"]; + for (const char of blacklist) { + if (text.includes(char)) { + new import_obsidian40.Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?'); + return false; + } + } + if (text.endsWith(".")) { + new import_obsidian40.Notice("File name cannot end with a dot"); + return; + } + let folder; + const folderPath = getFolderPathFromString(file.path); + if (folderPath === "") { + folder = this.plugin.app.vault.getAbstractFileByPath(text); + if (folder instanceof import_obsidian40.TFolder) { + new import_obsidian40.Notice("Folder note already exists"); + return false; + } + void this.plugin.app.vault.createFolder(text); + void createFolderNote(this.plugin, text, false); + } else { + const folderFullPath = folderPath + "/" + text; + folder = this.plugin.app.vault.getAbstractFileByPath(folderFullPath); + if (folder instanceof import_obsidian40.TFolder) { + new import_obsidian40.Notice("Folder note already exists"); + return false; + } + if (this.plugin.settings.storageLocation === "parentFolder") { + if (this.app.vault.getAbstractFileByPath(folderPath + "/" + text + this.plugin.settings.folderNoteType)) { + new import_obsidian40.Notice("File already exists"); + return false; + } + } + void this.plugin.app.vault.createFolder(folderPath + "/" + text); + void createFolderNote(this.plugin, folderPath + "/" + text, false); + } + const { folderNoteName } = this.plugin.settings; + const fileName = folderNoteName.replace("{{folder_name}}", text); + if (fileName !== text) { + editor.replaceSelection(`[[${fileName}]]`); + } else { + editor.replaceSelection(`[[${fileName}|${text}]]`); + } + return true; + } + return false; + } + }); + } + fileCommands() { + this.plugin.registerEvent(this.app.workspace.on("file-menu", (menu, file) => { + var _a, _b, _c; + let folder = file.parent; + if (file instanceof import_obsidian40.TFile) { + if (this.plugin.settings.storageLocation === "insideFolder") { + folder = file.parent; + } else { + const { folderNoteName } = this.plugin.settings; + const fileName = extractFolderName(folderNoteName, file.basename); + if (fileName) { + if (((_a = file.parent) == null ? void 0 : _a.path) === "" || ((_b = file.parent) == null ? void 0 : _b.path) === "/") { + folder = this.plugin.app.vault.getAbstractFileByPath(fileName); + } else { + folder = this.plugin.app.vault.getAbstractFileByPath(((_c = file.parent) == null ? void 0 : _c.path) + "/" + fileName); + } + } + } + if (folder instanceof import_obsidian40.TFolder) { + const folderNote = getFolderNote(this.plugin, folder.path); + const excludedFolder = getExcludedFolder(this.plugin, folder.path, true); + if ((folderNote == null ? void 0 : folderNote.path) === file.path && !(excludedFolder == null ? void 0 : excludedFolder.detached)) { + return; + } + } else if (file.parent instanceof import_obsidian40.TFolder) { + folder = file.parent; + } + } + const addFolderNoteActions = (folderMenu) => { + if (file instanceof import_obsidian40.TFile) { + folderMenu.addItem((item) => { + item.setTitle("Create folder note"); + item.setIcon("edit"); + item.onClick(async () => { + if (!folder) + return; + let newPath = folder.path + "/" + file.basename; + if (folder.path === "" || folder.path === "/") { + newPath = file.basename; + } + if (this.plugin.app.vault.getAbstractFileByPath(newPath)) { + return new import_obsidian40.Notice("Folder already exists"); + } + const automaticallyCreateFolderNote = this.plugin.settings.autoCreate; + this.plugin.settings.autoCreate = false; + void this.plugin.saveSettings(); + await this.plugin.app.vault.createFolder(newPath); + const newFolder = this.plugin.app.vault.getAbstractFileByPath(newPath); + if (!(newFolder instanceof import_obsidian40.TFolder)) + return; + await createFolderNote(this.plugin, newFolder.path, true, "." + file.extension, false, file); + this.plugin.settings.autoCreate = automaticallyCreateFolderNote; + void this.plugin.saveSettings(); + }); + }); + if (getFolderPathFromString(file.path) === "") + return; + if (!(folder instanceof import_obsidian40.TFolder)) + return; + if (folder.path === "" || folder.path === "/") + return; + folderMenu.addItem((item) => { + item.setTitle(`Turn into folder note for ${folder == null ? void 0 : folder.name}`); + item.setIcon("edit"); + item.onClick(() => { + if (!folder || !(folder instanceof import_obsidian40.TFolder)) + return; + const folderNote2 = getFolderNote(this.plugin, folder.path); + void turnIntoFolderNote(this.plugin, file, folder, folderNote2); + }); + }); + } + if (!(file instanceof import_obsidian40.TFolder)) + return; + const excludedFolder = getExcludedFolder(this.plugin, file.path, false); + const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path); + if (excludedFolder && !excludedFolder.hideInSettings) { + folderMenu.addItem((item) => { + item.setTitle("Remove folder from excluded folders"); + item.setIcon("trash"); + item.onClick(() => { + this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter((excluded) => excluded.path !== file.path || excluded.detached); + void this.plugin.saveSettings(true); + new import_obsidian40.Notice("Successfully removed folder from excluded folders"); + }); + }); + return; + } + if (detachedExcludedFolder) { + folderMenu.addItem((item) => { + item.setTitle("Remove folder from detached folders"); + item.setIcon("trash"); + item.onClick(() => { + void deleteExcludedFolder(this.plugin, detachedExcludedFolder); + }); + }); + } + if (detachedExcludedFolder) { + return; + } + folderMenu.addItem((item) => { + item.setTitle("Exclude folder from folder notes"); + item.setIcon("x-circle"); + item.onClick(() => { + const newExcludedFolder = new ExcludedFolder(file.path, this.plugin.settings.excludeFolders.length, void 0, this.plugin); + this.plugin.settings.excludeFolders.push(newExcludedFolder); + void this.plugin.saveSettings(true); + new import_obsidian40.Notice("Successfully excluded folder from folder notes"); + }); + }); + if (!(file instanceof import_obsidian40.TFolder)) + return; + const folderNote = getFolderNote(this.plugin, file.path); + if (folderNote instanceof import_obsidian40.TFile && !detachedExcludedFolder) { + folderMenu.addItem((item) => { + item.setTitle("Delete folder note"); + item.setIcon("trash"); + item.onClick(() => { + void deleteFolderNote(this.plugin, folderNote, true); + }); + }); + folderMenu.addItem((item) => { + item.setTitle("Open folder note"); + item.setIcon("chevron-right-square"); + item.onClick(() => { + void openFolderNote(this.plugin, folderNote); + }); + }); + folderMenu.addItem((item) => { + item.setTitle("Detach folder note"); + item.setIcon("unlink"); + item.onClick(() => { + detachFolderNote(this.plugin, folderNote); + }); + }); + folderMenu.addItem((item) => { + item.setTitle("Copy Obsidian URL"); + item.setIcon("link"); + item.onClick(() => { + this.app.copyObsidianUrl(folderNote); + }); + }); + if (this.plugin.settings.hideFolderNote) { + if (excludedFolder == null ? void 0 : excludedFolder.showFolderNote) { + folderMenu.addItem((item) => { + item.setTitle("Hide folder note in explorer"); + item.setIcon("eye-off"); + item.onClick(() => { + hideFolderNoteInFileExplorer(file.path, this.plugin); + }); + }); + } else { + folderMenu.addItem((item) => { + item.setTitle("Show folder note in explorer"); + item.setIcon("eye"); + item.onClick(() => { + showFolderNoteInFileExplorer(file.path, this.plugin); + }); + }); + } + } + } else { + folderMenu.addItem((item) => { + item.setTitle("Create Markdown folder note"); + item.setIcon("edit"); + item.onClick(() => { + void createFolderNote(this.plugin, file.path, true, ".md"); + }); + }); + this.plugin.settings.supportedFileTypes.forEach((fileType) => { + if (fileType === "md") + return; + folderMenu.addItem((item) => { + item.setTitle(`Create ${fileType} folder note`); + item.setIcon("edit"); + item.onClick(() => { + void createFolderNote(this.plugin, file.path, true, "." + fileType); + }); + }); + }); + } + }; + if (import_obsidian40.Platform.isDesktop && !import_obsidian40.Platform.isTablet && this.plugin.settings.useSubmenus) { + menu.addItem(async (item) => { + item.setTitle("Folder note commands").setIcon("folder-edit"); + let subMenu = item.setSubmenu(); + addFolderNoteActions(subMenu); + }); + } else { + addFolderNoteActions(menu); + } + })); + } + editorCommands() { + this.plugin.registerEvent(this.plugin.app.workspace.on("editor-menu", (menu, editor, view) => { + const text = editor.getSelection().trim(); + if (!text || text.trim() === "") + return; + menu.addItem((item) => { + item.setTitle("Create folder note").setIcon("edit").onClick(() => { + const { file } = view; + if (!(file instanceof import_obsidian40.TFile)) + return; + const blacklist = ["*", "\\", '"', "/", "<", ">", "?", "|", ":"]; + for (const char of blacklist) { + if (text.includes(char)) { + new import_obsidian40.Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?'); + return; + } + } + if (text.endsWith(".")) { + new import_obsidian40.Notice("File name cannot end with a dot"); + return; + } + let folder; + const folderPath = getFolderPathFromString(file.path); + const { folderNoteName } = this.plugin.settings; + const fileName = folderNoteName.replace("{{folder_name}}", text); + if (folderPath === "") { + folder = this.plugin.app.vault.getAbstractFileByPath(text); + if (folder instanceof import_obsidian40.TFolder) { + return new import_obsidian40.Notice("Folder note already exists"); + } + void this.plugin.app.vault.createFolder(text); + void createFolderNote(this.plugin, text, false); + } else { + folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + "/" + text); + if (folder instanceof import_obsidian40.TFolder) { + return new import_obsidian40.Notice("Folder note already exists"); + } + if (this.plugin.settings.storageLocation === "parentFolder") { + if (this.app.vault.getAbstractFileByPath(folderPath + "/" + fileName + this.plugin.settings.folderNoteType)) { + return new import_obsidian40.Notice("File already exists"); + } + } + void this.plugin.app.vault.createFolder(folderPath + "/" + text); + void createFolderNote(this.plugin, folderPath + "/" + text, false); + } + if (fileName !== text) { + editor.replaceSelection(`[[${fileName}]]`); + } else { + editor.replaceSelection(`[[${fileName}|${text}]]`); + } + }); + }); + })); + } +}; + +// src/events/MutationObserver.ts +var import_obsidian42 = require("obsidian"); + +// src/events/handleClick.ts +var import_obsidian41 = require("obsidian"); +async function handleViewHeaderClick(event, plugin) { + if (!plugin.settings.openFolderNoteOnClickInPath) + return; + event.stopImmediatePropagation(); + event.preventDefault(); + event.stopPropagation(); + if (!(event.target instanceof HTMLElement)) + return; + const folderPath = event.target.getAttribute("data-path"); + if (!folderPath) { + return; + } + if (await isExcludedFolder(event, plugin, folderPath)) + return; + const folderNote = getFolderNote(plugin, folderPath); + if (folderNote) { + await openFolderNote(plugin, folderNote, event).then(() => handleFolderNoteReveal(plugin, folderNote)); + return; + } else if (event.altKey || import_obsidian41.Keymap.isModEvent(event) === "tab") { + if (await handleFolderNoteCreation(event, plugin, folderPath)) + return; + } + event.target.onclick = null; + event.target.click(); +} +async function isExcludedFolder(event, plugin, folderPath) { + const excludedFolder = getExcludedFolder(plugin, folderPath, true); + if (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) { + event.target.onclick = null; + event.target.click(); + return true; + } else if ((excludedFolder == null ? void 0 : excludedFolder.enableCollapsing) || plugin.settings.enableCollapsing) { + event.target.onclick = null; + event.target.click(); + } + return false; +} +async function handleFolderNoteReveal(plugin, folderNote) { + const fileExplorerPlugin = plugin.app.internalPlugins.getEnabledPluginById("file-explorer"); + if (fileExplorerPlugin && import_obsidian41.Platform.isMobile && plugin.settings.openSidebar.mobile) { + const OPEN_SIDEBAR_DELAY = 200; + window.setTimeout(() => { + fileExplorerPlugin.revealInFolder(folderNote); + }, OPEN_SIDEBAR_DELAY); + } else if (fileExplorerPlugin && import_obsidian41.Platform.isDesktop && plugin.settings.openSidebar.desktop) { + fileExplorerPlugin.revealInFolder(folderNote); + } +} +async function handleFolderNoteCreation(event, plugin, folderPath) { + const usedCtrl = import_obsidian41.Platform.isMacOS ? event.metaKey : event.ctrlKey; + if (plugin.settings.altKey && event.altKey || usedCtrl && import_obsidian41.Keymap.isModEvent(event) === "tab") { + await createFolderNote(plugin, folderPath, true, void 0, true); + addCSSClassToFileExplorerEl(folderPath, "has-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(folderPath, "has-not-folder-note", false, plugin); + return true; + } + return false; +} + +// src/events/MutationObserver.ts +var fileExplorerMutationObserver = null; +function registerFileExplorerObserver(plugin) { + plugin.app.workspace.onLayoutReady(() => { + initializeFolderNoteFeatures(plugin); + initializeBreadcrumbs(plugin); + }); + plugin.registerEvent(plugin.app.workspace.on("layout-change", () => { + var _a; + initializeFolderNoteFeatures(plugin); + const activeLeaf = (_a = plugin.app.workspace.getActiveFileView()) == null ? void 0 : _a.containerEl; + if (!activeLeaf) + return; + const titleContainer = activeLeaf.querySelector(".view-header-title-container"); + if (!(titleContainer instanceof HTMLElement)) + return; + updateFolderNamesInPath(plugin, titleContainer); + })); +} +function unregisterFileExplorerObserver() { + if (fileExplorerMutationObserver) { + fileExplorerMutationObserver.disconnect(); + fileExplorerMutationObserver = null; + } +} +function initializeFolderNoteFeatures(plugin) { + initializeAllFolderTitles(plugin); + observeFolderTitleMutations(plugin); +} +function initializeBreadcrumbs(plugin) { + const titleContainers = activeDocument.querySelectorAll(".view-header-title-container"); + if (!titleContainers.length) + return; + titleContainers.forEach((container) => { + if (!container.instanceOf(HTMLElement)) + return; + scheduleIdle(() => updateFolderNamesInPath(plugin, container), { timeout: 1e3 }); + }); +} +function observeFolderTitleMutations(plugin) { + if (fileExplorerMutationObserver) { + fileExplorerMutationObserver.disconnect(); + } + fileExplorerMutationObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of Array.from(mutation.addedNodes)) { + if (!node.instanceOf(HTMLElement)) + continue; + processAddedFolders(node, plugin); + } + } + }); + fileExplorerMutationObserver.observe(document, { childList: true, subtree: true }); +} +function initializeAllFolderTitles(plugin) { + const allTitles = activeDocument.querySelectorAll(".nav-folder-title-content"); + for (const title of Array.from(allTitles)) { + const folderTitle = title; + const folderEl = folderTitle.closest(".nav-folder-title"); + if (!folderEl) + continue; + const folderPath = folderEl.getAttribute("data-path") || ""; + setupFolderTitle(folderTitle, plugin, folderPath); + } +} +function processAddedFolders(node, plugin) { + const titles = []; + if (node.matches(".nav-folder-title-content")) { + titles.push(node); + } + node.querySelectorAll(".nav-folder-title-content").forEach((el) => { + titles.push(el); + }); + titles.forEach((folderTitle) => { + const folderEl = folderTitle.closest(".nav-folder-title"); + const folderPath = (folderEl == null ? void 0 : folderEl.getAttribute("data-path")) || ""; + const RETRY_TIMEOUT = 50; + if (!folderEl || !folderPath) { + window.setTimeout(() => { + const retryFolderEl = folderTitle.closest(".nav-folder-title"); + const retryFolderPath = (retryFolderEl == null ? void 0 : retryFolderEl.getAttribute("data-path")) || ""; + if (retryFolderEl && retryFolderPath) { + setupFolderTitle(folderTitle, plugin, retryFolderPath); + } + }, RETRY_TIMEOUT); + return; + } + setupFolderTitle(folderTitle, plugin, folderPath); + }); +} +async function setupFolderTitle(folderTitle, plugin, folderPath) { + var _a; + if (folderTitle.dataset.initialized === "true") + return; + if (!folderPath) + return; + folderTitle.dataset.initialized = "true"; + await updateCSSClassesForFolder(folderPath, plugin); + if (plugin.settings.frontMatterTitle.enabled) { + (_a = plugin.fmtpHandler) == null ? void 0 : _a.fmptUpdateFolderName({ id: "", result: false, path: folderPath, pathOnly: false }, false); + } + if (import_obsidian42.Platform.isMobile && plugin.settings.disableOpenFolderNoteOnClick) + return; + plugin.registerDomEvent(folderTitle, "pointerover", (event) => { + plugin.hoveredElement = folderTitle; + plugin.mouseEvent = event; + if (!import_obsidian42.Keymap.isModEvent(event)) + return; + if (!(event.target instanceof HTMLElement)) + return; + const folderNote = getFolderNote(plugin, folderPath); + if (!folderNote) + return; + plugin.app.workspace.trigger("hover-link", { + event, + source: "preview", + hoverParent: { file: folderNote }, + targetEl: event.target, + linktext: folderNote.basename, + sourcePath: folderNote.path + }); + plugin.hoverLinkTriggered = true; + }); + plugin.registerDomEvent(folderTitle, "pointerout", () => { + plugin.hoveredElement = null; + plugin.mouseEvent = null; + plugin.hoverLinkTriggered = false; + }); +} +async function updateFolderNamesInPath(plugin, titleContainer) { + var _a, _b, _c, _d, _e, _f, _g; + const titleParent = titleContainer.querySelector(".view-header-title-parent"); + let path = ""; + const TRAILING_SLASH_LENGTH = 1; + if ((titleParent == null ? void 0 : titleParent.childNodes.length) === 0) { + titleContainer.classList.remove("hide-folder-note-title-in-path"); + } + for (const breadcrumb of Array.from((_a = titleParent == null ? void 0 : titleParent.childNodes) != null ? _a : [])) { + if (!(breadcrumb instanceof HTMLElement)) + continue; + if (breadcrumb.classList.contains("view-header-breadcrumb-separator")) { + if (breadcrumb.nextSibling === null) { + breadcrumb.classList.add("is-last-separator"); + } + continue; + } + path += (_b = breadcrumb.getAttribute("old-name")) != null ? _b : breadcrumb.innerText.trim(); + path += "/"; + const folderPath = path.slice(0, -TRAILING_SLASH_LENGTH); + const excludedFolder = getExcludedFolder(plugin, folderPath, true); + if (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) + return; + const folderNote = getFolderNote(plugin, folderPath); + const viewHeaderTitle = titleContainer.querySelector(".view-header-title"); + if (viewHeaderTitle && folderNote) { + const filePath = path + viewHeaderTitle.innerText.trim() + ".md"; + const file = plugin.app.vault.getAbstractFileByPath(filePath); + const folder = getFolderNoteFolder(plugin, folderNote, (_c = file == null ? void 0 : file.name) != null ? _c : ""); + if (folder && file && file.path === (folderNote == null ? void 0 : folderNote.path) && ((_d = file.parent) == null ? void 0 : _d.path) !== "/") { + (_e = viewHeaderTitle.parentElement) == null ? void 0 : _e.classList.add("hide-folder-note-title-in-path"); + viewHeaderTitle.classList.add("path-is-folder-note"); + } else { + (_f = viewHeaderTitle.parentElement) == null ? void 0 : _f.classList.remove("hide-folder-note-title-in-path"); + viewHeaderTitle.classList.remove("path-is-folder-note"); + } + } + if (!folderNote) + return; + if (folderNote) + breadcrumb.classList.add("has-folder-note"); + breadcrumb == null ? void 0 : breadcrumb.setAttribute("data-path", path.slice(0, -TRAILING_SLASH_LENGTH)); + if (!breadcrumb.onclick) { + breadcrumb.addEventListener("click", (e) => { + handleViewHeaderClick(e, plugin); + }, { capture: true }); + } + if (plugin.settings.frontMatterTitle.enabled) { + (_g = plugin.fmtpHandler) == null ? void 0 : _g.fmptUpdateFolderName({ id: "", result: false, path: folderPath, pathOnly: true, breadcrumb }, true); + } + } +} +function scheduleIdle(callback, options) { + const DEFAULT_IDLE_TIMEOUT = 200; + if ("requestIdleCallback" in window) { + const windowWithIdle = window; + windowWithIdle.requestIdleCallback(callback, options); + } else { + globalThis.setTimeout(callback, (options == null ? void 0 : options.timeout) || DEFAULT_IDLE_TIMEOUT); + } +} + +// src/events/handleRename.ts +var import_obsidian43 = require("obsidian"); +function handleRename(file, oldPath, plugin) { + let folder = file.parent; + const oldFolder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(oldPath)); + if (folder instanceof import_obsidian43.TFolder) { + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, "only-has-folder-note", true, plugin); + } else { + removeCSSClassFromFileExplorerEL(folder.path, "only-has-folder-note", true, plugin); + } + } + if (oldFolder instanceof import_obsidian43.TFolder) { + if (plugin.isEmptyFolderNoteFolder(oldFolder) && getFolderNote(plugin, oldFolder.path)) { + addCSSClassToFileExplorerEl(oldFolder.path, "only-has-folder-note", true, plugin); + } else { + removeCSSClassFromFileExplorerEL(oldFolder.path, "only-has-folder-note", true, plugin); + } + } + if (file instanceof import_obsidian43.TFolder) { + folder = file; + plugin.tabManager.updateTab(folder.path); + updateExcludedFolderPath(folder, oldPath, plugin); + if (isFolderRename(folder, oldPath)) { + handleFolderRename(folder, oldPath, plugin); + return; + } + return handleFolderMove(folder, oldPath, plugin); + } else if (file instanceof import_obsidian43.TFile) { + if (isFileRename(file, oldPath)) { + handleFileRename(file, oldPath, plugin); + return; + } + handleFileMove(file, oldPath, plugin); + return; + } +} +function isFileRename(file, oldPath) { + var _a; + const oldFolderPath = getFolderPathFromString(oldPath); + const newFolderPath = (_a = file.parent) == null ? void 0 : _a.path; + const oldName = getFileNameFromPathString(oldPath); + const newName = file.name; + return oldFolderPath === newFolderPath && oldName !== newName; +} +function isFolderRename(folder, oldPath) { + var _a; + const oldName = getFileNameFromPathString(oldPath); + const newName = folder.name; + const oldParent = getFolderPathFromString(oldPath); + const newParent = (_a = folder.parent) == null ? void 0 : _a.path; + return oldParent === newParent && oldName !== newName; +} +function handleFolderMove(file, oldPath, plugin) { + var _a; + if (plugin.settings.storageLocation === "insideFolder") { + return; + } + if (!plugin.settings.syncMove) { + return; + } + const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation); + if (!(file instanceof import_obsidian43.TFolder) || !folderNote) + return; + const newFolder = plugin.app.vault.getAbstractFileByPath(file.path); + if (!(newFolder instanceof import_obsidian43.TFolder)) + return; + let newPath = folderNote.path; + if (newFolder.path === "/") { + newPath = folderNote.name; + } else { + newPath = `${(_a = newFolder.parent) == null ? void 0 : _a.path}/${folderNote.name}`; + } + plugin.app.fileManager.renameFile(folderNote, newPath); +} +async function handleFileMove(file, oldPath, plugin) { + var _a; + const { folderName, oldFileName, newFolder, excludedFolder, oldFolder, folderNote } = getArgs2(plugin, file, oldPath); + const isFolderNoteInNewFolder = folderName === (newFolder == null ? void 0 : newFolder.name); + const fileMovedFromOldFolderNote = oldFolder && oldFolder.name === oldFileName && (newFolder == null ? void 0 : newFolder.path) !== oldFolder.path; + const isFileWithExistingNote = folderName === (newFolder == null ? void 0 : newFolder.name) && folderNote; + if (isFileWithExistingNote) { + renameExistingFolderNote(file, oldPath, plugin, excludedFolder, oldFolder); + } else if (isFolderNoteInNewFolder) { + if (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) { + return; + } + markFileAsFolderNote(file, plugin); + if (newFolder instanceof import_obsidian43.TFolder) { + markFolderWithFolderNoteClasses(newFolder, plugin); + if (((_a = plugin.app.workspace.getActiveFile()) == null ? void 0 : _a.path) === file.path) { + removeActiveFolder(plugin); + setActiveFolder(newFolder.path, plugin); + } + } + if (oldFolder instanceof import_obsidian43.TFolder) { + hideFolderNoteInFileExplorer(oldFolder.path, plugin); + unmarkFolderAsFolderNote(oldFolder, plugin); + } + } else if (fileMovedFromOldFolderNote) { + unmarkFileAsFolderNote(file, plugin); + if (oldFolder instanceof import_obsidian43.TFolder) { + removeActiveFolder(plugin); + hideFolderNoteInFileExplorer(oldFolder.path, plugin); + unmarkFolderAsFolderNote(oldFolder, plugin); + } + } +} +function getArgs2(plugin, file, oldPath) { + const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename; + const oldFileName = removeExtension(getFileNameFromPathString(oldPath)); + const newFolder = getFolderNoteFolder(plugin, file, file.basename); + let excludedFolder = getExcludedFolder(plugin, (newFolder == null ? void 0 : newFolder.path) || "", true); + const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName); + const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); + return { + folderName, + oldFileName, + newFolder, + excludedFolder, + oldFolder, + folderNote + }; +} +function renameExistingFolderNote(file, oldPath, plugin, excludedFolder, oldFolder) { + let excludedFolderExisted = true; + let disabledSync = false; + if (!excludedFolder) { + excludedFolderExisted = false; + excludedFolder = new ExcludedFolder((oldFolder == null ? void 0 : oldFolder.path) || "", plugin.settings.excludeFolders.length, void 0, plugin); + addExcludedFolder(plugin, excludedFolder); + } else if (!excludedFolder.disableSync) { + disabledSync = false; + excludedFolder.disableSync = true; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + plugin.app.fileManager.renameFile(file, oldPath).then(() => { + if (!excludedFolder) { + return; + } + if (!excludedFolderExisted) { + deleteExcludedFolder(plugin, excludedFolder); + } else if (!disabledSync) { + excludedFolder.disableSync = false; + updateExcludedFolder(plugin, excludedFolder, excludedFolder); + } + }); +} +async function handleFolderRename(file, oldPath, plugin) { + const fileName = plugin.settings.folderNoteName.replace("{{folder_name}}", file.name); + const oldFileName = plugin.settings.folderNoteName.replace("{{folder_name}}", getFileNameFromPathString(oldPath)); + if (fileName === oldFileName) { + return; + } + const folderNote = getFolderNote(plugin, oldPath); + if (!(folderNote instanceof import_obsidian43.TFile)) + return; + const excludedFolder = getExcludedFolder(plugin, file.path, true); + if ((excludedFolder == null ? void 0 : excludedFolder.disableSync) && !folderNote) { + return removeCSSClassFromFileExplorerEL(file.path, "has-folder-note", false, plugin); + } + if (!plugin.settings.syncFolderName) { + return; + } + let newPath = ""; + if (plugin.settings.storageLocation === "parentFolder") { + const parentFolderPath = getFolderPathFromString(file.path); + const oldParentFolderPath = getFolderPathFromString(oldPath); + if (parentFolderPath !== oldParentFolderPath) { + if (!plugin.settings.syncMove) { + return; + } + newPath = `${parentFolderPath}/${fileName}.${folderNote.extension}`; + } else if (parentFolderPath.trim() === "") { + folderNote.path = `${folderNote.name}`; + newPath = `${fileName}.${folderNote.extension}`; + } else { + folderNote.path = `${parentFolderPath}/${folderNote.name}`; + newPath = `${parentFolderPath}/${fileName}.${folderNote.extension}`; + } + } else { + folderNote.path = `${file.path}/${folderNote.name}`; + newPath = `${file.path}/${fileName}.${folderNote.extension}`; + } + plugin.app.fileManager.renameFile(folderNote, newPath); +} +async function handleFileRename(file, oldPath, plugin) { + const oldFileName = removeExtension(getFileNameFromPathString(oldPath)); + const newFileName = file.basename; + if (oldFileName === newFileName) { + return; + } + const oldFolder = getFolderNoteFolder(plugin, oldPath, oldFileName); + const folderName = extractFolderName(plugin.settings.folderNoteName, file.basename) || file.basename; + const oldFolderName = extractFolderName(plugin.settings.folderNoteName, oldFileName) || oldFileName; + const newFolder = getFolderNoteFolder(plugin, file, file.basename); + const excludedFolder = getExcludedFolder(plugin, (newFolder == null ? void 0 : newFolder.path) || "", true); + const detachedExcludedFolder = getDetachedFolder(plugin, (newFolder == null ? void 0 : newFolder.path) || ""); + const folderNote = getFolderNote(plugin, oldPath, plugin.settings.storageLocation, file); + if (shouldCreateFolderNote(excludedFolder, folderName, newFolder, detachedExcludedFolder)) { + if (newFolder) { + handleFolderNoteCreation2(file, newFolder, plugin); + } + return; + } + if (shouldRemoveFolderNoteClasses(excludedFolder, folderName, newFolder)) { + handleFolderNoteRemoval(file, newFolder, plugin); + } + if ((excludedFolder == null ? void 0 : excludedFolder.disableSync) || !plugin.settings.syncFolderName) { + return; + } + if (folderName === (newFolder == null ? void 0 : newFolder.name) && newFolder) { + handleSameFolderRename(file, newFolder, oldFolder, plugin); + return; + } + if (shouldRenameFolderOnFileRename(oldFolderName, oldFolder, newFolder, folderNote)) { + return renameFolderOnFileRename(file, oldPath, oldFolder, plugin); + } +} +async function renameFolderOnFileRename(file, oldPath, oldFolder, plugin) { + var _a, _b; + const newFolderName = extractFolderName(plugin.settings.folderNoteName, file.basename); + if (!newFolderName) { + removeCSSClassFromFileExplorerEL(oldFolder.path, "has-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(file.path, "is-folder-note", false, plugin); + return; + } else if (newFolderName === oldFolder.name) { + addCSSClassToFileExplorerEl(oldFolder.path, "has-folder-note", false, plugin); + addCSSClassToFileExplorerEl(file.path, "is-folder-note", false, plugin); + return; + } + let newFolderPath = ""; + if (plugin.settings.storageLocation === "insideFolder") { + if (((_a = oldFolder.parent) == null ? void 0 : _a.path) === "/") { + newFolderPath = `${newFolderName}`; + } else { + newFolderPath = ((_b = oldFolder.parent) == null ? void 0 : _b.path) + "/" + newFolderName; + } + } else { + const parentFolderPath = getFolderPathFromString(file.path); + if (parentFolderPath.trim() === "" || parentFolderPath.trim() === "/") { + newFolderPath = `${newFolderName}`; + } else { + newFolderPath = `${parentFolderPath}/${newFolderName}`; + } + } + if (plugin.app.vault.getAbstractFileByPath(newFolderPath)) { + await plugin.app.fileManager.renameFile(file, oldPath); + new import_obsidian43.Notice("A folder with the same name already exists"); + return; + } + plugin.app.fileManager.renameFile(oldFolder, newFolderPath); +} +function updateExcludedFolderPath(folder, oldPath, plugin) { + const excludedFolders = plugin.settings.excludeFolders.filter((excludedFolder) => { + var _a; + return (_a = excludedFolder.path) == null ? void 0 : _a.includes(oldPath); + }); + excludedFolders.forEach((excludedFolder) => { + if (excludedFolder.path === oldPath) { + excludedFolder.path = folder.path; + return; + } + if (!excludedFolder.path) + return; + const folders = excludedFolder.path.split("/"); + if (folders.length < 1) { + folders.push(excludedFolder.path); + } + folders[folders.indexOf(folder.name)] = folder.name; + excludedFolder.path = folders.join("/"); + }); + plugin.saveSettings(); +} +function shouldCreateFolderNote(excludedFolder, folderName, newFolder, detachedExcludedFolder) { + return !(excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) && folderName === (newFolder == null ? void 0 : newFolder.name) && !detachedExcludedFolder; +} +function shouldRemoveFolderNoteClasses(excludedFolder, folderName, newFolder) { + return (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) || folderName !== (newFolder == null ? void 0 : newFolder.name); +} +function handleFolderNoteCreation2(file, newFolder, plugin) { + addCSSClassToFileExplorerEl(file.path, "is-folder-note", false, plugin); + addCSSClassToFileExplorerEl(newFolder.path, "has-folder-note", false, plugin); +} +function handleFolderNoteRemoval(file, newFolder, plugin) { + removeCSSClassFromFileExplorerEL(file.path, "is-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL((newFolder == null ? void 0 : newFolder.path) || "", "has-folder-note", false, plugin); +} +function handleSameFolderRename(file, newFolder, oldFolder, plugin) { + addCSSClassToFileExplorerEl(file.path, "is-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(oldFolder == null ? void 0 : oldFolder.path, "has-folder-note", false, plugin); + addCSSClassToFileExplorerEl(newFolder.path, "has-folder-note", false, plugin); +} +function shouldRenameFolderOnFileRename(oldFolderName, oldFolder, newFolder, folderNote) { + if (!oldFolder) + return false; + const oldFolderAsFolder = oldFolder; + const newFolderAsFolder = newFolder; + return oldFolderName === oldFolderAsFolder.name && (newFolderAsFolder == null ? void 0 : newFolderAsFolder.path) === oldFolderAsFolder.path || folderNote !== null && oldFolderName === oldFolderAsFolder.name; +} + +// src/events/handleCreate.ts +var import_obsidian44 = require("obsidian"); +async function handleCreate(file, plugin) { + if (!plugin.app.workspace.layoutReady) + return; + const folder = file.parent; + if (folder instanceof import_obsidian44.TFolder) { + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, "only-has-folder-note", true, plugin); + } else { + removeCSSClassFromFileExplorerEL(folder.path, "only-has-folder-note", true, plugin); + } + } + if (file instanceof import_obsidian44.TFile) { + handleFileCreation(file, plugin); + } else if (file instanceof import_obsidian44.TFolder && plugin.settings.autoCreate) { + handleFolderCreation(file, plugin); + } +} +async function handleFileCreation(file, plugin) { + const folder = getFolder(plugin, file); + if (!(folder instanceof import_obsidian44.TFolder) && plugin.settings.autoCreateForFiles) { + if (!file.parent) { + return; + } + const newFolder = await plugin.app.fileManager.createNewFolder(file.parent); + turnIntoFolderNote(plugin, file, newFolder); + } else if (folder instanceof import_obsidian44.TFolder) { + if (folder.children.length >= 1) { + removeCSSClassFromFileExplorerEL(folder.path, "fn-empty-folder", false, plugin); + } + const detachedFolder = getExcludedFolder(plugin, folder.path, true); + if (detachedFolder) { + return; + } + const folderNote = getFolderNote(plugin, folder.path); + if (folderNote && folderNote.path === file.path) { + addCSSClassToFileExplorerEl(folder.path, "has-folder-note", false, plugin); + addCSSClassToFileExplorerEl(file.path, "is-folder-note", false, plugin); + } else if (plugin.settings.autoCreateForFiles && !isFileInAttachmentFolder(plugin, file)) { + if (!plugin.settings.supportedFileTypes.includes(file.extension)) { + return; + } + if (!file.parent) { + return; + } + const newFolder = await plugin.app.fileManager.createNewFolder(file.parent); + turnIntoFolderNote(plugin, file, newFolder); + } + } +} +async function handleFolderCreation(folder, plugin) { + let openFile = plugin.settings.autoCreateFocusFiles; + const attachmentFolderPath = plugin.app.vault.getConfig("attachmentFolderPath"); + const cleanAttachmentFolderPath = (attachmentFolderPath == null ? void 0 : attachmentFolderPath.replace("./", "")) || ""; + const attachmentsAreInRootFolder = attachmentFolderPath === "./" || attachmentFolderPath === ""; + addCSSClassToFileExplorerEl(folder.path, "fn-empty-folder", false, plugin); + if (!plugin.settings.autoCreateForAttachmentFolder) { + if (!attachmentsAreInRootFolder && cleanAttachmentFolderPath === folder.name) + return; + } else if (!attachmentsAreInRootFolder && cleanAttachmentFolderPath === folder.name) { + openFile = false; + } + const excludedFolder = getExcludedFolder(plugin, folder.path, true); + if (excludedFolder == null ? void 0 : excludedFolder.disableAutoCreate) + return; + const folderNote = getFolderNote(plugin, folder.path); + if (folderNote) + return; + void createFolderNote(plugin, folder.path, openFile, void 0, true); + addCSSClassToFileExplorerEl(folder.path, "has-folder-note", false, plugin); +} + +// src/events/TabManager.ts +var import_obsidian45 = require("obsidian"); +var TabManager = class { + constructor(plugin) { + this.plugin = plugin; + this.app = plugin.app; + } + resetTabs() { + if (!this.isEnabled()) + return; + this.app.workspace.iterateAllLeaves((leaf) => { + var _a; + if (!(leaf.view instanceof import_obsidian45.EditableFileView)) + return; + const file = (_a = leaf.view) == null ? void 0 : _a.file; + if (!file) + return; + leaf.tabHeaderInnerTitleEl.setText(file.basename); + }); + } + updateTabs() { + if (!this.isEnabled()) + return; + this.app.workspace.iterateAllLeaves((leaf) => { + var _a; + if (!(leaf.view instanceof import_obsidian45.EditableFileView)) + return; + const file = (_a = leaf.view) == null ? void 0 : _a.file; + if (!file) + return; + const folder = getFolder(this.plugin, file); + if (!folder) + return; + leaf.tabHeaderInnerTitleEl.setText(folder.name); + }); + } + updateTab(folderPath) { + if (!this.isEnabled()) + return; + const folder = this.app.vault.getAbstractFileByPath(folderPath); + if (!(folder instanceof import_obsidian45.TFolder)) + return; + const folderNote = getFolderNote(this.plugin, folder.path); + if (!folderNote) + return; + this.app.workspace.iterateAllLeaves((leaf) => { + var _a; + if (!(leaf.view instanceof import_obsidian45.EditableFileView)) + return; + const file = (_a = leaf.view) == null ? void 0 : _a.file; + if (!file) + return; + if (file.path === folderNote.path) { + leaf.tabHeaderInnerTitleEl.setText(folder.name); + } + }); + } + isEnabled() { + if (this.plugin.settings.folderNoteName === "{{folder_name}}") + return false; + return this.plugin.settings.tabManagerEnabled; + } +}; + +// src/events/handleDelete.ts +var import_obsidian46 = require("obsidian"); +function handleDelete(file, plugin) { + const folder = plugin.app.vault.getAbstractFileByPath(getFolderPathFromString(file.path)); + if (folder instanceof import_obsidian46.TFolder) { + if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) { + addCSSClassToFileExplorerEl(folder.path, "only-has-folder-note", true, plugin); + } else { + removeCSSClassFromFileExplorerEL(folder.path, "only-has-folder-note", true, plugin); + } + } + if (file instanceof import_obsidian46.TFile) { + const folderNoteFolder = getFolder(plugin, file); + if (!folderNoteFolder) { + return; + } + const folderNote2 = getFolderNote(plugin, folderNoteFolder.path); + if (folderNote2) { + return; + } + removeCSSClassFromFileExplorerEL(folderNoteFolder.path, "has-folder-note", false, plugin); + removeCSSClassFromFileExplorerEL(folderNoteFolder.path, "only-has-folder-note", true, plugin); + hideFolderNoteInFileExplorer(folderNoteFolder.path, plugin); + } + if (!(file instanceof import_obsidian46.TFolder)) { + return; + } + const folderNote = getFolderNote(plugin, file.path); + if (!folderNote) { + return; + } + removeCSSClassFromFileExplorerEL(folderNote.path, "is-folder-note", false, plugin); + if (!plugin.settings.syncDelete) { + return; + } + deleteFolderNote(plugin, folderNote, false); +} + +// src/obsidian-folder-overview/src/view.ts +var import_obsidian50 = require("obsidian"); + +// src/obsidian-folder-overview/src/main.ts +var import_obsidian49 = require("obsidian"); + +// src/obsidian-folder-overview/src/Commands.ts +var import_obsidian47 = require("obsidian"); +function registerOverviewCommands(plugin) { + plugin.addCommand({ + id: "open-folder-overview-settings", + name: "Edit folder overview", + callback: () => { + plugin.activateOverviewView(); + } + }); + plugin.addCommand({ + id: "insert-folder-overview", + name: "Insert folder overview", + editorCheckCallback: (checking, editor) => { + const line = editor.getCursor().line; + const lineText = editor.getLine(line); + if (lineText.trim() === "" || lineText.trim() === ">") { + if (!checking) { + insertOverview(editor, plugin); + } + return true; + } + return false; + } + }); + plugin.registerEvent(plugin.app.workspace.on("editor-menu", (menu, editor, _view) => { + const { line } = editor.getCursor(); + const lineText = editor.getLine(line); + if (lineText.trim() === "" || lineText.trim() === ">") { + menu.addItem((item) => { + item.setTitle("Insert folder overview").setIcon("edit").onClick(() => { + if (plugin.settings.firstTimeInsertOverview) { + plugin.settings.firstTimeInsertOverview = false; + plugin.saveSettings(); + const frag = document.createDocumentFragment(); + const text = document.createTextNode('You can edit the overview using the "Edit folder overview" command from the command palette. To find more about folder overview, check the plugin documentation: '); + const link = document.createElement("a"); + link.href = "https://lostpaul.github.io/obsidian-folder-notes/Folder%20overview/"; + link.textContent = "https://lostpaul.github.io/obsidian-folder-notes/Folder%20overview/"; + frag.appendChild(text); + frag.appendChild(link); + new import_obsidian47.Notice(frag); + } + insertOverview(editor, plugin); + }); + }); + } + })); +} +function insertOverview(editor, plugin) { + const { line: cursorLine } = editor.getCursor(); + const currentLineText = editor.getLine(cursorLine); + const json = getDefaultOverviewJson(plugin); + json.id = crypto.randomUUID(); + const yaml = (0, import_obsidian47.stringifyYaml)(json); + let overviewBlock = getOverviewBlock(yaml); + if (shouldUseActualLinks(plugin)) { + overviewBlock = addLinkSpans(overviewBlock, json.id); + } + if (currentLineText.trim() === "") { + editor.replaceSelection(overviewBlock); + } else if (currentLineText.trim() === ">") { + const yamlLines = yaml.split("\n"); + const quotedLines = yamlLines.map((yamlLine) => `> ${yamlLine}`); + let quotedBlock = `\`\`\`folder-overview +${quotedLines.join("\n")}\`\`\` +`; + if (shouldUseActualLinks(plugin)) { + quotedBlock = addLinkSpans(quotedBlock, json.id); + } + editor.replaceSelection(quotedBlock); + } + if (plugin.fvIndexDB.active) { + const activeFile = plugin.app.workspace.getActiveFile(); + if (activeFile) { + plugin.fvIndexDB.addNote(activeFile); + } + } +} +function getDefaultOverviewJson(plugin) { + const isOverviewPlugin = plugin instanceof FolderOverviewPlugin; + const defaultSettings = isOverviewPlugin ? plugin.settings.defaultOverviewSettings : plugin.settings.defaultOverview; + return Object.assign({}, defaultSettings); +} +function shouldUseActualLinks(plugin) { + if (plugin instanceof FolderOverviewPlugin) { + return plugin.settings.defaultOverviewSettings.useActualLinks; + } + return plugin.settings.defaultOverview.useActualLinks; +} +function getOverviewBlock(yaml) { + return `\`\`\`folder-overview +${yaml}\`\`\` +`; +} +function addLinkSpans(block, id) { + return `${block} + +`; +} + +// src/obsidian-folder-overview/src/utils/FmtpHandler.ts +var import_front_matter_plugin_api_provider2 = __toESM(require_lib()); +var FrontMatterTitlePluginHandler2 = class { + constructor(plugin) { + this.api = null; + this.deffer = null; + this.modifiedFolders = /* @__PURE__ */ new Map(); + this.eventRef = null; + this.dispatcher = null; + this.plugin = plugin; + this.app = plugin.app; + (async () => { + var _a; + this.deffer = (0, import_front_matter_plugin_api_provider2.getDefer)(this.app); + if (this.deffer.isPluginReady()) { + this.api = this.deffer.getApi(); + } else { + await this.deffer.awaitPlugin(); + this.api = this.deffer.getApi(); + if (!this.deffer.isFeaturesReady()) { + await this.deffer.awaitFeatures(); + } + } + const dispatcher = (_a = this.api) == null ? void 0 : _a.getEventDispatcher(); + if (dispatcher) { + this.dispatcher = dispatcher; + } + })(); + } + deleteEvent() { + var _a; + if (this.eventRef) { + (_a = this.dispatcher) == null ? void 0 : _a.removeListener(this.eventRef); + } + } + async getNewFileName(file) { + var _a, _b, _c; + const resolver = (_b = (_a = this.api) == null ? void 0 : _a.getResolverFactory()) == null ? void 0 : _b.createResolver("#feature-id#"); + const changedName = resolver == null ? void 0 : resolver.resolve((_c = file == null ? void 0 : file.path) != null ? _c : ""); + return changedName != null ? changedName : null; + } +}; + +// src/obsidian-folder-overview/src/utils/IndexDB.ts +var import_obsidian48 = require("obsidian"); +var FvIndexDB = class { + constructor(plugin) { + this.name = "fn-folder-overview"; + this.version = 1; + this.storeName = "files"; + this.keyPath = "sourcePath"; + this.active = false; + this.indexDB = null; + this.plugin = plugin; + } + init(showNotice) { + this.active = true; + const openRequest = indexedDB.open(this.name, this.version); + openRequest.onupgradeneeded = (event) => { + const target = event.target; + if (!target) + return; + const db = target.result; + if (!db.objectStoreNames.contains(this.storeName)) { + db.createObjectStore(this.storeName, { keyPath: this.keyPath }); + } + this.indexDB = db; + this.indexFiles(showNotice); + }; + openRequest.onsuccess = (event) => { + const target = event.target; + if (!target) + return; + this.indexDB = target.result; + openRequest.onblocked = (blockedEvent) => { + console.warn("IndexedDB is blocked:", blockedEvent); + }; + this.indexDB.onclose = () => { + this.indexDB = null; + }; + this.resetDatabase(); + this.indexFiles(showNotice); + }; + openRequest.onerror = (event) => { + const target = event.target; + const error = target == null ? void 0 : target.error; + if (error && error.name === "VersionError") { + const deleteRequest = indexedDB.deleteDatabase(this.name); + deleteRequest.onsuccess = () => { + this.init(showNotice); + }; + } + }; + } + async indexFiles(showNotice) { + if (showNotice) + new import_obsidian48.Notice("Indexing files for folder overview plugin..."); + const files = this.plugin.app.vault.getMarkdownFiles(); + for (const file of files) { + if (!await hasOverviewYaml(this.plugin, file)) + continue; + this.addNote(file); + } + if (showNotice) + new import_obsidian48.Notice("Indexed files for folder overview plugin."); + } + addNote(note) { + if (!this.active || !this.indexDB) + return; + const transaction = this.indexDB.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + store.put({ sourcePath: note.path }); + } + removeNote(notePath) { + if (!this.active || !this.indexDB) + return; + const transaction = this.indexDB.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + store.delete(notePath); + } + getNote(path) { + if (!this.active) + return Promise.resolve(null); + return new Promise((resolve, reject) => { + if (!this.indexDB) + return resolve(null); + const transaction = this.indexDB.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.get(path); + request.onsuccess = (event) => { + var _a; + const target = event.target; + resolve((_a = target == null ? void 0 : target.result) != null ? _a : null); + }; + request.onerror = (event) => { + reject(event); + }; + }); + } + getAllNotes() { + if (!this.active) + return Promise.resolve([]); + return new Promise((resolve, reject) => { + if (!this.indexDB) + return resolve([]); + const transaction = this.indexDB.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getAll(); + request.onsuccess = (event) => { + var _a; + const target = event.target; + const result = (_a = target == null ? void 0 : target.result) != null ? _a : []; + resolve(result.map((data) => data.sourcePath)); + }; + request.onerror = (event) => { + reject(event); + }; + }); + } + resetDatabase() { + if (!this.indexDB) + return; + const transaction = this.indexDB.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + store.clear(); + } +}; + +// src/obsidian-folder-overview/src/main.ts +var FolderOverviewPlugin = class extends import_obsidian49.Plugin { + constructor() { + super(...arguments); + this.settings = DEFAULT_SETTINGS; + this.settingsTab = new SettingsTab(this); + this.fvIndexDB = new FvIndexDB(this); + this.updateOverviewView = updateOverviewView; + this.updateViewDropdown = updateViewDropdown; + } + async onload() { + await this.loadSettings(); + this.addSettingTab(this.settingsTab); + this.settingsTab.display(); + registerOverviewCommands(this); + this.app.workspace.onLayoutReady(async () => { + this.registerView(FOLDER_OVERVIEW_VIEW, (leaf) => { + return new FolderOverviewView(leaf, this); + }); + if (this.app.plugins.getPlugin("obsidian-front-matter-title-plugin")) { + this.fmtpHandler = new FrontMatterTitlePluginHandler2(this); + } + if (this.settings.globalSettings.autoUpdateLinks) { + this.fvIndexDB.init(false); + } + }); + this.app.vault.on("rename", () => this.handleVaultChange()); + this.app.vault.on("create", () => this.handleVaultChange()); + this.app.vault.on("delete", () => this.handleVaultChange()); + this.registerMarkdownCodeBlockProcessor("folder-overview", (source, el, ctx) => { + this.handleOverviewBlock(source, el, ctx); + }); + console.debug("loading Folder Overview plugin"); + } + handleVaultChange() { + const DEBOUNCE_DELAY_MS = 2e3; + if (!this.settings.globalSettings.autoUpdateLinks) + return; + (0, import_obsidian49.debounce)(() => { + updateAllOverviews(this); + }, DEBOUNCE_DELAY_MS, true)(); + } + async handleOverviewBlock(source, el, ctx) { + const observer = new MutationObserver(() => { + var _a; + const editButton = (_a = el.parentElement) == null ? void 0 : _a.childNodes.item(1); + if (editButton) { + editButton.addEventListener("click", (e) => { + e.stopImmediatePropagation(); + e.preventDefault(); + e.stopPropagation(); + new FolderOverviewSettings(this.app, this, (0, import_obsidian49.parseYaml)(source), ctx, el, this.settings.defaultOverviewSettings).open(); + }, { capture: true }); + } + }); + observer.observe(el, { + childList: true, + subtree: true + }); + try { + this.app.workspace.onLayoutReady(async () => { + const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverviewSettings); + await folderOverview.create(this, el, ctx); + this.updateOverviewView(this); + }); + } catch (e) { + new import_obsidian49.Notice("Error creating folder overview (folder notes plugin) - check console for more details"); + console.error(e); + } + } + async onunload() { + console.debug("Unloading Folder Overview plugin"); + } + async loadSettings() { + var _a; + const data = await this.loadData(); + this.settings = Object.assign({}, DEFAULT_SETTINGS, data); + if (!this.settings.defaultOverviewSettings) { + this.settings.defaultOverviewSettings = { + ...DEFAULT_SETTINGS.defaultOverviewSettings, + ...(_a = data == null ? void 0 : data.defaultOverviewSettings) != null ? _a : {} + }; + } + if ((data == null ? void 0 : data.firstTimeInsertOverview) === void 0) { + this.settings.firstTimeInsertOverview = true; + } + } + async saveSettings() { + await this.saveData(this.settings); + } + async activateOverviewView() { + const { workspace } = this.app; + let leaf = null; + const leaves = workspace.getLeavesOfType(FOLDER_OVERVIEW_VIEW); + if (leaves.length > 0) { + leaf = leaves[0]; + } else { + leaf = workspace.getRightLeaf(false); + await (leaf == null ? void 0 : leaf.setViewState({ type: FOLDER_OVERVIEW_VIEW, active: true })); + } + if (!leaf) + return; + workspace.revealLeaf(leaf); + } +}; +async function updateOverviewView(plugin, newYaml) { + const { workspace } = plugin.app; + const leaf = workspace.getLeavesOfType(FOLDER_OVERVIEW_VIEW)[0]; + if (!leaf) + return; + const view = leaf.view; + if (!view) + return; + if (!view.yaml) + return; + const yaml = view.yaml.id === "" ? view.yaml : newYaml; + view.display(view.contentEl, yaml != null ? yaml : view.yaml, plugin, view.defaultSettings, view.display, void 0, void 0, view.activeFile, plugin.settingsTab, view.modal, "all"); +} +async function updateViewDropdown(plugin) { + const { workspace } = plugin.app; + const leaf = workspace.getLeavesOfType(FOLDER_OVERVIEW_VIEW)[0]; + if (!leaf) + return; + const view = leaf.view; + view.display(view.contentEl, view.yaml, plugin, view.defaultSettings, view.display, void 0, void 0, view.activeFile, plugin.settingsTab, view.modal, "dropdown"); +} + +// src/obsidian-folder-overview/src/view.ts +var FOLDER_OVERVIEW_VIEW = "folder-overview-view"; +var FolderOverviewView = class extends import_obsidian50.ItemView { + constructor(leaf, plugin) { + super(leaf); + this.contentEl = this.containerEl.children[1]; + this.plugin = plugin; + this.display = this.display.bind(this); + if (plugin instanceof FolderOverviewPlugin) { + this.defaultSettings = plugin.settings.defaultOverviewSettings; + } else if (plugin instanceof FolderNotesPlugin) { + this.defaultSettings = plugin.settings.defaultOverview; + } else { + throw new Error("Plugin must be an instance of FolderOverviewPlugin or FolderNotesPlugin"); + } + this.yaml = this.defaultSettings; + this.registerEvent(this.plugin.app.workspace.on("file-open", (file) => { + this.activeFile = file; + this.display(this.contentEl, this.yaml, this.plugin, this.defaultSettings, this.display, void 0, void 0, file, void 0, void 0, "all"); + })); + } + getViewType() { + return FOLDER_OVERVIEW_VIEW; + } + getDisplayText() { + return "Folder Overview settings"; + } + getIcon() { + return "settings"; + } + async onOpen() { + this.display(this.contentEl, this.yaml, this.plugin, this.defaultSettings, this.display, void 0, void 0, this.activeFile); + } + async display(contentEl, yaml, plugin, defaultSettings, display, el, ctx, file, settingsTab, modal, changedSection) { + this.contentEl = contentEl; + this.yaml = yaml; + this.defaultSettings = defaultSettings; + this.changedSection = changedSection; + if (file) { + this.activeFile = file; + } + let header = contentEl.querySelector(".fn-folder-overview-header"); + if (!header) { + header = contentEl.createEl("h4", { + cls: "fn-folder-overview-header", + text: "Folder Overview settings" + }); + } + const activeFile = plugin.app.workspace.getActiveFile(); + const overviews = await getOverviews(plugin, activeFile); + let settingsContainer = contentEl.querySelector(".fn-settings-container"); + if (!settingsContainer) { + settingsContainer = contentEl.createDiv({ cls: "fn-settings-container" }); + } + let dropdown = settingsContainer.querySelector(".fn-select-overview-setting"); + if (!dropdown || changedSection === "all" || changedSection === "dropdown") { + if (dropdown) { + settingsContainer.empty(); + } + dropdown = settingsContainer.createDiv({ cls: "fn-select-overview-setting" }); + const overviewSetting = new import_obsidian50.Setting(dropdown); + overviewSetting.setName("Select overview").setClass("fn-select-overview-setting").addDropdown((cb) => { + var _a; + if (activeFile) { + const titleCounts = {}; + const options = overviews.reduce((acc, overview) => { + var _a2; + const title = parseOverviewTitle(overview, plugin, activeFile.parent, ((_a2 = activeFile.parent) == null ? void 0 : _a2.path) || "", activeFile); + const count = (titleCounts[title] || 0) + 1; + titleCounts[title] = count; + acc[overview.id] = count > 1 ? `${title} (${count})` : title; + return acc; + }, {}); + cb.addOptions(options); + } + cb.addOption("default", "Default"); + cb.setValue((_a = yaml == null ? void 0 : yaml.id) != null ? _a : "default"); + const isDefault = cb.getValue() === "default"; + const isYamlIdEmpty = !(yaml == null ? void 0 : yaml.id.trim()); + const isCbValueEmpty = cb.getValue().trim() === ""; + if (isDefault || isYamlIdEmpty || isCbValueEmpty) { + yaml = defaultSettings; + cb.setValue("default"); + } else { + const foundOverview = overviews.find((overview) => overview.id === yaml.id); + yaml = foundOverview; + } + cb.onChange(async (value) => { + if (value === "default") { + yaml = defaultSettings; + } else { + const foundOverview = overviews.find((overview) => overview.id === value); + yaml = foundOverview; + } + await display(contentEl, yaml, plugin, defaultSettings, display, void 0, void 0, activeFile, void 0, void 0, "all"); + }); + }); + } + this.yaml = yaml; + await createOverviewSettings(settingsContainer, yaml, plugin, defaultSettings, display, void 0, void 0, activeFile, void 0, void 0, changedSection); + } +}; + +// src/main.ts +var FolderNotesPlugin = class extends import_obsidian51.Plugin { + constructor() { + super(...arguments); + this.fmtpHandler = null; + this.hoveredElement = null; + this.mouseEvent = null; + this.hoverLinkTriggered = false; + this.settingsOpened = false; + this.askModalCurrentlyOpen = false; + this.updateOverviewView = updateOverviewView; + this.updateViewDropdown = updateViewDropdown; + } + async onload() { + console.debug("loading folder notes plugin"); + await this.loadSettings(); + this.settingsTab = new SettingsTab2(this.app, this); + this.addSettingTab(this.settingsTab); + await this.saveSettings(); + this.fvIndexDB = new FvIndexDB(this); + this.addSettingCssClasses(); + new Commands(this.app, this).registerCommands(); + registerOverviewCommands(this); + this.app.workspace.onLayoutReady(() => this.onLayoutReady()); + if (!this.settings.persistentSettingsTab.afterRestart) { + this.settings.settingsTab = "general"; + } + this.registerDomEvent(window, "keydown", (event) => { + var _a; + const { hoveredElement } = this; + if (this.hoverLinkTriggered) + return; + if (!hoveredElement) + return; + if (!import_obsidian51.Keymap.isModEvent(event)) + return; + const folderPath = ((_a = hoveredElement == null ? void 0 : hoveredElement.parentElement) == null ? void 0 : _a.getAttribute("data-path")) || ""; + const folderNote = getFolderNote(this, folderPath); + if (!folderNote) + return; + this.app.workspace.trigger("hover-link", { + event: this.mouseEvent, + source: "preview", + hoverParent: { + file: folderNote + }, + targetEl: hoveredElement, + linktext: folderNote == null ? void 0 : folderNote.basename, + sourcePath: folderNote == null ? void 0 : folderNote.path + }); + this.hoverLinkTriggered = true; + }); + this.registerEvent(this.app.workspace.on("file-open", (openFile) => { + removeActiveFolder(this); + if (!openFile || !openFile.basename) { + return; + } + const folder = getFolder(this, openFile); + if (!folder) { + return; + } + const excludedFolder = getExcludedFolder(this, folder.path, true); + if (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) + return; + const folderNote = getFolderNote(this, folder.path); + if (!folderNote) { + return; + } + if (folderNote.path !== openFile.path) { + return; + } + setActiveFolder(folder.path, this); + })); + this.registerEvent(this.app.vault.on("create", (file) => { + handleCreate(file, this).catch((err) => { + console.error(err); + new import_obsidian51.Notice("Error handling file creation"); + }); + this.handleVaultChange(); + })); + this.registerEvent(this.app.vault.on("rename", (file, oldPath) => { + handleRename(file, oldPath, this); + this.handleVaultChange(); + })); + this.registerEvent(this.app.vault.on("delete", (file) => { + handleDelete(file, this); + this.handleVaultChange(); + })); + this.registerMarkdownCodeBlockProcessor("folder-overview", (source, el, ctx) => { + this.handleOverviewBlock(source, el, ctx); + }); + } + addSettingCssClasses() { + activeDocument.body.classList.add("folder-notes-plugin"); + if (this.settings.hideFolderNote) { + activeDocument.body.classList.add("hide-folder-note"); + } + if (this.settings.hideCollapsingIconForEmptyFolders) { + activeDocument.body.classList.add("fn-hide-empty-collapse-icon"); + } + if (this.settings.hideFolderNoteNameInPath) { + activeDocument.body.classList.add("folder-note-hide-name-path"); + } + if (this.settings.underlineFolder) { + activeDocument.body.classList.add("folder-note-underline"); + } + if (this.settings.boldName) { + activeDocument.body.classList.add("folder-note-bold"); + } + if (this.settings.cursiveName) { + activeDocument.body.classList.add("folder-note-cursive"); + } + if (this.settings.boldNameInPath) { + activeDocument.body.classList.add("folder-note-bold-path"); + } + if (this.settings.cursiveNameInPath) { + activeDocument.body.classList.add("folder-note-cursive-path"); + } + if (this.settings.underlineFolderInPath) { + activeDocument.body.classList.add("folder-note-underline-path"); + } + if (this.settings.stopWhitespaceCollapsing) { + activeDocument.body.classList.add("fn-whitespace-stop-collapsing"); + } + if (this.settings.hideCollapsingIcon) { + activeDocument.body.classList.add("fn-hide-collapse-icon"); + } + if (this.settings.ignoreAttachmentFolder) { + activeDocument.body.classList.add("fn-ignore-attachment-folder"); + } + if (!this.settings.highlightFolder) { + activeDocument.body.classList.add("disable-folder-highlight"); + } + if ((0, import_obsidian51.requireApiVersion)("1.7.2")) { + activeDocument.body.classList.add("version-1-7-2"); + } + } + onLayoutReady() { + var _a, _b, _c, _d; + if (!this._loaded) { + return; + } + registerFileExplorerObserver(this); + const fileExplorer = getFileExplorer(this); + const infinityScroll = (_b = (_a = fileExplorer == null ? void 0 : fileExplorer.view) == null ? void 0 : _a.tree) == null ? void 0 : _b.infinityScroll; + if (infinityScroll) { + infinityScroll.rootMargin = 1.5; + } + this.registerView(FOLDER_OVERVIEW_VIEW, (leaf2) => { + return new FolderOverviewView(leaf2, this); + }); + this.app.workspace.on("layout-change", () => { + this.tabManager.updateTabs(); + }); + if (this.app.plugins.getPlugin("obsidian-front-matter-title-plugin")) { + this.fmtpHandler = new FrontMatterTitlePluginHandler(this); + } + this.tabManager = new TabManager(this); + this.tabManager.updateTabs(); + this.registerDomEvent(activeDocument, "click", (evt) => { + this.handleFileExplorerClick(evt); + }, true); + this.registerDomEvent(activeDocument, "auxclick", (evt) => { + const rightClick = 2; + if (evt.button === rightClick) + return; + this.handleFileExplorerClick(evt); + }, true); + const fileExplorerPlugin = this.app.internalPlugins.getEnabledPluginById("file-explorer"); + if (fileExplorerPlugin) { + const fileExplorer2 = fileExplorerPlugin; + const originalRevealInFolder = fileExplorer2.revealInFolder.bind(fileExplorer2); + fileExplorer2.revealInFolder = (file) => { + if (file instanceof import_obsidian51.TFile) { + const folder = getFolder(this, file); + if (folder instanceof import_obsidian51.TFolder) { + const folderNote = getFolderNote(this, folder.path); + if (!folderNote || folderNote.path !== file.path) { + originalRevealInFolder(file); + return; + } + activeDocument.body.classList.remove("hide-folder-note"); + originalRevealInFolder(folder); + const FOLDER_REVEAL_DELAY = 100; + window.setTimeout(() => { + activeDocument.body.classList.add("hide-folder-note"); + }, FOLDER_REVEAL_DELAY); + return; + } + } + if (file instanceof import_obsidian51.TFolder || file instanceof import_obsidian51.TFile) { + originalRevealInFolder(file); + return; + } + }; + } + const leaf = this.app.workspace.getLeavesOfType("markdown").first(); + const view = leaf == null ? void 0 : leaf.view; + if (!view) { + return; + } + const viewWithEditModes = view; + const activeEditor = this.app.workspace.activeEditor; + const editMode = (_d = (_c = viewWithEditModes.editMode) != null ? _c : viewWithEditModes.sourceMode) != null ? _d : activeEditor == null ? void 0 : activeEditor.editMode; + if (!editMode) { + return; + } + const { clipboardManager } = editMode; + const clipboardProto = Object.getPrototypeOf(clipboardManager); + const originalHandleDragOver = clipboardProto.handleDragOver; + const originalHandleDrop = clipboardProto.handleDrop; + const folderNotePlugin = this; + clipboardProto.handleDragOver = function(evt, ...args) { + const { dragManager } = this.app; + const draggable = dragManager == null ? void 0 : dragManager.draggable; + if ((draggable == null ? void 0 : draggable.file) instanceof import_obsidian51.TFolder) { + const folderNote = getFolderNote(folderNotePlugin, draggable.file.path); + if (folderNote) { + dragManager.setAction(window.i18next.t("interface.drag-and-drop.insert-link-here")); + return; + } + } + return originalHandleDragOver.call(this, evt, ...args); + }; + clipboardProto.handleDrop = function(evt, ...args) { + const { dragManager } = this.app; + const draggable = dragManager == null ? void 0 : dragManager.draggable; + if ((draggable == null ? void 0 : draggable.file) instanceof import_obsidian51.TFolder) { + const folderNote = getFolderNote(folderNotePlugin, draggable.file.path); + if (folderNote) { + draggable.file = folderNote; + draggable.type = "file"; + } + } + return originalHandleDrop.call(this, evt, ...args); + }; + if (this.settings.fvGlobalSettings.autoUpdateLinks) { + this.fvIndexDB.init(false); + } + } + handleVaultChange() { + if (!this.settings.fvGlobalSettings.autoUpdateLinks) + return; + const DEBOUNCE_DELAY = 2e3; + (0, import_obsidian51.debounce)(() => { + void updateAllOverviews(this); + }, DEBOUNCE_DELAY, true)(); + } + handleFileExplorerClick(evt) { + const target = evt.target; + if (evt.shiftKey) + return; + if (this.isMobileClickDisabled()) + return; + const { folderTitleEl, onlyClickedOnFolderTitle } = this.getFolderTitleInfo(target); + if (!folderTitleEl) + return; + if (this.shouldIgnoreClickByWhitespaceOrCollapse(target, onlyClickedOnFolderTitle)) + return; + const folderPath = this.getValidFolderPath(folderTitleEl); + if (!folderPath) + return; + const usedCtrl = this.isCtrlUsed(evt); + const folderNote = getFolderNote(this, folderPath); + if (!folderNote && this.shouldCreateNote(evt, usedCtrl)) { + this.createNoteAndMark(folderPath); + return; + } + if (!(folderNote instanceof import_obsidian51.TFile)) + return; + if (!this.shouldOpenNote(usedCtrl, evt)) + return; + if (!this.settings.enableCollapsing || usedCtrl) { + evt.preventDefault(); + evt.stopImmediatePropagation(); + } + void openFolderNote(this, folderNote, evt); + } + isMobileClickDisabled() { + return import_obsidian51.Platform.isMobile && this.settings.disableOpenFolderNoteOnClick; + } + getFolderTitleInfo(target) { + const folderTitleEl = target.closest(".nav-folder-title"); + const onlyClickedOnFolderTitle = !!target.closest(".nav-folder-title-content"); + return { + folderTitleEl: folderTitleEl instanceof HTMLElement ? folderTitleEl : null, + onlyClickedOnFolderTitle + }; + } + shouldIgnoreClickByWhitespaceOrCollapse(target, onlyClickedOnFolderTitle) { + if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) + return true; + if (target.closest(".collapse-icon")) + return true; + return false; + } + getValidFolderPath(folderTitleEl) { + const folderPath = folderTitleEl.getAttribute("data-path"); + if (!folderPath) + return null; + const excludedFolder = getExcludedFolder(this, folderPath, true); + if (excludedFolder == null ? void 0 : excludedFolder.disableFolderNote) + return null; + return folderPath; + } + isCtrlUsed(evt) { + return import_obsidian51.Platform.isMacOS ? evt.metaKey : evt.ctrlKey; + } + shouldCreateNote(evt, usedCtrl) { + const isTabMod = import_obsidian51.Keymap.isModEvent(evt) === "tab"; + if (!(evt.altKey || isTabMod)) + return false; + return this.settings.altKey && evt.altKey || usedCtrl && this.settings.ctrlKey; + } + createNoteAndMark(folderPath) { + void createFolderNote(this, folderPath, true, void 0, true); + void addCSSClassToFileExplorerEl(folderPath, "has-folder-note", false, this); + void removeCSSClassFromFileExplorerEL(folderPath, "has-not-folder-note", false, this); + } + shouldOpenNote(usedCtrl, evt) { + if (this.settings.openWithCtrl && !usedCtrl) + return false; + if (this.settings.openWithAlt && !evt.altKey) + return false; + return true; + } + handleOverviewBlock(source, el, ctx) { + const observer = new MutationObserver(() => { + var _a; + const editButton = (_a = el.parentElement) == null ? void 0 : _a.childNodes.item(1); + if (editButton) { + editButton.addEventListener("click", (e) => { + e.stopImmediatePropagation(); + e.preventDefault(); + e.stopPropagation(); + new FolderOverviewSettings(this.app, this, (0, import_obsidian51.parseYaml)(source), ctx, el, this.settings.defaultOverview).open(); + }, { capture: true }); + } + }); + observer.observe(el, { + childList: true, + subtree: true + }); + try { + if (this.app.workspace.layoutReady) { + const { defaultOverview } = this.settings; + const folderOverview = new FolderOverview(this, ctx, source, el, defaultOverview); + void folderOverview.create(this, el, ctx); + } else { + this.app.workspace.onLayoutReady(() => { + const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview); + void folderOverview.create(this, el, ctx); + }); + } + } catch (e) { + new import_obsidian51.Notice("Error creating folder overview (folder notes plugin) - check console for more details"); + console.error(e); + } + } + async activateOverviewView() { + const { workspace } = this.app; + let leaf = null; + const leaves = workspace.getLeavesOfType(FOLDER_OVERVIEW_VIEW); + if (leaves.length > 0) { + leaf = leaves[0]; + } else { + leaf = workspace.getRightLeaf(false); + await (leaf == null ? void 0 : leaf.setViewState({ type: FOLDER_OVERVIEW_VIEW, active: true })); + } + if (!leaf) + return; + void workspace.revealLeaf(leaf); + } + isEmptyFolderNoteFolder(folder) { + let attachmentFolderPath = this.app.vault.getConfig("attachmentFolderPath"); + const cleanAttachmentFolderPath = (attachmentFolderPath == null ? void 0 : attachmentFolderPath.replace("./", "")) || ""; + const attachmentsAreInRootFolder = attachmentFolderPath === "./" || attachmentFolderPath === ""; + const threshold = this.settings.storageLocation === "insideFolder" ? 1 : 0; + if (folder.children.length === 0) { + void addCSSClassToFileExplorerEl(folder.path, "fn-empty-folder", false, this); + } + attachmentFolderPath = `${folder.path}/${cleanAttachmentFolderPath}`; + if (folder.children.length === threshold) { + void addCSSClassToFileExplorerEl(folder.path, "fn-empty-folder", false, this); + return true; + } else if (folder.children.length > threshold) { + if (attachmentsAreInRootFolder) { + return false; + } else if (this.app.vault.getAbstractFileByPath(attachmentFolderPath) instanceof import_obsidian51.TFolder) { + const attachmentFolder = this.app.vault.getAbstractFileByPath(attachmentFolderPath); + if (attachmentFolder instanceof import_obsidian51.TFolder && folder.children.length <= threshold + 1) { + void addCSSClassToFileExplorerEl(folder.path, "fn-empty-folder", false, this); + void addCSSClassToFileExplorerEl(folder.path, "fn-has-attachment-folder", false, this); + void addCSSClassToFileExplorerEl(folder.path, "fn-has-attachment-folder", true, this); + } + return folder.children.length <= threshold + 1; + } + return false; + } + return true; + } + async changeFolderNameInExplorer(folder, newName, waitForCreate = false, count = 0) { + const MAX_RETRY_COUNT = 5; + const RETRY_DELAY_MS = 500; + if (!newName) + newName = folder.name; + let fileExplorerItem = getFileExplorerElement(folder.path, this); + if (!fileExplorerItem) { + if (waitForCreate && count < MAX_RETRY_COUNT) { + await new Promise((resolve) => window.setTimeout(resolve, RETRY_DELAY_MS)); + void this.changeFolderNameInExplorer(folder, newName, waitForCreate, count + 1); + return; + } + return; + } + fileExplorerItem = fileExplorerItem == null ? void 0 : fileExplorerItem.querySelector("div.nav-folder-title-content"); + if (!fileExplorerItem) { + return; + } + if (this.settings.frontMatterTitle.explorer && this.settings.frontMatterTitle.enabled) { + fileExplorerItem.innerText = newName; + fileExplorerItem.setAttribute("old-name", folder.name); + } else { + fileExplorerItem.innerText = folder.name; + fileExplorerItem.removeAttribute("old-name"); + } + } + async changeFolderNameInPath(folder, newName, breadcrumb) { + if (!newName) + newName = folder.name; + breadcrumb.textContent = folder.newName || folder.name; + breadcrumb.setAttribute("old-name", folder.name); + breadcrumb.setAttribute("data-path", folder.path); + } + updateAllBreadcrumbs(remove) { + if (!this.settings.frontMatterTitle.path && !remove) { + return; + } + const viewHeaderItems = activeDocument.querySelectorAll("span.view-header-breadcrumb"); + const files = this.app.vault.getAllLoadedFiles().filter((file) => file instanceof import_obsidian51.TFolder); + viewHeaderItems.forEach((item) => { + if (!item.hasAttribute("data-path")) { + return; + } + const path = item.getAttribute("data-path"); + const folder = files.find((file) => file.path === path); + if (!(folder instanceof import_obsidian51.TFolder)) { + return; + } + if (remove) { + item.textContent = folder.name; + item.removeAttribute("old-name"); + } else { + item.textContent = folder.newName || folder.name; + item.setAttribute("old-name", folder.name); + item.setAttribute("data-path", folder.path); + } + }); + } + onunload() { + unregisterFileExplorerObserver(); + activeDocument.body.classList.remove("folder-notes-plugin"); + activeDocument.body.classList.remove("folder-note-underline"); + activeDocument.body.classList.remove("hide-folder-note"); + activeDocument.body.classList.remove("fn-whitespace-stop-collapsing"); + removeActiveFolder(this); + if (this.fmtpHandler) { + this.fmtpHandler.deleteEvent(); + } + } + async loadSettings() { + const data = await this.loadData(); + if (data) { + if (data.allowWhitespaceCollapsing === true) { + data.stopWhitespaceCollapsing = false; + delete data.allowWhitespaceCollapsing; + } else if (data.allowWhitespaceCollapsing === false) { + data.stopWhitespaceCollapsing = true; + delete data.allowWhitespaceCollapsing; + } + } + this.settings = Object.assign({}, DEFAULT_SETTINGS2, data); + if (!this.settings.oldFolderNoteName) { + this.settings.oldFolderNoteName = this.settings.folderNoteName; + } + if (!data) { + return; + } + const overview = data.defaultOverview; + if (!overview) { + return; + } + this.settings.defaultOverview = Object.assign({}, DEFAULT_SETTINGS2.defaultOverview, overview); + } + async saveSettings(reloadStyles) { + await this.saveData(this.settings); + if ((!this.settingsOpened || reloadStyles === true) && reloadStyles !== false) { + refreshAllFolderStyles(true, this); + } + } +}; + +/* nosourcemap */ \ No newline at end of file diff --git a/.obsidian/plugins/folder-notes/manifest.json b/.obsidian/plugins/folder-notes/manifest.json new file mode 100644 index 0000000..7e7fd5a --- /dev/null +++ b/.obsidian/plugins/folder-notes/manifest.json @@ -0,0 +1,12 @@ +{ + "id": "folder-notes", + "name": "Folder notes", + "version": "1.8.26", + "minAppVersion": "1.4.10", + "description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.", + "author": "Lost Paul", + "authorUrl": "https://github.com/LostPaul", + "fundingUrl": "https://ko-fi.com/paul305844", + "helpUrl": "https://lostpaul.github.io/obsidian-folder-notes/", + "isDesktopOnly": false +} diff --git a/.obsidian/plugins/folder-notes/styles.css b/.obsidian/plugins/folder-notes/styles.css new file mode 100644 index 0000000..217d003 --- /dev/null +++ b/.obsidian/plugins/folder-notes/styles.css @@ -0,0 +1,359 @@ +/* ========================================================================== + General States & Utilities + ========================================================================== */ + +.hide, +.hide-folder .folder-name, +.hide-folder-note .is-folder-note { + display: none; +} + +/* make.md plugin integration */ +.hide-folder-note .mk-tree-node > .mk-tree-wrapper > .dropzone > .mk-tree-item.is-folder-note { + opacity: 40%; + display: flex; +} + +.pointer-cursor, +.has-folder-note .nav-folder-title-content:hover, +.has-folder-note.view-header-breadcrumb:hover, +.nav-folder-collapse-indicator:hover, +.fn-delete-confirmation-modal-buttons span:hover, +.fn-delete-confirmation-modal-buttons input:hover { + cursor: pointer !important; +} + +.hide-folder-note :not(.show-folder-note-in-explorer):not(.fn-has-attachment-folder).only-has-folder-note .nav-folder-children { + display: none !important; +} + + +.fn-warning-text { + color: #fb464c +} + +/* ========================================================================== + Tree Items + ========================================================================== */ + +body:not(.is-grabbing) .tree-item-self.fn-is-active:hover, +body:not(.disable-folder-highlight) .tree-item-self.fn-is-active { + color: var(--nav-item-color-active); + background-color: var(--nav-item-background-active); + font-weight: var(--nav-item-weight-active); +} + + +/* ========================================================================== + Exclude Folder Settings + ========================================================================== */ + +.fn-excluded-folder-heading { + margin-top: 0 !important; + border-top: 1px solid var(--background-modifier-border); +} + +.add-exclude-folder-item, +.fn-exclude-folder-list { + padding-bottom: 0 !important; +} + +.fn-exclude-folder-list.setting-item { + border-top: 0 !important; + border-bottom: 0 !important; +} + +.fn-exclude-folder-list .setting-item-control { + display: flex; + justify-content: flex-start !important; +} + +.fn-exclude-folder-list .setting-item-info { + display: none !important; +} + +.fn-exclude-folder-list .search-input-container { + width: 100%; +} + + +/* ========================================================================== + Modal Styles + ========================================================================== */ + +.fn-backup-warning-modal .fn-modal-button-container { + display: flex; + gap: 0.5rem; + justify-content: flex-end; +} + +.fn-confirmation-modal { + padding-bottom: 0; +} + +.fn-confirmation-modal .setting-item { + border-top: 0 !important; + padding-top: 0 !important; +} + +:not(.is-phone) .fn-confirmation-modal-button { + margin-right: 0.7rem; +} + +:not(.is-phone) .fn-delete-confirmation-modal-buttons { + display: flex; + align-items: center; + margin-top: 10px; +} + +:not(.is-phone) .fn-delete-confirmation-modal-buttons .fn-confirmation-modal-button { + margin-left: auto; +} + +:not(.is-phone) .fn-delete-confirmation-modal-buttons input[type="checkbox"] { + margin-right: 5px; +} + +.is-phone .fn-delete-confirmation-modal-buttons { + display: flex; + flex-direction: column; + align-items: center; +} + +.is-phone .fn-delete-confirmation-modal-buttons .fn-confirmation-modal-button { + margin-top: 10px; +} + + +/* ========================================================================== + Folder Overview + ========================================================================== */ + +.folder-overview-container.fv-remove-edit-button .folder-overview-edit-button { + display: none; +} + +.cm-line:has(.fv-link-list-item), +li:has(.fv-link-list-item), +.el-ul:has(.fv-link-list-item), +.cm-line:has(.fv-link-list-start), +.cm-line:has(.fv-link-list-end), +.fv-hide-overview { + display: none !important; +} + + +.folder-overview-list { + margin-top: 0 !important; + margin-bottom: 0 !important; + padding-bottom: 1.200 !important; + padding-top: 1.200 !important; +} + +.folder-overview-list-item { + display: flex; +} + +.folder-overview-list::marker { + color: var(--text-faint); +} + +.folder-list::marker { + color: var(--text-normal) !important; +} + +.folder-overview-grid { + display: grid; + grid-gap: 20px; + grid-template-columns: repeat(3, 1fr); +} + +.folder-overview-grid-item { + flex: 1 1 auto; + margin: 0 1.2rem 1.2rem 0; +} + +.folder-overview-grid-item-article article { + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 15px; + flex: 1; +} + +.folder-overview-grid-item-article a { + text-decoration: none !important; +} + +.folder-overview-grid-item-article h1 { + font-size: 1.2rem; +} + +.overview-setting-item-fv { + border-top: 1px solid var(--background-modifier-border); + padding: 0.75em 0; + align-items: center; +} + +.overview-setting-item-fv .setting-item { + padding: 0; +} + + +/* ========================================================================== + File Explorer & Path Styling + ========================================================================== */ + +.folder-note-underline .has-folder-note .nav-folder-title-content { + text-decoration-line: underline; + text-decoration-color: var(--text-faint); + text-decoration-thickness: 2px; + text-underline-offset: 1px; +} + +.folder-note-underline-path .has-folder-note.view-header-breadcrumb { + text-decoration-line: underline; + text-decoration-color: var(--text-faint); + text-decoration-thickness: 1px; + text-underline-offset: 2px; +} + +.folder-note-bold .has-folder-note .nav-folder-title-content, +.folder-note-bold-path .has-folder-note.view-header-breadcrumb { + font-weight: bold; +} + +.folder-note-cursive .has-folder-note .nav-folder-title-content, +.folder-note-cursive-path .has-folder-note.view-header-breadcrumb { + font-style: italic; +} +.folder-note-hide-name-path .hide-folder-note-title-in-path .is-last-separator, +.folder-note-hide-name-path .hide-folder-note-title-in-path .path-is-folder-note { + display: none; +} + + +/* Collapse Icon Handling */ + +.fn-folder-overview-collapse-icon { + display: block !important; +} + +.fn-has-no-files .collapse-icon, +.fn-hide-collapse-icon .has-folder-note.only-has-folder-note .tree-item-icon, +body.fn-hide-collapse-icon.fn-ignore-attachment-folder .only-has-folder-note .fn-empty-folder.fn-has-attachment-folder .tree-item-icon, +body.fn-hide-collapse-icon .only-has-folder-note .fn-empty-folder:not(.fn-has-attachment-folder) .tree-item-icon, +body.fn-hide-empty-collapse-icon :not(.only-has-folder-note) > .fn-empty-folder:not(.fn-has-attachment-folder) .tree-item-icon, +body.fn-hide-collapse-icon.only-has-folder-note:not(.is-collapsed):not(.show-folder-note-in-explorer)>.nav-folder-children { + display: none; +} + + + +/* ========================================================================== + Settings Tabs + ========================================================================== */ + +.fn-settings-tab-bar { + display: flex; + flex-direction: row; + padding-bottom: 1rem; +} + +.fn-settings-tab { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--size-4-2); + padding: 10px; + border: 1px solid var(--background-modifier-border); +} + +.fn-settings-tab-active { + background-color: var(--color-accent); + color: var(--text-on-accent); +} + +.fn-settings-tab-name { + font-weight: bold; +} + +.fn-settings-tab-icon { + display: flex; +} + + +/* ========================================================================== + Suggestion Container + ========================================================================== */ + +.fn-suggestion-container { + position: absolute; + overflow: hidden; + display: flex; + flex-direction: column; + background-color: var(--background-primary); + max-width: 500px; + max-height: 300px; + border-radius: var(--radius-m); + border: 1px solid var(--background-modifier-border); + box-shadow: var(--shadow-s); + z-index: var(--layer-notice); +} + + +/* ========================================================================== + Whitelist Folder Input (Desktop & Mobile) + ========================================================================== */ + +/* Default Desktop Layout */ +.fn-whitelist-folder-input-container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + margin: 0; +} + +.fn-whitelist-folder-input-container input { + flex-grow: 1; + width: auto; + margin-right: 8px; + height: 40px; + box-sizing: border-box; +} + +.fn-whitelist-folder-buttons { + display: flex; + gap: 8px; + justify-content: flex-end; + align-items: center; + flex-grow: 0; + flex-shrink: 0; +} + +/* Mobile Overrides */ +@media (max-width: 768px) { + .fn-whitelist-folder-input-container { + display: block; + width: 100%; + text-align: center; + } + + .fn-whitelist-folder-input-container input { + width: 100%; + margin-right: 0; + } + + .fn-whitelist-folder-buttons { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + width: 100%; + } + + .is-phone .fn-overview-folder-path .setting-item-control { + display: block; + } +} diff --git a/.obsidian/plugins/gitea-cards/data.json b/.obsidian/plugins/gitea-cards/data.json new file mode 100644 index 0000000..4461045 --- /dev/null +++ b/.obsidian/plugins/gitea-cards/data.json @@ -0,0 +1,17 @@ +{ + "baseUrl": "https://git.zaugg.ca/", + "keyword": "GITEA", + "refreshIntervalSeconds": 60, + "truncationLength": 160, + "commentPreviewCount": 3, + "showCommentImages": false, + "showAttachments": false, + "defaultRepo": "", + "lastRepo": "", + "insertFormat": "card", + "repoAccents": {}, + "inProgressLabel": "In-Progress", + "secrets": { + "gitea-cards-gitea-pat": "bed6afdea0784aa54ae63ad7c3b6c8e36dba84d6" + } +} \ No newline at end of file diff --git a/.obsidian/plugins/gitea-cards/main.js b/.obsidian/plugins/gitea-cards/main.js new file mode 100644 index 0000000..830dcdb --- /dev/null +++ b/.obsidian/plugins/gitea-cards/main.js @@ -0,0 +1,29 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +"use strict";var ne=Object.defineProperty;var ft=Object.getOwnPropertyDescriptor;var bt=Object.getOwnPropertyNames;var vt=Object.prototype.hasOwnProperty;var wt=(o,r)=>{for(var e in r)ne(o,e,{get:r[e],enumerable:!0})},yt=(o,r,e,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of bt(r))!vt.call(o,s)&&s!==e&&ne(o,s,{get:()=>r[s],enumerable:!(t=ft(r,s))||t.enumerable});return o};var Ct=o=>yt(ne({},"__esModule",{value:!0}),o);var kt={};wt(kt,{COMMENTS_MAX_PREVIEW:()=>Ie,CoalescingCache:()=>$,DEFAULT_ACCENT_COLOR:()=>Be,DEFAULT_SETTINGS:()=>H,GRAPH_DIRECTIONS:()=>ot,IssueCache:()=>X,MAX_BACKOFF_MS:()=>Fe,MIN_REFRESH_SECONDS:()=>Y,SEARCH_DEFAULT_LIMIT:()=>W,SEARCH_LAYOUTS:()=>Ze,SEARCH_MAX_LIMIT:()=>T,assignGraphLayers:()=>pt,attachmentMarkdown:()=>$e,buildGraphBlock:()=>Me,buildGraphQuerySource:()=>ut,buildInlineChipReference:()=>Ye,buildIssueCardBlock:()=>Je,buildIssueUrl:()=>j,buildMilestoneUrl:()=>oe,buildMultipart:()=>qe,buildRepoUrl:()=>ie,buildSearchBlock:()=>Pe,buildSearchQuerySource:()=>st,default:()=>Z,defaultImageName:()=>Te,escapeRegExp:()=>te,formatRelativeTime:()=>de,graphNodeId:()=>le,graphQueryKey:()=>ct,graphSubtitle:()=>Pt,graphTitle:()=>dt,initialsFromLogin:()=>ue,insertAtCursor:()=>je,insertGraphBlock:()=>ht,insertIssueCard:()=>Ae,insertIssueReference:()=>Re,insertSearchBlock:()=>nt,isCloudflareBaseUrl:()=>Ke,isImageAsset:()=>Se,labelTextColor:()=>K,milestoneProgressPercent:()=>at,milestoneQueryKey:()=>rt,parseBlockedByLabel:()=>he,parseGraphQuery:()=>lt,parseIssueBlock:()=>Qe,parseIssueRef:()=>Ve,parseIssueUrl:()=>We,parseMilestoneQuery:()=>it,parseSearchQuery:()=>et,resolveImageSrc:()=>re,searchQueryKey:()=>tt,splitRepo:()=>A,stripMarkdown:()=>gt,truncate:()=>ce});module.exports=Ct(kt);var d=require("obsidian"),Ne=require("@codemirror/language"),He=require("@codemirror/state"),F=require("@codemirror/view"),Y=30,Fe=30*60*1e3,_t=0,N={pat:"gitea-cards-gitea-pat",cfClientId:"gitea-cards-cf-access-client-id",cfClientSecret:"gitea-cards-cf-access-client-secret"},Be="#4c8dff",H={baseUrl:"",keyword:"GITEA",refreshIntervalSeconds:60,truncationLength:160,commentPreviewCount:3,showCommentImages:!1,showAttachments:!1,defaultRepo:"",lastRepo:"",insertFormat:"card",repoAccents:{},inProgressLabel:"",secrets:{}},L=class extends Error{constructor(r){super(r),this.name="GiteaRequestError"}},$=class{constructor(r,e){this.keyOf=r;this.fetcher=e;this.entries=new Map}ensure(r){let e=this.entries.get(r);return e||(e={value:null,fetchedAt:0,inFlight:null,failureCount:0,nextRetryAt:0,lastError:null},this.entries.set(r,e)),e}invalidate(r){this.entries.delete(this.keyOf(r))}async load(r,e){let t=this.ensure(this.keyOf(r)),s=Date.now(),n=Math.max(0,e*.8);return t.value!==null&&s-t.fetchedAt`${e.owner}/${e.repo}#${e.index}`,r)}};function z(o){if(!o.isConnected)return!1;let r=o;return typeof r.isShown=="function"?r.isShown():o.offsetParent!==null}var xt=/^([^/\s]+)\/([^#\s]+)#(\d+)$/;function Ve(o){let r=xt.exec(o.trim());return r?{owner:r[1],repo:r[2],index:Number.parseInt(r[3],10)}:null}var Ie=20;function Qe(o){let r=null,e=null;for(let t of o.split(/\r?\n/)){let s=t.trim();if(s.length===0)continue;let n=Ve(s);if(n){r=n;continue}let i=s.indexOf(":");if(i===-1)return{block:null,error:`Expected owner/repo#index or "key: value"; got "${s}".`};let a=s.slice(0,i).trim().toLowerCase(),l=s.slice(i+1).trim();switch(a){case"comments":{let c=Number.parseInt(l,10);e=Number.isFinite(c)&&c>0?Math.min(c,Ie):0;break}default:return{block:null,error:`Unknown field "${a}".`}}}return r?{block:{ref:r,comments:e},error:null}:{block:null,error:"Expected a single reference in owner/repo#index form."}}function j(o,r){return`${o.replace(/\/+$/,"")}/${r.owner}/${r.repo}/issues/${r.index}`}function ie(o,r,e){return`${o.replace(/\/+$/,"")}/${r}/${e}`}var Et=/\/attachments\/[^/]+/i;function re(o,r){let e=r.trim();if(!e||/^(data|blob):/i.test(e))return{url:e,authed:!1};let t;try{t=new URL(o)}catch(n){return{url:e,authed:!1}}let s=null;try{s=new URL(e)}catch(n){s=null}if(!s)try{let n=o.replace(/\/+$/,"")+"/";return{url:new URL(e,n).href,authed:!0}}catch(n){return{url:e,authed:!1}}if(s.host.toLowerCase()===t.host.toLowerCase())return{url:s.href,authed:!0};if(Et.test(s.pathname)){let n=new URL(t.origin);return n.pathname=s.pathname,n.search=s.search,{url:n.href,authed:!0}}return{url:e,authed:!1}}var It=/\.(png|jpe?g|gif|webp|svg|bmp|avif|ico|tiff?)$/i;function Se(o){return It.test(o.trim())}function $e(o){let r=o.name.replace(/[\r\n\]]/g," ").trim()||"attachment",e=`/attachments/${o.uuid}`;return Se(o.name)?`![${r}](${e})`:`[${r}](${e})`}function Te(o){var e,t;return`pasted-image.${((t=(e=o.split("/")[1])==null?void 0:e.split(";")[0])==null?void 0:t.trim())||"png"}`}function je(o,r){var c,u;let e=(c=o.selectionStart)!=null?c:o.value.length,t=(u=o.selectionEnd)!=null?u:o.value.length,s=o.value.slice(0,e),n=o.value.slice(t),a=`${s&&!s.endsWith(` +`)?` +`:""}${r} +`;o.value=`${s}${a}${n}`;let l=s.length+a.length;o.setSelectionRange(l,l)}var St=0;function qe(o,r,e,t){let s=`----GiteaCards${(St++).toString(36)}${Date.now().toString(36)}`,n=r.replace(/[\r\n"]/g,"_"),i=new TextEncoder,a=i.encode(`--${s}\r +Content-Disposition: form-data; name="${o}"; filename="${n}"\r +Content-Type: ${e}\r +\r +`),l=i.encode(`\r +--${s}--\r +`),c=new Uint8Array(t),u=new Uint8Array(a.length+c.length+l.length);return u.set(a,0),u.set(c,a.length),u.set(l,a.length+c.length),{body:u.buffer,contentType:`multipart/form-data; boundary=${s}`}}var $t=/^\/([^/\s]+)\/([^/\s]+)\/issues\/(\d+)\/?$/;function We(o,r){let e,t;try{e=new URL(r.trim()),t=new URL(o)}catch(i){return null}if(e.host.toLowerCase()!==t.host.toLowerCase())return null;let s=t.pathname.replace(/\/+$/,"");if(s&&!e.pathname.startsWith(`${s}/`))return null;let n=$t.exec(e.pathname.slice(s.length));return n?{owner:n[1],repo:n[2],index:Number.parseInt(n[3],10)}:null}function Tt(o){return new RegExp(`^${te(o)}:([^\\s:/]+)/([^\\s:/]+):#(\\d+)$`)}function Lt(o){return new RegExp("`"+te(o)+":([^\\s:/`]+)/([^\\s:/`]+):#(\\d+)`","g")}function ze(o,r,e){let t=createEl("a",{cls:"gitea-chip",href:j(o,r)});return t.setAttr("target","_blank"),t.setAttr("rel","noopener"),t.setAttr("aria-label",`${r.owner}/${r.repo}#${r.index}`),e&&(t.addClass("gitea-chip--accented"),t.style.setProperty("--gitea-accent",e)),t.createSpan({cls:"gitea-chip__dot"}),t.createSpan({cls:"gitea-chip__number",text:`#${r.index}`}),t.createSpan({cls:"gitea-chip__title"}),t.createSpan({cls:"gitea-chip__state"}),t}function ae(o,r){let e=r.trim().toLowerCase();return e?o.labels.some(t=>t.name.trim().toLowerCase()===e):!1}function Le(o,r,e){var c;o.setAttr("aria-label",`#${r.number} \u2014 ${r.title}`);let t=r.state==="closed",s=!t&&ae(r,e);o.toggleClass("gitea-chip--closed",t);let n=t?"closed":s?"in-progress":"open",i=s?"in progress":n,a=o.querySelector(".gitea-chip__dot");a&&(a.removeClass("gitea-chip__dot--open","gitea-chip__dot--closed","gitea-chip__dot--in-progress"),a.addClass(`gitea-chip__dot--${n}`)),(c=o.querySelector(".gitea-chip__title"))==null||c.setText(r.title);let l=o.querySelector(".gitea-chip__state");l&&(l.removeClass("gitea-chip__state--open","gitea-chip__state--closed","gitea-chip__state--in-progress"),l.addClass(`gitea-chip__state--${n}`),l.setText(i))}function At(o){let r=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(o);if(!r)return!1;let e=Number.parseInt(r[1],10),t=Number.parseInt(r[2],10);return e===0||e===10||e===127||e===192&&t===168||e===172&&t>=16&&t<=31||e===169&&t===254||e===100&&t>=64&&t<=127}function Ke(o){let r;try{r=new URL(o).hostname.toLowerCase()}catch(e){return!1}return r=r.replace(/^\[/,"").replace(/\]$/,""),!(r.length===0||r==="localhost"||r.endsWith(".localhost")||/\.(local|lan|internal|home|ts\.net)$/.test(r)||At(r)||r==="::1"||/^f[cd]/.test(r)||r.startsWith("fe80:"))}function J(o,r){var s,n;let e=(s=o.headers)!=null?s:{},t=r.toLowerCase();for(let i of Object.keys(e))if(i.toLowerCase()===t)return(n=e[i])!=null?n:"";return""}function te(o){return o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Rt=/^([^/\s]+)\/([^/\s]+)$/;function A(o){let r=Rt.exec(o.trim());return r?{owner:r[1],repo:r[2]}:null}function Je(o){return["```gitea-issue",`${o.owner}/${o.repo}#${o.index}`,"```",""].join(` +`)}function Ye(o,r){return`\`${o}:${r.owner}/${r.repo}:#${r.index}\``}function Ae(o,r){let e=o.getCursor("from"),s=o.getLine(e.line).slice(0,e.ch).trim().length>0?` +`:"";o.replaceSelection(s+Je(r))}function Re(o,r,e,t){e!=="none"&&(e==="inline"?o.replaceSelection(Ye(t,r)):Ae(o,r))}var W=10,T=50,Xe=["open","closed","all"],Ze=["table","cards"];function D(o){return o.split(",").map(r=>r.trim()).filter(r=>r.length>0)}function et(o){let r={repo:null,state:"open",labels:[],milestones:[],q:null,limit:W,layout:"table"};for(let e of o.split(/\r?\n/)){let t=e.trim();if(t.length===0)continue;let s=t.indexOf(":");if(s===-1)return{query:null,error:`Expected "key: value" lines; got "${t}".`};let n=t.slice(0,s).trim().toLowerCase(),i=t.slice(s+1).trim();switch(n){case"repo":r.repo=i.length>0?i:null;break;case"state":{let a=i.toLowerCase();if(!Xe.includes(a))return{query:null,error:`Unknown state "${i}" \u2014 use open, closed, or all.`};r.state=a;break}case"label":case"labels":r.labels=D(i);break;case"milestone":case"milestones":r.milestones=D(i);break;case"q":case"query":r.q=i.length>0?i:null;break;case"limit":{let a=Number.parseInt(i,10);Number.isFinite(a)&&a>0&&(r.limit=Math.min(a,T));break}case"layout":case"view":{let a=i.toLowerCase(),l=a==="card"?"cards":a;if(!Ze.includes(l))return{query:null,error:`Unknown layout "${i}" \u2014 use table or cards.`};r.layout=l;break}default:return{query:null,error:`Unknown field "${n}".`}}}return r.repo&&!/^[^/\s]+\/[^/\s]+$/.test(r.repo)?{query:null,error:`Invalid repo "${r.repo}" \u2014 use owner/repo.`}:{query:r,error:null}}function tt(o){return JSON.stringify({repo:o.repo,state:o.state,labels:[...o.labels].sort(),milestones:[...o.milestones].sort(),q:o.q,limit:o.limit})}function st(o){let r=[];return o.repo&&r.push(`repo: ${o.repo}`),o.state!=="open"&&r.push(`state: ${o.state}`),o.labels.length>0&&r.push(`labels: ${o.labels.join(", ")}`),o.milestones.length>0&&r.push(`milestones: ${o.milestones.join(", ")}`),o.q&&r.push(`query: ${o.q}`),o.limit!==W&&r.push(`limit: ${o.limit}`),o.layout!=="table"&&r.push(`layout: ${o.layout}`),r.join(` +`)}function Pe(o){let r=st(o);return"```gitea-search"+(r.length>0?` +${r}`:"")+"\n```\n"}function nt(o,r){let e=o.getCursor("from"),s=o.getLine(e.line).slice(0,e.ch).trim().length>0?` +`:"";o.replaceSelection(s+Pe(r))}function it(o){let r="",e="";for(let t of o.split(/\r?\n/)){let s=t.trim();if(s.length===0)continue;let n=s.indexOf(":");if(n===-1)return{query:null,error:`Expected "key: value" lines; got "${s}".`};let i=s.slice(0,n).trim().toLowerCase(),a=s.slice(n+1).trim();switch(i){case"repo":r=a;break;case"milestone":e=a;break;default:return{query:null,error:`Unknown field "${i}".`}}}return r?/^[^/\s]+\/[^/\s]+$/.test(r)?e?{query:{repo:r,milestone:e},error:null}:{query:null,error:"Missing milestone \u2014 add a milestone: name line."}:{query:null,error:`Invalid repo "${r}" \u2014 use owner/repo.`}:{query:null,error:"Missing repo \u2014 add a repo: owner/repo line."}}function rt(o){return`${o.repo}!milestone:${o.milestone}`}function at(o,r){let e=o+r;return e<=0?0:Math.round(r/e*100)}function oe(o,r,e){return`${o.replace(/\/+$/,"")}/${r}/milestone/${e}`}var ot=["TB","LR"];function le(o){return`${o.owner}/${o.repo}#${o.index}`}function lt(o){let r={repo:null,state:"all",labels:[],milestones:[],q:null,limit:T,direction:"TB"};for(let e of o.split(/\r?\n/)){let t=e.trim();if(t.length===0)continue;let s=t.indexOf(":");if(s===-1)return{query:null,error:`Expected "key: value" lines; got "${t}".`};let n=t.slice(0,s).trim().toLowerCase(),i=t.slice(s+1).trim();switch(n){case"repo":r.repo=i.length>0?i:null;break;case"state":{let a=i.toLowerCase();if(!Xe.includes(a))return{query:null,error:`Unknown state "${i}" \u2014 use open, closed, or all.`};r.state=a;break}case"label":case"labels":r.labels=D(i);break;case"milestone":case"milestones":r.milestones=D(i);break;case"q":case"query":r.q=i.length>0?i:null;break;case"limit":{let a=Number.parseInt(i,10);Number.isFinite(a)&&a>0&&(r.limit=Math.min(a,T));break}case"direction":case"dir":{let a=i.toUpperCase()==="TD"?"TB":i.toUpperCase();if(!ot.includes(a))return{query:null,error:`Unknown direction "${i}" \u2014 use TB or LR.`};r.direction=a;break}default:return{query:null,error:`Unknown field "${n}".`}}}return r.repo&&!/^[^/\s]+\/[^/\s]+$/.test(r.repo)?{query:null,error:`Invalid repo "${r.repo}" \u2014 use owner/repo.`}:{query:r,error:null}}function ct(o){return"graph!"+JSON.stringify({repo:o.repo,state:o.state,labels:[...o.labels].sort(),milestones:[...o.milestones].sort(),q:o.q,limit:o.limit})}function dt(o,r){var t;let e=(t=o.repo)!=null?t:r.length>0?r:null;return e!=null?e:"All repositories"}function Pt(o){return o.milestones.length===0?"":`${o.milestones.length===1?"milestone":"milestones"} ${o.milestones.join(", ")}`}function ut(o){let r=[];return o.repo&&r.push(`repo: ${o.repo}`),o.state!=="all"&&r.push(`state: ${o.state}`),o.labels.length>0&&r.push(`labels: ${o.labels.join(", ")}`),o.milestones.length>0&&r.push(`milestones: ${o.milestones.join(", ")}`),o.q&&r.push(`query: ${o.q}`),o.limit!==T&&r.push(`limit: ${o.limit}`),o.direction!=="TB"&&r.push(`direction: ${o.direction}`),r.join(` +`)}function Me(o){let r=ut(o);return"```gitea-graph"+(r.length>0?` +${r}`:"")+"\n```\n"}function ht(o,r){let e=o.getCursor("from"),s=o.getLine(e.line).slice(0,e.ch).trim().length>0?` +`:"";o.replaceSelection(s+Me(r))}function pt(o,r){var a;let e=new Set(o.map(l=>l.id)),t=new Map;for(let l of o)t.set(l.id,[]);for(let l of r)e.has(l.from)&&e.has(l.to)&&((a=t.get(l.to))==null||a.push(l.from));let s=new Map,n=new Set,i=l=>{var h;let c=s.get(l);if(c!==void 0)return c;if(n.has(l))return 0;n.add(l);let u=0;for(let p of(h=t.get(l))!=null?h:[])u=Math.max(u,i(p)+1);return n.delete(l),s.set(l,u),u};for(let l of o)i(l.id);return s}function gt(o){var e;return((e=o.split(/\r?\n/,1)[0])!=null?e:"").replace(/^\s{0,3}(#{1,6}\s+|>\s+|[-*+]\s+)/,"").replace(/!?\[([^\]]*)\]\([^)]*\)/g,"$1").replace(/`([^`]*)`/g,"$1").replace(/[*_~]/g,"").replace(/\s+/g," ").trim()}function ce(o,r){return r<=0||o.length<=r?o:`${o.slice(0,r).trimEnd()}\u2026`}function de(o,r=Date.now()){let e=Date.parse(o);if(Number.isNaN(e))return"";let t=Math.max(0,Math.round((r-e)/1e3));if(t<45)return"just now";let s=Math.round(t/60);if(s<60)return`${s}m ago`;let n=Math.round(s/60);if(n<24)return`${n}h ago`;let i=Math.round(n/24);if(i<7)return`${i}d ago`;let a=Math.round(i/7);if(a<5)return`${a}w ago`;let l=Math.round(i/30);return l<12?`${l}mo ago`:`${Math.round(i/365)}y ago`}function ue(o){let r=o.split(/[\s._-]+/).filter(Boolean);return r.length===0?"?":r.length===1?r[0].slice(0,2).toUpperCase():`${r[0][0]}${r[1][0]}`.toUpperCase()}function he(o){let r=/^blocked-by:#(\d+)$/.exec(o.trim());return r?Number.parseInt(r[1],10):null}function K(o){let r=o.replace(/^#/,"");if(r.length!==6)return"#ffffff";let e=Number.parseInt(r.slice(0,2),16),t=Number.parseInt(r.slice(2,4),16),s=Number.parseInt(r.slice(4,6),16);return(.299*e+.587*t+.114*s)/255>.6?"#000000":"#ffffff"}var Z=class extends d.Plugin{constructor(){super(...arguments);this.settings=H;this.cache=new X(e=>this.fetchIssue(e));this.commentsCache=new $(e=>`${e.owner}/${e.repo}#${e.index}`,e=>this.fetchComments(e));this.searchCache=new $(tt,e=>this.fetchSearchIssues(e));this.milestoneCache=new $(rt,e=>this.fetchMilestone(e));this.graphCache=new $(ct,e=>this.fetchGraph(e));this.repoListCache=new $(()=>"user-repos",()=>this.fetchRepos());this.inlineIssueCache=new $(e=>e,e=>this.fetchInlineIssues(e));this.repoWriteCache=new $(e=>e,e=>this.fetchRepoCanWrite(e));this.imageCache=new $(e=>`img:${e}`,e=>this.fetchAuthedImage(e));this.assetsCache=new $(e=>`assets:${e.owner}/${e.repo}#${e.index}`,e=>this.fetchIssueAssets(e))}async onload(){await this.loadSettings(),this.addSettingTab(new Ee(this.app,this)),this.registerMarkdownCodeBlockProcessor("gitea-issue",(e,t,s)=>{t.empty();let{block:n,error:i}=Qe(e);if(!n){this.renderError(t,i!=null?i:"Expected a single reference in owner/repo#index form.");return}s.addChild(new pe(t,this,n.ref,s.sourcePath,n.comments))}),this.registerMarkdownCodeBlockProcessor("gitea-search",(e,t,s)=>{t.empty();let{query:n,error:i}=et(e);if(!n){this.renderError(t,i!=null?i:"Invalid search query.");return}s.addChild(new ge(t,this,n))}),this.registerMarkdownCodeBlockProcessor("gitea-milestone",(e,t,s)=>{t.empty();let{query:n,error:i}=it(e);if(!n){this.renderError(t,i!=null?i:"Invalid milestone query.");return}s.addChild(new me(t,this,n))}),this.registerMarkdownCodeBlockProcessor("gitea-graph",(e,t,s)=>{t.empty();let{query:n,error:i}=lt(e);if(!n){this.renderError(t,i!=null?i:"Invalid graph query.");return}s.addChild(new fe(t,this,n))}),this.addCommand({id:"new-issue",name:"New issue",callback:()=>{var t,s;let e=(s=(t=this.app.workspace.getActiveViewOfType(d.MarkdownView))==null?void 0:t.editor)!=null?s:null;new q(this,{editor:e}).open()}}),this.addCommand({id:"convert-selection-to-issue",name:"Convert selection to issue",editorCallback:e=>{new q(this,{editor:e,body:e.getSelection()}).open()}}),this.addCommand({id:"insert-existing-issue",name:"Insert existing issue",editorCallback:e=>{new ye(this,e).open()}}),this.addCommand({id:"build-search-query",name:"Build search query",editorCallback:e=>{new Ce(this,e).open()}}),this.addCommand({id:"build-graph-view",name:"Build graph view",editorCallback:e=>{new _e(this,e).open()}}),this.registerEditorSuggest(new we(this)),this.registerMarkdownPostProcessor((e,t)=>{this.processInlineChips(e,t)}),this.registerEditorExtension(Mt(this)),this.registerEvent(this.app.workspace.on("editor-paste",(e,t)=>{var i;if(e.defaultPrevented)return;let s=(i=e.clipboardData)==null?void 0:i.getData("text/plain");if(!s)return;let n=We(this.settings.baseUrl,s);n&&(e.preventDefault(),Ae(t,n))}))}async loadSettings(){var t,s;let e=await this.loadData();this.settings=Object.assign({},H,e!=null?e:{}),this.settings.secrets={...(t=e==null?void 0:e.secrets)!=null?t:{}},this.settings.repoAccents={...(s=e==null?void 0:e.repoAccents)!=null?s:{}}}async saveSettings(){await this.saveData(this.settings)}get refreshIntervalMs(){return Math.max(Y,this.settings.refreshIntervalSeconds)*1e3}async getSecret(e){var t;return(t=this.settings.secrets[e])!=null?t:null}async setSecret(e,t){t.length===0?delete this.settings.secrets[e]:this.settings.secrets[e]=t,await this.saveSettings()}async buildAuthHeaders(){let e={Accept:"application/json"},t=await this.getSecret(N.pat);if(t&&(e.Authorization=`token ${t}`),Ke(this.settings.baseUrl)){let s=await this.getSecret(N.cfClientId),n=await this.getSecret(N.cfClientSecret);s&&n&&(e["CF-Access-Client-Id"]=s,e["CF-Access-Client-Secret"]=n)}return e}ensureGiteaStatusOk(e,t){if(e.status>=300&&e.status<400)throw J(e,"location").includes("cloudflareaccess.com")?new L("Cloudflare Access rejected the service token."):new L(`Unexpected redirect (HTTP ${e.status}).`);if(e.status===401||e.status===403)throw J(e,"content-type").toLowerCase().includes("application/json")?new L("Gitea token invalid or insufficient scope."):new L("Cloudflare Access rejected the service token.");if(e.status===404)throw new L(t);if(e.status>=400)throw new L(`Gitea returned HTTP ${e.status}.`)}ensureGiteaOk(e,t){this.ensureGiteaStatusOk(e,t);let s=J(e,"content-type").toLowerCase();if(s&&!s.includes("json"))throw new L("Gitea returned a non-JSON response \u2014 check the base URL points at your Gitea server, not a login or proxy page.")}async testConnection(){var c,u,h,p;let e=this.settings.baseUrl.replace(/\/+$/,"");if(!e)throw new L("Set the Gitea base URL first.");let t=await this.buildAuthHeaders(),s=await(0,d.requestUrl)({url:`${e}/api/v1/version`,method:"GET",headers:t,throw:!1});this.ensureGiteaOk(s,"No Gitea API found at this base URL.");let n=(u=(c=s.json)==null?void 0:c.version)!=null?u:"unknown";if(!!!await this.getSecret(N.pat))return`Reached Gitea ${n}, but no API token is set.`;let a=await(0,d.requestUrl)({url:`${e}/api/v1/user`,method:"GET",headers:t,throw:!1});this.ensureGiteaOk(a,"Could not look up the token's user.");let l=(p=(h=a.json)==null?void 0:h.login)!=null?p:"unknown";return`Connected to Gitea ${n} as ${l}.`}async fetchIssue(e){let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${encodeURIComponent(e.owner)}/${encodeURIComponent(e.repo)}/issues/${e.index}`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Issue not found."),i.json}async fetchComments(e){var a;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/comments`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Issue not found."),(a=i.json)!=null?a:[]}async fetchIssueAssets(e){var a;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/assets`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Issue not found."),(a=i.json)!=null?a:[]}async fetchAuthedImage(e){let t=await this.buildAuthHeaders(),s=await(0,d.requestUrl)({url:e,method:"GET",headers:t,throw:!1});return this.ensureGiteaStatusOk(s,"Image not found."),{data:s.arrayBuffer,contentType:J(s,"content-type")||"application/octet-stream"}}async loadAuthedImage(e,t){var a;let s=await this.imageCache.load(e,this.refreshIntervalMs);if(!s.value)throw(a=s.error)!=null?a:new L("Image could not be loaded.");let n=new Blob([s.value.data],{type:s.value.contentType}),i=URL.createObjectURL(n);return t.register(()=>URL.revokeObjectURL(i)),i}buildSearchParams(e){let t=new URLSearchParams;return t.set("type","issues"),t.set("state",e.state),t.set("limit",String(e.limit)),t.set("page","1"),e.labels.length>0&&t.set("labels",e.labels.join(",")),e.milestones.length>0&&t.set("milestones",e.milestones.join(",")),e.q&&t.set("q",e.q),t.toString()}refForResult(e,t){var i,a,l;let s=(l=(a=e.repo)!=null?a:(i=t.repository)==null?void 0:i.full_name)!=null?l:"",n=s.indexOf("/");return n<=0||n===s.length-1?null:{owner:s.slice(0,n),repo:s.slice(n+1),index:t.number}}async fetchSearchIssues(e){var u;let t=this.settings.baseUrl.replace(/\/+$/,""),s=this.buildSearchParams(e),n=e.repo?`${t}/api/v1/repos/${e.repo.split("/").map(encodeURIComponent).join("/")}/issues?${s}`:`${t}/api/v1/repos/issues/search?${s}`,i=await this.buildAuthHeaders(),a=await(0,d.requestUrl)({url:n,method:"GET",headers:i,throw:!1});this.ensureGiteaOk(a,e.repo?"Repository not found.":"Search endpoint not found.");let l=(u=a.json)!=null?u:[],c=[];for(let h of l){if(h.pull_request!=null)continue;let p=this.refForResult(e,h);p&&c.push({ref:p,issue:h})}return c}async fetchMilestone(e){let t=this.settings.baseUrl.replace(/\/+$/,""),s=e.repo.split("/").map(encodeURIComponent).join("/"),n=`${t}/api/v1/repos/${s}/milestones/${encodeURIComponent(e.milestone)}`,i=await this.buildAuthHeaders(),a=await(0,d.requestUrl)({url:n,method:"GET",headers:i,throw:!1});return this.ensureGiteaOk(a,"Milestone not found."),a.json}graphRepo(e){var t;return(t=e.repo)!=null?t:this.settings.defaultRepo||null}graphToSearchQuery(e){return{repo:this.graphRepo(e),state:e.state,labels:e.labels,milestones:e.milestones,q:e.q,limit:e.limit,layout:"table"}}async assertMilestonesExist(e,t){let s=this.settings.baseUrl.replace(/\/+$/,""),n=this.repoPath(e),i=await this.buildAuthHeaders(),a=[];if(await Promise.all(t.map(async l=>{let c=`${s}/api/v1/repos/${n}/milestones/${encodeURIComponent(l)}`,u=await(0,d.requestUrl)({url:c,method:"GET",headers:i,throw:!1});if(u.status===404){a.push(l);return}this.ensureGiteaOk(u,`Milestone "${l}" not found.`)})),a.length>0){let l=a.length===1?"Milestone":"Milestones",c=a.map(u=>`"${u}"`).join(", ");throw new L(`${l} ${c} not found in ${e} \u2014 check the milestone name.`)}}async fetchIssueDependencies(e){var a;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/dependencies`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Issue not found."),(a=i.json)!=null?a:[]}async fetchGraph(e){let t=this.graphRepo(e);e.milestones.length>0&&t&&await this.assertMilestonesExist(t,e.milestones);let s=await this.fetchSearchIssues(this.graphToSearchQuery(e)),n=new Map;for(let p of s)n.set(le(p.ref),p);let i=await Promise.all(s.map(p=>this.fetchIssueDependencies(p.ref))),a=[],l=[],c={},u=new Set,h=(p,b)=>{if(p===b||!n.has(p)||!n.has(b))return;let g=`${p}->${b}`;u.has(g)||(u.add(g),l.push({from:p,to:b}))};return s.forEach((p,b)=>{var G,R;let{ref:g,issue:v}=p,y=le(g),x=v.state==="closed",w=!x&&ae(v,this.settings.inProgressLabel),M=!1;for(let m of i[b]){if(m.pull_request!=null)continue;let P=`${(R=(G=m.repository)==null?void 0:G.full_name)!=null?R:`${g.owner}/${g.repo}`}#${m.number}`;h(P,y),!x&&m.state==="open"&&(M=!0)}for(let m of v.labels){let S=he(m.name);if(S==null)continue;let P=`${g.owner}/${g.repo}#${S}`;h(P,y);let O=n.get(P);!x&&O&&O.issue.state==="open"&&(M=!0)}v.milestone&&typeof v.milestone.id=="number"&&(c[v.milestone.title]=v.milestone.id),a.push({id:y,ref:g,number:v.number,title:v.title,state:x?"closed":"open",inProgress:w,blocked:M})}),{nodes:a,edges:l,milestoneIds:c}}repoPath(e){return e.split("/").map(encodeURIComponent).join("/")}async fetchRepos(){var a;let t=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/user/repos?limit=50`,s=await this.buildAuthHeaders(),n=await(0,d.requestUrl)({url:t,method:"GET",headers:s,throw:!1});return this.ensureGiteaOk(n,"Could not list repositories."),((a=n.json)!=null?a:[]).map(l=>l.full_name).filter(l=>typeof l=="string"&&l.length>0)}async fetchInlineIssues(e){var l;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.repoPath(e)}/issues?type=issues&state=all&limit=50`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Repository not found."),((l=i.json)!=null?l:[]).filter(c=>c.pull_request==null).map(c=>({index:c.number,title:c.title,state:c.state}))}async loadRepoList(){var t;return(t=(await this.repoListCache.load(null,this.refreshIntervalMs)).value)!=null?t:[]}async loadInlineIssues(e){var s;return(s=(await this.inlineIssueCache.load(e,this.refreshIntervalMs)).value)!=null?s:[]}async fetchLabels(e){var a;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.repoPath(e)}/labels?limit=50`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Repository not found."),(a=i.json)!=null?a:[]}async fetchMilestones(e){var a;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.repoPath(e)}/milestones?state=open&limit=50`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});return this.ensureGiteaOk(i,"Repository not found."),(a=i.json)!=null?a:[]}async createIssue(e,t){let n=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.repoPath(e)}/issues`,i=await this.buildAuthHeaders();i["Content-Type"]="application/json";let a=await(0,d.requestUrl)({url:n,method:"POST",headers:i,body:JSON.stringify(t),throw:!1});return this.ensureGiteaOk(a,"Repository not found."),a.json}issuePath(e){return`${encodeURIComponent(e.owner)}/${encodeURIComponent(e.repo)}/issues/${e.index}`}async giteaMutate(e,t,s){let n=await this.buildAuthHeaders();s!==void 0&&(n["Content-Type"]="application/json");let i=await(0,d.requestUrl)({url:e,method:t,headers:n,body:s!==void 0?JSON.stringify(s):void 0,throw:!1});return this.ensureGiteaOk(i,"Issue not found."),i}async fetchRepoCanWrite(e){var l,c;let s=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.repoPath(e)}`,n=await this.buildAuthHeaders(),i=await(0,d.requestUrl)({url:s,method:"GET",headers:n,throw:!1});this.ensureGiteaOk(i,"Repository not found.");let a=i.json;return!!((l=a.permissions)!=null&&l.push||(c=a.permissions)!=null&&c.admin)}async loadCanWriteIssues(e){return this.loadRepoCanWrite(`${e.owner}/${e.repo}`)}async loadRepoCanWrite(e){var s;return(s=(await this.repoWriteCache.load(e,this.refreshIntervalMs)).value)!=null?s:!1}async setIssueState(e,t){let n=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}`;await this.giteaMutate(n,"PATCH",{state:t})}async addIssueComment(e,t){let n=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/comments`;await this.giteaMutate(n,"POST",{body:t})}async editIssueBody(e,t){let n=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}`;await this.giteaMutate(n,"PATCH",{body:t})}async uploadAsset(e,t,s,n){let i=await this.buildAuthHeaders(),{body:a,contentType:l}=qe("attachment",t,s,n);i["Content-Type"]=l;let c=await(0,d.requestUrl)({url:e,method:"POST",headers:i,body:a,throw:!1});return this.ensureGiteaOk(c,"Issue not found."),c.json}async uploadIssueAsset(e,t,s,n){let a=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/assets`;return this.uploadAsset(a,t,s,n)}async uploadCommentAsset(e,t,s,n,i){let l=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.repoPath(e)}/issues/comments/${t}/assets`;return this.uploadAsset(l,s,n,i)}async addIssueLabels(e,t){let n=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/labels`;await this.giteaMutate(n,"POST",{labels:t})}async removeIssueLabel(e,t){let n=`${this.settings.baseUrl.replace(/\/+$/,"")}/api/v1/repos/${this.issuePath(e)}/labels/${t}`;await this.giteaMutate(n,"DELETE")}renderCardResult(e,t,s,n,i){var a,l;if(e.empty(),s.value){this.renderIssueCard(e,t,s.value,s.stale,n,i);return}this.renderError(e,(l=(a=s.error)==null?void 0:a.message)!=null?l:"Failed to load issue.")}renderSearchResult(e,t,s,n){var a,l;if(e.empty(),!s.value){this.renderError(e,(l=(a=s.error)==null?void 0:a.message)!=null?l:"Failed to load issues.");return}let i=e.createDiv({cls:"gitea-search"});if(s.stale&&i.createDiv({cls:"gitea-search__notice"}).setText("Gitea is unreachable \u2014 showing last-known results."),n&&this.renderCornerRefresh(i,"Refresh results now",n),this.renderSearchToolbar(i,t),s.value.length===0){i.createDiv({cls:"gitea-card gitea-card--empty",text:"No issues match this query."});return}if(t.layout==="table"){this.renderSearchTable(i,t,s.value);return}for(let c of s.value)this.renderIssueCard(i,c.ref,c.issue,!1)}renderSearchToolbar(e,t){let s=e.createDiv({cls:"gitea-search__toolbar"});new d.ButtonComponent(s).setIcon("plus").setButtonText("Add issue").setTooltip("Create an issue prefilled from this search").onClick(()=>{var a,l,c;let n=(l=(a=this.app.workspace.getActiveViewOfType(d.MarkdownView))==null?void 0:a.editor)!=null?l:null,i=t.repo!==null;new q(this,{editor:n,repo:(c=t.repo)!=null?c:void 0,labels:i?t.labels:void 0,milestone:i&&t.milestones.length===1?t.milestones[0]:void 0,defaultInsertFormat:"none"}).open()})}renderSearchTable(e,t,s){let n=t.repo===null,a=e.createEl("table",{cls:"gitea-search-table"}).createEl("tbody");for(let{ref:l,issue:c}of s){let u=c.state==="closed",h=!u&&ae(c,this.settings.inProgressLabel),p=u?"closed":h?"in-progress":"open",b=h?"in progress":p,g=a.createEl("tr",{cls:"gitea-search-table__row"});u&&g.addClass("gitea-search-table__row--closed");let v=this.settings.repoAccents[`${l.owner}/${l.repo}`];v&&(g.addClass("gitea-search-table__row--accented"),g.style.setProperty("--gitea-accent",v)),g.createEl("td",{cls:"gitea-search-table__cell gitea-search-table__cell--dot"}).createSpan({cls:`gitea-search-table__dot gitea-search-table__dot--${p}`});let w=g.createEl("td",{cls:"gitea-search-table__cell gitea-search-table__cell--number"}).createEl("a",{cls:"gitea-search-table__number",text:`#${c.number}`,href:j(this.settings.baseUrl,l)});w.setAttr("target","_blank"),w.setAttr("rel","noopener"),n&&g.createEl("td",{cls:"gitea-search-table__cell gitea-search-table__cell--repo",text:`${l.owner}/${l.repo}`}),g.createEl("td",{cls:"gitea-search-table__cell gitea-search-table__cell--title",text:c.title}),g.createEl("td",{cls:"gitea-search-table__cell gitea-search-table__cell--state"}).createSpan({cls:`gitea-search-table__state gitea-search-table__state--${p}`,text:b})}}renderMilestoneResult(e,t,s){var n,i;if(e.empty(),!s.value){this.renderError(e,(i=(n=s.error)==null?void 0:n.message)!=null?i:"Failed to load milestone.");return}this.renderMilestoneCard(e,t,s.value,s.stale)}renderMilestoneCard(e,t,s,n=!1){var x,w;let i=(x=s.open_issues)!=null?x:0,a=(w=s.closed_issues)!=null?w:0,l=i+a,c=at(i,a),u=s.state==="closed",h=e.createDiv({cls:"gitea-card gitea-milestone"});this.applyRepoAccent(h,t.repo),u&&h.addClass("gitea-card--closed"),n&&h.addClass("gitea-card--stale");let p=h.createDiv({cls:"gitea-milestone__header"}),b=p.createEl("a",{cls:"gitea-milestone__title",text:s.title,href:oe(this.settings.baseUrl,t.repo,s.id)});b.setAttr("target","_blank"),b.setAttr("rel","noopener"),n&&p.createSpan({cls:"gitea-card__stale-badge",text:"stale"}).setAttr("aria-label","Showing last-known data \u2014 Gitea is currently unreachable"),p.createSpan({cls:"gitea-milestone__percent",text:`${c}%`});let g=h.createDiv({cls:"gitea-milestone__bar"});g.setAttr("role","progressbar"),g.setAttr("aria-valuemin","0"),g.setAttr("aria-valuemax","100"),g.setAttr("aria-valuenow",String(c)),g.createDiv({cls:"gitea-milestone__bar-fill"}).style.setProperty("--gitea-progress",`${c}%`);let y=h.createDiv({cls:"gitea-milestone__stats"});y.createSpan({cls:"gitea-milestone__stat",text:l===0?"No issues in this milestone":`${a} of ${l} closed`}),i>0&&y.createSpan({cls:"gitea-milestone__stat",text:`${i} open`})}renderGraphResult(e,t,s,n){var i,a;if(e.empty(),!s.value){this.renderError(e,(a=(i=s.error)==null?void 0:i.message)!=null?a:"Failed to load graph.");return}this.renderGraph(e,t,s.value,s.stale,n)}renderGraph(e,t,s,n=!1,i){var De,Oe,Ue;let a=e.createDiv({cls:"gitea-card gitea-graph"});n&&(a.addClass("gitea-card--stale"),a.createSpan({cls:"gitea-card__stale-badge gitea-graph__stale-badge",text:"stale"}).setAttr("aria-label","Showing last-known data \u2014 Gitea is currently unreachable")),i&&this.renderCornerRefresh(a,"Refresh graph now",i);let c=a.createDiv({cls:"gitea-graph__header"}).createDiv({cls:"gitea-graph__heading"}),u=(De=t.repo)!=null?De:this.settings.defaultRepo.length>0?this.settings.defaultRepo:null,h=u?A(u):null,p=dt(t,this.settings.defaultRepo);if(h){let f=c.createEl("a",{cls:"gitea-graph__title",text:p,href:ie(this.settings.baseUrl,h.owner,h.repo)});f.setAttr("target","_blank"),f.setAttr("rel","noopener")}else c.createDiv({cls:"gitea-graph__title",text:p});if(t.milestones.length>0){let f=c.createDiv({cls:"gitea-graph__subtitle"}),C=t.milestones.length===1?"milestone":"milestones";f.createSpan({text:`${C} `}),t.milestones.forEach((_,k)=>{var E;k>0&&f.createSpan({text:", "});let I=(E=s.milestoneIds)==null?void 0:E[_];if(u&&typeof I=="number"){let U=f.createEl("a",{cls:"gitea-graph__milestone-link",text:_,href:oe(this.settings.baseUrl,u,I)});U.setAttr("target","_blank"),U.setAttr("rel","noopener")}else f.createSpan({text:_})})}if(s.nodes.length===0){a.addClass("gitea-card--empty"),a.createSpan({text:"No issues match this query."});return}let b=200,g=48,v=60,y=18,x=10,w=t.direction==="LR",M=pt(s.nodes,s.edges),G=new Map,R=0;for(let f of s.nodes){let C=(Oe=M.get(f.id))!=null?Oe:0;R=Math.max(R,C);let _=G.get(C);_?_.push(f):G.set(C,[f])}let m=new Map,S=1;for(let f=0;f<=R;f++){let C=(Ue=G.get(f))!=null?Ue:[];S=Math.max(S,C.length),C.forEach((_,k)=>{let I=x+f*((w?b:g)+v),E=x+k*((w?g:b)+y);m.set(_.id,{x:w?I:E,y:w?E:I})})}let P=x*2+(R+1)*(w?b:g)+R*v,O=x*2+S*(w?g:b)+(S-1)*y,B=w?P:O,ke=w?O:P,se=a.createSvg("svg",{cls:"gitea-graph__svg",attr:{viewBox:`0 0 ${B} ${ke}`,width:B,height:ke,preserveAspectRatio:"xMinYMin meet",role:"img","aria-label":`Dependency graph of ${s.nodes.length} issues`}}),Ge=`gitea-graph-arrow-${_t++}`;se.createSvg("defs").createSvg("marker",{attr:{id:Ge,markerWidth:8,markerHeight:8,refX:7,refY:3,orient:"auto",markerUnits:"strokeWidth"}}).createSvg("path",{cls:"gitea-graph__arrow",attr:{d:"M0,0 L7,3 L0,6 Z"}});for(let f of s.edges){let C=m.get(f.from),_=m.get(f.to);if(!C||!_)continue;let k;if(w){let I=C.x+b,E=C.y+g/2,U=_.x,V=_.y+g/2,Q=(I+U)/2;k=`M${I},${E} C${Q},${E} ${Q},${V} ${U},${V}`}else{let I=C.x+b/2,E=C.y+g,U=_.x+b/2,V=_.y,Q=(E+V)/2;k=`M${I},${E} C${I},${Q} ${U},${Q} ${U},${V}`}se.createSvg("path",{cls:"gitea-graph__edge",attr:{d:k,"marker-end":`url(#${Ge})`}})}for(let f of s.nodes){let C=m.get(f.id);if(!C)continue;let _=f.state==="closed"?"closed":f.inProgress?"in-progress":"open",k=["gitea-graph__node",`gitea-graph__node--${_}`];f.blocked&&k.push("gitea-graph__node--blocked");let I=se.createSvg("a",{cls:k,attr:{href:j(this.settings.baseUrl,f.ref),target:"_blank",rel:"noopener"}});I.createSvg("title").textContent=`#${f.number} ${f.title}`;let E=I.createSvg("g",{attr:{transform:`translate(${C.x},${C.y})`}});E.createSvg("rect",{cls:"gitea-graph__node-rect",attr:{x:0,y:0,width:b,height:g,rx:6,ry:6}}),E.createSvg("circle",{cls:["gitea-graph__node-dot",`gitea-graph__node-dot--${_}`],attr:{cx:16,cy:g/2,r:5}}),E.createSvg("text",{cls:"gitea-graph__node-number",attr:{x:30,y:20}}).textContent=`#${f.number}`,E.createSvg("text",{cls:"gitea-graph__node-title",attr:{x:30,y:36}}).textContent=ce(f.title,24)}}processInlineChips(e,t){var a;let s=this.settings.keyword;if(!s)return;let n=`${s}:`,i=Tt(s);for(let l of e.findAll("code")){if(l.closest("pre, a, .gitea-chip"))continue;let c=((a=l.textContent)!=null?a:"").trim();if(!c.includes(n))continue;let u=i.exec(c);if(!u)continue;let h={owner:u[1],repo:u[2],index:Number.parseInt(u[3],10)};l.replaceWith(this.createInlineChip(h,t))}}createInlineChip(e,t){let s=ze(this.settings.baseUrl,e,this.settings.repoAccents[`${e.owner}/${e.repo}`]);return t.addChild(new be(s,this,e)),s}renderError(e,t){e.createDiv({cls:"gitea-card gitea-card--error"}).createSpan({cls:"gitea-card__error",text:t})}applyRepoAccent(e,t){let s=this.settings.repoAccents[t];s&&(e.addClass("gitea-card--accented"),e.style.setProperty("--gitea-accent",s))}renderIssueCard(e,t,s,n=!1,i,a){var G,R;let l=s.state==="closed",c=e.createDiv({cls:"gitea-card"});this.applyRepoAccent(c,`${t.owner}/${t.repo}`),l&&c.addClass("gitea-card--closed"),n&&c.addClass("gitea-card--stale");let u=c.createDiv({cls:"gitea-card__header"}),h=u.createEl("a",{cls:"gitea-card__number",text:`#${s.number}`,href:j(this.settings.baseUrl,t)});h.setAttr("target","_blank"),h.setAttr("rel","noopener"),u.createSpan({cls:"gitea-card__title",text:s.title}),n&&u.createSpan({cls:"gitea-card__stale-badge",text:"stale"}).setAttr("aria-label","Showing last-known data \u2014 Gitea is currently unreachable");let p=ce(gt((G=s.body)!=null?G:""),this.settings.truncationLength);if(p&&c.createDiv({cls:"gitea-card__body",text:p}),a&&this.settings.showAttachments&&!n){let m=c.createDiv({cls:"gitea-card__attachments"});this.renderAttachmentStrip(m,t,a.component)}let b=c.createDiv({cls:"gitea-card__meta"});if((R=s.labels)!=null&&R.length){let m=b.createDiv({cls:"gitea-card__labels"});for(let S of s.labels){let P=he(S.name);if(P===null){let B=m.createSpan({cls:"gitea-card__label",text:S.name});B.style.setProperty("--gitea-label-bg",`#${S.color}`),B.style.setProperty("--gitea-label-fg",K(S.color));continue}let O=m.createSpan({cls:"gitea-card__label gitea-card__label--blocked-by"});O.createSpan({cls:"gitea-card__label-name",text:`Blocked by #${P}`}),this.renderBlockerState(O,{owner:t.owner,repo:t.repo,index:P})}}let g=b.createDiv({cls:"gitea-card__author"});if(g.createSpan({cls:"gitea-card__avatar",text:ue(s.user.login)}),g.createSpan({cls:"gitea-card__author-name",text:s.user.login}),s.milestone){let m=b.createDiv({cls:"gitea-card__milestone"});m.createSpan({cls:"gitea-card__milestone-icon",text:"\u{1F3AF}"}),m.createSpan({text:s.milestone.title})}let v=c.createDiv({cls:"gitea-card__status"}),y=v.createEl("a",{cls:"gitea-card__repo-name",href:ie(this.settings.baseUrl,t.owner,t.repo)});y.setAttr("target","_blank"),y.setAttr("rel","noopener"),y.setAttr("aria-label",`Repository ${t.owner}/${t.repo}`),y.createSpan({cls:"gitea-card__repo-icon",text:"\u{1F4E6}"}),y.createSpan({text:`${t.owner}/${t.repo}`});let x=a!==void 0&&s.comments>0;if(x){let m=v.createEl("button",{cls:"gitea-card__comments gitea-card__comments--toggle"});m.setAttr("aria-expanded",String(a.expanded)),m.setAttr("aria-label",a.expanded?"Hide comments":"Show recent comments"),m.createSpan({cls:"gitea-card__comments-icon",text:"\u{1F4AC}"}),m.createSpan({text:String(s.comments)}),m.createSpan({cls:"gitea-card__comments-caret",text:a.expanded?"\u25BE":"\u25B8"}),m.addEventListener("click",()=>a.onToggle())}else{let m=v.createSpan({cls:"gitea-card__comments"});m.createSpan({cls:"gitea-card__comments-icon",text:"\u{1F4AC}"}),m.createSpan({text:String(s.comments)})}let w=v.createSpan({cls:"gitea-card__state"});w.createSpan({cls:l?"gitea-card__dot gitea-card__dot--closed":"gitea-card__dot gitea-card__dot--open"}),w.createSpan({text:l?"closed":"open"});let M=de(s.updated_at);if(M){let m=v.createSpan({cls:"gitea-card__updated"});m.createSpan({cls:"gitea-card__updated-icon",text:"\u27F3"}),m.createSpan({text:`updated ${M}`})}if(x&&a.expanded){let m=c.createDiv({cls:"gitea-card__comments-thread"});this.renderCommentThread(m,t,a)}if(i){let m=c.createDiv({cls:"gitea-card__actions"});this.renderRefreshAction(m,i),n||this.renderQuickActions(m,t,s,i)}}async renderCommentThread(e,t,s){var l,c;e.createDiv({cls:"gitea-card__comments-loading",text:"Loading comments\u2026"});let n=await this.commentsCache.load(t,this.refreshIntervalMs);if(!e.isConnected)return;e.empty();let i=n.value;if(!i){e.createDiv({cls:"gitea-card__comments-message",text:(c=(l=n.error)==null?void 0:l.message)!=null?c:"Failed to load comments."});return}if(n.stale&&e.createDiv({cls:"gitea-card__comments-message",text:"Gitea is unreachable \u2014 showing last-known comments."}),i.length===0){e.createDiv({cls:"gitea-card__comments-message",text:"No comments yet."});return}let a=s.count>0?i.slice(-s.count):i;i.length>a.length&&e.createDiv({cls:"gitea-card__comments-message",text:`Showing the latest ${a.length} of ${i.length} comments.`});for(let u of a)this.renderComment(e,u,s)}renderComment(e,t,s){var c;let n=e.createDiv({cls:"gitea-card__comment"}),i=n.createDiv({cls:"gitea-card__comment-header"});i.createSpan({cls:"gitea-card__avatar",text:ue(t.user.login)}),i.createSpan({cls:"gitea-card__comment-author",text:t.user.login});let a=de(t.created_at);a&&i.createSpan({cls:"gitea-card__comment-time",text:a});let l=n.createDiv({cls:"gitea-card__comment-body"});this.renderCommentBody(l,(c=t.body)!=null?c:"",s)}async renderCommentBody(e,t,s){var i;if(await d.MarkdownRenderer.render(this.app,t,e,s.sourcePath,s.component),!this.settings.showCommentImages||!e.isConnected)return;let n=e.querySelectorAll("img");for(let a=0;a{e.isConnected&&(e.setAttr("src",a),e.removeClass("gitea-card__comment-image--loading"),e.addClass("gitea-card__comment-image--loaded"),e.setAttr("aria-label",n?`View image: ${n}`:"View image"),s.registerDomEvent(e,"click",()=>{new ee(this,t,n).open()}))}).catch(a=>{if(!e.isConnected)return;let l=a instanceof Error?a.message:"Image could not be loaded.";e.replaceWith(createSpan({cls:"gitea-card__comment-image-error",text:`\u{1F512} ${l}`}))})}async renderAttachmentStrip(e,t,s){var a;let n=await this.assetsCache.load(t,this.refreshIntervalMs);if(!e.isConnected)return;let i=(a=n.value)!=null?a:[];if(i.length===0){e.remove();return}for(let l of i){let c=re(this.settings.baseUrl,l.browser_download_url);Se(l.name)?this.renderAttachmentThumbnail(e,l,c.url,s):this.renderAttachmentFileChip(e,l,c.url)}}renderAttachmentThumbnail(e,t,s,n){let i=e.createEl("img",{cls:"gitea-card__attachment-thumb gitea-card__attachment-thumb--loading"});i.setAttr("alt",t.name),this.loadAuthedImage(s,n).then(a=>{i.isConnected&&(i.setAttr("src",a),i.removeClass("gitea-card__attachment-thumb--loading"),i.addClass("gitea-card__attachment-thumb--loaded"),i.setAttr("aria-label",`View image: ${t.name}`),n.registerDomEvent(i,"click",()=>{new ee(this,s,t.name).open()}))}).catch(a=>{if(!i.isConnected)return;let l=a instanceof Error?a.message:"Image could not be loaded.";i.replaceWith(createSpan({cls:"gitea-card__attachment-error",text:`\u{1F512} ${l}`}))})}renderAttachmentFileChip(e,t,s){let n=e.createEl("a",{cls:"gitea-card__attachment-file",href:s});n.setAttr("target","_blank"),n.setAttr("rel","noopener"),n.setAttr("aria-label",`Download ${t.name}`),n.createSpan({cls:"gitea-card__attachment-file-icon",text:"\u{1F4CE}"}),n.createSpan({cls:"gitea-card__attachment-file-name",text:t.name})}async renderBlockerState(e,t){let s=e.createSpan({cls:"gitea-card__blocker-dot"});e.setAttr("aria-label",`Blocked by #${t.index}`);let n=await this.cache.load(t,this.refreshIntervalMs);if(!e.isConnected)return;let i=n.value;if(!i)return;let a=i.state==="closed";s.addClass(a?"gitea-card__blocker-dot--closed":"gitea-card__blocker-dot--open"),a&&e.addClass("gitea-card__label--resolved"),e.setAttr("aria-label",`Blocked by #${t.index} \u2014 ${a?"closed":"still open"}`)}renderRefreshButton(e,t,s){let n=new d.ExtraButtonComponent(e).setIcon("refresh-cw").setTooltip(t).onClick(()=>{n.extraSettingsEl.addClass("gitea-refresh-button--spinning"),s()});return n.extraSettingsEl.addClass("gitea-refresh-button"),n}renderCornerRefresh(e,t,s){this.renderRefreshButton(e,t,()=>s()).extraSettingsEl.addClass("gitea-refresh-button--corner")}renderRefreshAction(e,t){let s=this.renderRefreshButton(e,"Refresh this card now",()=>{s.setDisabled(!0),t.onChanged()});s.extraSettingsEl.addClass("gitea-card__action--refresh")}async renderQuickActions(e,t,s,n){let i;try{i=await this.loadCanWriteIssues(t)}catch(p){i=!1}if(!i||!e.isConnected)return;let a=s.state==="closed",l=e.createEl("button",{cls:"gitea-card__action",text:"Comment"});l.setAttr("aria-label","Add a comment");let c=e.createEl("button",{cls:"gitea-card__action",text:a?"Reopen":"Close"});c.setAttr("aria-label",a?"Reopen this issue":"Close this issue"),c.addEventListener("click",()=>{this.runCardAction(c,()=>this.setIssueState(t,a?"open":"closed"),n,a?"Reopened issue.":"Closed issue.")});let u=e.createEl("button",{cls:"gitea-card__action",text:"Labels"});u.setAttr("aria-label","Add or remove labels"),u.addEventListener("click",p=>{this.openLabelMenu(p,t,s,n)});let h=e.createDiv({cls:"gitea-card__comment-form gitea-card__comment-form--hidden"});l.addEventListener("click",()=>{h.hasClass("gitea-card__comment-form--hidden")?this.openCommentForm(h,t,n):this.closeCommentForm(h,n)})}openCommentForm(e,t,s){e.empty(),e.removeClass("gitea-card__comment-form--hidden"),s.setSuspended(!0);let n=e.createEl("textarea",{cls:"gitea-card__comment-input"});n.setAttr("placeholder","Leave a comment\u2026"),n.rows=3,n.focus();let i=e.createDiv({cls:"gitea-card__comment-buttons"}),a=i.createEl("button",{cls:"gitea-card__action mod-cta",text:"Send"}),l=i.createEl("button",{cls:"gitea-card__action",text:"Cancel"});if(this.settings.showCommentImages){let c=i.createEl("input",{cls:"gitea-card__comment-file"});c.type="file",c.accept="image/*",c.setAttr("aria-label","Attach an image"),c.addEventListener("change",()=>{var h,p;let u=(p=(h=c.files)==null?void 0:h[0])!=null?p:null;c.value="",u&&this.attachCommentImage(t,u,n,a,c)}),n.addEventListener("paste",u=>{var b,g,v;let h=Array.from((g=(b=u.clipboardData)==null?void 0:b.items)!=null?g:[]).find(y=>y.kind==="file"&&y.type.startsWith("image/")),p=(v=h==null?void 0:h.getAsFile())!=null?v:null;p&&(u.preventDefault(),this.attachCommentImage(t,p,n,a,c))})}l.addEventListener("click",()=>this.closeCommentForm(e,s)),a.addEventListener("click",()=>{let c=n.value.trim();if(!c){new d.Notice("Enter a comment first.");return}this.runCardAction(a,()=>this.addIssueComment(t,c),s,"Comment posted.")})}async attachCommentImage(e,t,s,n,i){if(!t.type.startsWith("image/")){new d.Notice("Only images can be attached.");return}let a=n.disabled;n.disabled=!0,i.disabled=!0;let l=new d.Notice("Uploading image\u2026",0);try{let c=await t.arrayBuffer(),u=t.name&&t.name.trim()?t.name:Te(t.type),h=await this.uploadIssueAsset(e,u,t.type,c);je(s,$e(h)),new d.Notice("Image attached.")}catch(c){new d.Notice(c instanceof Error?c.message:"Image upload failed.")}finally{l.hide(),n.disabled=a,i.disabled=!1,s.focus()}}closeCommentForm(e,t){e.empty(),e.addClass("gitea-card__comment-form--hidden"),t.setSuspended(!1)}async openLabelMenu(e,t,s,n){var c;let i;try{i=await this.fetchLabels(`${t.owner}/${t.repo}`)}catch(u){new d.Notice(u instanceof Error?u.message:"Could not load labels.");return}if(i.length===0){new d.Notice("This repository has no labels.");return}let a=new Set((c=s.labels)==null?void 0:c.map(u=>u.id)),l=new d.Menu;for(let u of i){let h=a.has(u.id);l.addItem(p=>{p.setTitle(u.name).setChecked(h).onClick(()=>{this.runCardAction(null,()=>h?this.removeIssueLabel(t,u.id):this.addIssueLabels(t,[u.id]),n,h?`Removed \u201C${u.name}\u201D.`:`Added \u201C${u.name}\u201D.`)})})}l.showAtMouseEvent(e)}async runCardAction(e,t,s,n){e&&(e.disabled=!0);try{await t(),new d.Notice(n),s.onChanged()}catch(i){new d.Notice(i instanceof Error?i.message:"Action failed."),e&&(e.disabled=!1)}}},pe=class extends d.MarkdownRenderChild{constructor(e,t,s,n,i){super(e);this.plugin=t;this.ref=s;this.sourcePath=n;this.suspended=!1;this.commentsExpanded=i!==null,this.commentCount=i&&i>0?i:t.settings.commentPreviewCount}onload(){this.containerEl.empty(),this.containerEl.createDiv({cls:"gitea-card gitea-card--loading",text:"Loading issue\u2026"}),this.refresh(),this.registerInterval(window.setInterval(()=>{this.suspended||!z(this.containerEl)||this.refresh()},this.plugin.refreshIntervalMs))}async refresh(){let e=await this.plugin.cache.load(this.ref,this.plugin.refreshIntervalMs);this.containerEl.isConnected&&this.plugin.renderCardResult(this.containerEl,this.ref,e,{onChanged:()=>{this.suspended=!1,this.plugin.cache.invalidate(this.ref),this.plugin.commentsCache.invalidate(this.ref),this.plugin.assetsCache.invalidate(this.ref),this.refresh()},setSuspended:t=>{this.suspended=t}},{count:this.commentCount,expanded:this.commentsExpanded,component:this,sourcePath:this.sourcePath,onToggle:()=>{this.commentsExpanded=!this.commentsExpanded,this.refresh()}})}},ge=class extends d.MarkdownRenderChild{constructor(e,t,s){super(e);this.plugin=t;this.query=s}onload(){this.containerEl.empty(),this.containerEl.createDiv({cls:"gitea-card gitea-card--loading",text:"Loading issues\u2026"}),this.refresh(),this.registerInterval(window.setInterval(()=>{z(this.containerEl)&&this.refresh()},this.plugin.refreshIntervalMs))}async refresh(){let e=await this.plugin.searchCache.load(this.query,this.plugin.refreshIntervalMs);this.containerEl.isConnected&&this.plugin.renderSearchResult(this.containerEl,this.query,e,()=>void this.refreshNow())}async refreshNow(){this.plugin.searchCache.invalidate(this.query),await this.refresh()}},me=class extends d.MarkdownRenderChild{constructor(e,t,s){super(e);this.plugin=t;this.query=s}onload(){this.containerEl.empty(),this.containerEl.createDiv({cls:"gitea-card gitea-card--loading",text:"Loading milestone\u2026"}),this.refresh(),this.registerInterval(window.setInterval(()=>{z(this.containerEl)&&this.refresh()},this.plugin.refreshIntervalMs))}async refresh(){let e=await this.plugin.milestoneCache.load(this.query,this.plugin.refreshIntervalMs);this.containerEl.isConnected&&this.plugin.renderMilestoneResult(this.containerEl,this.query,e)}},fe=class extends d.MarkdownRenderChild{constructor(e,t,s){super(e);this.plugin=t;this.query=s}onload(){this.containerEl.empty(),this.containerEl.createDiv({cls:"gitea-card gitea-card--loading",text:"Loading graph\u2026"}),this.refresh(),this.registerInterval(window.setInterval(()=>{z(this.containerEl)&&this.refresh()},this.plugin.refreshIntervalMs))}async refresh(){let e=await this.plugin.graphCache.load(this.query,this.plugin.refreshIntervalMs);this.containerEl.isConnected&&this.plugin.renderGraphResult(this.containerEl,this.query,e,()=>void this.refreshNow())}async refreshNow(){this.plugin.graphCache.invalidate(this.query),await this.refresh()}},be=class extends d.MarkdownRenderChild{constructor(e,t,s){super(e);this.plugin=t;this.ref=s}onload(){this.loadIssue(),this.registerInterval(window.setInterval(()=>{z(this.containerEl)&&this.loadIssue()},this.plugin.refreshIntervalMs))}async loadIssue(){let e=await this.plugin.cache.load(this.ref,this.plugin.refreshIntervalMs);if(!this.containerEl.isConnected)return;let t=e.value;t&&Le(this.containerEl,t,this.plugin.settings.inProgressLabel)}},mt=new WeakMap,ve=class extends F.WidgetType{constructor(e,t){super();this.plugin=e;this.ref=t}eq(e){return e.ref.owner===this.ref.owner&&e.ref.repo===this.ref.repo&&e.ref.index===this.ref.index}toDOM(){let e=ze(this.plugin.settings.baseUrl,this.ref,this.plugin.settings.repoAccents[`${this.ref.owner}/${this.ref.repo}`]);return mt.set(e,this.ref),this.plugin.cache.load(this.ref,this.plugin.refreshIntervalMs).then(t=>{e.isConnected&&t.value&&Le(e,t.value,this.plugin.settings.inProgressLabel)}),e}ignoreEvent(){return!0}};function Mt(o){return F.ViewPlugin.fromClass(class{constructor(r){this.view=r;this.decorations=this.build(r),this.refreshTimer=window.setInterval(()=>{this.refreshChips()},o.refreshIntervalMs)}update(r){(r.docChanged||r.viewportChanged||r.selectionSet)&&(this.decorations=this.build(r.view))}destroy(){window.clearInterval(this.refreshTimer)}refreshChips(){this.view.dom.querySelectorAll(".gitea-chip").forEach(e=>{let t=mt.get(e);t&&o.cache.load(t,o.refreshIntervalMs).then(s=>{e.isConnected&&s.value&&Le(e,s.value,o.settings.inProgressLabel)})})}build(r){let e=new He.RangeSetBuilder;if(!r.state.field(d.editorLivePreviewField)||!o.settings.keyword)return e.finish();let t=(0,Ne.syntaxTree)(r.state),s=r.state.selection;for(let{from:n,to:i}of r.visibleRanges){let a=r.state.doc.sliceString(n,i),l=Lt(o.settings.keyword);for(let c=l.exec(a);c!==null;c=l.exec(a)){let u=n+c.index,h=u+c[0].length;if(s.ranges.some(v=>v.from<=h&&v.to>=u))continue;let b=t.resolveInner(u+1,1).type.name;if(!/inline.?code/i.test(b))continue;let g={owner:c[1],repo:c[2],index:Number.parseInt(c[3],10)};e.add(u,h,F.Decoration.replace({widget:new ve(o,g)}))}}return e.finish()}},{decorations:r=>r.decorations})}var we=class extends d.EditorSuggest{constructor(e){super(e.app);this.plugin=e;this.stage="repo";this.repoSlug="";this.referenceStart=0}onTrigger(e,t,s){let n=this.plugin.settings.keyword;if(!n)return null;let i=t.getLine(e.line).slice(0,e.ch);if(!i.includes(`${n}:`))return null;let l=new RegExp(`${te(n)}:([^\\s:]*)(?::([^\\s:]*))?$`).exec(i);if(!l)return null;let c=l[1],u=l[2];if(u===void 0)return this.stage="repo",this.repoSlug="",{start:{line:e.line,ch:e.ch-c.length},end:e,query:c};let h=A(c);return h?(this.stage="issue",this.repoSlug=`${h.owner}/${h.repo}`,this.referenceStart=l.index,{start:{line:e.line,ch:e.ch-u.length},end:e,query:u}):null}async getSuggestions(e){let t=e.query.toLowerCase();try{if(this.stage==="repo")return(await this.plugin.loadRepoList()).filter(a=>a.toLowerCase().includes(t)).slice(0,20).map(a=>({kind:"repo",repo:a}));let s=t.replace(/^#/,"");return(await this.plugin.loadInlineIssues(this.repoSlug)).filter(i=>String(i.index).includes(s)||i.title.toLowerCase().includes(s)).slice(0,20).map(i=>({kind:"issue",index:i.index,title:i.title,state:i.state}))}catch(s){return[]}}renderSuggestion(e,t){if(t.addClass("gitea-suggestion"),e.kind==="repo"){t.createSpan({cls:"gitea-suggestion__repo",text:e.repo});return}t.createSpan({cls:"gitea-suggestion__number",text:`#${e.index}`}),t.createSpan({cls:"gitea-suggestion__title",text:`\u2014 ${e.title}`})}selectSuggestion(e){let t=this.context;if(!t)return;if(e.kind==="repo"){let l=`${e.repo}:`;t.editor.replaceRange(l,t.start,t.end),t.editor.setCursor({line:t.start.line,ch:t.start.ch+l.length});return}let s=t.editor.getLine(t.start.line),n=this.referenceStart;n>0&&s.charAt(n-1)==="`"&&(n-=1);let i=t.end.ch;s.charAt(i)==="`"&&(i+=1);let a=`\`${this.plugin.settings.keyword}:${this.repoSlug}:#${e.index}\``;t.editor.replaceRange(a,{line:t.start.line,ch:n},{line:t.end.line,ch:i}),t.editor.setCursor({line:t.start.line,ch:n+a.length})}},ee=class extends d.Modal{constructor(e,t,s){super(e.app);this.plugin=e;this.url=t;this.alt=s;this.owner=new d.Component}async onOpen(){this.owner.load();let{contentEl:e,modalEl:t}=this;t.addClass("gitea-lightbox-modal"),e.empty(),e.addClass("gitea-lightbox");let s=e.createDiv({cls:"gitea-lightbox__status",text:"Loading image\u2026"});try{let n=await this.plugin.loadAuthedImage(this.url,this.owner);if(!e.isConnected)return;s.remove();let i=e.createEl("img",{cls:"gitea-lightbox__image"});i.setAttr("src",n),this.alt&&i.setAttr("alt",this.alt)}catch(n){if(!e.isConnected)return;s.setText(n instanceof Error?n.message:"Image could not be loaded.")}}onClose(){this.owner.unload(),this.contentEl.empty()}},ye=class extends d.Modal{constructor(e,t){super(e.app);this.plugin=e;this.editor=t;this.issues=[];this.selectedIndex=null;this.repoSetting=null;this.issueEl=null;this.insertButton=null;this.repo=e.settings.defaultRepo,this.insertFormat=e.settings.insertFormat==="none"?"card":e.settings.insertFormat}async onOpen(){this.titleEl.setText("Insert existing issue");let{contentEl:e}=this;e.empty(),e.addClass("gitea-insert-issue"),this.repoSetting=new d.Setting(e).setName("Repository"),this.issueEl=e.createDiv(),new d.Setting(e).setName("Insert as").setDesc("Drop a full card or a compact inline status into the note.").addDropdown(t=>{t.addOption("card","Card"),t.addOption("inline","Inline status"),t.setValue(this.insertFormat),t.onChange(s=>{this.insertFormat=s,this.plugin.settings.insertFormat=this.insertFormat,this.plugin.saveSettings()})}),new d.Setting(e).addButton(t=>{this.insertButton=t,t.setButtonText("Insert").setCta().onClick(()=>this.submit())}),await this.renderRepoControl(),await this.renderIssues()}onClose(){this.contentEl.empty()}async renderRepoControl(){let e=this.repoSetting;if(e)try{let t=await this.plugin.fetchRepos(),s=[...new Set([this.repo,...t].filter(n=>n.length>0))];!this.repo&&s.length>0&&(this.repo=s[0]),e.setDesc("Repository to insert an issue from."),e.addDropdown(n=>{for(let i of s)n.addOption(i,i);n.setValue(this.repo),n.onChange(i=>{this.repo=i,this.renderIssues()})})}catch(t){e.setDesc("Repository in owner/repo form."),e.addText(s=>{s.setValue(this.repo).onChange(n=>{this.repo=n.trim()})}),e.addExtraButton(s=>{s.setIcon("refresh-cw").setTooltip("Load issues").onClick(()=>void this.renderIssues())})}}async renderIssues(){let e=this.issueEl;if(!e)return;if(this.selectedIndex=null,this.issues=[],e.empty(),!A(this.repo)){e.createDiv({cls:"gitea-insert-issue__notice",text:"Enter a repository in owner/repo form."});return}let s=this.repo;e.createDiv({cls:"gitea-insert-issue__notice",text:"Loading issues\u2026"});let n;try{n=await this.plugin.fetchInlineIssues(this.repo)}catch(i){if(this.repo!==s)return;e.empty(),e.createDiv({cls:"gitea-insert-issue__notice",text:i instanceof Error?i.message:"Could not load issues."});return}if(this.repo===s){if(this.issues=n,e.empty(),n.length===0){e.createDiv({cls:"gitea-insert-issue__notice",text:"This repository has no issues."});return}this.selectedIndex=n[0].index,new d.Setting(e).setName("Issue").addDropdown(i=>{for(let a of n){let l=a.state==="closed"?" (closed)":"";i.addOption(String(a.index),`#${a.index} \u2014 ${a.title}${l}`)}i.setValue(String(this.selectedIndex)),i.onChange(a=>{this.selectedIndex=Number.parseInt(a,10)})})}}submit(){let e=A(this.repo);if(!e){new d.Notice("Enter a repository in owner/repo form.");return}if(this.selectedIndex===null){new d.Notice("Choose an issue to insert.");return}let t={owner:e.owner,repo:e.repo,index:this.selectedIndex};Re(this.editor,t,this.insertFormat,this.plugin.settings.keyword),this.close()}},q=class extends d.Modal{constructor(e,t){var s,n;super(e.app);this.plugin=e;this.options=t;this.title="";this.selectedLabelIds=new Set;this.milestoneId=null;this.assignees="";this.labels=[];this.milestones=[];this.repoSetting=null;this.metaEl=null;this.attachEl=null;this.submitButton=null;this.submitting=!1;this.pendingFiles=[];this.previewUrls=[];this.previewsEl=null;this.attachEnabled=!1;this.repo=t.repo||e.settings.lastRepo||e.settings.defaultRepo,this.body=(s=t.body)!=null?s:"",this.insertFormat=(n=t.defaultInsertFormat)!=null?n:e.settings.insertFormat}async onOpen(){this.titleEl.setText("New issue");let{contentEl:e}=this;e.empty(),e.addClass("gitea-new-issue"),this.repoSetting=new d.Setting(e).setName("Repository"),new d.Setting(e).setName("Title").addText(t=>{t.setPlaceholder("Issue title").onChange(s=>{this.title=s})}),new d.Setting(e).setName("Description").addTextArea(t=>{t.setValue(this.body).onChange(s=>{this.body=s}),t.inputEl.rows=6,t.inputEl.addClass("gitea-new-issue__body"),t.inputEl.addEventListener("paste",s=>{this.handleBodyPaste(s)})}),this.metaEl=e.createDiv(),this.attachEl=e.createDiv(),this.options.editor&&new d.Setting(e).setName("Insert as").setDesc("Drop a full card, a compact inline status, or nothing into the note.").addDropdown(t=>{t.addOption("card","Card"),t.addOption("inline","Inline status"),t.addOption("none","None"),t.setValue(this.insertFormat),t.onChange(s=>{this.insertFormat=s,this.plugin.settings.insertFormat=this.insertFormat,this.plugin.saveSettings()})}),new d.Setting(e).addButton(t=>{this.submitButton=t,t.setButtonText("Create issue").setCta().onClick(()=>void this.submit())}),this.scope.register(["Mod"],"Enter",t=>(t.preventDefault(),this.submit(),!1)),await this.renderRepoControl(),await this.renderMetadata(),await this.renderAttachSection()}onClose(){this.clearPreviewUrls(),this.contentEl.empty()}async renderRepoControl(){let e=this.repoSetting;if(e)try{let t=await this.plugin.fetchRepos(),s=[...new Set([this.repo,...t].filter(n=>n.length>0))];!this.repo&&s.length>0&&(this.repo=s[0]),e.setDesc("Repository to create the issue in."),e.addDropdown(n=>{for(let i of s)n.addOption(i,i);n.setValue(this.repo),n.onChange(i=>{this.repo=i,this.renderMetadata(),this.renderAttachSection()})})}catch(t){e.setDesc("Repository in owner/repo form."),e.addText(s=>{s.setValue(this.repo).onChange(n=>{this.repo=n.trim()})}),e.addExtraButton(s=>{s.setIcon("refresh-cw").setTooltip("Load labels and milestones").onClick(()=>{this.renderMetadata(),this.renderAttachSection()})})}}async renderMetadata(){let e=this.metaEl;if(!e)return;if(this.selectedLabelIds.clear(),this.milestoneId=null,e.empty(),!A(this.repo)){e.createDiv({cls:"gitea-new-issue__notice",text:"Enter a repository in owner/repo form."});return}let s=this.repo;e.createDiv({cls:"gitea-new-issue__notice",text:"Loading labels and milestones\u2026"});let n,i;try{[n,i]=await Promise.all([this.plugin.fetchLabels(this.repo),this.plugin.fetchMilestones(this.repo)])}catch(a){if(this.repo!==s)return;e.empty(),e.createDiv({cls:"gitea-new-issue__notice",text:a instanceof Error?a.message:"Could not load repository metadata."});return}this.repo===s&&(this.labels=n,this.milestones=i.filter(a=>a.state!=="closed"),this.options.repo&&this.repo===this.options.repo&&this.applyPrefill(),e.empty(),this.renderLabelOptions(e),this.renderMilestoneOption(e),this.renderAssigneesOption(e))}applyPrefill(){var s,n;let e=new Set(((s=this.options.labels)!=null?s:[]).map(i=>i.trim().toLowerCase()).filter(i=>i.length>0));if(e.size>0)for(let i of this.labels)e.has(i.name.trim().toLowerCase())&&this.selectedLabelIds.add(i.id);let t=(n=this.options.milestone)==null?void 0:n.trim().toLowerCase();if(t){let i=this.milestones.find(a=>a.title.trim().toLowerCase()===t);i&&(this.milestoneId=i.id)}}renderLabelOptions(e){if(new d.Setting(e).setName("Labels").setHeading(),this.labels.length===0){e.createDiv({cls:"gitea-new-issue__notice",text:"This repository has no labels."});return}let t=e.createDiv({cls:"gitea-new-issue__labels"});for(let s of this.labels){let n=t.createEl("button",{cls:"gitea-new-issue__label",text:s.name});n.type="button",n.style.setProperty("--gitea-label-bg",`#${s.color}`),n.style.setProperty("--gitea-label-fg",K(s.color));let i=()=>{let a=this.selectedLabelIds.has(s.id);n.toggleClass("is-selected",a),n.setAttr("aria-pressed",a?"true":"false")};i(),n.addEventListener("click",()=>{this.selectedLabelIds.has(s.id)?this.selectedLabelIds.delete(s.id):this.selectedLabelIds.add(s.id),i()})}}renderMilestoneOption(e){new d.Setting(e).setName("Milestone").addDropdown(t=>{t.addOption("","None");for(let s of this.milestones)t.addOption(String(s.id),s.title);t.setValue(this.milestoneId!==null?String(this.milestoneId):""),t.onChange(s=>{this.milestoneId=s?Number.parseInt(s,10):null})})}renderAssigneesOption(e){new d.Setting(e).setName("Assignees").setDesc("Comma-separated usernames.").addText(t=>{t.setValue(this.assignees).onChange(s=>{this.assignees=s})})}async renderAttachSection(){let e=this.attachEl;if(!e)return;if(e.empty(),this.clearPreviewUrls(),this.previewsEl=null,this.attachEnabled=!1,!this.plugin.settings.showCommentImages){this.pendingFiles=[];return}let t=this.repo,s=!1;try{s=await this.plugin.loadRepoCanWrite(this.repo)}catch(a){s=!1}if(this.repo!==t)return;if(!s){this.pendingFiles=[];return}new d.Setting(e).setName("Images").setHeading();let n=e.createEl("input",{cls:"gitea-new-issue__file"});n.type="file",n.accept="image/*",n.multiple=!0,n.setAttr("aria-label","Attach images");let i=e.createDiv({cls:"gitea-new-issue__previews"});this.previewsEl=i,this.attachEnabled=!0,n.addEventListener("change",()=>{var l;let a=Array.from((l=n.files)!=null?l:[]).filter(c=>c.type.startsWith("image/"));n.value="",a.length>0&&(this.pendingFiles.push(...a),this.renderPreviews(i))}),this.renderPreviews(i)}handleBodyPaste(e){var s,n;if(!this.attachEnabled||!this.previewsEl)return;let t=Array.from((n=(s=e.clipboardData)==null?void 0:s.items)!=null?n:[]).filter(i=>i.kind==="file"&&i.type.startsWith("image/")).map(i=>i.getAsFile()).filter(i=>i!==null);t.length!==0&&(e.preventDefault(),this.pendingFiles.push(...t),this.renderPreviews(this.previewsEl),new d.Notice(t.length===1?"Image queued.":`${t.length} images queued.`))}renderPreviews(e){e.empty(),this.clearPreviewUrls(),this.pendingFiles.forEach((t,s)=>{let n=e.createDiv({cls:"gitea-new-issue__preview"}),i=URL.createObjectURL(t);this.previewUrls.push(i);let a=n.createEl("img",{cls:"gitea-new-issue__preview-img"});a.src=i,a.alt=t.name;let l=n.createEl("button",{cls:"gitea-new-issue__preview-remove",text:"\xD7"});l.type="button",l.setAttr("aria-label",`Remove ${t.name}`),l.addEventListener("click",()=>{this.pendingFiles.splice(s,1),this.renderPreviews(e)})})}clearPreviewUrls(){for(let e of this.previewUrls)URL.revokeObjectURL(e);this.previewUrls=[]}async submit(){var a,l,c;if(this.submitting)return;let e=this.title.trim();if(!e){new d.Notice("Enter a title for the issue.");return}let t=A(this.repo);if(!t){new d.Notice("Enter a repository in owner/repo form.");return}this.submitting=!0,(a=this.submitButton)==null||a.setDisabled(!0).setButtonText("Creating\u2026");let s={title:e,body:this.body,labels:[...this.selectedLabelIds],assignees:D(this.assignees)};this.milestoneId!==null&&(s.milestone=this.milestoneId);let n;try{n=await this.plugin.createIssue(this.repo,s)}catch(u){new d.Notice(u instanceof Error?u.message:"Could not create the issue."),this.submitting=!1,(l=this.submitButton)==null||l.setDisabled(!1).setButtonText("Create issue");return}let i={owner:t.owner,repo:t.repo,index:n.number};this.pendingFiles.length>0&&((c=this.submitButton)==null||c.setButtonText("Uploading images\u2026"),await this.attachPendingImages(i,n.number)),this.options.editor&&Re(this.options.editor,i,this.insertFormat,this.plugin.settings.keyword),this.plugin.settings.lastRepo!==this.repo&&(this.plugin.settings.lastRepo=this.repo,this.plugin.saveSettings()),new d.Notice(`Created issue #${n.number}.`),this.close()}async attachPendingImages(e,t){let s=[],n=null;for(let i of this.pendingFiles)try{let a=i.name&&i.name.trim()?i.name:Te(i.type),l=await i.arrayBuffer(),c=await this.plugin.uploadIssueAsset(e,a,i.type,l);s.push($e(c))}catch(a){n=a instanceof Error?a:new Error("Image upload failed.");break}if(s.length>0){let i=s.join(` + +`),a=this.body.trim().length>0?`${this.body.replace(/\s+$/,"")} + +${i}`:i;try{await this.plugin.editIssueBody(e,a)}catch(l){n=l instanceof Error?l:new Error("Could not update the issue body.")}}n&&new d.Notice(`Created issue #${t}, but not all images were attached: ${n.message}`)}},Ce=class extends d.Modal{constructor(e,t){super(e.app);this.plugin=e;this.editor=t;this.state="open";this.selectedLabels=new Set;this.labelsText="";this.milestone="";this.milestonesText="";this.q="";this.limit=W;this.layout="table";this.labels=[];this.milestones=[];this.repoSetting=null;this.metaEl=null;this.previewEl=null;this.repo=e.settings.lastRepo||e.settings.defaultRepo}async onOpen(){this.titleEl.setText("Build search query");let{contentEl:e}=this;e.empty(),e.addClass("gitea-search-wizard"),this.repoSetting=new d.Setting(e).setName("Repository"),new d.Setting(e).setName("State").setDesc("Which issues to include.").addDropdown(t=>{t.addOption("open","Open"),t.addOption("closed","Closed"),t.addOption("all","All"),t.setValue(this.state),t.onChange(s=>{this.state=s,this.updatePreview()})}),this.metaEl=e.createDiv(),new d.Setting(e).setName("Keyword").setDesc("Free-text match on issue title and body.").addText(t=>{t.setPlaceholder("Optional").onChange(s=>{this.q=s,this.updatePreview()})}),new d.Setting(e).setName("Limit").setDesc(`How many results to render (max ${T}).`).addText(t=>{t.inputEl.type="number",t.inputEl.min="1",t.inputEl.max=String(T),t.setValue(String(this.limit)).onChange(s=>{let n=Number.parseInt(s,10);this.limit=Number.isFinite(n)&&n>0?Math.min(n,T):W,this.updatePreview()})}),new d.Setting(e).setName("Layout").setDesc("Compact table of rows or a list of full cards.").addDropdown(t=>{t.addOption("table","Table"),t.addOption("cards","Cards"),t.setValue(this.layout),t.onChange(s=>{this.layout=s,this.updatePreview()})}),new d.Setting(e).setName("Preview").setHeading(),this.previewEl=e.createEl("pre",{cls:"gitea-search-wizard__preview"}),new d.Setting(e).addButton(t=>{t.setButtonText("Insert").setCta().onClick(()=>this.submit())}),this.scope.register(["Mod"],"Enter",t=>(t.preventDefault(),this.submit(),!1)),await this.renderRepoControl(),await this.renderMetadata()}onClose(){this.contentEl.empty()}currentQuery(){let e=A(this.repo)!==null;return{repo:this.repo.trim()||null,state:this.state,labels:e?[...this.selectedLabels]:D(this.labelsText),milestones:e?this.milestone?[this.milestone]:[]:D(this.milestonesText),q:this.q.trim()||null,limit:this.limit,layout:this.layout}}updatePreview(){var e;(e=this.previewEl)==null||e.setText(Pe(this.currentQuery()).trimEnd())}async renderRepoControl(){let e=this.repoSetting;if(e)try{let t=await this.plugin.fetchRepos(),s=[...new Set([this.repo,...t].filter(n=>n.length>0))];e.setDesc("Repository to search, or all repositories."),e.addDropdown(n=>{n.addOption("","All repositories (global)");for(let i of s)n.addOption(i,i);n.setValue(this.repo),n.onChange(i=>{this.repo=i,this.renderMetadata()})})}catch(t){e.setDesc("Repository in owner/repo form, or blank for all."),e.addText(s=>{s.setValue(this.repo).onChange(n=>{this.repo=n.trim(),this.updatePreview()})}),e.addExtraButton(s=>{s.setIcon("refresh-cw").setTooltip("Load labels and milestones").onClick(()=>void this.renderMetadata())})}}async renderMetadata(){let e=this.metaEl;if(!e)return;if(this.selectedLabels.clear(),this.milestone="",e.empty(),!A(this.repo)){this.renderLabelTextOption(e),this.renderMilestoneTextOption(e),this.updatePreview();return}let s=this.repo;e.createDiv({cls:"gitea-search-wizard__notice",text:"Loading labels and milestones\u2026"});let n,i;try{[n,i]=await Promise.all([this.plugin.fetchLabels(this.repo),this.plugin.fetchMilestones(this.repo)])}catch(a){if(this.repo!==s)return;e.empty(),this.renderLabelTextOption(e),this.renderMilestoneTextOption(e),this.updatePreview();return}this.repo===s&&(this.labels=n,this.milestones=i,e.empty(),this.renderLabelChips(e),this.renderMilestoneDropdown(e),this.updatePreview())}renderLabelChips(e){if(new d.Setting(e).setName("Labels").setHeading(),this.labels.length===0){e.createDiv({cls:"gitea-search-wizard__notice",text:"This repository has no labels."});return}let t=e.createDiv({cls:"gitea-new-issue__labels"});for(let s of this.labels){let n=t.createEl("button",{cls:"gitea-new-issue__label",text:s.name});n.type="button",n.style.setProperty("--gitea-label-bg",`#${s.color}`),n.style.setProperty("--gitea-label-fg",K(s.color));let i=()=>{let a=this.selectedLabels.has(s.name);n.toggleClass("is-selected",a),n.setAttr("aria-pressed",a?"true":"false")};i(),n.addEventListener("click",()=>{this.selectedLabels.has(s.name)?this.selectedLabels.delete(s.name):this.selectedLabels.add(s.name),i(),this.updatePreview()})}}renderMilestoneDropdown(e){new d.Setting(e).setName("Milestone").addDropdown(t=>{t.addOption("","None");for(let s of this.milestones)t.addOption(s.title,s.title);t.setValue(""),t.onChange(s=>{this.milestone=s,this.updatePreview()})})}renderLabelTextOption(e){new d.Setting(e).setName("Labels").setDesc("Comma-separated label names.").addText(t=>{t.setValue(this.labelsText).onChange(s=>{this.labelsText=s,this.updatePreview()})})}renderMilestoneTextOption(e){new d.Setting(e).setName("Milestones").setDesc("Comma-separated milestone names.").addText(t=>{t.setValue(this.milestonesText).onChange(s=>{this.milestonesText=s,this.updatePreview()})})}submit(){nt(this.editor,this.currentQuery()),this.close()}},_e=class extends d.Modal{constructor(e,t){super(e.app);this.plugin=e;this.editor=t;this.state="all";this.selectedLabels=new Set;this.labelsText="";this.selectedMilestones=new Set;this.milestonesText="";this.q="";this.limit=T;this.direction="TB";this.labels=[];this.milestones=[];this.repoSetting=null;this.metaEl=null;this.previewEl=null;this.repo=e.settings.lastRepo||e.settings.defaultRepo}async onOpen(){this.titleEl.setText("Build graph view");let{contentEl:e}=this;e.empty(),e.addClass("gitea-search-wizard"),this.repoSetting=new d.Setting(e).setName("Repository"),this.metaEl=e.createDiv(),new d.Setting(e).setName("State").setDesc("Which issues to include. All keeps completed blockers visible.").addDropdown(t=>{t.addOption("open","Open"),t.addOption("closed","Closed"),t.addOption("all","All"),t.setValue(this.state),t.onChange(s=>{this.state=s,this.updatePreview()})}),new d.Setting(e).setName("Keyword").setDesc("Free-text match on issue title and body.").addText(t=>{t.setPlaceholder("Optional").onChange(s=>{this.q=s,this.updatePreview()})}),new d.Setting(e).setName("Limit").setDesc(`Maximum number of nodes (max ${T}).`).addText(t=>{t.inputEl.type="number",t.inputEl.min="1",t.inputEl.max=String(T),t.setValue(String(this.limit)).onChange(s=>{let n=Number.parseInt(s,10);this.limit=Number.isFinite(n)&&n>0?Math.min(n,T):T,this.updatePreview()})}),new d.Setting(e).setName("Direction").setDesc("Orientation of the dependency layers.").addDropdown(t=>{t.addOption("TB","Top to bottom"),t.addOption("LR","Left to right"),t.setValue(this.direction),t.onChange(s=>{this.direction=s,this.updatePreview()})}),new d.Setting(e).setName("Preview").setHeading(),this.previewEl=e.createEl("pre",{cls:"gitea-search-wizard__preview"}),new d.Setting(e).addButton(t=>{t.setButtonText("Insert").setCta().onClick(()=>this.submit())}),this.scope.register(["Mod"],"Enter",t=>(t.preventDefault(),this.submit(),!1)),await this.renderRepoControl(),await this.renderMetadata()}onClose(){this.contentEl.empty()}currentQuery(){let e=A(this.repo)!==null;return{repo:this.repo.trim()||null,state:this.state,labels:e?[...this.selectedLabels]:D(this.labelsText),milestones:e?[...this.selectedMilestones]:D(this.milestonesText),q:this.q.trim()||null,limit:this.limit,direction:this.direction}}updatePreview(){var e;(e=this.previewEl)==null||e.setText(Me(this.currentQuery()).trimEnd())}async renderRepoControl(){let e=this.repoSetting;if(e)try{let t=await this.plugin.fetchRepos(),s=[...new Set([this.repo,...t].filter(n=>n.length>0))];e.setDesc("Repository to graph, or all repositories."),e.addDropdown(n=>{n.addOption("","All repositories (global)");for(let i of s)n.addOption(i,i);n.setValue(this.repo),n.onChange(i=>{this.repo=i,this.renderMetadata()})})}catch(t){e.setDesc("Repository in owner/repo form, or blank for all."),e.addText(s=>{s.setValue(this.repo).onChange(n=>{this.repo=n.trim(),this.updatePreview()})}),e.addExtraButton(s=>{s.setIcon("refresh-cw").setTooltip("Load milestones and labels").onClick(()=>void this.renderMetadata())})}}async renderMetadata(){let e=this.metaEl;if(!e)return;if(this.selectedLabels.clear(),this.selectedMilestones.clear(),e.empty(),!A(this.repo)){this.renderMilestoneTextOption(e),this.renderLabelTextOption(e),this.updatePreview();return}let s=this.repo;e.createDiv({cls:"gitea-search-wizard__notice",text:"Loading milestones and labels\u2026"});let n,i;try{[n,i]=await Promise.all([this.plugin.fetchLabels(this.repo),this.plugin.fetchMilestones(this.repo)])}catch(a){if(this.repo!==s)return;e.empty(),this.renderMilestoneTextOption(e),this.renderLabelTextOption(e),this.updatePreview();return}this.repo===s&&(this.labels=n,this.milestones=i,e.empty(),this.renderMilestonePicker(e),this.renderLabelChips(e),this.updatePreview())}renderMilestonePicker(e){if(new d.Setting(e).setName("Milestones").setHeading(),this.milestones.length===0){e.createDiv({cls:"gitea-search-wizard__notice",text:"This repository has no milestones."});return}this.renderMilestoneControls(e.createDiv())}renderMilestoneControls(e){e.empty();let t=this.milestones.filter(n=>!this.selectedMilestones.has(n.title));if(new d.Setting(e).setName("Add milestone").setDesc("Pick a milestone to add. A graph can span several.").addDropdown(n=>{n.addOption("",t.length>0?"Choose a milestone\u2026":"All milestones added");for(let i of t)n.addOption(i.title,i.title);n.setValue(""),n.setDisabled(t.length===0),n.onChange(i=>{i&&(this.selectedMilestones.add(i),this.renderMilestoneControls(e),this.updatePreview())})}),this.selectedMilestones.size===0)return;let s=e.createDiv({cls:"gitea-new-issue__labels"});for(let n of this.selectedMilestones){let i=s.createEl("button",{cls:"gitea-new-issue__label is-selected"});i.type="button",i.setAttr("aria-label",`Remove ${n}`),i.createSpan({text:n}),i.createSpan({cls:"gitea-new-issue__label-remove",text:"\u2715"}),i.addEventListener("click",()=>{this.selectedMilestones.delete(n),this.renderMilestoneControls(e),this.updatePreview()})}}renderLabelChips(e){if(new d.Setting(e).setName("Labels").setHeading(),this.labels.length===0){e.createDiv({cls:"gitea-search-wizard__notice",text:"This repository has no labels."});return}let t=e.createDiv({cls:"gitea-new-issue__labels"});for(let s of this.labels){let n=t.createEl("button",{cls:"gitea-new-issue__label",text:s.name});n.type="button",n.style.setProperty("--gitea-label-bg",`#${s.color}`),n.style.setProperty("--gitea-label-fg",K(s.color));let i=()=>{let a=this.selectedLabels.has(s.name);n.toggleClass("is-selected",a),n.setAttr("aria-pressed",a?"true":"false")};i(),n.addEventListener("click",()=>{this.selectedLabels.has(s.name)?this.selectedLabels.delete(s.name):this.selectedLabels.add(s.name),i(),this.updatePreview()})}}renderMilestoneTextOption(e){new d.Setting(e).setName("Milestones").setDesc("Comma-separated milestone names.").addText(t=>{t.setValue(this.milestonesText).onChange(s=>{this.milestonesText=s,this.updatePreview()})})}renderLabelTextOption(e){new d.Setting(e).setName("Labels").setDesc("Comma-separated label names.").addText(t=>{t.setValue(this.labelsText).onChange(s=>{this.labelsText=s,this.updatePreview()})})}submit(){ht(this.editor,this.currentQuery()),this.close()}},xe=class extends d.AbstractInputSuggest{constructor(e,t,s,n){super(e,t);this.plugin=s;this.taken=n}async getSuggestions(e){let t=this.taken(),s=e.trim().toLowerCase();return(await this.plugin.loadRepoList()).filter(i=>!t[i]).filter(i=>i.toLowerCase().includes(s))}renderSuggestion(e,t){t.setText(e)}},Ee=class extends d.PluginSettingTab{constructor(r,e){super(r,e),this.plugin=e}display(){let{containerEl:r}=this;r.empty(),new d.Setting(r).setName("Gitea base URL").setDesc("Public, globally reachable URL. A local-network or remote address is a manual fallback. Card links are built from this URL.").addText(e=>e.setPlaceholder("https://gitea.example.com").setValue(this.plugin.settings.baseUrl).onChange(async t=>{this.plugin.settings.baseUrl=t.trim(),await this.plugin.saveSettings()})),new d.Setting(r).setName("Default repository").setDesc("Used by the new-issue flow and bare references, in owner/repo form.").addText(e=>e.setValue(this.plugin.settings.defaultRepo).onChange(async t=>{this.plugin.settings.defaultRepo=t.trim(),await this.plugin.saveSettings()})),new d.Setting(r).setName("Inline keyword").setDesc("Trigger word for the inline reference syntax, placed before owner/repo:#123.").addText(e=>e.setPlaceholder("GITEA").setValue(this.plugin.settings.keyword).onChange(async t=>{this.plugin.settings.keyword=t.trim()||H.keyword,await this.plugin.saveSettings()})),new d.Setting(r).setName("In-progress label").setDesc("Label name that marks an issue as in progress. An open issue carrying it shows an amber dot and badge on its inline chip. Leave empty to disable.").addText(e=>e.setPlaceholder("In-progress").setValue(this.plugin.settings.inProgressLabel).onChange(async t=>{this.plugin.settings.inProgressLabel=t.trim(),await this.plugin.saveSettings()})),new d.Setting(r).setName("Refresh interval").setDesc(`How often visible cards refresh, in seconds. Floored at ${Y} seconds.`).addText(e=>e.setPlaceholder("60").setValue(String(this.plugin.settings.refreshIntervalSeconds)).onChange(async t=>{let s=Number.parseInt(t,10);this.plugin.settings.refreshIntervalSeconds=Number.isFinite(s)?Math.max(Y,s):H.refreshIntervalSeconds,await this.plugin.saveSettings()})),new d.Setting(r).setName("Description truncation length").setDesc("Maximum characters of issue body shown on a card.").addText(e=>e.setPlaceholder("160").setValue(String(this.plugin.settings.truncationLength)).onChange(async t=>{let s=Number.parseInt(t,10);this.plugin.settings.truncationLength=Number.isFinite(s)&&s>0?s:H.truncationLength,await this.plugin.saveSettings()})),new d.Setting(r).setName("Comment preview count").setDesc("Number of recent comments shown when a card's comment thread is expanded.").addText(e=>e.setPlaceholder("3").setValue(String(this.plugin.settings.commentPreviewCount)).onChange(async t=>{let s=Number.parseInt(t,10);this.plugin.settings.commentPreviewCount=Number.isFinite(s)&&s>0?Math.min(s,Ie):H.commentPreviewCount,await this.plugin.saveSettings()})),new d.Setting(r).setName("Show images in comments").setDesc("Load images pasted into comments inline in expanded threads. Off by default \u2014 when off, comments stay text-only. Images are fetched with your token, so private and access-gated attachments load; click one to open it full size.").addToggle(e=>e.setValue(this.plugin.settings.showCommentImages).onChange(async t=>{this.plugin.settings.showCommentImages=t,await this.plugin.saveSettings()})),new d.Setting(r).setName("Show attachments").setDesc("Show an attachment strip \u2014 image thumbnails and file chips \u2014 under single issue cards. Off by default. When on, each single issue card makes one extra request for its attachments; list blocks are unaffected and stay one request per card. Click a thumbnail to open it full size.").addToggle(e=>e.setValue(this.plugin.settings.showAttachments).onChange(async t=>{this.plugin.settings.showAttachments=t,await this.plugin.saveSettings()})),this.renderAccentSettings(r),new d.Setting(r).setName("Credentials").setHeading(),this.addSecretSetting(r,"Gitea API token","Personal access token with write:issue and read:repository scope. Stored in plaintext in this vault's data.json.",N.pat),this.addSecretSetting(r,"Cloudflare Access client ID","Service-token client ID. Sent only when the base URL is a public host behind Cloudflare Access \u2014 ignored on LAN or Tailscale addresses.",N.cfClientId),this.addSecretSetting(r,"Cloudflare Access client secret","Service-token client secret. Shown only once at creation in Cloudflare Zero Trust.",N.cfClientSecret),new d.Setting(r).setName("Test connection").setDesc("Query the server with the settings above to confirm the base URL and token work.").addButton(e=>e.setButtonText("Test connection").onClick(async()=>{e.setDisabled(!0).setButtonText("Testing\u2026");try{let t=await this.plugin.testConnection();new d.Notice(t)}catch(t){let s=t instanceof Error?t.message:"Connection test failed.";new d.Notice(`Connection failed: ${s}`)}finally{e.setDisabled(!1).setButtonText("Test connection")}}))}renderAccentSettings(r){new d.Setting(r).setName("Repository accent colours").setHeading();let e=r.createDiv();this.renderAccentRows(e);let t="",s=null;new d.Setting(r).setName("Add repository").setDesc("Assign an accent colour to a repository, in owner/repo form.").addText(n=>{s=n,n.setPlaceholder("Owner/repo").onChange(a=>{t=a.trim()});let i=new xe(this.app,n.inputEl,this.plugin,()=>this.plugin.settings.repoAccents);i.onSelect(a=>{t=a,n.setValue(a),i.close()})}).addButton(n=>n.setButtonText("Add").onClick(async()=>{if(!A(t)){new d.Notice("Enter a repository in owner/repo form.");return}if(this.plugin.settings.repoAccents[t]){new d.Notice("That repository already has an accent colour.");return}this.plugin.settings.repoAccents[t]=Be,await this.plugin.saveSettings(),this.renderAccentRows(e),t="",s==null||s.setValue("")}))}renderAccentRows(r){r.empty();let e=this.plugin.settings.repoAccents,t=Object.keys(e).sort();if(t.length===0){new d.Setting(r).setDesc("No accent colours set. Add a repository below to tint its cards with a subtle coloured left border.");return}for(let s of t)new d.Setting(r).setName(s).addColorPicker(n=>n.setValue(e[s]).onChange(async i=>{this.plugin.settings.repoAccents[s]=i,await this.plugin.saveSettings()})).addExtraButton(n=>n.setIcon("trash").setTooltip("Remove accent colour").onClick(async()=>{delete this.plugin.settings.repoAccents[s],await this.plugin.saveSettings(),this.renderAccentRows(r)}))}addSecretSetting(r,e,t,s){new d.Setting(r).setName(e).setDesc(t).addText(i=>{i.inputEl.type="password",i.setPlaceholder("Stored in data.json"),this.plugin.getSecret(s).then(a=>{a&&i.setPlaceholder("\u2022\u2022\u2022\u2022\u2022\u2022 (stored)")}),i.onChange(async a=>{try{await this.plugin.setSecret(s,a.trim())}catch(l){let c=l instanceof Error?l.message:String(l);new d.Notice(`Could not save "${e}": ${c}`)}})})}}; + +/* nosourcemap */ \ No newline at end of file diff --git a/.obsidian/plugins/gitea-cards/manifest.json b/.obsidian/plugins/gitea-cards/manifest.json new file mode 100644 index 0000000..705fce7 --- /dev/null +++ b/.obsidian/plugins/gitea-cards/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "gitea-cards", + "name": "Gitea Cards", + "version": "0.42.1", + "minAppVersion": "1.5.0", + "description": "Render live, auto-refreshing Gitea issue cards in your notes and create issues without leaving the editor.", + "author": "lunerfox", + "authorUrl": "https://github.com/lunerfox", + "isDesktopOnly": false +} \ No newline at end of file diff --git a/.obsidian/plugins/gitea-cards/styles.css b/.obsidian/plugins/gitea-cards/styles.css new file mode 100644 index 0000000..f547e48 --- /dev/null +++ b/.obsidian/plugins/gitea-cards/styles.css @@ -0,0 +1,1199 @@ +/* + * Gitea Cards — styles + * + * Card, chip, and badge rules are added by the feature tickets (F1–F8). + * Keep all styling here as CSS classes driven by Obsidian CSS variables; + * the plugin must not set inline styles (community-review guideline). + */ + +.gitea-card { + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-m); + padding: var(--size-4-2) var(--size-4-3); + background-color: var(--background-secondary); +} + +/* Per-project accent (issue #19): a subtle coloured left border, driven by the + * --gitea-accent custom property the plugin sets per repo. The repo-less default + * keeps the neutral border, so only configured projects are tinted. */ +.gitea-card--accented { + border-left: 3px solid var(--gitea-accent, var(--background-modifier-border)); +} + +.gitea-card--closed { + opacity: 0.6; +} + +/* Closed issues read as resolved at a glance — strike the title through */ +.gitea-card--closed .gitea-card__title { + text-decoration: line-through; +} + +.gitea-card--stale { + opacity: 0.5; + border-style: dashed; +} + +/* Stale badge — shown when a card falls back to last-known data offline */ +.gitea-card__stale-badge { + flex-shrink: 0; + margin-left: auto; + padding: 0 var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--background-modifier-border); + color: var(--text-muted); + font-size: var(--font-ui-smaller); + text-transform: uppercase; + letter-spacing: 0.05em; + /* Intra-token break lock lives in the shared theme-proof rule below. */ +} + +.gitea-card--loading, +.gitea-card--error, +.gitea-card--empty { + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +.gitea-card--error { + color: var(--text-error); +} + +/* Line 1 — number link + title */ +.gitea-card__header { + display: flex; + align-items: baseline; + gap: var(--size-4-2); +} + +.gitea-card__number { + flex-shrink: 0; + font-weight: var(--font-semibold); + font-variant-numeric: tabular-nums; + /* A number like #21 is one indivisible token — never let a narrow phone + * width (or a theme that boxes it into a tight pill) split it mid-token. + * The break lock lives in the shared theme-proof rule below. */ +} + +.gitea-card__title { + font-weight: var(--font-medium); +} + + +/* Search block — a vertical list of cards. Positioning context for the + corner-pinned refresh button (issue #45). */ +.gitea-search { + position: relative; + display: flex; + flex-direction: column; + gap: var(--size-4-2); +} + +.gitea-search__notice { + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +/* Search toolbar (issue #35, #36) — right-aligned "Refresh" and "Add issue" + buttons. Refresh forces a fresh fetch; Add issue opens the new-issue form + prefilled from the query. */ +.gitea-search__toolbar { + display: flex; + justify-content: flex-end; + gap: var(--size-4-2); +} + +/* Search table (issue #33) — a compact, chip-like grid of results so the + progress of a set of issues reads at a glance, the default search layout. */ +.gitea-search-table { + width: 100%; + border-collapse: collapse; + font-size: var(--font-ui-small); + font-variant-numeric: tabular-nums; +} + +.gitea-search-table__row { + border-bottom: 1px solid var(--background-modifier-border); +} + +.gitea-search-table__row:last-child { + border-bottom: none; +} + +.gitea-search-table__row:hover { + background-color: var(--background-modifier-hover); +} + +.gitea-search-table__row--closed { + opacity: 0.6; +} + +/* Per-project accent (issue #30): a coloured left edge mirroring the cards. */ +.gitea-search-table__row--accented .gitea-search-table__cell--dot { + border-left: 3px solid var(--gitea-accent, var(--background-modifier-border)); +} + +.gitea-search-table__cell { + padding: var(--size-4-1) var(--size-4-2); + vertical-align: middle; + text-align: left; +} + +.gitea-search-table__cell--dot { + width: 1em; + padding-right: 0; +} + +.gitea-search-table__dot { + display: inline-block; + width: 0.55em; + height: 0.55em; + border-radius: 50%; + background-color: var(--text-muted); +} + +.gitea-search-table__dot--open { + background-color: var(--color-green); +} + +.gitea-search-table__dot--closed { + background-color: var(--color-red); +} + +.gitea-search-table__dot--in-progress { + background-color: var(--color-orange); +} + +.gitea-search-table__cell--number { + white-space: nowrap; + width: 1%; +} + +.gitea-search-table__number { + text-decoration: none; + color: var(--text-muted); + /* #21 is one indivisible token. The cell sets nowrap, but mobile themes that + * restyle the inner link into a pill target this element directly and can + * override the cell's value — the shared theme-proof rule below locks all + * three intra-token break properties to win the cascade. */ +} + +.gitea-search-table__number:hover { + color: var(--text-accent); + text-decoration: none; +} + +.gitea-search-table__cell--repo { + white-space: nowrap; + width: 1%; + color: var(--text-muted); +} + +/* Title takes the remaining width and truncates so a long title can't blow out + the row — the same restraint the inline chip title uses. */ +.gitea-search-table__cell--title { + width: 100%; + max-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.gitea-search-table__cell--state { + white-space: nowrap; + width: 1%; + text-align: right; +} + +/* Status badge, mirroring the inline chip's state pill. */ +.gitea-search-table__state { + padding: 0 var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--background-modifier-border); + color: var(--text-muted); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); + letter-spacing: 0.02em; + text-transform: uppercase; + /* CLOSED must never split to CLOS / ED. A plain nowrap (set previously) lost + * to the user's mobile theme, which floats this badge into a tight top-right + * pill and resets the break behaviour — the shared theme-proof rule below + * forces all three break properties to stay whole regardless of theme. */ +} + +.gitea-search-table__state--open { + background-color: var(--color-green); + color: var(--text-on-accent); +} + +.gitea-search-table__state--closed { + background-color: var(--color-red); + color: var(--text-on-accent); +} + +.gitea-search-table__state--in-progress { + background-color: var(--color-orange); + color: var(--text-on-accent); +} + +/* Line 2 — truncated body */ +.gitea-card__body { + margin-top: var(--size-4-1); + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +/* Line 3 — labels, author, milestone */ +.gitea-card__meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--size-4-2); + margin-top: var(--size-4-2); + font-size: var(--font-ui-smaller); +} + +.gitea-card__labels { + display: flex; + flex-wrap: wrap; + gap: var(--size-4-1); +} + +.gitea-card__label { + padding: 0 var(--size-4-2); + border-radius: var(--radius-s); + background-color: var(--gitea-label-bg, var(--background-modifier-border)); + color: var(--gitea-label-fg, var(--text-normal)); + font-size: var(--font-ui-smaller); + line-height: 1.6; + /* Labels wrap *between* chips (the row flex-wraps), but an individual label + * name shouldn't split mid-word. */ + white-space: nowrap; +} + +/* blocked-by:#N dependency chips — highlighted so blockers stand out, with the + * blocker's live state shown inline via the dot below. */ +.gitea-card__label--blocked-by { + display: inline-flex; + align-items: center; + gap: var(--size-4-1); + background-color: var(--background-modifier-error); + color: var(--text-normal); + font-weight: var(--font-medium); +} + +/* The blocker has closed — the dependency is cleared, so de-emphasise it */ +.gitea-card__label--resolved { + background-color: var(--background-modifier-success); + font-weight: var(--font-normal); + text-decoration: line-through; + opacity: 0.85; +} + +/* Inline blocker-state dot. Semantics are the inverse of a card's own state + * dot: an open blocker is still blocking (red), a closed one is cleared (green). */ +.gitea-card__blocker-dot { + width: 0.55em; + height: 0.55em; + border-radius: 50%; + background-color: var(--text-muted); +} + +.gitea-card__blocker-dot--open { + background-color: var(--color-red); +} + +.gitea-card__blocker-dot--closed { + background-color: var(--color-green); +} + +.gitea-card__author, +.gitea-card__milestone, +.gitea-card__repo-name, +.gitea-card__comments, +.gitea-card__state, +.gitea-card__updated { + display: inline-flex; + align-items: center; + gap: var(--size-4-1); + color: var(--text-muted); +} + +/* The open/closed state badge is a short word (OPEN / CLOSED) — keep it whole + * so a narrow phone width or a theme that floats it into a tight pill can't + * break it mid-token (e.g. CLOS / ED). The break lock lives in the shared + * theme-proof rule below. */ + +/* Repo identity at the front of the status row (issue #22) */ +.gitea-card__repo-name { + font-variant-numeric: tabular-nums; + text-decoration: none; +} + +.gitea-card__repo-name:hover { + color: var(--text-normal); + text-decoration: underline; +} + +.gitea-card__avatar { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.4em; + height: 1.4em; + border-radius: 50%; + background-color: var(--background-modifier-border); + color: var(--text-normal); + font-size: var(--font-ui-smaller); + font-weight: var(--font-semibold); +} + +/* Line 4 — comments, state, updated */ +.gitea-card__status { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--size-4-3); + margin-top: var(--size-4-1); + font-size: var(--font-ui-smaller); +} + +.gitea-card__dot { + width: 0.6em; + height: 0.6em; + border-radius: 50%; + background-color: var(--text-muted); +} + +.gitea-card__dot--open { + background-color: var(--color-green); +} + +.gitea-card__dot--closed { + background-color: var(--color-red); +} + +/* Milestone progress block */ +.gitea-milestone__header { + display: flex; + align-items: baseline; + gap: var(--size-4-2); +} + +.gitea-milestone__title { + font-weight: var(--font-medium); +} + +.gitea-milestone__percent { + flex-shrink: 0; + margin-left: auto; + color: var(--text-muted); + font-size: var(--font-ui-smaller); + font-variant-numeric: tabular-nums; +} + +/* Track + fill — fill width is driven by the --gitea-progress custom property + * the plugin sets per render (no inline style rules, like the label chips). */ +.gitea-milestone__bar { + margin-top: var(--size-4-2); + height: var(--size-4-2); + border-radius: var(--radius-s); + background-color: var(--background-modifier-border); + overflow: hidden; +} + +.gitea-milestone__bar-fill { + width: var(--gitea-progress, 0%); + height: 100%; + border-radius: var(--radius-s); + background-color: var(--interactive-accent); +} + +.gitea-milestone__stats { + display: flex; + flex-wrap: wrap; + gap: var(--size-4-3); + margin-top: var(--size-4-1); + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +/* Dependency graph (issue #39) — a DAG of issue nodes drawn as an SVG. The SVG + * carries only geometry; every colour/weight is a CSS class driven by Obsidian + * variables, mirroring the card/chip state vocabulary (open green, closed red, + * in-progress orange). The block scrolls horizontally if the graph is wider than + * the note, and the SVG scales down to fit. */ +.gitea-graph { + position: relative; + overflow-x: auto; +} + +.gitea-graph__stale-badge { + display: inline-block; + margin-bottom: var(--size-4-2); +} + +/* Graph header (issue #42) — the scope title on the left, a "Refresh" button on + the right that forces a fresh fetch on demand, matching the search toolbar. */ +.gitea-graph__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--size-4-2); + margin-bottom: var(--size-4-2); +} + +/* Title/subtitle stack — sized down to match the graph node text so the header + reads as a quiet caption, not a banner (#44): the title mirrors the node + number (Issue #) and the subtitle mirrors the node title (Description). */ +.gitea-graph__heading { + display: flex; + flex-direction: column; +} + +.gitea-graph__title { + font-size: var(--font-ui-small); + font-weight: var(--font-medium); + color: var(--text-normal); +} + +.gitea-graph__subtitle { + font-size: var(--font-ui-smaller); + color: var(--text-muted); +} + +/* Header links (issue #47) — the title opens the repo and each milestone opens + its page in Gitea. Keep the quiet caption colour so the header still reads as a + caption, not a banner; only the hover underline signals they are clickable. */ +a.gitea-graph__title, +.gitea-graph__milestone-link { + color: inherit; + text-decoration: none; +} + +a.gitea-graph__title:hover, +.gitea-graph__milestone-link:hover { + cursor: pointer; + text-decoration: underline; +} + +.gitea-graph__svg { + max-width: 100%; + height: auto; +} + +.gitea-graph__svg text { + font-family: var(--font-interface); +} + +/* Edges — blocker → dependent, with the arrowhead marker below. */ +.gitea-graph__edge { + fill: none; + stroke: var(--text-muted); + stroke-width: 1.5px; +} + +.gitea-graph__arrow { + fill: var(--text-muted); +} + +/* Nodes — an SVG anchor opening the issue in Gitea. */ +.gitea-graph__node { + cursor: pointer; +} + +.gitea-graph__node-rect { + fill: var(--background-secondary); + stroke: var(--background-modifier-border); + stroke-width: 1.5px; +} + +.gitea-graph__node:hover .gitea-graph__node-rect { + fill: var(--background-modifier-hover); +} + +/* State colours the node outline, matching the dot/chip palette. */ +.gitea-graph__node--open .gitea-graph__node-rect { + stroke: var(--color-green); +} + +.gitea-graph__node--closed .gitea-graph__node-rect { + stroke: var(--color-red); +} + +.gitea-graph__node--in-progress .gitea-graph__node-rect { + stroke: var(--color-orange); +} + +/* Closed work recedes; a still-blocked open issue is dimmed with a dashed + * outline, so the currently-unblocked nodes (what the processor picks up next) + * read as the live front. */ +.gitea-graph__node--closed { + opacity: 0.7; +} + +.gitea-graph__node--blocked { + opacity: 0.5; +} + +.gitea-graph__node--blocked .gitea-graph__node-rect { + stroke-dasharray: 4 3; +} + +/* State dot inside each node, same palette as the cards/table. */ +.gitea-graph__node-dot { + fill: var(--text-muted); +} + +.gitea-graph__node-dot--open { + fill: var(--color-green); +} + +.gitea-graph__node-dot--closed { + fill: var(--color-red); +} + +.gitea-graph__node-dot--in-progress { + fill: var(--color-orange); +} + +.gitea-graph__node-number { + fill: var(--text-normal); + font-weight: var(--font-semibold); + font-size: var(--font-ui-small); + font-variant-numeric: tabular-nums; +} + +.gitea-graph__node-title { + fill: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +/* Line 5 — on-card quick actions (comment / toggle state / labels) */ +.gitea-card__actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--size-4-2); + margin-top: var(--size-4-2); +} + +.gitea-card__action { + padding: var(--size-4-1) var(--size-4-2); + font-size: var(--font-ui-smaller); +} + +/* Refresh sits at the far end of the bar, away from the write actions */ +.gitea-card__action--refresh { + order: 1; + margin-left: auto; +} + +/* Shared manual-refresh control (issue #45) — one small, subtle ghost icon + used by the single-issue card, the search toolbar, and the graph header so + "refresh now" reads identically everywhere and stays out of the way until + hovered. Builds on Obsidian's clickable-icon, matching the settings-tab + refresh buttons. */ +.gitea-refresh-button { + color: var(--text-muted); + --icon-size: var(--icon-s); +} + +.gitea-refresh-button:hover { + color: var(--text-normal); +} + +/* Corner placement (issue #45) — the search and graph blocks pin their refresh + to the bottom-right corner so every "refresh now" affordance lives in the + same out-of-the-way spot, over the block rather than in a top toolbar. */ +.gitea-refresh-button--corner { + position: absolute; + right: var(--size-4-2); + bottom: var(--size-4-2); + z-index: 1; +} + +/* Spin the icon while a refresh is in flight (issue #45) — the click adds this + class and it spins until the refreshed block tears the button down. */ +.gitea-refresh-button--spinning svg { + animation: gitea-refresh-spin 0.8s linear infinite; +} + +@keyframes gitea-refresh-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: reduce) { + .gitea-refresh-button--spinning svg { + animation: none; + } +} + +/* Inline comment box, revealed under the action bar */ +.gitea-card__comment-form { + display: flex; + flex-direction: column; + gap: var(--size-4-2); + flex-basis: 100%; + margin-top: var(--size-4-1); +} + +.gitea-card__comment-form--hidden { + display: none; +} + +.gitea-card__comment-input { + width: 100%; + resize: vertical; + font-family: var(--font-text); +} + +.gitea-card__comment-buttons { + display: flex; + gap: var(--size-4-2); + align-items: center; + flex-wrap: wrap; +} + +/* Image-attach picker in the comment box (issue #56), shown only when "Show images + * in comments" is on. Pushed to the right so it trails the Send/Cancel buttons, and + * shrunk to the small UI scale so it sits inline without dominating the row. */ +.gitea-card__comment-file { + margin-left: auto; + font-size: var(--font-ui-smaller); + max-width: 100%; +} + +/* Comment-thread preview (issue #40) — the comment count on a single-issue card + * becomes a toggle button that expands a list of the latest comments inline. The + * button is reset to look like the plain count it replaces, with a hover/caret + * affordance to signal it is clickable. */ +.gitea-card__comments--toggle { + border: none; + box-shadow: none; + padding: 0; + margin: 0; + height: auto; + background-color: transparent; + color: var(--text-muted); + font-size: inherit; + line-height: inherit; + cursor: pointer; +} + +.gitea-card__comments--toggle:hover { + color: var(--text-normal); +} + +.gitea-card__comments-caret { + font-size: 0.85em; +} + +/* The expanded thread sits between the status line and the action bar. */ +.gitea-card__comments-thread { + display: flex; + flex-direction: column; + gap: var(--size-4-2); + margin-top: var(--size-4-2); + padding-top: var(--size-4-2); + border-top: 1px solid var(--background-modifier-border); +} + +/* Loading / empty / stale / "older hidden" one-liners inside the thread. */ +.gitea-card__comments-loading, +.gitea-card__comments-message { + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +.gitea-card__comment-header { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--size-4-1); + font-size: var(--font-ui-smaller); +} + +.gitea-card__comment-author { + font-weight: var(--font-medium); + color: var(--text-normal); +} + +.gitea-card__comment-time { + color: var(--text-muted); +} + +/* Rendered Markdown body — kept compact; trim the outer margins Obsidian's + * Markdown renderer adds to the first/last block so comments stack tightly. */ +.gitea-card__comment-body { + margin-top: var(--size-4-1); + font-size: var(--font-ui-small); +} + +.gitea-card__comment-body > :first-child { + margin-top: 0; +} + +.gitea-card__comment-body > :last-child { + margin-bottom: 0; +} + +/* Images pasted into Gitea comments (issue #53), shown only when the + * "Show images in comments" setting is on. Sized by CSS (community rule — + * no inline styles): never wider than the card, capped height. */ +.gitea-card__comment-image { + display: block; + max-width: 100%; + height: auto; + max-height: 320px; + margin: var(--size-4-2) 0; + border-radius: var(--radius-s); +} + +/* While the authed bytes are in flight the native src is blanked, so show a + * dashed placeholder box in its place. */ +.gitea-card__comment-image--loading { + min-width: 64px; + min-height: 48px; + background-color: var(--background-secondary); + border: 1px dashed var(--background-modifier-border); +} + +/* A loaded image is clickable — opens the in-app lightbox. */ +.gitea-card__comment-image--loaded { + cursor: zoom-in; +} + +/* Shown in place of an image our token couldn't fetch (CF-Access reject / + * invalid token), carrying the auth-taxonomy message. */ +.gitea-card__comment-image-error { + display: inline-block; + padding: 0 var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--background-secondary); + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +/* Attachment strip (issue #54) — a wrapping row of image thumbnails and file + * chips under a single-issue card's body, shown only when "Show attachments" is + * on. Sized entirely in CSS (community rule — no inline styles). */ +.gitea-card__attachments { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--size-4-2); + margin-top: var(--size-4-2); +} + +/* Image attachment thumbnail — a small fixed-height preview; click opens the + * shared lightbox (the --loaded state below adds the zoom cursor). */ +.gitea-card__attachment-thumb { + height: 64px; + width: auto; + max-width: 96px; + object-fit: cover; + border-radius: var(--radius-s); + border: 1px solid var(--background-modifier-border); +} + +/* While the authed bytes are in flight there is no src — show a dashed box. */ +.gitea-card__attachment-thumb--loading { + width: 64px; + background-color: var(--background-secondary); + border-style: dashed; +} + +.gitea-card__attachment-thumb--loaded { + cursor: zoom-in; +} + +/* Non-image attachment — a 📎 filename chip linking to the authed download. */ +.gitea-card__attachment-file { + display: inline-flex; + align-items: center; + gap: var(--size-4-1); + padding: 0 var(--size-4-2); + border-radius: var(--radius-s); + background-color: var(--background-modifier-border); + color: var(--text-normal); + font-size: var(--font-ui-smaller); + line-height: 1.6; + text-decoration: none; +} + +.gitea-card__attachment-file:hover { + background-color: var(--background-modifier-hover); + text-decoration: none; +} + +.gitea-card__attachment-file-name { + overflow: hidden; + max-width: 16em; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Shown in place of a thumbnail our token couldn't fetch (CF-Access reject / + * invalid token), carrying the auth-taxonomy message — mirrors the comment one. */ +.gitea-card__attachment-error { + display: inline-block; + padding: 0 var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--background-secondary); + color: var(--text-muted); + font-size: var(--font-ui-smaller); +} + +/* In-app lightbox modal for a full-size comment image (issue #53). */ +.gitea-lightbox-modal { + max-width: 90vw; +} + +.gitea-lightbox { + display: flex; + align-items: center; + justify-content: center; +} + +.gitea-lightbox__image { + max-width: 100%; + max-height: 80vh; + height: auto; + border-radius: var(--radius-s); +} + +.gitea-lightbox__status { + padding: var(--size-4-4); + color: var(--text-muted); +} + +/* Inline chip — a completed GITEA:owner/repo:#123 reference in rendered prose */ +.gitea-chip { + display: inline-flex; + align-items: center; + gap: var(--size-4-1); + padding: 0 var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--background-modifier-border); + color: var(--text-normal); + /* Chips render inline in prose, so size relative to the surrounding text + * (em) rather than a fixed UI scale — this keeps them in step with the + * note's font size instead of staying small when the reader zooms text. */ + font-size: 0.9em; + font-variant-numeric: tabular-nums; + text-decoration: none; + line-height: 1.5; +} + +.gitea-chip:hover { + background-color: var(--background-modifier-hover); + text-decoration: none; +} + +/* Per-project accent (issue #30): mirror the card's coloured left border on the + * inline chip, driven by the same --gitea-accent custom property the plugin + * sets per repo. Chips without a configured accent keep the plain background. */ +.gitea-chip--accented { + border-left: 3px solid var(--gitea-accent, var(--background-modifier-border)); +} + +.gitea-chip--closed { + opacity: 0.6; +} + +.gitea-chip__dot { + width: 0.55em; + height: 0.55em; + border-radius: 50%; + background-color: var(--text-muted); +} + +.gitea-chip__dot--open { + background-color: var(--color-green); +} + +.gitea-chip__dot--closed { + background-color: var(--color-red); +} + +.gitea-chip__dot--in-progress { + background-color: var(--color-orange); +} + +/* Issue title, shown once the issue loads — truncated so a long title can't + blow out the line it sits in. */ +.gitea-chip__title { + overflow: hidden; + max-width: 24em; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Open/closed status badge, mirroring the full card's state pill. */ +.gitea-chip__state { + padding: 0 var(--size-4-1); + border-radius: var(--radius-s); + background-color: var(--background-modifier-border); + color: var(--text-muted); + /* Relative to the chip (em) so it tracks the surrounding text too. */ + font-size: 0.85em; + font-weight: var(--font-semibold); + letter-spacing: 0.02em; + text-transform: uppercase; + /* Intra-token break lock lives in the shared theme-proof rule below. */ +} + +.gitea-chip__state--open { + background-color: var(--color-green); + color: var(--text-on-accent); +} + +.gitea-chip__state--closed { + background-color: var(--color-red); + color: var(--text-on-accent); +} + +.gitea-chip__state--in-progress { + background-color: var(--color-orange); + color: var(--text-on-accent); +} + +/* Keep the placeholder chip compact (just the dot + number) before the issue + has loaded — empty title/state slots add no width or stray padding. */ +.gitea-chip__title:empty, +.gitea-chip__state:empty { + display: none; +} + +/* Inline autocomplete suggestions (EditorSuggest dropdown) */ +.gitea-suggestion { + display: flex; + align-items: baseline; + gap: var(--size-4-2); +} + +.gitea-suggestion__number { + flex-shrink: 0; + font-weight: var(--font-semibold); + font-variant-numeric: tabular-nums; +} + +.gitea-suggestion__title { + color: var(--text-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* New-issue / convert-selection modal */ +.gitea-new-issue__body { + width: 100%; + resize: vertical; + font-family: var(--font-text); +} + +/* Toggleable label chips. The grid wraps and is height-capped + scrollable, so + * a repo with many labels never grows the modal unbounded. */ +.gitea-new-issue__labels { + display: flex; + flex-wrap: wrap; + gap: var(--size-4-1); + max-height: 12em; + overflow-y: auto; + padding: var(--size-4-1) 0; +} + +/* Unselected chip: outlined in the label's colour. Tapping fills it in. */ +.gitea-new-issue__label { + height: auto; + padding: var(--size-4-1) var(--size-4-2); + border: 1px solid var(--gitea-label-bg, var(--background-modifier-border)); + border-radius: var(--radius-s); + background-color: transparent; + box-shadow: none; + color: var(--text-muted); + font-size: var(--font-ui-smaller); + line-height: 1.4; + cursor: pointer; +} + +.gitea-new-issue__label.is-selected { + background-color: var(--gitea-label-bg, var(--background-modifier-border)); + color: var(--gitea-label-fg, var(--text-normal)); +} + +/* + * Remove affordance on a selected milestone chip in the graph wizard (issue #43): + * the chip is a dropdown-added member of the set, clicked to drop it back out. + */ +.gitea-new-issue__label-remove { + margin-inline-start: var(--size-4-1); + opacity: 0.7; +} + +.gitea-new-issue__notice { + padding: var(--size-4-2) 0; + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +/* Image picker (issue #57) — file input plus a wrapping grid of thumbnail + * previews of the queued-but-not-yet-uploaded images. */ +.gitea-new-issue__file { + margin: var(--size-4-1) 0; +} + +.gitea-new-issue__previews { + display: flex; + flex-wrap: wrap; + gap: var(--size-4-2); + padding: var(--size-4-1) 0; +} + +/* One preview: the thumbnail with a remove affordance pinned to its corner. */ +.gitea-new-issue__preview { + position: relative; +} + +.gitea-new-issue__preview-img { + height: 64px; + width: auto; + max-width: 96px; + object-fit: cover; + border-radius: var(--radius-s); + border: 1px solid var(--background-modifier-border); +} + +.gitea-new-issue__preview-remove { + position: absolute; + top: calc(-1 * var(--size-4-1)); + inset-inline-end: calc(-1 * var(--size-4-1)); + height: 20px; + width: 20px; + padding: 0; + border-radius: 50%; + line-height: 1; + cursor: pointer; +} + +/* Insert-existing-issue modal */ +.gitea-insert-issue__notice { + padding: var(--size-4-2) 0; + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +/* Search-query wizard (issue #34) */ +.gitea-search-wizard__notice { + padding: var(--size-4-2) 0; + color: var(--text-muted); + font-size: var(--font-ui-small); +} + +/* Live preview of the generated block — read-only, monospace, wraps long lines. */ +.gitea-search-wizard__preview { + margin: 0 0 var(--size-4-2); + padding: var(--size-4-2); + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + background-color: var(--background-secondary); + color: var(--text-normal); + font-family: var(--font-monospace); + font-size: var(--font-ui-smaller); + white-space: pre-wrap; + word-break: break-word; +} + +/* Theme-proof intra-token lock (issues #21, #28, #59) — issue numbers (#123) and + * state badges (OPEN / CLOSED / STALE) are indivisible tokens that must never + * split mid-token (CLOS / ED), even under mobile themes that pill them and reset + * the break behaviour with multi-class selectors. Each class is repeated three + * times to raise specificity to (0,3,0) — no importance flag (the community + * linter rejects it), yet above any realistic theme selector on these spans. + * keep-all guards against themes that set word-break: break-all globally. */ +.gitea-card__stale-badge.gitea-card__stale-badge.gitea-card__stale-badge, +.gitea-card__number.gitea-card__number.gitea-card__number, +.gitea-card__state.gitea-card__state.gitea-card__state, +.gitea-search-table__number.gitea-search-table__number.gitea-search-table__number, +.gitea-search-table__state.gitea-search-table__state.gitea-search-table__state, +.gitea-chip__state.gitea-chip__state.gitea-chip__state { + white-space: nowrap; + word-break: keep-all; + overflow-wrap: normal; +} + +/* Mobile (issue #28) — Obsidian sets body.is-mobile on phones. Tighten the + * inter-item gaps in the meta/status rows so the now-unbreakable badges have a + * little more room to sit on one line before the row wraps between them. + * Desktop rendering is untouched. */ +body.is-mobile .gitea-card__meta, +body.is-mobile .gitea-card__status { + gap: var(--size-4-1); +} + +/* Mobile search table (issue #59) — phone Obsidian themes routinely restyle plain + * s for small screens: they float each row into a spaced card, inflate cell + * padding, override our intra-token break rules, and let the table grow past the + * viewport. On the compact search table that reads as the reported breakage — big + * gaps between rows, the issue number wrapping (#5 / 6), and the title + state badge + * spilling off the right edge. Desktop is perfect because desktop themes leave + * tables alone, so this repair is scoped to body.is-mobile and forces the table back + * to a tight, fixed-width grid. The resets repeat each class three times on + * purpose (the same theme-proofing rationale as the intra-token lock above): + * with body.is-mobile that lands at (0,4,1) specificity, so a hostile mobile + * theme's `td`/`tr`/`table` rules can't out-specify ours — no importance flag + * needed (the community linter rejects it). */ +body.is-mobile .gitea-search-table.gitea-search-table.gitea-search-table { + /* table-layout: fixed pins the table to the container width so it can never + * overflow the viewport; the title column then absorbs the slack and truncates + * with its existing ellipsis instead of pushing the row off-screen. */ + display: table; + table-layout: fixed; + width: 100%; + margin: 0; +} + +body.is-mobile .gitea-search-table.gitea-search-table.gitea-search-table tbody { + display: table-row-group; +} + +/* Undo themes that turn each row into a spaced, padded card — the source of the + * "weird spacing" between rows. */ +body.is-mobile .gitea-search-table__row.gitea-search-table__row.gitea-search-table__row { + display: table-row; + margin: 0; + padding: 0; + background-color: transparent; + border-radius: 0; +} + +body.is-mobile .gitea-search-table__cell.gitea-search-table__cell.gitea-search-table__cell { + display: table-cell; + padding: var(--size-4-1); + vertical-align: middle; +} + +/* The state dot already encodes open/closed/in-progress, so the text badge is + * redundant on a narrow phone — and it, plus the cross-repo owner/repo column, were + * the cells overflowing off the right edge. Drop both on mobile so the number and + * title own the row; the linked number still taps through to the full issue. */ +body.is-mobile .gitea-search-table__cell--state.gitea-search-table__cell--state.gitea-search-table__cell--state, +body.is-mobile .gitea-search-table__cell--repo.gitea-search-table__cell--repo.gitea-search-table__cell--repo { + display: none; +} + +/* With table-layout: fixed the dot and number need explicit columns: the dot a + * snug fixed width, the number wide enough for "#999" so it never wraps to #9 / 9. */ +body.is-mobile .gitea-search-table__cell--dot.gitea-search-table__cell--dot.gitea-search-table__cell--dot { + width: 1.4em; +} + +body.is-mobile .gitea-search-table__cell--number.gitea-search-table__cell--number.gitea-search-table__cell--number { + width: 3.2em; +} diff --git a/.obsidian/plugins/highlightr-plus/main.js b/.obsidian/plugins/highlightr-plus/main.js new file mode 100644 index 0000000..01b0a72 --- /dev/null +++ b/.obsidian/plugins/highlightr-plus/main.js @@ -0,0 +1,6016 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ROLLUP +if you want to view the source visit the plugins github repository +*/ + +'use strict'; + +var obsidian = require('obsidian'); + +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ + + +function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +function wait(delay) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => window.setTimeout(resolve, delay)); + }); +} + +function debounce(func, wait = 0, immediate = false) { + let timeout = null; + return function executedFunction(...args) { + const later = () => { + timeout = null; + if (!immediate) { + func.apply(this, args); + } + }; + const callNow = immediate && timeout === null; + if (timeout !== null) { + window.clearTimeout(timeout); + } + timeout = window.setTimeout(later, wait); + if (callNow) { + func.apply(this, args); + } + }; +} + +function normalizeSvgFillStyle(input) { + const color = (input !== null && input !== void 0 ? input : "").trim(); + const hex = color.startsWith("#") ? color.slice(1) : color; + if (/^[0-9a-fA-F]{8}$/.test(hex)) { + const rgb = hex.slice(0, 6); + const alpha = Number.parseInt(hex.slice(6, 8), 16) / 255; + return `fill:#${rgb} !important;fill-opacity:${alpha.toFixed(3)} !important;`; + } + if (/^[0-9a-fA-F]{6}$/.test(hex)) { + return `fill:#${hex} !important;fill-opacity:1 !important;`; + } + if (color.length > 0) { + return `fill:${color} !important;fill-opacity:1 !important;`; + } + return `fill:transparent !important;fill-opacity:1 !important;`; +} +function getColorSignature(input) { + const normalized = (input !== null && input !== void 0 ? input : "transparent").toLowerCase().replace(/[^a-z0-9]/g, ""); + return normalized.length > 0 ? normalized : "transparent"; +} +function getHighlighterPenIconId(highlighter, color) { + return `highlightr-pen-${highlighter}-${getColorSignature(color)}`.toLowerCase(); +} +const icons = { + "highlightr-eraser": ``, + "highlightr-pen": ``, + "highlightr-add": ``, + "highlightr-save": ``, + "highlightr-delete": ``, + "highlightr-copy": `copy`, +}; +function createHighlighterIcons(settings, plugin) { + const highlighterIcons = {}; + const orderedHighlighters = plugin.settings.highlighterOrder.length > 0 + ? plugin.settings.highlighterOrder + : Object.keys(settings.highlighters); + for (const key of orderedHighlighters) { + const colorValue = settings.highlighters[key]; + const fillStyle = normalizeSvgFillStyle(colorValue); + const highlighterPen = getHighlighterPenIconId(key, colorValue); + highlighterIcons[highlighterPen] = ``; + } + Object.keys(highlighterIcons).forEach((key) => { + obsidian.addIcon(key, highlighterIcons[key]); + }); + return highlighterIcons; +} +function addIcons() { + Object.keys(icons).forEach((key) => { + obsidian.addIcon(key, icons[key]); + }); +} + +function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +var pickr_min = {exports: {}}; + +/*! Pickr 1.8.2 MIT | https://github.com/Simonwep/pickr */ + +var hasRequiredPickr_min; + +function requirePickr_min () { + if (hasRequiredPickr_min) return pickr_min.exports; + hasRequiredPickr_min = 1; + (function (module, exports) { + !function(t,e){module.exports=e();}(self,(function(){return (()=>{var t={d:(e,o)=>{for(var n in o)t.o(o,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:true,get:o[n]});},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:true});}},e={};t.d(e,{default:()=>L});var o={};function n(t,e,o,n,i={}){e instanceof HTMLCollection||e instanceof NodeList?e=Array.from(e):Array.isArray(e)||(e=[e]),Array.isArray(o)||(o=[o]);for(const s of e)for(const e of o)s[t](e,n,{capture:false,...i});return Array.prototype.slice.call(arguments,1)}t.r(o),t.d(o,{adjustableInputNumbers:()=>p,createElementFromString:()=>r,createFromTemplate:()=>a,eventPath:()=>l,off:()=>s,on:()=>i,resolveElement:()=>c});const i=n.bind(null,"addEventListener"),s=n.bind(null,"removeEventListener");function r(t){const e=document.createElement("div");return e.innerHTML=t.trim(),e.firstElementChild}function a(t){const e=(t,e)=>{const o=t.getAttribute(e);return t.removeAttribute(e),o},o=(t,n={})=>{const i=e(t,":obj"),s=e(t,":ref"),r=i?n[i]={}:n;s&&(n[s]=t);for(const n of Array.from(t.children)){const t=e(n,":arr"),i=o(n,t?{}:r);t&&(r[t]||(r[t]=[])).push(Object.keys(i).length?i:n);}return n};return o(r(t))}function l(t){let e=t.path||t.composedPath&&t.composedPath();if(e)return e;let o=t.target.parentElement;for(e=[t.target,o];o=o.parentElement;)e.push(o);return e.push(document,window),e}function c(t){return t instanceof Element?t:"string"==typeof t?t.split(/>>/g).reduce(((t,e,o,n)=>(t=t.querySelector(e),ot)){function o(o){const n=[.001,.01,.1][Number(o.shiftKey||2*o.ctrlKey)]*(o.deltaY<0?1:-1);let i=0,s=t.selectionStart;t.value=t.value.replace(/[\d.]+/g,((t,o)=>o<=s&&o+t.length>=s?(s=o,e(Number(t),n,i)):(i++,t))),t.focus(),t.setSelectionRange(s,s),o.preventDefault(),t.dispatchEvent(new Event("input"));}i(t,"focus",(()=>i(window,"wheel",o,{passive:false}))),i(t,"blur",(()=>s(window,"wheel",o)));}const{min:u,max:h,floor:d,round:m}=Math;function f(t,e,o){e/=100,o/=100;const n=d(t=t/360*6),i=t-n,s=o*(1-e),r=o*(1-i*e),a=o*(1-(1-i)*e),l=n%6;return [255*[o,r,s,s,a,o][l],255*[a,o,o,r,s,s][l],255*[s,s,a,o,o,r][l]]}function v(t,e,o){const n=(2-(e/=100))*(o/=100)/2;return 0!==n&&(e=1===n?0:n<.5?e*o/(2*n):e*o/(2-2*n)),[t,100*e,100*n]}function b(t,e,o){const n=u(t/=255,e/=255,o/=255),i=h(t,e,o),s=i-n;let r,a;if(0===s)r=a=0;else {a=s/i;const n=((i-t)/6+s/2)/s,l=((i-e)/6+s/2)/s,c=((i-o)/6+s/2)/s;t===i?r=c-l:e===i?r=1/3+n-c:o===i&&(r=2/3+l-n),r<0?r+=1:r>1&&(r-=1);}return [360*r,100*a,100*i]}function y(t,e,o,n){e/=100,o/=100;return [...b(255*(1-u(1,(t/=100)*(1-(n/=100))+n)),255*(1-u(1,e*(1-n)+n)),255*(1-u(1,o*(1-n)+n)))]}function g(t,e,o){e/=100;const n=2*(e*=(o/=100)<.5?o:1-o)/(o+e)*100,i=100*(o+e);return [t,isNaN(n)?0:n,i]}function _(t){return b(...t.match(/.{2}/g).map((t=>parseInt(t,16))))}function w(t){t=t.match(/^[a-zA-Z]+$/)?function(t){if("black"===t.toLowerCase())return "#000";const e=document.createElement("canvas").getContext("2d");return e.fillStyle=t,"#000"===e.fillStyle?null:e.fillStyle}(t):t;const e={cmyk:/^cmyk[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)/i,rgba:/^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hsla:/^((hsla)|hsl)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hsva:/^((hsva)|hsv)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hexa:/^#?(([\dA-Fa-f]{3,4})|([\dA-Fa-f]{6})|([\dA-Fa-f]{8}))$/i},o=t=>t.map((t=>/^(|\d+)\.\d+|\d+$/.test(t)?Number(t):void 0));let n;t:for(const i in e){if(!(n=e[i].exec(t)))continue;const s=t=>!!n[2]==("number"==typeof t);switch(i){case "cmyk":{const[,t,e,s,r]=o(n);if(t>100||e>100||s>100||r>100)break t;return {values:y(t,e,s,r),type:i}}case "rgba":{const[,,,t,e,r,a]=o(n);if(t>255||e>255||r>255||a<0||a>1||!s(a))break t;return {values:[...b(t,e,r),a],a,type:i}}case "hexa":{let[,t]=n;4!==t.length&&3!==t.length||(t=t.split("").map((t=>t+t)).join(""));const e=t.substring(0,6);let o=t.substring(6);return o=o?parseInt(o,16)/255:void 0,{values:[..._(e),o],a:o,type:i}}case "hsla":{const[,,,t,e,r,a]=o(n);if(t>360||e>100||r>100||a<0||a>1||!s(a))break t;return {values:[...g(t,e,r),a],a,type:i}}case "hsva":{const[,,,t,e,r,a]=o(n);if(t>360||e>100||r>100||a<0||a>1||!s(a))break t;return {values:[t,e,r,a],a,type:i}}}}return {values:null,type:null}}function A(t=0,e=0,o=0,n=1){const i=(t,e)=>(o=-1)=>e(~o?t.map((t=>Number(t.toFixed(o)))):t),s={h:t,s:e,v:o,a:n,toHSVA(){const t=[s.h,s.s,s.v,s.a];return t.toString=i(t,(t=>`hsva(${t[0]}, ${t[1]}%, ${t[2]}%, ${s.a})`)),t},toHSLA(){const t=[...v(s.h,s.s,s.v),s.a];return t.toString=i(t,(t=>`hsla(${t[0]}, ${t[1]}%, ${t[2]}%, ${s.a})`)),t},toRGBA(){const t=[...f(s.h,s.s,s.v),s.a];return t.toString=i(t,(t=>`rgba(${t[0]}, ${t[1]}, ${t[2]}, ${s.a})`)),t},toCMYK(){const t=function(t,e,o){const n=f(t,e,o),i=n[0]/255,s=n[1]/255,r=n[2]/255,a=u(1-i,1-s,1-r);return [100*(1===a?0:(1-i-a)/(1-a)),100*(1===a?0:(1-s-a)/(1-a)),100*(1===a?0:(1-r-a)/(1-a)),100*a]}(s.h,s.s,s.v);return t.toString=i(t,(t=>`cmyk(${t[0]}%, ${t[1]}%, ${t[2]}%, ${t[3]}%)`)),t},toHEXA(){const t=function(t,e,o){return f(t,e,o).map((t=>m(t).toString(16).padStart(2,"0")))}(s.h,s.s,s.v),e=s.a>=1?"":Number((255*s.a).toFixed(0)).toString(16).toUpperCase().padStart(2,"0");return e&&t.push(e),t.toString=()=>`#${t.join("").toUpperCase()}`,t},clone:()=>A(s.h,s.s,s.v,s.a)};return s}const C=t=>Math.max(Math.min(t,1),0);function $(t){const e={options:Object.assign({lock:null,onchange:()=>0,onstop:()=>0},t),_keyboard(t){const{options:o}=e,{type:n,key:i}=t;if(document.activeElement===o.wrapper){const{lock:o}=e.options,s="ArrowUp"===i,r="ArrowRight"===i,a="ArrowDown"===i,l="ArrowLeft"===i;if("keydown"===n&&(s||r||a||l)){let n=0,i=0;"v"===o?n=s||r?1:-1:"h"===o?n=s||r?-1:1:(i=s?-1:a?1:0,n=l?-1:r?1:0),e.update(C(e.cache.x+.01*n),C(e.cache.y+.01*i)),t.preventDefault();}else i.startsWith("Arrow")&&(e.options.onstop(),t.preventDefault());}},_tapstart(t){i(document,["mouseup","touchend","touchcancel"],e._tapstop),i(document,["mousemove","touchmove"],e._tapmove),t.cancelable&&t.preventDefault(),e._tapmove(t);},_tapmove(t){const{options:o,cache:n}=e,{lock:i,element:s,wrapper:r}=o,a=r.getBoundingClientRect();let l=0,c=0;if(t){const e=t&&t.touches&&t.touches[0];l=t?(e||t).clientX:0,c=t?(e||t).clientY:0,la.left+a.width&&(l=a.left+a.width),ca.top+a.height&&(c=a.top+a.height),l-=a.left,c-=a.top;}else n&&(l=n.x*a.width,c=n.y*a.height);"h"!==i&&(s.style.left=`calc(${l/a.width*100}% - ${s.offsetWidth/2}px)`),"v"!==i&&(s.style.top=`calc(${c/a.height*100}% - ${s.offsetHeight/2}px)`),e.cache={x:l/a.width,y:c/a.height};const p=C(l/a.width),u=C(c/a.height);switch(i){case "v":return o.onchange(p);case "h":return o.onchange(u);default:return o.onchange(p,u)}},_tapstop(){e.options.onstop(),s(document,["mouseup","touchend","touchcancel"],e._tapstop),s(document,["mousemove","touchmove"],e._tapmove);},trigger(){e._tapmove();},update(t=0,o=0){const{left:n,top:i,width:s,height:r}=e.options.wrapper.getBoundingClientRect();"h"===e.options.lock&&(o=t),e._tapmove({clientX:n+s*t,clientY:i+r*o});},destroy(){const{options:t,_tapstart:o,_keyboard:n}=e;s(document,["keydown","keyup"],n),s([t.wrapper,t.element],"mousedown",o),s([t.wrapper,t.element],"touchstart",o,{passive:false});}},{options:o,_tapstart:n,_keyboard:r}=e;return i([o.wrapper,o.element],"mousedown",n),i([o.wrapper,o.element],"touchstart",n,{passive:false}),i(document,["keydown","keyup"],r),e}function k(t={}){t=Object.assign({onchange:()=>0,className:"",elements:[]},t);const e=i(t.elements,"click",(e=>{t.elements.forEach((o=>o.classList[e.target===o?"add":"remove"](t.className))),t.onchange(e),e.stopPropagation();}));return {destroy:()=>s(...e)}}const S={variantFlipOrder:{start:"sme",middle:"mse",end:"ems"},positionFlipOrder:{top:"tbrl",right:"rltb",bottom:"btrl",left:"lrbt"},position:"bottom",margin:8},O=(t,e,o)=>{const{container:n,margin:i,position:s,variantFlipOrder:r,positionFlipOrder:a}={container:document.documentElement.getBoundingClientRect(),...S,...o},{left:l,top:c}=e.style;e.style.left="0",e.style.top="0";const p=t.getBoundingClientRect(),u=e.getBoundingClientRect(),h={t:p.top-u.height-i,b:p.bottom+i,r:p.right+i,l:p.left-u.width-i},d={vs:p.left,vm:p.left+p.width/2+-u.width/2,ve:p.left+p.width-u.width,hs:p.top,hm:p.bottom-p.height/2-u.height/2,he:p.bottom-u.height},[m,f="middle"]=s.split("-"),v=a[m],b=r[f],{top:y,left:g,bottom:_,right:w}=n;for(const t of v){const o="t"===t||"b"===t,n=h[t],[i,s]=o?["top","left"]:["left","top"],[r,a]=o?[u.height,u.width]:[u.width,u.height],[l,c]=o?[_,w]:[w,_],[p,m]=o?[y,g]:[g,y];if(!(nl))for(const r of b){const l=d[(o?"v":"h")+r];if(!(lc))return e.style[s]=l-u[s]+"px",e.style[i]=n-u[i]+"px",t+r}}return e.style.left=l,e.style.top=c,null};function E(t,e,o){return e in t?Object.defineProperty(t,e,{value:o,enumerable:true,configurable:true,writable:true}):t[e]=o,t}class L{constructor(t){E(this,"_initializingActive",true),E(this,"_recalc",true),E(this,"_nanopop",null),E(this,"_root",null),E(this,"_color",A()),E(this,"_lastColor",A()),E(this,"_swatchColors",[]),E(this,"_setupAnimationFrame",null),E(this,"_eventListener",{init:[],save:[],hide:[],show:[],clear:[],change:[],changestop:[],cancel:[],swatchselect:[]}),this.options=t=Object.assign({...L.DEFAULT_OPTIONS},t);const{swatches:e,components:o,theme:n,sliders:i,lockOpacity:s,padding:r}=t;["nano","monolith"].includes(n)&&!i&&(t.sliders="h"),o.interaction||(o.interaction={});const{preview:a,opacity:l,hue:c,palette:p}=o;o.opacity=!s&&l,o.palette=p||a||l||c,this._preBuild(),this._buildComponents(),this._bindEvents(),this._finalBuild(),e&&e.length&&e.forEach((t=>this.addSwatch(t)));const{button:u,app:h}=this._root;this._nanopop=((t,e,o)=>{const n="object"!=typeof t||t instanceof HTMLElement?{reference:t,popper:e,...o}:t;return {update(t=n){const{reference:e,popper:o}=Object.assign(n,t);if(!o||!e)throw new Error("Popper- or reference-element missing.");return O(e,o,n)}}})(u,h,{margin:r}),u.setAttribute("role","button"),u.setAttribute("aria-label",this._t("btn:toggle"));const d=this;this._setupAnimationFrame=requestAnimationFrame((function e(){if(!h.offsetWidth)return requestAnimationFrame(e);d.setColor(t.default),d._rePositioningPicker(),t.defaultRepresentation&&(d._representation=t.defaultRepresentation,d.setColorRepresentation(d._representation)),t.showAlways&&d.show(),d._initializingActive=false,d._emit("init");}));}_preBuild(){const{options:t}=this;for(const e of ["el","container"])t[e]=c(t[e]);this._root=(t=>{const{components:e,useAsButton:o,inline:n,appClass:i,theme:s,lockOpacity:r}=t.options,l=t=>t?"":'style="display:none" hidden',c=e=>t._t(e),p=a(`\n
\n\n ${o?"":''}\n\n
\n
\n
\n \n
\n
\n\n
\n
\n
\n
\n\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n\n
\n\n
\n \n\n \n \n \n \n \n\n \n \n \n
\n
\n
\n `),u=p.interaction;return u.options.find((t=>!t.hidden&&!t.classList.add("active"))),u.type=()=>u.options.find((t=>t.classList.contains("active"))),p})(this),t.useAsButton&&(this._root.button=t.el),t.container.appendChild(this._root.root);}_finalBuild(){const t=this.options,e=this._root;if(t.container.removeChild(e.root),t.inline){const o=t.el.parentElement;t.el.nextSibling?o.insertBefore(e.app,t.el.nextSibling):o.appendChild(e.app);}else t.container.appendChild(e.app);t.useAsButton?t.inline&&t.el.remove():t.el.parentNode.replaceChild(e.root,t.el),t.disabled&&this.disable(),t.comparison||(e.button.style.transition="none",t.useAsButton||(e.preview.lastColor.style.transition="none")),this.hide();}_buildComponents(){const t=this,e=this.options.components,o=(t.options.sliders||"v").repeat(2),[n,i]=o.match(/^[vh]+$/g)?o:[],s=()=>this._color||(this._color=this._lastColor.clone()),r={palette:$({element:t._root.palette.picker,wrapper:t._root.palette.palette,onstop:()=>t._emit("changestop","slider",t),onchange(o,n){if(!e.palette)return;const i=s(),{_root:r,options:a}=t,{lastColor:l,currentColor:c}=r.preview;t._recalc&&(i.s=100*o,i.v=100-100*n,i.v<0&&(i.v=0),t._updateOutput("slider"));const p=i.toRGBA().toString(0);this.element.style.background=p,this.wrapper.style.background=`\n linear-gradient(to top, rgba(0, 0, 0, ${i.a}), transparent),\n linear-gradient(to left, hsla(${i.h}, 100%, 50%, ${i.a}), rgba(255, 255, 255, ${i.a}))\n `,a.comparison?a.useAsButton||t._lastColor||l.style.setProperty("--pcr-color",p):(r.button.style.setProperty("--pcr-color",p),r.button.classList.remove("clear"));const u=i.toHEXA().toString();for(const{el:e,color:o}of t._swatchColors)e.classList[u===o.toHEXA().toString()?"add":"remove"]("pcr-active");c.style.setProperty("--pcr-color",p);}}),hue:$({lock:"v"===i?"h":"v",element:t._root.hue.picker,wrapper:t._root.hue.slider,onstop:()=>t._emit("changestop","slider",t),onchange(o){if(!e.hue||!e.palette)return;const n=s();t._recalc&&(n.h=360*o),this.element.style.backgroundColor=`hsl(${n.h}, 100%, 50%)`,r.palette.trigger();}}),opacity:$({lock:"v"===n?"h":"v",element:t._root.opacity.picker,wrapper:t._root.opacity.slider,onstop:()=>t._emit("changestop","slider",t),onchange(o){if(!e.opacity||!e.palette)return;const n=s();t._recalc&&(n.a=Math.round(100*o)/100),this.element.style.background=`rgba(0, 0, 0, ${n.a})`,r.palette.trigger();}}),selectable:k({elements:t._root.interaction.options,className:"active",onchange(e){t._representation=e.target.getAttribute("data-type").toUpperCase(),t._recalc&&t._updateOutput("swatch");}})};this._components=r;}_bindEvents(){const{_root:t,options:e}=this,o=[i(t.interaction.clear,"click",(()=>this._clearColor())),i([t.interaction.cancel,t.preview.lastColor],"click",(()=>{this.setHSVA(...(this._lastColor||this._color).toHSVA(),true),this._emit("cancel");})),i(t.interaction.save,"click",(()=>{!this.applyColor()&&!e.showAlways&&this.hide();})),i(t.interaction.result,["keyup","input"],(t=>{this.setColor(t.target.value,true)&&!this._initializingActive&&(this._emit("change",this._color,"input",this),this._emit("changestop","input",this)),t.stopImmediatePropagation();})),i(t.interaction.result,["focus","blur"],(t=>{this._recalc="blur"===t.type,this._recalc&&this._updateOutput(null);})),i([t.palette.palette,t.palette.picker,t.hue.slider,t.hue.picker,t.opacity.slider,t.opacity.picker],["mousedown","touchstart"],(()=>this._recalc=true),{passive:true})];if(!e.showAlways){const n=e.closeWithKey;o.push(i(t.button,"click",(()=>this.isOpen()?this.hide():this.show())),i(document,"keyup",(t=>this.isOpen()&&(t.key===n||t.code===n)&&this.hide())),i(document,["touchstart","mousedown"],(e=>{this.isOpen()&&!l(e).some((e=>e===t.app||e===t.button))&&this.hide();}),{capture:true}));}if(e.adjustableNumbers){const e={rgba:[255,255,255,1],hsva:[360,100,100,1],hsla:[360,100,100,1],cmyk:[100,100,100,100]};p(t.interaction.result,((t,o,n)=>{const i=e[this.getColorRepresentation().toLowerCase()];if(i){const e=i[n],s=t+(e>=100?1e3*o:o);return s<=0?0:Number((s{n.isOpen()&&(e.closeOnScroll&&n.hide(),null===t?(t=setTimeout((()=>t=null),100),requestAnimationFrame((function e(){n._rePositioningPicker(),null!==t&&requestAnimationFrame(e);}))):(clearTimeout(t),t=setTimeout((()=>t=null),100)));}),{capture:true}));}this._eventBindings=o;}_rePositioningPicker(){const{options:t}=this;if(!t.inline){if(!this._nanopop.update({container:document.body.getBoundingClientRect(),position:t.position})){const t=this._root.app,e=t.getBoundingClientRect();t.style.top=(window.innerHeight-e.height)/2+"px",t.style.left=(window.innerWidth-e.width)/2+"px";}}}_updateOutput(t){const{_root:e,_color:o,options:n}=this;if(e.interaction.type()){const t=`to${e.interaction.type().getAttribute("data-type")}`;e.interaction.result.value="function"==typeof o[t]?o[t]().toString(n.outputPrecision):"";}!this._initializingActive&&this._recalc&&this._emit("change",o,t,this);}_clearColor(t=false){const{_root:e,options:o}=this;o.useAsButton||e.button.style.setProperty("--pcr-color","rgba(0, 0, 0, 0.15)"),e.button.classList.add("clear"),o.showAlways||this.hide(),this._lastColor=null,this._initializingActive||t||(this._emit("save",null),this._emit("clear"));}_parseLocalColor(t){const{values:e,type:o,a:n}=w(t),{lockOpacity:i}=this.options,s=void 0!==n&&1!==n;return e&&3===e.length&&(e[3]=void 0),{values:!e||i&&s?null:e,type:o}}_t(t){return this.options.i18n[t]||L.I18N_DEFAULTS[t]}_emit(t,...e){this._eventListener[t].forEach((t=>t(...e,this)));}on(t,e){return this._eventListener[t].push(e),this}off(t,e){const o=this._eventListener[t]||[],n=o.indexOf(e);return ~n&&o.splice(n,1),this}addSwatch(t){const{values:e}=this._parseLocalColor(t);if(e){const{_swatchColors:t,_root:o}=this,n=A(...e),s=r(`