/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // main.ts var main_exports = {}; __export(main_exports, { default: () => RecentFilesPlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian = require("obsidian"); var defaultMaxLength = 50; var DEFAULT_DATA = { recentFiles: [], omittedPaths: [] }; var RecentFilesListViewType = "recent-files"; var RecentFilesListView = class extends import_obsidian.ItemView { constructor(leaf, plugin, data) { super(leaf); this.redraw = () => { const openFile = this.app.workspace.getActiveFile(); const rootEl = createDiv({ cls: "nav-folder mod-root" }); const childrenEl = rootEl.createDiv({ cls: "nav-folder-children" }); this.data.recentFiles.forEach((currentFile) => { const navFile = childrenEl.createDiv({ cls: "tree-item nav-file recent-files-file" }); const navFileTitle = navFile.createDiv({ cls: "tree-item-self is-clickable nav-file-title recent-files-title" }); const navFileTitleContent = navFileTitle.createDiv({ cls: "tree-item-inner nav-file-title-content recent-files-title-content" }); navFileTitleContent.setText(currentFile.basename); if (openFile && currentFile.path === openFile.path) { navFileTitle.addClass("is-active"); } navFileTitle.setAttr("draggable", "true"); navFileTitle.addEventListener("dragstart", (event) => { const file = this.app.metadataCache.getFirstLinkpathDest( currentFile.path, "" ); const dragManager = this.app.dragManager; const dragData = dragManager.dragFile(event, file); dragManager.onDragStart(event, dragData); }); navFileTitle.addEventListener("mouseover", (event) => { this.app.workspace.trigger("hover-link", { event, source: RecentFilesListViewType, hoverParent: rootEl, targetEl: navFile, linktext: currentFile.path }); }); navFileTitle.addEventListener("contextmenu", (event) => { const menu = new import_obsidian.Menu(); menu.addItem( (item) => item.setSection("action").setTitle("Open in new tab").setIcon("file-plus").onClick(() => { this.focusFile(currentFile, "tab"); }) ); const file = this.app.vault.getAbstractFileByPath(currentFile.path); this.app.workspace.trigger( "file-menu", menu, file, "link-context-menu" ); menu.showAtPosition({ x: event.clientX, y: event.clientY }); }); navFileTitle.addEventListener("click", (event) => { const newLeaf = import_obsidian.Keymap.isModEvent(event); this.focusFile(currentFile, newLeaf); }); navFileTitleContent.addEventListener("mousedown", (event) => { if (event.button === 1) { event.preventDefault(); this.focusFile(currentFile, "tab"); } }); const navFileDelete = navFileTitle.createDiv({ cls: "recent-files-file-delete menu-item-icon" }); (0, import_obsidian.setIcon)(navFileDelete, "lucide-x"); navFileDelete.addEventListener("click", async (event) => { event.stopPropagation(); await this.removeFile(currentFile); this.redraw(); }); }); this.contentEl.setChildrenInPlace([rootEl]); }; this.removeFile = async (file) => { this.data.recentFiles = this.data.recentFiles.filter( (currFile) => currFile.path !== file.path ); await this.plugin.pruneLength(); }; this.updateData = async (file) => { this.data.recentFiles = this.data.recentFiles.filter( (currFile) => currFile.path !== file.path ); this.data.recentFiles.unshift({ basename: file.basename, path: file.path }); await this.plugin.pruneLength(); }; this.update = async (openedFile) => { await sleep(15); if (!openedFile || !this.plugin.shouldAddFile(openedFile)) { return; } await this.updateData(openedFile); this.redraw(); }; /** * Open the provided file in the most recent leaf. * * @param shouldSplit Whether the file should be opened in a new split, or in * the most recent split. If the most recent split is pinned, this is set to * true. */ this.focusFile = (file, newLeaf) => { const targetFile = this.app.vault.getFiles().find((f) => f.path === file.path); if (targetFile) { const leaf = this.app.workspace.getLeaf(newLeaf); leaf.openFile(targetFile); } else { new import_obsidian.Notice("Cannot find a file with that name"); this.data.recentFiles = this.data.recentFiles.filter( (fp) => fp.path !== file.path ); this.plugin.saveData(); this.redraw(); } }; this.plugin = plugin; this.data = data; } async onOpen() { this.redraw(); } getViewType() { return RecentFilesListViewType; } getDisplayText() { return "Recent Files"; } getIcon() { return "clock"; } onPaneMenu(menu) { menu.addItem((item) => { item.setTitle("Clear list").setIcon("sweep").onClick(async () => { this.data.recentFiles = []; await this.plugin.saveData(); this.redraw(); }); }).addItem((item) => { item.setTitle("Close").setIcon("cross").onClick(() => { this.app.workspace.detachLeavesOfType(RecentFilesListViewType); }); }); } load() { super.load(); this.registerEvent(this.app.workspace.on("file-open", this.update)); } }; var RecentFilesPlugin = class extends import_obsidian.Plugin { constructor() { super(...arguments); this.pruneOmittedFiles = async () => { this.data.recentFiles = this.data.recentFiles.filter(this.shouldAddFile); await this.saveData(); }; this.pruneLength = async () => { const toRemove = this.data.recentFiles.length - (this.data.maxLength || defaultMaxLength); if (toRemove > 0) { this.data.recentFiles.splice( this.data.recentFiles.length - toRemove, toRemove ); } await this.saveData(); }; this.shouldAddFile = (file) => { const patterns = this.data.omittedPaths.filter( (path) => path.length > 0 ); const fileMatchesRegex = (pattern) => { try { return new RegExp(pattern).test(file.path); } catch (err) { console.error("Recent Files: Invalid regex pattern: " + pattern); return false; } }; return !patterns.some(fileMatchesRegex); }; this.initView = async () => { var _a; let leaf = null; for (leaf of this.app.workspace.getLeavesOfType(RecentFilesListViewType)) { if (leaf.view instanceof RecentFilesListView) return; await leaf.setViewState({ type: "empty" }); break; } (_a = leaf != null ? leaf : this.app.workspace.getLeftLeaf(false)) == null ? void 0 : _a.setViewState({ type: RecentFilesListViewType, active: true }); }; this.handleRename = async (file, oldPath) => { const entry = this.data.recentFiles.find( (recentFile) => recentFile.path === oldPath ); if (entry) { entry.path = file.path; entry.basename = this.trimExtension(file.name); this.view.redraw(); await this.saveData(); } }; this.handleDelete = async (file) => { const beforeLen = this.data.recentFiles.length; this.data.recentFiles = this.data.recentFiles.filter( (recentFile) => recentFile.path !== file.path ); if (beforeLen !== this.data.recentFiles.length) { this.view.redraw(); await this.saveData(); } }; // trimExtension can be used to turn a filename into a basename when // interacting with a TAbstractFile that does not have a basename property. // private readonly trimExtension = (name: string): string => name.split('.')[0]; // from: https://stackoverflow.com/a/4250408/617864 this.trimExtension = (name) => name.replace(/\.[^/.]+$/, ""); } async onload() { console.log("Recent Files: Loading plugin v" + this.manifest.version); await this.loadData(); (0, import_obsidian.addIcon)("sweep", sweepIcon); this.registerView( RecentFilesListViewType, (leaf) => this.view = new RecentFilesListView(leaf, this, this.data) ); this.addCommand({ id: "recent-files-open", name: "Open", callback: async () => { let leaf; [leaf] = this.app.workspace.getLeavesOfType( RecentFilesListViewType ); if (!leaf) { leaf = this.app.workspace.getLeftLeaf(false); await (leaf == null ? void 0 : leaf.setViewState({ type: RecentFilesListViewType })); } if (leaf) { this.app.workspace.revealLeaf(leaf); } } }); this.app.workspace.registerHoverLinkSource( RecentFilesListViewType, { display: "Recent Files", defaultMod: true } ); this.app.workspace.onLayoutReady(() => { this.initView(); }); this.registerEvent(this.app.vault.on("rename", this.handleRename)); this.registerEvent(this.app.vault.on("delete", this.handleDelete)); this.addSettingTab(new RecentFilesSettingTab(this.app, this)); } onunload() { this.app.workspace.unregisterHoverLinkSource( RecentFilesListViewType ); } async loadData() { this.data = Object.assign(DEFAULT_DATA, await super.loadData()); } async saveData() { await super.saveData(this.data); } }; var RecentFilesSettingTab = class extends import_obsidian.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; } display() { const { containerEl } = this; containerEl.empty(); containerEl.createEl("h2", { text: "Recent Files List" }); const fragment = document.createDocumentFragment(); const link = document.createElement("a"); link.href = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#writing_a_regular_expression_pattern"; link.text = "MDN - Regular expressions"; fragment.append("RegExp patterns to ignore. One pattern per line. See "); fragment.append(link); fragment.append(" for help."); new import_obsidian.Setting(containerEl).setName("Omitted pathname patterns").setDesc(fragment).addTextArea((textArea) => { textArea.inputEl.setAttr("rows", 6); textArea.setPlaceholder("^daily/\n\\.png$\nfoobar.*baz").setValue(this.plugin.data.omittedPaths.join("\n")); textArea.inputEl.onblur = (e) => { const patterns = e.target.value; this.plugin.data.omittedPaths = patterns.split("\n"); this.plugin.pruneOmittedFiles(); this.plugin.view.redraw(); }; }); new import_obsidian.Setting(containerEl).setName("List length").setDesc("Maximum number of filenames to keep in the list.").addText((text) => { var _a; text.inputEl.setAttr("type", "number"); text.inputEl.setAttr("placeholder", defaultMaxLength); text.setValue(((_a = this.plugin.data.maxLength) == null ? void 0 : _a.toString()) || "").onChange((value) => { const parsed = parseInt(value, 10); if (!Number.isNaN(parsed) && parsed <= 0) { new import_obsidian.Notice("List length must be a positive integer"); return; } }); text.inputEl.onblur = (e) => { const maxfiles = e.target.value; const parsed = parseInt(maxfiles, 10); this.plugin.data.maxLength = parsed; this.plugin.pruneLength(); this.plugin.view.redraw(); }; }); const div = containerEl.createEl("div", { cls: "recent-files-donation" }); const donateText = document.createElement("p"); donateText.appendText( "If this plugin adds value for you and you would like to help support continued development, please use the buttons below:" ); div.appendChild(donateText); const parser = new DOMParser(); div.appendChild( createDonateButton( "https://paypal.me/tgrosinger", parser.parseFromString(paypal, "text/xml").documentElement ) ); div.appendChild( createDonateButton( "https://www.buymeacoffee.com/tgrosinger", parser.parseFromString(buyMeACoffee, "text/xml").documentElement ) ); } }; var createDonateButton = (link, img) => { const a = document.createElement("a"); a.setAttribute("href", link); a.addClass("recent-files-donate-button"); a.appendChild(img); return a; }; var sweepIcon = ` `; var buyMeACoffee = ` `; var paypal = ` `;