/*
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 = `
`;