mirror of
https://github.com/Pantonius/pantosite-astro.git
synced 2026-04-26 09:24:38 +00:00
819 lines
106 KiB
JavaScript
819 lines
106 KiB
JavaScript
|
|
/*
|
||
|
|
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 __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);
|
||
|
|
|
||
|
|
// src/main.ts
|
||
|
|
var main_exports = {};
|
||
|
|
__export(main_exports, {
|
||
|
|
ExplorerFocusPlugin: () => ExplorerFocusPlugin,
|
||
|
|
default: () => main_default
|
||
|
|
});
|
||
|
|
module.exports = __toCommonJS(main_exports);
|
||
|
|
var import_obsidian5 = require("obsidian");
|
||
|
|
|
||
|
|
// src/types.ts
|
||
|
|
var DEFAULT_SETTINGS = {
|
||
|
|
showRightClickMenu: true,
|
||
|
|
showFileExplorerIcon: true,
|
||
|
|
focusLevel: "parent",
|
||
|
|
customFolderPath: "",
|
||
|
|
hideAncestorFolders: false,
|
||
|
|
autoHidePaths: []
|
||
|
|
};
|
||
|
|
|
||
|
|
// src/ui/settings-tab.ts
|
||
|
|
var import_obsidian4 = require("obsidian");
|
||
|
|
|
||
|
|
// src/utils/file-explorer-patch.ts
|
||
|
|
var import_obsidian = require("obsidian");
|
||
|
|
|
||
|
|
// node_modules/.pnpm/monkey-around@3.0.0/node_modules/monkey-around/dist/index.mjs
|
||
|
|
function around(obj, factories) {
|
||
|
|
const removers = Object.keys(factories).map((key) => around1(obj, key, factories[key]));
|
||
|
|
return removers.length === 1 ? removers[0] : function() {
|
||
|
|
removers.forEach((r) => r());
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function around1(obj, method, createWrapper) {
|
||
|
|
const inherited = obj[method], hadOwn = obj.hasOwnProperty(method), original = hadOwn ? inherited : function() {
|
||
|
|
return Object.getPrototypeOf(obj)[method].apply(this, arguments);
|
||
|
|
};
|
||
|
|
let current = createWrapper(original);
|
||
|
|
if (inherited)
|
||
|
|
Object.setPrototypeOf(current, inherited);
|
||
|
|
Object.setPrototypeOf(wrapper, current);
|
||
|
|
obj[method] = wrapper;
|
||
|
|
return remove;
|
||
|
|
function wrapper(...args) {
|
||
|
|
if (current === original && obj[method] === wrapper)
|
||
|
|
remove();
|
||
|
|
return current.apply(this, args);
|
||
|
|
}
|
||
|
|
function remove() {
|
||
|
|
if (obj[method] === wrapper) {
|
||
|
|
if (hadOwn)
|
||
|
|
obj[method] = original;
|
||
|
|
else
|
||
|
|
delete obj[method];
|
||
|
|
}
|
||
|
|
if (current === original)
|
||
|
|
return;
|
||
|
|
current = original;
|
||
|
|
Object.setPrototypeOf(wrapper, inherited || Function);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/utils/file-explorer-patch.ts
|
||
|
|
var prototypePatched = false;
|
||
|
|
var patchingFailed = false;
|
||
|
|
function patchFileExplorer(plugin) {
|
||
|
|
if (patchingFailed) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (import_obsidian.Platform.isMobile) {
|
||
|
|
patchingFailed = true;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const fileExplorer = getFileExplorer(plugin);
|
||
|
|
if (!fileExplorer) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!prototypePatched) {
|
||
|
|
try {
|
||
|
|
const prototype = Object.getPrototypeOf(fileExplorer);
|
||
|
|
if (typeof prototype.getSortedFolderItems !== "function") {
|
||
|
|
throw new Error("getSortedFolderItems method not found on file explorer prototype");
|
||
|
|
}
|
||
|
|
plugin.register(
|
||
|
|
around(prototype, {
|
||
|
|
getSortedFolderItems(old) {
|
||
|
|
return function(folder) {
|
||
|
|
let sortedChildren = old.call(this, folder);
|
||
|
|
if (plugin.isFocus && plugin.focusedPath) {
|
||
|
|
const focusedPath = plugin.focusedPath;
|
||
|
|
sortedChildren = sortedChildren.filter((vEl) => {
|
||
|
|
const filePath = vEl.file.path;
|
||
|
|
if (filePath === focusedPath) {
|
||
|
|
vEl.info.hidden = false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (filePath.startsWith(focusedPath + "/")) {
|
||
|
|
vEl.info.hidden = false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (focusedPath.startsWith(filePath + "/")) {
|
||
|
|
vEl.info.hidden = false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
vEl.info.hidden = true;
|
||
|
|
return false;
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
sortedChildren.forEach((vEl) => {
|
||
|
|
vEl.info.hidden = false;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return sortedChildren;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
prototypePatched = true;
|
||
|
|
} catch (error) {
|
||
|
|
patchingFailed = true;
|
||
|
|
console.warn(
|
||
|
|
"[Explorer Focus] Failed to patch file explorer. The plugin will use CSS-based hiding as a fallback, which may be less precise. This usually happens after an Obsidian update - please check for plugin updates.",
|
||
|
|
error
|
||
|
|
);
|
||
|
|
new import_obsidian.Notice(
|
||
|
|
"Explorer Focus: File explorer patching failed. The plugin will still work but may need an update for full functionality.",
|
||
|
|
8e3
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fileExplorer.fileExplorerPlusPatched = true;
|
||
|
|
}
|
||
|
|
function getFileExplorer(plugin) {
|
||
|
|
var _a;
|
||
|
|
const fileExplorerContainer = (_a = plugin.app.workspace.getLeavesOfType("file-explorer")) == null ? void 0 : _a.first();
|
||
|
|
return fileExplorerContainer == null ? void 0 : fileExplorerContainer.view;
|
||
|
|
}
|
||
|
|
function getAllFileExplorers(plugin) {
|
||
|
|
const fileExplorerLeaves = plugin.app.workspace.getLeavesOfType("file-explorer");
|
||
|
|
return fileExplorerLeaves.map((leaf) => leaf.view);
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/ui/folder-suggest.ts
|
||
|
|
var import_obsidian2 = require("obsidian");
|
||
|
|
var FolderSuggest = class extends import_obsidian2.AbstractInputSuggest {
|
||
|
|
constructor(app, inputEl) {
|
||
|
|
super(app, inputEl);
|
||
|
|
this.inputEl = inputEl;
|
||
|
|
}
|
||
|
|
getSuggestions(query) {
|
||
|
|
const lowerQuery = query.toLowerCase();
|
||
|
|
const folders = [];
|
||
|
|
this.app.vault.getAllLoadedFiles().forEach((file) => {
|
||
|
|
if (file instanceof import_obsidian2.TFolder) {
|
||
|
|
if (file.path.toLowerCase().includes(lowerQuery)) {
|
||
|
|
folders.push(file);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
folders.sort((a, b) => a.path.localeCompare(b.path));
|
||
|
|
return folders.slice(0, 20);
|
||
|
|
}
|
||
|
|
renderSuggestion(folder, el) {
|
||
|
|
el.createEl("div", { text: folder.path || "/" });
|
||
|
|
}
|
||
|
|
selectSuggestion(folder) {
|
||
|
|
this.inputEl.value = folder.path;
|
||
|
|
this.inputEl.trigger("input");
|
||
|
|
this.close();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// src/ui/auto-hide-modal.ts
|
||
|
|
var import_obsidian3 = require("obsidian");
|
||
|
|
var AutoHideModal = class extends import_obsidian3.Modal {
|
||
|
|
constructor(app, plugin, onSave) {
|
||
|
|
var _a;
|
||
|
|
super(app);
|
||
|
|
this.addInput = null;
|
||
|
|
this.plugin = plugin;
|
||
|
|
this.paths = [...(_a = plugin.settings.autoHidePaths) != null ? _a : []];
|
||
|
|
this.onSave = onSave;
|
||
|
|
}
|
||
|
|
onOpen() {
|
||
|
|
this.setTitle("Auto-hide folders");
|
||
|
|
this.renderContent();
|
||
|
|
}
|
||
|
|
renderContent() {
|
||
|
|
var _a;
|
||
|
|
const { contentEl } = this;
|
||
|
|
contentEl.empty();
|
||
|
|
contentEl.createEl("p", {
|
||
|
|
text: "The following folders are currently hidden from the file explorer."
|
||
|
|
});
|
||
|
|
const listWrapper = contentEl.createDiv();
|
||
|
|
this.paths.forEach((path, index) => {
|
||
|
|
const row = listWrapper.createDiv({ cls: "mobile-option-setting-item" });
|
||
|
|
row.createSpan({
|
||
|
|
text: path || "(empty)",
|
||
|
|
cls: "mobile-option-setting-item-name"
|
||
|
|
});
|
||
|
|
const removeBtn = row.createDiv({
|
||
|
|
cls: "clickable-icon mobile-option-setting-item-option-icon",
|
||
|
|
attr: { "aria-label": "Delete" }
|
||
|
|
});
|
||
|
|
(0, import_obsidian3.setIcon)(removeBtn, "x");
|
||
|
|
removeBtn.addEventListener("click", () => {
|
||
|
|
this.paths.splice(index, 1);
|
||
|
|
this.renderContent();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
new import_obsidian3.Setting(contentEl).setName("Folder").addText((text) => {
|
||
|
|
new FolderSuggest(this.app, text.inputEl);
|
||
|
|
text.setPlaceholder("Enter folder path");
|
||
|
|
this.addInput = text;
|
||
|
|
}).addButton((button) => button.setButtonText("Add").onClick(() => {
|
||
|
|
var _a2, _b, _c;
|
||
|
|
const value = (_b = (_a2 = this.addInput) == null ? void 0 : _a2.getValue()) == null ? void 0 : _b.trim();
|
||
|
|
if (value) {
|
||
|
|
this.paths.push(value);
|
||
|
|
(_c = this.addInput) == null ? void 0 : _c.setValue("");
|
||
|
|
this.renderContent();
|
||
|
|
}
|
||
|
|
}));
|
||
|
|
(_a = this.modalEl.querySelector(".modal-button-container")) == null ? void 0 : _a.remove();
|
||
|
|
const buttonContainer = this.modalEl.createDiv({ cls: "modal-button-container" });
|
||
|
|
const saveBtn = buttonContainer.createEl("button", {
|
||
|
|
text: "Save",
|
||
|
|
cls: "mod-cta"
|
||
|
|
});
|
||
|
|
saveBtn.addEventListener("click", () => {
|
||
|
|
this.plugin.settings.autoHidePaths = this.paths;
|
||
|
|
void this.plugin.saveSettings();
|
||
|
|
this.plugin.updateAutoHideStyles();
|
||
|
|
this.onSave();
|
||
|
|
this.close();
|
||
|
|
});
|
||
|
|
const cancelBtn = buttonContainer.createEl("button", {
|
||
|
|
text: "Cancel",
|
||
|
|
cls: "mod-cancel"
|
||
|
|
});
|
||
|
|
cancelBtn.addEventListener("click", () => {
|
||
|
|
this.close();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// src/ui/settings-tab.ts
|
||
|
|
var ExplorerFocusSettingTab = class extends import_obsidian4.PluginSettingTab {
|
||
|
|
constructor(app, plugin) {
|
||
|
|
super(app, plugin);
|
||
|
|
this.icon = "lucide-focus";
|
||
|
|
this.plugin = plugin;
|
||
|
|
}
|
||
|
|
display() {
|
||
|
|
const { containerEl } = this;
|
||
|
|
containerEl.empty();
|
||
|
|
const generalGroup = new import_obsidian4.SettingGroup(containerEl);
|
||
|
|
generalGroup.addSetting((setting) => {
|
||
|
|
setting.setName("Show right-click menu option").addToggle((toggle) => toggle.setValue(this.plugin.settings.showRightClickMenu).onChange(async (value) => {
|
||
|
|
this.plugin.settings.showRightClickMenu = value;
|
||
|
|
await this.plugin.saveSettings();
|
||
|
|
}));
|
||
|
|
});
|
||
|
|
generalGroup.addSetting((setting) => {
|
||
|
|
setting.setName("Show file explorer icon").addToggle((toggle) => toggle.setValue(this.plugin.settings.showFileExplorerIcon).onChange(async (value) => {
|
||
|
|
this.plugin.settings.showFileExplorerIcon = value;
|
||
|
|
await this.plugin.saveSettings();
|
||
|
|
this.plugin.updateFileExplorerIcon();
|
||
|
|
}));
|
||
|
|
});
|
||
|
|
generalGroup.addSetting((setting) => {
|
||
|
|
setting.setName("Command focus level").setDesc("Determines what to focus when using the toggle command or file explorer icon. Right-click menu always focuses the clicked file/folder.").addDropdown((dropdown) => dropdown.addOption("file", "Current file only").addOption("parent", "Parent folder").addOption("grandparent", "Grandparent folder").addOption("greatgrandparent", "Great grandparent folder").addOption("custom", "Specific folder").setValue(this.plugin.settings.focusLevel).onChange(async (value) => {
|
||
|
|
this.plugin.settings.focusLevel = value;
|
||
|
|
await this.plugin.saveSettings();
|
||
|
|
if (this.plugin.isFocus) {
|
||
|
|
const fileExplorers = getAllFileExplorers(this.plugin);
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
if (fileExplorer == null ? void 0 : fileExplorer.requestSort) {
|
||
|
|
fileExplorer.requestSort();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
this.display();
|
||
|
|
}));
|
||
|
|
});
|
||
|
|
if (this.plugin.settings.focusLevel === "custom") {
|
||
|
|
generalGroup.addSetting((setting) => {
|
||
|
|
setting.setName("Custom folder path").setDesc("Enter a folder path (folder/subfolder). This folder will be focused regardless of which file is open.").addText((text) => {
|
||
|
|
new FolderSuggest(this.app, text.inputEl);
|
||
|
|
text.setPlaceholder("Folder/subfolder").setValue(this.plugin.settings.customFolderPath).onChange(async (value) => {
|
||
|
|
this.plugin.settings.customFolderPath = value;
|
||
|
|
await this.plugin.saveSettings();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
const autoHideGroup = new import_obsidian4.SettingGroup(containerEl).setHeading("Auto-hide folders");
|
||
|
|
autoHideGroup.addSetting((setting) => {
|
||
|
|
var _a;
|
||
|
|
const autoHidePaths = ((_a = this.plugin.settings.autoHidePaths) != null ? _a : []).filter((p) => p.trim().length > 0);
|
||
|
|
const descFragment = document.createDocumentFragment();
|
||
|
|
descFragment.appendText("These folders are always hidden from the file explorer.");
|
||
|
|
if (autoHidePaths.length > 0) {
|
||
|
|
const list = descFragment.createEl("ul");
|
||
|
|
autoHidePaths.forEach((p) => {
|
||
|
|
list.createEl("li", { text: p });
|
||
|
|
});
|
||
|
|
}
|
||
|
|
setting.setName("Hidden folders").setDesc(descFragment).addButton((button) => button.setButtonText("Manage").onClick(() => {
|
||
|
|
new AutoHideModal(this.app, this.plugin, () => {
|
||
|
|
this.display();
|
||
|
|
}).open();
|
||
|
|
}));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// src/utils/focus.ts
|
||
|
|
function getFocusPath(path, level, settings) {
|
||
|
|
if (level === "custom") {
|
||
|
|
return settings.customFolderPath || path;
|
||
|
|
}
|
||
|
|
if (level === "file") {
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
const parts = path.split("/");
|
||
|
|
if (level === "parent") {
|
||
|
|
if (parts.length === 1) {
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
return parts.slice(0, -1).join("/");
|
||
|
|
}
|
||
|
|
if (level === "grandparent") {
|
||
|
|
if (parts.length <= 2) {
|
||
|
|
return parts[0] || path;
|
||
|
|
}
|
||
|
|
return parts.slice(0, -2).join("/");
|
||
|
|
}
|
||
|
|
if (parts.length <= 3) {
|
||
|
|
return parts[0] || path;
|
||
|
|
}
|
||
|
|
return parts.slice(0, -3).join("/");
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/commands/index.ts
|
||
|
|
function registerCommands(plugin) {
|
||
|
|
plugin.addCommand({
|
||
|
|
id: "toggle",
|
||
|
|
name: "Toggle focus",
|
||
|
|
callback: () => {
|
||
|
|
if (plugin.isFocus) {
|
||
|
|
plugin.exitFocus();
|
||
|
|
} else {
|
||
|
|
if (plugin.settings.focusLevel === "custom") {
|
||
|
|
if (plugin.settings.customFolderPath) {
|
||
|
|
plugin.enterFocus(plugin.settings.customFolderPath);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
const file = plugin.app.workspace.getActiveFile();
|
||
|
|
if (file == null ? void 0 : file.path) {
|
||
|
|
const focusPath = getFocusPath(file.path, plugin.settings.focusLevel, plugin.settings);
|
||
|
|
plugin.enterFocus(focusPath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
if (plugin.settings.showRightClickMenu) {
|
||
|
|
plugin.registerEvent(
|
||
|
|
plugin.app.workspace.on("file-menu", (menu, file) => {
|
||
|
|
if (!plugin.settings.showRightClickMenu) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
menu.addItem((item) => {
|
||
|
|
item.setTitle(plugin.isFocus ? "Exit focus" : "Focus").setIcon(plugin.isFocus ? "log-out" : "focus").onClick(() => {
|
||
|
|
plugin.toggleFocus(file == null ? void 0 : file.path);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/utils/file-explorer.ts
|
||
|
|
function findNavButtonsContainer(fileExplorerView) {
|
||
|
|
var _a;
|
||
|
|
let container = fileExplorerView.querySelector(".nav-buttons-container");
|
||
|
|
if (container) {
|
||
|
|
return container;
|
||
|
|
}
|
||
|
|
container = fileExplorerView.querySelector(".nav-header-button-container");
|
||
|
|
if (container) {
|
||
|
|
return container;
|
||
|
|
}
|
||
|
|
const existingButtons = fileExplorerView.querySelectorAll(".nav-action-button");
|
||
|
|
if (existingButtons.length > 0) {
|
||
|
|
const firstButton = existingButtons[0];
|
||
|
|
const parent = firstButton.parentElement;
|
||
|
|
if (parent && (parent.classList.contains("nav-buttons-container") || parent.classList.contains("nav-header-button-container") || Array.from(parent.children).some((el) => el.classList.contains("nav-action-button")))) {
|
||
|
|
return parent;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const viewHeader = fileExplorerView.querySelector(".view-header");
|
||
|
|
if (viewHeader) {
|
||
|
|
const headerContainer = viewHeader.querySelector(".nav-buttons-container") || viewHeader.querySelector(".nav-header-button-container") || viewHeader.querySelector(".view-header-title-container");
|
||
|
|
if (headerContainer) {
|
||
|
|
return headerContainer;
|
||
|
|
}
|
||
|
|
return viewHeader;
|
||
|
|
}
|
||
|
|
const navFilesContainer = fileExplorerView.querySelector(".nav-files-container");
|
||
|
|
if (navFilesContainer) {
|
||
|
|
let mobileButtonContainer = fileExplorerView.querySelector(".explorer-focus-mobile-buttons");
|
||
|
|
if (!mobileButtonContainer) {
|
||
|
|
mobileButtonContainer = document.createElement("div");
|
||
|
|
mobileButtonContainer.className = "nav-buttons-container explorer-focus-mobile-buttons";
|
||
|
|
mobileButtonContainer.setCssProps({
|
||
|
|
display: "flex",
|
||
|
|
alignItems: "center",
|
||
|
|
gap: "4px",
|
||
|
|
padding: "8px",
|
||
|
|
borderBottom: "1px solid var(--background-modifier-border)"
|
||
|
|
});
|
||
|
|
(_a = navFilesContainer.parentElement) == null ? void 0 : _a.insertBefore(mobileButtonContainer, navFilesContainer);
|
||
|
|
}
|
||
|
|
return mobileButtonContainer;
|
||
|
|
}
|
||
|
|
let topContainer = fileExplorerView.querySelector(".explorer-focus-mobile-buttons");
|
||
|
|
if (!topContainer) {
|
||
|
|
topContainer = document.createElement("div");
|
||
|
|
topContainer.className = "nav-buttons-container explorer-focus-mobile-buttons";
|
||
|
|
topContainer.setCssProps({
|
||
|
|
display: "flex",
|
||
|
|
alignItems: "center",
|
||
|
|
gap: "4px",
|
||
|
|
padding: "8px",
|
||
|
|
borderBottom: "1px solid var(--background-modifier-border)"
|
||
|
|
});
|
||
|
|
fileExplorerView.insertBefore(topContainer, fileExplorerView.firstChild);
|
||
|
|
}
|
||
|
|
return topContainer;
|
||
|
|
}
|
||
|
|
function createFileExplorerIcon(plugin) {
|
||
|
|
const icon = document.createElement("div");
|
||
|
|
icon.className = "clickable-icon nav-action-button";
|
||
|
|
icon.setAttribute("aria-label", "Toggle focus");
|
||
|
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||
|
|
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
||
|
|
svg.setAttribute("width", "24");
|
||
|
|
svg.setAttribute("height", "24");
|
||
|
|
svg.setAttribute("viewBox", "0 0 24 24");
|
||
|
|
svg.setAttribute("fill", "none");
|
||
|
|
svg.setAttribute("stroke", "currentColor");
|
||
|
|
svg.setAttribute("stroke-width", "2");
|
||
|
|
svg.setAttribute("stroke-linecap", "round");
|
||
|
|
svg.setAttribute("stroke-linejoin", "round");
|
||
|
|
svg.setAttribute("class", "svg-icon lucide-focus");
|
||
|
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
||
|
|
circle.setAttribute("cx", "12");
|
||
|
|
circle.setAttribute("cy", "12");
|
||
|
|
circle.setAttribute("r", "3");
|
||
|
|
svg.appendChild(circle);
|
||
|
|
const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||
|
|
path1.setAttribute("d", "M3 7V5a2 2 0 0 1 2-2h2");
|
||
|
|
svg.appendChild(path1);
|
||
|
|
const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||
|
|
path2.setAttribute("d", "M17 3h2a2 2 0 0 1 2 2v2");
|
||
|
|
svg.appendChild(path2);
|
||
|
|
const path3 = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||
|
|
path3.setAttribute("d", "M21 17v2a2 2 0 0 1-2 2h-2");
|
||
|
|
svg.appendChild(path3);
|
||
|
|
const path4 = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||
|
|
path4.setAttribute("d", "M7 21H5a2 2 0 0 1-2-2v-2");
|
||
|
|
svg.appendChild(path4);
|
||
|
|
icon.appendChild(svg);
|
||
|
|
return icon;
|
||
|
|
}
|
||
|
|
function insertFileExplorerIcon(icon, navButtonsContainer) {
|
||
|
|
if (navButtonsContainer.contains(icon)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const allIcons = Array.from(navButtonsContainer.querySelectorAll(".clickable-icon.nav-action-button"));
|
||
|
|
const defaultIcons = allIcons.filter((el) => !el.classList.contains("cmdr") && el !== icon);
|
||
|
|
const cmdrIcons = Array.from(navButtonsContainer.querySelectorAll(".cmdr"));
|
||
|
|
if (cmdrIcons.length > 0) {
|
||
|
|
navButtonsContainer.insertBefore(icon, cmdrIcons[0]);
|
||
|
|
} else if (defaultIcons.length > 0) {
|
||
|
|
const lastIcon = defaultIcons[defaultIcons.length - 1];
|
||
|
|
if (lastIcon.nextSibling) {
|
||
|
|
navButtonsContainer.insertBefore(icon, lastIcon.nextSibling);
|
||
|
|
} else {
|
||
|
|
navButtonsContainer.appendChild(icon);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
navButtonsContainer.appendChild(icon);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// src/main.ts
|
||
|
|
var ExplorerFocusPlugin = class extends import_obsidian5.Plugin {
|
||
|
|
constructor(app, manifest) {
|
||
|
|
super(app, manifest);
|
||
|
|
this.isFocus = false;
|
||
|
|
this.focusedPath = null;
|
||
|
|
this.fileExplorerIcon = null;
|
||
|
|
}
|
||
|
|
async onload() {
|
||
|
|
await this.loadSettings();
|
||
|
|
this.addSettingTab(new ExplorerFocusSettingTab(this.app, this));
|
||
|
|
registerCommands(this);
|
||
|
|
if (this.settings.showFileExplorerIcon) {
|
||
|
|
this.addFileExplorerIcon();
|
||
|
|
}
|
||
|
|
this.app.workspace.onLayoutReady(() => {
|
||
|
|
this.patchAllFileExplorers();
|
||
|
|
this.updateFocusModeClasses();
|
||
|
|
this.updateAutoHideStyles();
|
||
|
|
});
|
||
|
|
this.app.workspace.on("layout-change", () => {
|
||
|
|
this.patchAllFileExplorers();
|
||
|
|
this.updateFocusModeClasses();
|
||
|
|
this.updateAutoHideStyles();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
async loadSettings() {
|
||
|
|
const loadedData = await this.loadData();
|
||
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, loadedData);
|
||
|
|
}
|
||
|
|
async saveSettings() {
|
||
|
|
await this.saveData(this.settings);
|
||
|
|
}
|
||
|
|
toggleFocus(path) {
|
||
|
|
if (this.isFocus) {
|
||
|
|
this.exitFocus();
|
||
|
|
} else if (path) {
|
||
|
|
this.enterFocus(path);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
enterFocus(path) {
|
||
|
|
this.isFocus = true;
|
||
|
|
this.focusedPath = path;
|
||
|
|
this.updateFileExplorerIcon();
|
||
|
|
this.updateFocusModeClasses();
|
||
|
|
const fileExplorers = getAllFileExplorers(this);
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
if (fileExplorer == null ? void 0 : fileExplorer.requestSort) {
|
||
|
|
fileExplorer.requestSort();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
setTimeout(() => {
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
this.refreshFileExplorerVisibility(fileExplorer);
|
||
|
|
});
|
||
|
|
}, 0);
|
||
|
|
}
|
||
|
|
patchAllFileExplorers() {
|
||
|
|
patchFileExplorer(this);
|
||
|
|
const fileExplorers = getAllFileExplorers(this);
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
fileExplorer.fileExplorerPlusPatched = true;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
exitFocus() {
|
||
|
|
this.isFocus = false;
|
||
|
|
this.focusedPath = null;
|
||
|
|
this.updateFileExplorerIcon();
|
||
|
|
this.updateFocusModeClasses();
|
||
|
|
const fileExplorers = getAllFileExplorers(this);
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
if (fileExplorer == null ? void 0 : fileExplorer.requestSort) {
|
||
|
|
fileExplorer.requestSort();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
setTimeout(() => {
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
this.refreshFileExplorerVisibility(fileExplorer);
|
||
|
|
});
|
||
|
|
this.updateAutoHideStyles();
|
||
|
|
}, 0);
|
||
|
|
}
|
||
|
|
refreshFileExplorerVisibility(fileExplorer) {
|
||
|
|
if (!(fileExplorer == null ? void 0 : fileExplorer.fileItems)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!this.isFocus || !this.focusedPath) {
|
||
|
|
Object.values(fileExplorer.fileItems).forEach((vEl) => {
|
||
|
|
if (!(vEl == null ? void 0 : vEl.el)) return;
|
||
|
|
if (vEl.el.hasAttribute("data-explorer-focus-hidden")) {
|
||
|
|
if (vEl.info) vEl.info.hidden = false;
|
||
|
|
vEl.el.setCssProps({ display: "" });
|
||
|
|
vEl.el.removeAttribute("data-explorer-focus-hidden");
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const focusedPath = this.focusedPath;
|
||
|
|
const ancestorPaths = /* @__PURE__ */ new Set();
|
||
|
|
const pathParts = focusedPath.split("/");
|
||
|
|
for (let i = 1; i < pathParts.length; i++) {
|
||
|
|
ancestorPaths.add(pathParts.slice(0, i).join("/"));
|
||
|
|
}
|
||
|
|
const topLevelFolders = /* @__PURE__ */ new Map();
|
||
|
|
const rootItems = [];
|
||
|
|
for (const path of Object.keys(fileExplorer.fileItems)) {
|
||
|
|
const firstSlash = path.indexOf("/");
|
||
|
|
if (firstSlash === -1) {
|
||
|
|
rootItems.push(path);
|
||
|
|
} else {
|
||
|
|
const topLevel = path.substring(0, firstSlash);
|
||
|
|
if (!topLevelFolders.has(topLevel)) {
|
||
|
|
topLevelFolders.set(topLevel, []);
|
||
|
|
}
|
||
|
|
topLevelFolders.get(topLevel).push(path);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const focusedTopLevel = focusedPath.split("/")[0];
|
||
|
|
for (const path of rootItems) {
|
||
|
|
const vEl = fileExplorer.fileItems[path];
|
||
|
|
if (!(vEl == null ? void 0 : vEl.el)) continue;
|
||
|
|
const shouldShow = path === focusedPath || focusedPath.startsWith(path + "/") || path.startsWith(focusedPath + "/");
|
||
|
|
this.updateItemVisibility(vEl, shouldShow);
|
||
|
|
}
|
||
|
|
for (const [topLevel, paths] of topLevelFolders) {
|
||
|
|
const isInFocusPath = topLevel === focusedTopLevel || ancestorPaths.has(topLevel) || focusedPath === topLevel;
|
||
|
|
if (!isInFocusPath) {
|
||
|
|
for (const path of paths) {
|
||
|
|
const vEl = fileExplorer.fileItems[path];
|
||
|
|
if (!(vEl == null ? void 0 : vEl.el)) continue;
|
||
|
|
this.updateItemVisibility(vEl, false);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
for (const path of paths) {
|
||
|
|
const vEl = fileExplorer.fileItems[path];
|
||
|
|
if (!(vEl == null ? void 0 : vEl.el)) continue;
|
||
|
|
const shouldShow = path === focusedPath || path.startsWith(focusedPath + "/") || ancestorPaths.has(path);
|
||
|
|
this.updateItemVisibility(vEl, shouldShow);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
updateItemVisibility(vEl, shouldShow) {
|
||
|
|
const currentlyHidden = vEl.el.hasAttribute("data-explorer-focus-hidden");
|
||
|
|
if (shouldShow && currentlyHidden) {
|
||
|
|
if (vEl.info) vEl.info.hidden = false;
|
||
|
|
vEl.el.setCssProps({ display: "" });
|
||
|
|
vEl.el.removeAttribute("data-explorer-focus-hidden");
|
||
|
|
} else if (!shouldShow && !currentlyHidden) {
|
||
|
|
if (vEl.info) vEl.info.hidden = true;
|
||
|
|
vEl.el.setCssProps({ display: "none" });
|
||
|
|
vEl.el.setAttribute("data-explorer-focus-hidden", "true");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
updateFocusModeClasses() {
|
||
|
|
const fileExplorers = getAllFileExplorers(this);
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
const containerEl = fileExplorer.containerEl;
|
||
|
|
if (this.isFocus) {
|
||
|
|
containerEl.addClass("explorer-focus-mode");
|
||
|
|
} else {
|
||
|
|
containerEl.removeClass("explorer-focus-mode");
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
addFileExplorerIcon() {
|
||
|
|
const handleIconClick = () => {
|
||
|
|
if (this.isFocus) {
|
||
|
|
this.exitFocus();
|
||
|
|
} else {
|
||
|
|
if (this.settings.focusLevel === "custom") {
|
||
|
|
if (this.settings.customFolderPath) {
|
||
|
|
this.enterFocus(this.settings.customFolderPath);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
const file = this.app.workspace.getActiveFile();
|
||
|
|
if (file == null ? void 0 : file.path) {
|
||
|
|
const focusPath = getFocusPath(file.path, this.settings.focusLevel, this.settings);
|
||
|
|
this.enterFocus(focusPath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const addIconToFileExplorer = () => {
|
||
|
|
const fileExplorerLeaves = this.app.workspace.getLeavesOfType("file-explorer");
|
||
|
|
if (fileExplorerLeaves.length === 0) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const fileExplorerView = fileExplorerLeaves[0].view.containerEl;
|
||
|
|
const navButtonsContainer = findNavButtonsContainer(fileExplorerView);
|
||
|
|
if (!navButtonsContainer) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this.fileExplorerIcon && navButtonsContainer.contains(this.fileExplorerIcon)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this.fileExplorerIcon && this.fileExplorerIcon.parentElement) {
|
||
|
|
this.fileExplorerIcon.remove();
|
||
|
|
}
|
||
|
|
if (!this.fileExplorerIcon) {
|
||
|
|
this.fileExplorerIcon = createFileExplorerIcon(this);
|
||
|
|
this.fileExplorerIcon.setCssProps({ touchAction: "manipulation" });
|
||
|
|
let touchHandled = false;
|
||
|
|
this.registerDomEvent(this.fileExplorerIcon, "touchstart", (evt) => {
|
||
|
|
touchHandled = true;
|
||
|
|
evt.preventDefault();
|
||
|
|
evt.stopPropagation();
|
||
|
|
handleIconClick();
|
||
|
|
setTimeout(() => {
|
||
|
|
touchHandled = false;
|
||
|
|
}, 300);
|
||
|
|
});
|
||
|
|
this.registerDomEvent(this.fileExplorerIcon, "click", (evt) => {
|
||
|
|
if (touchHandled) {
|
||
|
|
evt.preventDefault();
|
||
|
|
evt.stopPropagation();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
evt.preventDefault();
|
||
|
|
evt.stopPropagation();
|
||
|
|
handleIconClick();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
this.updateFileExplorerIcon();
|
||
|
|
insertFileExplorerIcon(this.fileExplorerIcon, navButtonsContainer);
|
||
|
|
};
|
||
|
|
addIconToFileExplorer();
|
||
|
|
this.registerEvent(
|
||
|
|
this.app.workspace.on("layout-change", () => {
|
||
|
|
if (this.settings.showFileExplorerIcon) {
|
||
|
|
addIconToFileExplorer();
|
||
|
|
}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
updateFileExplorerIcon() {
|
||
|
|
if (!this.fileExplorerIcon) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!this.settings.showFileExplorerIcon) {
|
||
|
|
if (this.fileExplorerIcon.parentElement) {
|
||
|
|
this.fileExplorerIcon.remove();
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!this.fileExplorerIcon.parentElement) {
|
||
|
|
const fileExplorerLeaves = this.app.workspace.getLeavesOfType("file-explorer");
|
||
|
|
if (fileExplorerLeaves.length > 0) {
|
||
|
|
const fileExplorerView = fileExplorerLeaves[0].view.containerEl;
|
||
|
|
const navButtonsContainer = findNavButtonsContainer(fileExplorerView);
|
||
|
|
if (navButtonsContainer) {
|
||
|
|
insertFileExplorerIcon(this.fileExplorerIcon, navButtonsContainer);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (this.fileExplorerIcon.style.cursor) {
|
||
|
|
this.fileExplorerIcon.style.removeProperty("cursor");
|
||
|
|
}
|
||
|
|
if (this.isFocus) {
|
||
|
|
this.fileExplorerIcon.addClass("is-active");
|
||
|
|
} else {
|
||
|
|
this.fileExplorerIcon.removeClass("is-active");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
updateAutoHideStyles() {
|
||
|
|
var _a;
|
||
|
|
const paths = new Set(
|
||
|
|
((_a = this.settings.autoHidePaths) != null ? _a : []).map((p) => p.trim()).filter((p) => p.length > 0)
|
||
|
|
);
|
||
|
|
const fileExplorers = getAllFileExplorers(this);
|
||
|
|
fileExplorers.forEach((fileExplorer) => {
|
||
|
|
if (!(fileExplorer == null ? void 0 : fileExplorer.fileItems)) return;
|
||
|
|
for (const [filePath, vEl] of Object.entries(fileExplorer.fileItems)) {
|
||
|
|
if (!(vEl == null ? void 0 : vEl.el)) continue;
|
||
|
|
const shouldHide = paths.has(filePath);
|
||
|
|
const isHidden = vEl.el.hasAttribute("data-explorer-focus-auto-hidden");
|
||
|
|
if (shouldHide && !isHidden) {
|
||
|
|
vEl.el.setCssProps({ display: "none" });
|
||
|
|
vEl.el.setAttribute("data-explorer-focus-auto-hidden", "true");
|
||
|
|
} else if (!shouldHide && isHidden) {
|
||
|
|
vEl.el.setCssProps({ display: "" });
|
||
|
|
vEl.el.removeAttribute("data-explorer-focus-auto-hidden");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
onunload() {
|
||
|
|
if (this.fileExplorerIcon) {
|
||
|
|
this.fileExplorerIcon.remove();
|
||
|
|
this.fileExplorerIcon = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
var main_default = ExplorerFocusPlugin;
|
||
|
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL21haW4udHMiLCAic3JjL3R5cGVzLnRzIiwgInNyYy91aS9zZXR0aW5ncy10YWIudHMiLCAic3JjL3V0aWxzL2ZpbGUtZXhwbG9yZXItcGF0Y2gudHMiLCAibm9kZV9tb2R1bGVzLy5wbnBtL21vbmtleS1hcm91bmRAMy4wLjAvbm9kZV9tb2R1bGVzL21vbmtleS1hcm91bmQvZGlzdC9pbmRleC5tanMiLCAic3JjL3VpL2ZvbGRlci1zdWdnZXN0LnRzIiwgInNyYy91aS9hdXRvLWhpZGUtbW9kYWwudHMiLCAic3JjL3V0aWxzL2ZvY3VzLnRzIiwgInNyYy9jb21tYW5kcy9pbmRleC50cyIsICJzcmMvdXRpbHMvZmlsZS1leHBsb3Jlci50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiaW1wb3J0IHsgQXBwLCBQbHVnaW4sIFBsdWdpbk1hbmlmZXN0IH0gZnJvbSBcIm9ic2lkaWFuXCI7XHJcbmltcG9ydCB7IEV4cGxvcmVyRm9jdXNTZXR0aW5ncywgREVGQVVMVF9TRVRUSU5HUyB9IGZyb20gJy4vdHlwZXMnO1xyXG5pbXBvcnQgeyBFeHBsb3JlckZvY3VzU2V0dGluZ1RhYiB9IGZyb20gJy4vdWkvc2V0dGluZ3MtdGFiJztcclxuaW1wb3J0IHsgcmVnaXN0ZXJDb21tYW5kcyB9IGZyb20gJy4vY29tbWFuZHMnO1xyXG5pbXBvcnQgeyBjcmVhdGVGaWxlRXhwbG9yZXJJY29uLCBpbnNlcnRGaWxlRXhwbG9yZXJJY29uLCBmaW5kTmF2QnV0dG9uc0NvbnRhaW5lciB9IGZyb20gJy4vdXRpbHMvZmlsZS1leHBsb3Jlcic7XHJcbmltcG9ydCB7IHBhdGNoRmlsZUV4cGxvcmVyIGFzIHBhdGNoRmlsZUV4cGxvcmVyVXRpbCwgZ2V0QWxsRmlsZUV4cGxvcmVycywgRmlsZUV4cGxvcmVyVmlldyB9IGZyb20gJy4vdXRpbHMvZmlsZS1leHBsb3Jlci1wYXRjaCc7XHJcbmltcG9ydCB7IGdldEZvY3VzUGF0aCB9IGZyb20gJy4vdXRpbHMvZm9jdXMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIEV4cGxvcmVyRm9jdXNQbHVnaW4gZXh0ZW5kcyBQbHVnaW4ge1xyXG5cdGlzRm9jdXM6IGJvb2xlYW47XHJcblx0Zm9jdXNlZFBhdGg6IHN0cmluZyB8IG51bGw7XHJcblx0c2V0dGluZ3MhOiBFeHBsb3JlckZvY3VzU2V0dGluZ3M7XHJcblx0ZmlsZUV4cGxvcmVySWNvbjogSFRNTEVsZW1lbnQgfCBudWxsO1xyXG5cclxuXHRjb25zdHJ1Y3RvcihhcHA6IEFwcCwgbWFuaWZlc3Q6IFBsdWdpbk1hbmlmZXN0KSB7XHJcblx0XHRzdXBlcihhcHAsIG1hbmlmZXN0KTtcclxuXHRcdHRoaXMuaXNGb2N1cyA9IGZhbHNlO1xyXG5cdFx0dGhpcy5mb2N1c2VkUGF0aCA9IG51bGw7XHJcblx0XHR0aGlzLmZpbGVFeHBsb3Jlckljb24gPSBudWxsO1xyXG5cdH1cclxuXHJcblx0YXN5bmMgb25sb2FkKCkge1xyXG5cdFx0YXdhaXQgdGhpcy5sb2FkU2V0dGluZ3MoKTtcclxuXHJcblx0XHR0aGlzLmFkZFNldHRpbmdUYWIobmV3IEV4cGxvcmVyRm9jdXNTZXR0aW5nVGFiKHRoaXMuYXBwLCB0aGlzKSk7XHJcblxyXG5cdFx0cmVnaXN0ZXJDb21tYW5kcyh0aGlzKTtcclxuXHJcblx0XHRpZiAodGhpcy5zZXR0aW5ncy5zaG93RmlsZUV4cGxvcmVySWNvbikge1xyXG5cdFx0XHR0aGlzLmFkZEZpbGVFeHBsb3Jlckljb24oKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLmFwcC53b3Jrc3BhY2Uub25MYXlvdXRSZWFkeSgoKSA9PiB7XHJcblx0XHRcdHRoaXMucGF0Y2hBbGxGaWxlRXhwbG9yZXJzKCk7XHJcblx0XHRcdHRoaXMudXBkYXRlRm9jdXNNb2RlQ2xhc3NlcygpO1xyXG5cdFx0XHR0aGlzLnVwZGF0ZUF1dG9IaWRlU3R5bGVzKCk7XHJcblx0XHR9KTtcclxuXHJcblx0XHR0aGlzLmFwcC53b3Jrc3BhY2Uub24oXCJsYXlvdXQtY2hhbmdlXCIsICgpID0+IHtcclxuXHRcdFx0dGhpcy5wYXRjaEFsbEZpbGVFeHBsb3JlcnMoKTtcclxuXHRcdFx0dGhpcy51cGRhdGVGb2N1c01vZGVDbGFzc2VzKCk7XHJcblx0XHRcdHRoaXMudXBkYXRlQXV0b0hpZGVTdHlsZXMoKTtcclxuXHRcdH0pO1xyXG5cdH1cclxuXHJcblx0YXN5bmMgbG9hZFNldHRpbmdzKCkge1xyXG5cdFx0Y29uc3QgbG9hZGVkRGF0YSA9IGF3YWl0IHRoaXMubG9hZERhdGEoKSBhcyBQYXJ0aWFsPEV4cGxvcmVyRm9jdXNTZXR0aW5ncz4gfCBudWxsO1xyXG5cdFx0dGhpcy5zZXR0aW5ncyA9IE9iamVjdC5hc3NpZ24oe30sIERFRkFVTFRfU0VUVElOR1MsIGxvYWRlZERhdGEpO1xyXG5cdH1cclxuXHJcblx0YXN5bmMgc2F2ZVNldHRpbmdzKCkge1xyXG5cdFx0YXdhaXQgdGhpcy5zYXZlRGF0YSh0aGlzLnNldHRpbmdzKTtcclxuXHR9XHJcblxyXG5cdHRvZ2dsZUZvY3VzKHBhdGg6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xyXG5cdFx0aWYgKHRoaXMuaXNGb2N1cykge1xyXG5cdFx0XHR0aGlzLmV4aXRGb2N1cygpO1xyXG5cdFx0fSBlbHNlIGlmIChwYXRoKSB7XHJcblx0XHRcdHRoaXMuZW50ZXJGb2N1cyhwYXRoKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdGVudGVyRm9jdXMocGF0aDogc3RyaW5nKSB7XHJcblx0XHR0aGlzLmlzRm9jdXMgPSB0cnVlO1xyXG5cdFx0dGhpcy5mb2N1c2VkUGF0aCA9IHBhdGg7XHJcblx0XHRcclxuXHRcdC8vIFVwZGF0ZSBpY29uIGlmIGl0IGV4aXN0c1xyXG5cdFx0dGhpcy51cGRhdGVGaWxlRXhwbG9yZXJJY29uKCk7XHJcblx0XHRcclxuXHRcdC8vIFVwZGF0ZSBDU1MgY2xhc3Nlc1xyXG5cdFx0dGhpcy51cGRhdGVGb2N1c01vZGVDbGFzc2VzKCk7XHJcblx0XHRcclxuXHRcdC8vIFRyaWdnZXIgZmlsZSBleHBsb3JlciByZWZyZXNoIG9uIGFsbCBmaWxlIGV4cGxvcmVyIGluc3RhbmNlc1xyXG5cdFx0Y29uc3QgZmlsZUV4cGxvcmVycyA9IGdldEFsbEZpbGVFeHBsb3JlcnModGhpcyk7XHJcblx0XHRmaWxlRXhwbG9yZXJzLmZvckVhY2goZmlsZUV4cGxvcmVyID0+IHtcclxuXHRcdFx0aWYgKGZpbGVFeHBsb3Jlcj8ucmVxdWVzdFNvcnQpIHtcclxuXHRcdFx0XHRmaWxlRXhwbG9yZXIucmVxdWVzdFNvcnQoKTtcclxuXHRcdFx0fVxyXG5cdFx0fSk7XHJcblx0XHRcclxuXHRcdC8vIEZvcmNlIHJlZnJlc2ggYnkgbWFudWFsbHkgdXBkYXRpbmcgdmlzaWJpbGl0eSAoZXNwZWNpYWxseSBpbXBvcnRhbnQgb24gbW9iaWxlKVxyX
|