Files
pantosite/src/content/.obsidian/plugins/home-base/main.js
T

3959 lines
543 KiB
JavaScript
Raw Normal View History

2026-04-11 00:41:28 +02:00
/*
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(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// node_modules/.pnpm/obsidian-daily-notes-interf_8d9fc24b0fc622d94fd11fa5e9b185b3/node_modules/obsidian-daily-notes-interface/dist/main.js
var require_main = __commonJS({
"node_modules/.pnpm/obsidian-daily-notes-interf_8d9fc24b0fc622d94fd11fa5e9b185b3/node_modules/obsidian-daily-notes-interface/dist/main.js"(exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var obsidian = require("obsidian");
var DEFAULT_DAILY_NOTE_FORMAT = "YYYY-MM-DD";
var DEFAULT_WEEKLY_NOTE_FORMAT = "gggg-[W]ww";
var DEFAULT_MONTHLY_NOTE_FORMAT = "YYYY-MM";
var DEFAULT_QUARTERLY_NOTE_FORMAT = "YYYY-[Q]Q";
var DEFAULT_YEARLY_NOTE_FORMAT = "YYYY";
function shouldUsePeriodicNotesSettings(periodicity) {
var _a, _b;
const periodicNotes = window.app.plugins.getPlugin("periodic-notes");
return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a[periodicity]) == null ? void 0 : _b.enabled);
}
function getDailyNoteSettings() {
var _a, _b, _c, _d;
try {
const { internalPlugins, plugins } = window.app;
if (shouldUsePeriodicNotesSettings("daily")) {
const { format: format2, folder: folder2, template: template2 } = ((_b = (_a = plugins.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.daily) || {};
return {
format: format2 || DEFAULT_DAILY_NOTE_FORMAT,
folder: (folder2 == null ? void 0 : folder2.trim()) || "",
template: (template2 == null ? void 0 : template2.trim()) || ""
};
}
const { folder, format, template } = ((_d = (_c = internalPlugins.getPluginById("daily-notes")) == null ? void 0 : _c.instance) == null ? void 0 : _d.options) || {};
return {
format: format || DEFAULT_DAILY_NOTE_FORMAT,
folder: (folder == null ? void 0 : folder.trim()) || "",
template: (template == null ? void 0 : template.trim()) || ""
};
} catch (err) {
console.info("No custom daily note settings found!", err);
}
}
function getWeeklyNoteSettings() {
var _a, _b, _c, _d, _e, _f, _g;
try {
const pluginManager = window.app.plugins;
const calendarSettings = (_a = pluginManager.getPlugin("calendar")) == null ? void 0 : _a.options;
const periodicNotesSettings = (_c = (_b = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _b.settings) == null ? void 0 : _c.weekly;
if (shouldUsePeriodicNotesSettings("weekly")) {
return {
format: periodicNotesSettings.format || DEFAULT_WEEKLY_NOTE_FORMAT,
folder: ((_d = periodicNotesSettings.folder) == null ? void 0 : _d.trim()) || "",
template: ((_e = periodicNotesSettings.template) == null ? void 0 : _e.trim()) || ""
};
}
const settings = calendarSettings || {};
return {
format: settings.weeklyNoteFormat || DEFAULT_WEEKLY_NOTE_FORMAT,
folder: ((_f = settings.weeklyNoteFolder) == null ? void 0 : _f.trim()) || "",
template: ((_g = settings.weeklyNoteTemplate) == null ? void 0 : _g.trim()) || ""
};
} catch (err) {
console.info("No custom weekly note settings found!", err);
}
}
function getMonthlyNoteSettings() {
var _a, _b, _c, _d;
const pluginManager = window.app.plugins;
try {
const settings = shouldUsePeriodicNotesSettings("monthly") && ((_b = (_a = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.monthly) || {};
return {
format: settings.format || DEFAULT_MONTHLY_NOTE_FORMAT,
folder: ((_c = settings.folder) == null ? void 0 : _c.trim()) || "",
template: ((_d = settings.template) == null ? void 0 : _d.trim()) || ""
};
} catch (err) {
console.info("No custom monthly note settings found!", err);
}
}
function getQuarterlyNoteSettings() {
var _a, _b, _c, _d;
const pluginManager = window.app.plugins;
try {
const settings = shouldUsePeriodicNotesSettings("quarterly") && ((_b = (_a = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.quarterly) || {};
return {
format: settings.format || DEFAULT_QUARTERLY_NOTE_FORMAT,
folder: ((_c = settings.folder) == null ? void 0 : _c.trim()) || "",
template: ((_d = settings.template) == null ? void 0 : _d.trim()) || ""
};
} catch (err) {
console.info("No custom quarterly note settings found!", err);
}
}
function getYearlyNoteSettings() {
var _a, _b, _c, _d;
const pluginManager = window.app.plugins;
try {
const settings = shouldUsePeriodicNotesSettings("yearly") && ((_b = (_a = pluginManager.getPlugin("periodic-notes")) == null ? void 0 : _a.settings) == null ? void 0 : _b.yearly) || {};
return {
format: settings.format || DEFAULT_YEARLY_NOTE_FORMAT,
folder: ((_c = settings.folder) == null ? void 0 : _c.trim()) || "",
template: ((_d = settings.template) == null ? void 0 : _d.trim()) || ""
};
} catch (err) {
console.info("No custom yearly note settings found!", err);
}
}
function join(...partSegments) {
let parts = [];
for (let i = 0, l = partSegments.length; i < l; i++) {
parts = parts.concat(partSegments[i].split("/"));
}
const newParts = [];
for (let i = 0, l = parts.length; i < l; i++) {
const part = parts[i];
if (!part || part === ".")
continue;
else
newParts.push(part);
}
if (parts[0] === "")
newParts.unshift("");
return newParts.join("/");
}
function basename(fullPath) {
let base = fullPath.substring(fullPath.lastIndexOf("/") + 1);
if (base.lastIndexOf(".") != -1)
base = base.substring(0, base.lastIndexOf("."));
return base;
}
async function ensureFolderExists(path) {
const dirs = path.replace(/\\/g, "/").split("/");
dirs.pop();
if (dirs.length) {
const dir = join(...dirs);
if (!window.app.vault.getAbstractFileByPath(dir)) {
await window.app.vault.createFolder(dir);
}
}
}
async function getNotePath(directory, filename) {
if (!filename.endsWith(".md")) {
filename += ".md";
}
const path = obsidian.normalizePath(join(directory, filename));
await ensureFolderExists(path);
return path;
}
async function getTemplateInfo(template) {
const { metadataCache, vault } = window.app;
const templatePath = obsidian.normalizePath(template);
if (templatePath === "/") {
return Promise.resolve(["", null]);
}
try {
const templateFile = metadataCache.getFirstLinkpathDest(templatePath, "");
const contents = await vault.cachedRead(templateFile);
const IFoldInfo = window.app.foldManager.load(templateFile);
return [contents, IFoldInfo];
} catch (err) {
console.error(`Failed to read the daily note template '${templatePath}'`, err);
new obsidian.Notice("Failed to read the daily note template");
return ["", null];
}
}
function getDateUID(date, granularity = "day") {
const ts = date.clone().startOf(granularity).format();
return `${granularity}-${ts}`;
}
function removeEscapedCharacters(format) {
return format.replace(/\[[^\]]*\]/g, "");
}
function isFormatAmbiguous(format, granularity) {
if (granularity === "week") {
const cleanFormat = removeEscapedCharacters(format);
return /w{1,2}/i.test(cleanFormat) && (/M{1,4}/.test(cleanFormat) || /D{1,4}/.test(cleanFormat));
}
return false;
}
function getDateFromFile(file, granularity) {
return getDateFromFilename(file.basename, granularity);
}
function getDateFromPath(path, granularity) {
return getDateFromFilename(basename(path), granularity);
}
function getDateFromFilename(filename, granularity) {
const getSettings = {
day: getDailyNoteSettings,
week: getWeeklyNoteSettings,
month: getMonthlyNoteSettings,
quarter: getQuarterlyNoteSettings,
year: getYearlyNoteSettings
};
const format = getSettings[granularity]().format.split("/").pop();
const noteDate = window.moment(filename, format, true);
if (!noteDate.isValid()) {
return null;
}
if (isFormatAmbiguous(format, granularity)) {
if (granularity === "week") {
const cleanFormat = removeEscapedCharacters(format);
if (/w{1,2}/i.test(cleanFormat)) {
return window.moment(
filename,
// If format contains week, remove day & month formatting
format.replace(/M{1,4}/g, "").replace(/D{1,4}/g, ""),
false
);
}
}
}
return noteDate;
}
var DailyNotesFolderMissingError = class extends Error {
};
async function createDailyNote2(date) {
const app = window.app;
const { vault } = app;
const moment2 = window.moment;
const { template, format, folder } = getDailyNoteSettings();
const [templateContents, IFoldInfo] = await getTemplateInfo(template);
const filename = date.format(format);
const normalizedPath = await getNotePath(folder, filename);
try {
const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, moment2().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename).replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
const now = moment2();
const currentDate = date.clone().set({
hour: now.get("hour"),
minute: now.get("minute"),
second: now.get("second")
});
if (calc) {
currentDate.add(parseInt(timeDelta, 10), unit);
}
if (momentFormat) {
return currentDate.format(momentFormat.substring(1).trim());
}
return currentDate.format(format);
}).replace(/{{\s*yesterday\s*}}/gi, date.clone().subtract(1, "day").format(format)).replace(/{{\s*tomorrow\s*}}/gi, date.clone().add(1, "d").format(format)));
app.foldManager.save(createdFile, IFoldInfo);
return createdFile;
} catch (err) {
console.error(`Failed to create file: '${normalizedPath}'`, err);
new obsidian.Notice("Unable to create new file.");
}
}
function getDailyNote2(date, dailyNotes) {
var _a;
return (_a = dailyNotes[getDateUID(date, "day")]) != null ? _a : null;
}
function getAllDailyNotes2() {
const { vault } = window.app;
const { folder } = getDailyNoteSettings();
const dailyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
if (!dailyNotesFolder) {
throw new DailyNotesFolderMissingError("Failed to find daily notes folder");
}
const dailyNotes = {};
obsidian.Vault.recurseChildren(dailyNotesFolder, (note) => {
if (note instanceof obsidian.TFile) {
const date = getDateFromFile(note, "day");
if (date) {
const dateString = getDateUID(date, "day");
dailyNotes[dateString] = note;
}
}
});
return dailyNotes;
}
var WeeklyNotesFolderMissingError = class extends Error {
};
function getDaysOfWeek() {
const { moment: moment2 } = window;
let weekStart = moment2.localeData()._week.dow;
const daysOfWeek = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday"
];
while (weekStart) {
daysOfWeek.push(daysOfWeek.shift());
weekStart--;
}
return daysOfWeek;
}
function getDayOfWeekNumericalValue(dayOfWeekName) {
return getDaysOfWeek().indexOf(dayOfWeekName.toLowerCase());
}
async function createWeeklyNote2(date) {
const { vault } = window.app;
const { template, format, folder } = getWeeklyNoteSettings();
const [templateContents, IFoldInfo] = await getTemplateInfo(template);
const filename = date.format(format);
const normalizedPath = await getNotePath(folder, filename);
try {
const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
const now = window.moment();
const currentDate = date.clone().set({
hour: now.get("hour"),
minute: now.get("minute"),
second: now.get("second")
});
if (calc) {
currentDate.add(parseInt(timeDelta, 10), unit);
}
if (momentFormat) {
return currentDate.format(momentFormat.substring(1).trim());
}
return currentDate.format(format);
}).replace(/{{\s*title\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*:(.*?)}}/gi, (_, dayOfWeek, momentFormat) => {
const day = getDayOfWeekNumericalValue(dayOfWeek);
return date.weekday(day).format(momentFormat.trim());
}));
window.app.foldManager.save(createdFile, IFoldInfo);
return createdFile;
} catch (err) {
console.error(`Failed to create file: '${normalizedPath}'`, err);
new obsidian.Notice("Unable to create new file.");
}
}
function getWeeklyNote2(date, weeklyNotes) {
var _a;
return (_a = weeklyNotes[getDateUID(date, "week")]) != null ? _a : null;
}
function getAllWeeklyNotes2() {
const weeklyNotes = {};
if (!appHasWeeklyNotesPluginLoaded()) {
return weeklyNotes;
}
const { vault } = window.app;
const { folder } = getWeeklyNoteSettings();
const weeklyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
if (!weeklyNotesFolder) {
throw new WeeklyNotesFolderMissingError("Failed to find weekly notes folder");
}
obsidian.Vault.recurseChildren(weeklyNotesFolder, (note) => {
if (note instanceof obsidian.TFile) {
const date = getDateFromFile(note, "week");
if (date) {
const dateString = getDateUID(date, "week");
weeklyNotes[dateString] = note;
}
}
});
return weeklyNotes;
}
var MonthlyNotesFolderMissingError = class extends Error {
};
async function createMonthlyNote2(date) {
const { vault } = window.app;
const { template, format, folder } = getMonthlyNoteSettings();
const [templateContents, IFoldInfo] = await getTemplateInfo(template);
const filename = date.format(format);
const normalizedPath = await getNotePath(folder, filename);
try {
const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
const now = window.moment();
const currentDate = date.clone().set({
hour: now.get("hour"),
minute: now.get("minute"),
second: now.get("second")
});
if (calc) {
currentDate.add(parseInt(timeDelta, 10), unit);
}
if (momentFormat) {
return currentDate.format(momentFormat.substring(1).trim());
}
return currentDate.format(format);
}).replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename));
window.app.foldManager.save(createdFile, IFoldInfo);
return createdFile;
} catch (err) {
console.error(`Failed to create file: '${normalizedPath}'`, err);
new obsidian.Notice("Unable to create new file.");
}
}
function getMonthlyNote2(date, monthlyNotes) {
var _a;
return (_a = monthlyNotes[getDateUID(date, "month")]) != null ? _a : null;
}
function getAllMonthlyNotes2() {
const monthlyNotes = {};
if (!appHasMonthlyNotesPluginLoaded()) {
return monthlyNotes;
}
const { vault } = window.app;
const { folder } = getMonthlyNoteSettings();
const monthlyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
if (!monthlyNotesFolder) {
throw new MonthlyNotesFolderMissingError("Failed to find monthly notes folder");
}
obsidian.Vault.recurseChildren(monthlyNotesFolder, (note) => {
if (note instanceof obsidian.TFile) {
const date = getDateFromFile(note, "month");
if (date) {
const dateString = getDateUID(date, "month");
monthlyNotes[dateString] = note;
}
}
});
return monthlyNotes;
}
var QuarterlyNotesFolderMissingError = class extends Error {
};
async function createQuarterlyNote2(date) {
const { vault } = window.app;
const { template, format, folder } = getQuarterlyNoteSettings();
const [templateContents, IFoldInfo] = await getTemplateInfo(template);
const filename = date.format(format);
const normalizedPath = await getNotePath(folder, filename);
try {
const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
const now = window.moment();
const currentDate = date.clone().set({
hour: now.get("hour"),
minute: now.get("minute"),
second: now.get("second")
});
if (calc) {
currentDate.add(parseInt(timeDelta, 10), unit);
}
if (momentFormat) {
return currentDate.format(momentFormat.substring(1).trim());
}
return currentDate.format(format);
}).replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename));
window.app.foldManager.save(createdFile, IFoldInfo);
return createdFile;
} catch (err) {
console.error(`Failed to create file: '${normalizedPath}'`, err);
new obsidian.Notice("Unable to create new file.");
}
}
function getQuarterlyNote2(date, quarterly) {
var _a;
return (_a = quarterly[getDateUID(date, "quarter")]) != null ? _a : null;
}
function getAllQuarterlyNotes2() {
const quarterly = {};
if (!appHasQuarterlyNotesPluginLoaded()) {
return quarterly;
}
const { vault } = window.app;
const { folder } = getQuarterlyNoteSettings();
const quarterlyFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
if (!quarterlyFolder) {
throw new QuarterlyNotesFolderMissingError("Failed to find quarterly notes folder");
}
obsidian.Vault.recurseChildren(quarterlyFolder, (note) => {
if (note instanceof obsidian.TFile) {
const date = getDateFromFile(note, "quarter");
if (date) {
const dateString = getDateUID(date, "quarter");
quarterly[dateString] = note;
}
}
});
return quarterly;
}
var YearlyNotesFolderMissingError = class extends Error {
};
async function createYearlyNote2(date) {
const { vault } = window.app;
const { template, format, folder } = getYearlyNoteSettings();
const [templateContents, IFoldInfo] = await getTemplateInfo(template);
const filename = date.format(format);
const normalizedPath = await getNotePath(folder, filename);
try {
const createdFile = await vault.create(normalizedPath, templateContents.replace(/{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => {
const now = window.moment();
const currentDate = date.clone().set({
hour: now.get("hour"),
minute: now.get("minute"),
second: now.get("second")
});
if (calc) {
currentDate.add(parseInt(timeDelta, 10), unit);
}
if (momentFormat) {
return currentDate.format(momentFormat.substring(1).trim());
}
return currentDate.format(format);
}).replace(/{{\s*date\s*}}/gi, filename).replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")).replace(/{{\s*title\s*}}/gi, filename));
window.app.foldManager.save(createdFile, IFoldInfo);
return createdFile;
} catch (err) {
console.error(`Failed to create file: '${normalizedPath}'`, err);
new obsidian.Notice("Unable to create new file.");
}
}
function getYearlyNote2(date, yearlyNotes) {
var _a;
return (_a = yearlyNotes[getDateUID(date, "year")]) != null ? _a : null;
}
function getAllYearlyNotes2() {
const yearlyNotes = {};
if (!appHasYearlyNotesPluginLoaded()) {
return yearlyNotes;
}
const { vault } = window.app;
const { folder } = getYearlyNoteSettings();
const yearlyNotesFolder = vault.getAbstractFileByPath(obsidian.normalizePath(folder));
if (!yearlyNotesFolder) {
throw new YearlyNotesFolderMissingError("Failed to find yearly notes folder");
}
obsidian.Vault.recurseChildren(yearlyNotesFolder, (note) => {
if (note instanceof obsidian.TFile) {
const date = getDateFromFile(note, "year");
if (date) {
const dateString = getDateUID(date, "year");
yearlyNotes[dateString] = note;
}
}
});
return yearlyNotes;
}
function appHasDailyNotesPluginLoaded() {
var _a, _b;
const { app } = window;
const dailyNotesPlugin = app.internalPlugins.plugins["daily-notes"];
if (dailyNotesPlugin && dailyNotesPlugin.enabled) {
return true;
}
const periodicNotes = app.plugins.getPlugin("periodic-notes");
return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.daily) == null ? void 0 : _b.enabled);
}
function appHasWeeklyNotesPluginLoaded() {
var _a, _b;
const { app } = window;
if (app.plugins.getPlugin("calendar")) {
return true;
}
const periodicNotes = app.plugins.getPlugin("periodic-notes");
return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.weekly) == null ? void 0 : _b.enabled);
}
function appHasMonthlyNotesPluginLoaded() {
var _a, _b;
const { app } = window;
const periodicNotes = app.plugins.getPlugin("periodic-notes");
return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.monthly) == null ? void 0 : _b.enabled);
}
function appHasQuarterlyNotesPluginLoaded() {
var _a, _b;
const { app } = window;
const periodicNotes = app.plugins.getPlugin("periodic-notes");
return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.quarterly) == null ? void 0 : _b.enabled);
}
function appHasYearlyNotesPluginLoaded() {
var _a, _b;
const { app } = window;
const periodicNotes = app.plugins.getPlugin("periodic-notes");
return periodicNotes && ((_b = (_a = periodicNotes.settings) == null ? void 0 : _a.yearly) == null ? void 0 : _b.enabled);
}
function getPeriodicNoteSettings(granularity) {
const getSettings = {
day: getDailyNoteSettings,
week: getWeeklyNoteSettings,
month: getMonthlyNoteSettings,
quarter: getQuarterlyNoteSettings,
year: getYearlyNoteSettings
}[granularity];
return getSettings();
}
function createPeriodicNote(granularity, date) {
const createFn = {
day: createDailyNote2,
month: createMonthlyNote2,
week: createWeeklyNote2
};
return createFn[granularity](date);
}
exports.DEFAULT_DAILY_NOTE_FORMAT = DEFAULT_DAILY_NOTE_FORMAT;
exports.DEFAULT_MONTHLY_NOTE_FORMAT = DEFAULT_MONTHLY_NOTE_FORMAT;
exports.DEFAULT_QUARTERLY_NOTE_FORMAT = DEFAULT_QUARTERLY_NOTE_FORMAT;
exports.DEFAULT_WEEKLY_NOTE_FORMAT = DEFAULT_WEEKLY_NOTE_FORMAT;
exports.DEFAULT_YEARLY_NOTE_FORMAT = DEFAULT_YEARLY_NOTE_FORMAT;
exports.appHasDailyNotesPluginLoaded = appHasDailyNotesPluginLoaded;
exports.appHasMonthlyNotesPluginLoaded = appHasMonthlyNotesPluginLoaded;
exports.appHasQuarterlyNotesPluginLoaded = appHasQuarterlyNotesPluginLoaded;
exports.appHasWeeklyNotesPluginLoaded = appHasWeeklyNotesPluginLoaded;
exports.appHasYearlyNotesPluginLoaded = appHasYearlyNotesPluginLoaded;
exports.createDailyNote = createDailyNote2;
exports.createMonthlyNote = createMonthlyNote2;
exports.createPeriodicNote = createPeriodicNote;
exports.createQuarterlyNote = createQuarterlyNote2;
exports.createWeeklyNote = createWeeklyNote2;
exports.createYearlyNote = createYearlyNote2;
exports.getAllDailyNotes = getAllDailyNotes2;
exports.getAllMonthlyNotes = getAllMonthlyNotes2;
exports.getAllQuarterlyNotes = getAllQuarterlyNotes2;
exports.getAllWeeklyNotes = getAllWeeklyNotes2;
exports.getAllYearlyNotes = getAllYearlyNotes2;
exports.getDailyNote = getDailyNote2;
exports.getDailyNoteSettings = getDailyNoteSettings;
exports.getDateFromFile = getDateFromFile;
exports.getDateFromPath = getDateFromPath;
exports.getDateUID = getDateUID;
exports.getMonthlyNote = getMonthlyNote2;
exports.getMonthlyNoteSettings = getMonthlyNoteSettings;
exports.getPeriodicNoteSettings = getPeriodicNoteSettings;
exports.getQuarterlyNote = getQuarterlyNote2;
exports.getQuarterlyNoteSettings = getQuarterlyNoteSettings;
exports.getTemplateInfo = getTemplateInfo;
exports.getWeeklyNote = getWeeklyNote2;
exports.getWeeklyNoteSettings = getWeeklyNoteSettings;
exports.getYearlyNote = getYearlyNote2;
exports.getYearlyNoteSettings = getYearlyNoteSettings;
}
});
// src/main.ts
var main_exports = {};
__export(main_exports, {
default: () => HomeBasePlugin
});
module.exports = __toCommonJS(main_exports);
var import_obsidian11 = require("obsidian");
// src/settings.ts
var HomeBaseType = /* @__PURE__ */ ((HomeBaseType2) => {
HomeBaseType2["File"] = "File";
HomeBaseType2["Workspace"] = "Workspace";
HomeBaseType2["Random"] = "Random file";
HomeBaseType2["RandomFolder"] = "Random in folder";
HomeBaseType2["Graph"] = "Graph view";
HomeBaseType2["None"] = "Nothing";
HomeBaseType2["Journal"] = "Journal";
HomeBaseType2["NewNote"] = "New note";
HomeBaseType2["DailyNote"] = "Daily Note";
HomeBaseType2["WeeklyNote"] = "Weekly Note";
HomeBaseType2["MonthlyNote"] = "Monthly Note";
HomeBaseType2["QuarterlyNote"] = "Quarterly Note";
HomeBaseType2["YearlyNote"] = "Yearly Note";
return HomeBaseType2;
})(HomeBaseType || {});
var DEFAULT_SETTINGS = {
// General
homeBaseType: "File" /* File */,
homeBaseValue: "",
openOnStartup: true,
openViewMode: "default",
openMode: "replace-all",
manualOpenMode: "retain",
// Tab Behavior
replaceNewTab: false,
newTabMode: "only-when-empty",
// Default: only replace when no tabs are open
openWhenAllTabsClosed: true,
// Default: open home base when all tabs are closed
useDifferentFileForNewTab: false,
newTabType: "File" /* File */,
newTabValue: "",
newTabSeparateMobile: false,
mobileNewTabType: "File" /* File */,
mobileNewTabValue: "",
// UI Features
showStickyHomeIcon: false,
stickyIconName: "home",
hideHomeTabHeader: false,
replaceMobileNewTab: false,
// Mobile
separateMobile: false,
mobileHomeBaseType: "File" /* File */,
mobileHomeBaseValue: "",
// Automation
commandOnOpen: "",
waitForGitSync: false,
gitSyncTimeout: 3,
// Default 3 seconds
// View behavior
revertView: false,
autoScroll: false,
hideReleaseNotes: false
// OFF by default
};
var VIEW_MODE_OPTIONS = {
"default": "Default",
"preview": "Reading view",
"source": "Source mode",
"live": "Live Preview"
};
var NEW_TAB_MODE_OPTIONS = {
"only-when-empty": "Only when no tabs are open",
"always": "Always replace new tabs"
};
var OPENING_MODE_OPTIONS = {
"replace-all": "Replace all open notes",
"replace-last": "Replace last note",
"retain": "Keep open notes"
};
var UNCHANGEABLE_TYPES = [
"Random file" /* Random */,
"Graph view" /* Graph */,
"Nothing" /* None */,
"Daily Note" /* DailyNote */,
"Weekly Note" /* WeeklyNote */,
"Monthly Note" /* MonthlyNote */,
"Quarterly Note" /* QuarterlyNote */,
"Yearly Note" /* YearlyNote */
];
// src/ui/settings-tab.ts
var import_obsidian4 = require("obsidian");
// src/ui/file-suggest.ts
var import_obsidian = require("obsidian");
var SUPPORTED_EXTENSIONS = ["md", "mdx", "canvas", "base"];
var FilePathSuggest = class extends import_obsidian.AbstractInputSuggest {
constructor(app, inputEl) {
super(app, inputEl);
this.inputEl = inputEl;
}
getSuggestions(query) {
const lowerQuery = query.toLowerCase();
const files = [];
this.app.vault.getAllLoadedFiles().forEach((file) => {
if (file instanceof import_obsidian.TFile) {
if (SUPPORTED_EXTENSIONS.includes(file.extension)) {
if (file.path.toLowerCase().includes(lowerQuery) || file.basename.toLowerCase().includes(lowerQuery)) {
files.push(file);
}
}
}
});
files.sort((a, b) => {
const aStartsWith = a.path.toLowerCase().startsWith(lowerQuery);
const bStartsWith = b.path.toLowerCase().startsWith(lowerQuery);
if (aStartsWith && !bStartsWith) return -1;
if (!aStartsWith && bStartsWith) return 1;
return a.path.localeCompare(b.path);
});
return files.slice(0, 20);
}
renderSuggestion(file, el) {
el.addClass("home-base-suggestion-item");
const titleEl = el.createEl("div", {
cls: "suggestion-title"
});
titleEl.createEl("span", { text: file.basename });
if (file.extension !== "md") {
titleEl.createEl("span", {
text: file.extension.toUpperCase(),
cls: "suggestion-flair"
});
}
if (file.parent && file.parent.path !== "/") {
el.createEl("div", {
text: file.parent.path,
cls: "suggestion-note"
});
}
}
selectSuggestion(file) {
this.inputEl.value = file.path;
this.inputEl.trigger("input");
this.close();
}
};
var FolderSuggest = class extends import_obsidian.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_obsidian.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();
}
};
var WorkspaceSuggest = class extends import_obsidian.AbstractInputSuggest {
constructor(app, inputEl) {
super(app, inputEl);
this.inputEl = inputEl;
}
getSuggestions(query) {
var _a, _b, _c;
const workspacesPlugin = (_b = (_a = this.app.internalPlugins) == null ? void 0 : _a.plugins) == null ? void 0 : _b.workspaces;
if (!(workspacesPlugin == null ? void 0 : workspacesPlugin.enabled) || !((_c = workspacesPlugin.instance) == null ? void 0 : _c.workspaces)) {
return [];
}
const workspaces = Object.keys(workspacesPlugin.instance.workspaces);
const lowerQuery = query.toLowerCase();
return workspaces.filter(
(workspace) => workspace.toLowerCase().includes(lowerQuery)
);
}
renderSuggestion(workspace, el) {
el.createEl("div", { text: workspace });
}
selectSuggestion(workspace) {
this.inputEl.value = workspace;
this.inputEl.trigger("input");
this.close();
}
};
// src/ui/command-suggest.ts
var import_obsidian2 = require("obsidian");
var CommandSuggest = class extends import_obsidian2.AbstractInputSuggest {
constructor(app, inputEl) {
super(app, inputEl);
this.inputEl = inputEl;
}
getSuggestions(query) {
var _a;
const lowerQuery = query.toLowerCase();
const commands = [];
const appWithCommands = this.app;
const allCommands = (_a = appWithCommands.commands) == null ? void 0 : _a.commands;
if (allCommands) {
for (const command of Object.values(allCommands)) {
if (command.name.toLowerCase().includes(lowerQuery) || command.id.toLowerCase().includes(lowerQuery)) {
commands.push(command);
}
}
}
commands.sort((a, b) => a.name.localeCompare(b.name));
return commands.slice(0, 30);
}
renderSuggestion(command, el) {
el.createEl("div", {
text: command.name,
cls: "suggestion-title"
});
el.createEl("small", {
text: command.id,
cls: "suggestion-note"
});
}
selectSuggestion(command) {
this.inputEl.value = command.id;
this.inputEl.trigger("input");
this.close();
}
};
function getCommandById(app, commandId) {
var _a;
const appWithCommands = app;
const commands = (_a = appWithCommands.commands) == null ? void 0 : _a.commands;
return commands == null ? void 0 : commands[commandId];
}
function executeCommand(app, commandId) {
var _a, _b;
if (!commandId) return false;
const appWithCommands = app;
const result = (_b = (_a = appWithCommands.commands) == null ? void 0 : _a.executeCommandById) == null ? void 0 : _b.call(_a, commandId);
return result !== false;
}
// src/ui/icon-picker.ts
var import_obsidian3 = require("obsidian");
var IconPicker = class extends import_obsidian3.Modal {
constructor(app, currentIcon, callback) {
super(app);
this.searchResults = [];
this.selectedIcon = currentIcon;
this.callback = callback;
}
onOpen() {
this.containerEl.addClass("mod-confirmation");
this.modalEl.addClass("iconic-icon-picker");
this.setTitle("Change icon");
const searchSetting = new import_obsidian3.Setting(this.contentEl);
if (!import_obsidian3.Platform.isPhone) {
searchSetting.setName("Search");
}
searchSetting.addSearch((searchField) => {
searchField.setPlaceholder("Search icons...").onChange(() => this.updateSearchResults());
searchField.inputEl.enterKeyHint = "go";
this.searchField = searchField;
});
if (this.selectedIcon) {
this.searchField.setValue(this.selectedIcon);
}
this.searchResultsSetting = new import_obsidian3.Setting(this.contentEl);
this.searchResultsSetting.settingEl.addClass("iconic-search-results");
this.searchResultsSetting.settingEl.tabIndex = 0;
this.searchResultsSetting.settingEl.addEventListener("wheel", (event) => {
if (document.body.hasClass("mod-rtl")) {
this.searchResultsSetting.settingEl.scrollLeft -= event.deltaY;
} else {
this.searchResultsSetting.settingEl.scrollLeft += event.deltaY;
}
}, { passive: true });
const buttonContainer = this.modalEl.createDiv({ cls: "modal-button-container" });
new import_obsidian3.ButtonComponent(buttonContainer).setButtonText("Cancel").onClick(() => this.close()).buttonEl.addClass("mod-cancel");
new import_obsidian3.ButtonComponent(buttonContainer).setButtonText("Save").setCta().onClick(() => {
this.callback(this.selectedIcon);
this.close();
});
requestAnimationFrame(() => {
this.searchField.inputEl.select();
this.updateSearchResults();
});
}
/**
* Update search results based on current query
*/
updateSearchResults() {
const query = this.searchField.getValue().toLowerCase().trim();
const fuzzySearch = (0, import_obsidian3.prepareFuzzySearch)(query);
const matches = [];
const iconIds = (0, import_obsidian3.getIconIds)();
if (query) {
for (const iconId of iconIds) {
const iconName = this.formatIconName(iconId);
if (iconId === query || iconId.toLowerCase() === query) {
matches.push([0, [iconId, iconName]]);
} else {
const fuzzyMatch = fuzzySearch(iconName);
if (fuzzyMatch) {
matches.push([fuzzyMatch.score, [iconId, iconName]]);
}
}
}
} else {
for (const iconId of iconIds) {
const iconName = this.formatIconName(iconId);
matches.push([0, [iconId, iconName]]);
}
}
matches.sort(([scoreA], [scoreB]) => scoreA > scoreB ? -1 : 1);
this.searchResults.length = 0;
const maxResults = 100;
for (const [, iconEntry] of matches) {
this.searchResults.push(iconEntry);
if (this.searchResults.length >= maxResults) break;
}
this.searchResultsSetting.clear();
for (const [iconId, iconName] of this.searchResults) {
this.searchResultsSetting.addExtraButton((iconButton) => {
iconButton.setTooltip(iconName, {
delay: 300,
placement: import_obsidian3.Platform.isPhone ? "top" : "bottom"
});
const iconEl = iconButton.extraSettingsEl;
iconEl.addClass("iconic-search-result");
iconEl.tabIndex = -1;
(0, import_obsidian3.setIcon)(iconEl, iconId);
if (iconId === this.selectedIcon) {
iconEl.addClass("is-selected");
}
iconEl.addEventListener("click", () => {
this.selectedIcon = iconId;
this.callback(iconId);
this.close();
});
if (import_obsidian3.Platform.isPhone) {
iconEl.addEventListener("contextmenu", () => {
var _a;
(_a = navigator.vibrate) == null ? void 0 : _a.call(navigator, 100);
});
}
});
}
if (this.searchResults.length === 0) {
this.searchResultsSetting.addExtraButton((button) => {
button.extraSettingsEl.addClasses(["iconic-invisible", "iconic-search-result"]);
});
}
}
/**
* Format icon ID into readable name
*/
formatIconName(iconId) {
let name = iconId.replace(/^lucide-/, "");
name = name.replace(/-/g, " ");
return name.split(" ").map(
(word) => word.charAt(0).toUpperCase() + word.slice(1)
).join(" ");
}
onClose() {
this.contentEl.empty();
}
};
// src/ui/settings-tab.ts
var HomeBaseSettingTab = class extends import_obsidian4.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.icon = "lucide-house";
this.plugin = plugin;
}
display() {
const { containerEl } = this;
containerEl.empty();
const isMobile = this.plugin.settings.separateMobile;
const activeType = isMobile ? this.plugin.settings.mobileHomeBaseType : this.plugin.settings.homeBaseType;
const activeValue = isMobile ? this.plugin.settings.mobileHomeBaseValue : this.plugin.settings.homeBaseValue;
const generalGroup = new import_obsidian4.SettingGroup(containerEl);
generalGroup.addSetting((setting) => {
setting.setName("Type").setDesc("What to open as your home base").addDropdown((dropdown) => {
let pluginDisabled = false;
for (const type of Object.values(HomeBaseType)) {
if (!this.plugin.hasRequiredPlugin(type)) {
if (type === activeType) {
pluginDisabled = true;
dropdown.addOption(type, type);
} else {
dropdown.selectEl.createEl("option", {
text: type,
attr: { disabled: "true" }
});
continue;
}
} else {
dropdown.addOption(type, type);
}
}
dropdown.setValue(activeType || "File" /* File */).onChange(async (value) => {
if (isMobile) {
this.plugin.settings.mobileHomeBaseType = value;
} else {
this.plugin.settings.homeBaseType = value;
}
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
if (pluginDisabled) {
setting.descEl.createDiv({
text: "The required plugin has not been enabled or configured for this type.",
cls: "mod-warning"
});
}
});
});
if (!UNCHANGEABLE_TYPES.includes(activeType)) {
generalGroup.addSetting((setting) => {
let desc = "";
let placeholder = "";
if (activeType === "File" /* File */) {
desc = "The file to open as your home base (supports .md, .mdx, .canvas, .base)";
placeholder = "Path to home base file";
} else if (activeType === "Workspace" /* Workspace */) {
desc = "The workspace to load as your home base";
placeholder = "Workspace name";
} else if (activeType === "Random in folder" /* RandomFolder */ || activeType === "New note" /* NewNote */) {
desc = activeType === "Random in folder" /* RandomFolder */ ? "The folder to pick a random file from" : "The folder to create new notes in";
placeholder = "Folder path";
} else if (activeType === "Journal" /* Journal */) {
desc = "The journal name";
placeholder = "Journal name";
}
setting.setName(activeType === "File" /* File */ ? "File" : activeType === "Workspace" /* Workspace */ ? "Workspace" : activeType === "Random in folder" /* RandomFolder */ || activeType === "New note" /* NewNote */ ? "Folder" : activeType === "Journal" /* Journal */ ? "Journal" : "Value").setDesc(desc).addText((text) => {
if (activeType === "File" /* File */) {
new FilePathSuggest(this.app, text.inputEl);
} else if (activeType === "Workspace" /* Workspace */) {
new WorkspaceSuggest(this.app, text.inputEl);
} else if (activeType === "Random in folder" /* RandomFolder */ || activeType === "New note" /* NewNote */) {
new FolderSuggest(this.app, text.inputEl);
}
text.setPlaceholder(placeholder).setValue(activeValue || "").onChange(async (value) => {
if (isMobile) {
this.plugin.settings.mobileHomeBaseValue = value;
} else {
this.plugin.settings.homeBaseValue = value;
}
await this.plugin.saveSettings();
});
});
});
}
generalGroup.addSetting((setting) => {
setting.setName("Open on startup").setDesc("Open the home base when launching Obsidian").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.openOnStartup).onChange(async (value) => {
this.plugin.settings.openOnStartup = value;
await this.plugin.saveSettings();
});
});
if ((0, import_obsidian4.requireApiVersion)("1.11.0")) {
const nativeOpenBehavior = this.plugin.homeService.getNativeOpenBehavior();
if (nativeOpenBehavior) {
setting.descEl.createDiv({
text: `Note: This will override Obsidian's native "Default file to open" setting (currently set to "${nativeOpenBehavior}").`,
cls: "mod-warning"
});
}
}
});
generalGroup.addSetting((setting) => {
setting.setName("Opening mode (startup)").setDesc("How to handle existing tabs when opening on startup").addDropdown((dropdown) => {
for (const [value, label] of Object.entries(OPENING_MODE_OPTIONS)) {
dropdown.addOption(value, label);
}
dropdown.setValue(this.plugin.settings.openMode).onChange(async (value) => {
this.plugin.settings.openMode = value;
await this.plugin.saveSettings();
});
});
});
generalGroup.addSetting((setting) => {
setting.setName("Opening mode (manual)").setDesc("How to handle existing tabs when opening manually").addDropdown((dropdown) => {
for (const [value, label] of Object.entries(OPENING_MODE_OPTIONS)) {
dropdown.addOption(value, label);
}
dropdown.setValue(this.plugin.settings.manualOpenMode).onChange(async (value) => {
this.plugin.settings.manualOpenMode = value;
await this.plugin.saveSettings();
});
});
});
generalGroup.addSetting((setting) => {
setting.setName("View mode").setDesc("How to open Markdown files").addDropdown((dropdown) => {
for (const [value, label] of Object.entries(VIEW_MODE_OPTIONS)) {
dropdown.addOption(value, label);
}
dropdown.setValue(this.plugin.settings.openViewMode).onChange(async (value) => {
this.plugin.settings.openViewMode = value;
await this.plugin.saveSettings();
});
});
});
generalGroup.addSetting((setting) => {
setting.setName("Revert view on close").setDesc("When navigating away from the home base, restore the default view").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.revertView).onChange(async (value) => {
this.plugin.settings.revertView = value;
await this.plugin.saveSettings();
});
});
});
generalGroup.addSetting((setting) => {
setting.setName("Auto-scroll").setDesc("When opening the home base, scroll to the bottom and focus on the last line").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.autoScroll).onChange(async (value) => {
this.plugin.settings.autoScroll = value;
await this.plugin.saveSettings();
});
});
});
generalGroup.addSetting((setting) => {
setting.setName("Hide release notes").setDesc("Never display release notes when Obsidian updates").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.hideReleaseNotes).onChange(async (value) => {
this.plugin.settings.hideReleaseNotes = value;
await this.plugin.saveSettings();
});
});
});
const tabGroup = new import_obsidian4.SettingGroup(containerEl).setHeading("Tab Behavior");
tabGroup.addSetting((setting) => {
setting.setName("Open home base when all tabs are closed").setDesc("When you close all tabs, automatically open the home base").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.openWhenAllTabsClosed).onChange(async (value) => {
this.plugin.settings.openWhenAllTabsClosed = value;
await this.plugin.saveSettings();
});
});
});
tabGroup.addSetting((setting) => {
setting.setName("Replace new tabs").setDesc('Open home base instead of new empty tabs (works independently of "open home base when all tabs are closed")').addToggle((toggle) => {
toggle.setValue(this.plugin.settings.replaceNewTab).onChange(async (value) => {
this.plugin.settings.replaceNewTab = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
});
});
if (this.plugin.settings.replaceNewTab) {
tabGroup.addSetting((setting) => {
setting.setName("New tab replacement mode").setDesc("When to replace new tabs (only when no tabs are open, or always)").addDropdown((dropdown) => {
for (const [value, label] of Object.entries(NEW_TAB_MODE_OPTIONS)) {
dropdown.addOption(value, label);
}
dropdown.setValue(this.plugin.settings.newTabMode).onChange(async (value) => {
this.plugin.settings.newTabMode = value;
await this.plugin.saveSettings();
});
});
});
tabGroup.addSetting((setting) => {
setting.setName("Use different home base for new tabs").setDesc("Configure a different home base to open for new tabs (instead of the main home base)").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.useDifferentFileForNewTab).onChange(async (value) => {
this.plugin.settings.useDifferentFileForNewTab = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
});
});
if (this.plugin.settings.useDifferentFileForNewTab) {
const desktopType = this.plugin.settings.newTabType || "File" /* File */;
const desktopValue = this.plugin.settings.newTabValue || "";
tabGroup.addSetting((setting) => {
setting.setName("New tab type").setDesc("What to open for new tabs").addDropdown((dropdown) => {
let pluginDisabled = false;
for (const type of Object.values(HomeBaseType)) {
if (!this.plugin.hasRequiredPlugin(type)) {
if (type === desktopType) {
pluginDisabled = true;
dropdown.addOption(type, type);
} else {
dropdown.selectEl.createEl("option", {
text: type,
attr: { disabled: "true" }
});
continue;
}
} else {
dropdown.addOption(type, type);
}
}
dropdown.setValue(desktopType).onChange(async (value) => {
this.plugin.settings.newTabType = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
if (pluginDisabled) {
setting.descEl.createDiv({
text: "The required plugin has not been enabled or configured for this type.",
cls: "mod-warning"
});
}
});
});
if (!UNCHANGEABLE_TYPES.includes(desktopType)) {
tabGroup.addSetting((setting) => {
let desc = "";
let placeholder = "";
if (desktopType === "File" /* File */) {
desc = "The file to open for new tabs (supports .md, .mdx, .canvas, .base)";
placeholder = "Path to new tab file";
} else if (desktopType === "Workspace" /* Workspace */) {
desc = "The workspace to load for new tabs";
placeholder = "Workspace name";
} else if (desktopType === "Random in folder" /* RandomFolder */) {
desc = "The folder to pick a random file from for new tabs";
placeholder = "Folder path";
} else if (desktopType === "Journal" /* Journal */) {
desc = "The journal name for new tabs";
placeholder = "Journal name";
}
setting.setName(desktopType === "File" /* File */ ? "New tab file" : desktopType === "Workspace" /* Workspace */ ? "New tab workspace" : desktopType === "Random in folder" /* RandomFolder */ ? "New tab folder" : desktopType === "Journal" /* Journal */ ? "New tab journal" : "New tab value").setDesc(desc).addText((text) => {
if (desktopType === "File" /* File */) {
new FilePathSuggest(this.app, text.inputEl);
} else if (desktopType === "Workspace" /* Workspace */) {
new WorkspaceSuggest(this.app, text.inputEl);
} else if (desktopType === "Random in folder" /* RandomFolder */) {
new FolderSuggest(this.app, text.inputEl);
}
text.setPlaceholder(placeholder).setValue(desktopValue || "").onChange(async (value) => {
this.plugin.settings.newTabValue = value;
await this.plugin.saveSettings();
});
});
});
}
}
}
const uiGroup = new import_obsidian4.SettingGroup(containerEl).setHeading("UI Features");
uiGroup.addSetting((setting) => {
setting.setName("Sticky home icon").setDesc("Show a home icon in the tab bar that stays pinned to the left (desktop only)").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.showStickyHomeIcon).onChange(async (value) => {
this.plugin.settings.showStickyHomeIcon = value;
await this.plugin.saveSettings();
this.plugin.updateStickyTabIcon();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
});
});
if (this.plugin.settings.showStickyHomeIcon) {
uiGroup.addSetting((setting) => {
setting.setName("Icon").setDesc("The icon to display in the sticky home icon").addButton((button) => {
const iconName = this.plugin.settings.stickyIconName || "home";
button.setButtonText("Change icon").setIcon(iconName).onClick(() => {
const picker = new IconPicker(
this.app,
this.plugin.settings.stickyIconName,
(icon) => {
void (async () => {
this.plugin.settings.stickyIconName = icon;
await this.plugin.saveSettings();
this.plugin.stickyTabService.update();
this.display();
})();
}
);
picker.open();
});
});
});
uiGroup.addSetting((setting) => {
setting.setName("Hide tab header").setDesc("Hide the sticky home tab header when it's open, using the sticky icon as the tab indicator").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.hideHomeTabHeader).onChange(async (value) => {
this.plugin.settings.hideHomeTabHeader = value;
await this.plugin.saveSettings();
this.plugin.stickyTabService.updateTabHeaders();
});
});
});
}
const mobileGroup = new import_obsidian4.SettingGroup(containerEl).setHeading("Mobile");
mobileGroup.addSetting((setting) => {
setting.setName("Separate mobile home base").setDesc("Use a different home base on mobile devices").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.separateMobile).onChange(async (value) => {
this.plugin.settings.separateMobile = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
});
});
if (this.plugin.settings.separateMobile) {
mobileGroup.addSetting((setting) => {
setting.setName("Mobile home base").setDesc("What to open as your home base on mobile").addDropdown((dropdown) => {
const mobileType = this.plugin.settings.mobileHomeBaseType || "File" /* File */;
let pluginDisabled = false;
for (const type of Object.values(HomeBaseType)) {
if (!this.plugin.hasRequiredPlugin(type)) {
if (type === mobileType) {
pluginDisabled = true;
dropdown.addOption(type, type);
} else {
dropdown.selectEl.createEl("option", {
text: type,
attr: { disabled: "true" }
});
continue;
}
} else {
dropdown.addOption(type, type);
}
}
dropdown.setValue(mobileType).onChange(async (value) => {
this.plugin.settings.mobileHomeBaseType = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
if (pluginDisabled) {
setting.descEl.createDiv({
text: "The required plugin has not been enabled or configured for this type.",
cls: "mod-warning"
});
}
});
});
if (!UNCHANGEABLE_TYPES.includes(this.plugin.settings.mobileHomeBaseType)) {
mobileGroup.addSetting((setting) => {
const mobileType = this.plugin.settings.mobileHomeBaseType;
let desc = "";
let placeholder = "";
if (mobileType === "File" /* File */) {
desc = "The file to open as your home base on mobile";
placeholder = "Path to home base file";
} else if (mobileType === "Workspace" /* Workspace */) {
desc = "The workspace to load as your home base on mobile";
placeholder = "Workspace name";
} else if (mobileType === "Random in folder" /* RandomFolder */) {
desc = "The folder to pick a random file from on mobile";
placeholder = "Folder path";
} else if (mobileType === "Journal" /* Journal */) {
desc = "The journal name for mobile";
placeholder = "Journal name";
}
setting.setName(mobileType === "File" /* File */ ? "Mobile file" : mobileType === "Workspace" /* Workspace */ ? "Mobile workspace" : mobileType === "Random in folder" /* RandomFolder */ ? "Mobile folder" : mobileType === "Journal" /* Journal */ ? "Mobile journal" : "Mobile value").setDesc(desc).addText((text) => {
if (mobileType === "File" /* File */) {
new FilePathSuggest(this.app, text.inputEl);
} else if (mobileType === "Workspace" /* Workspace */) {
new WorkspaceSuggest(this.app, text.inputEl);
} else if (mobileType === "Random in folder" /* RandomFolder */) {
new FolderSuggest(this.app, text.inputEl);
}
text.setPlaceholder(placeholder).setValue(this.plugin.settings.mobileHomeBaseValue || "").onChange(async (value) => {
this.plugin.settings.mobileHomeBaseValue = value;
await this.plugin.saveSettings();
});
});
});
}
}
mobileGroup.addSetting((setting) => {
setting.setName("Replace mobile new tab button").setDesc("Change the mobile new tab button to a home icon").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.replaceMobileNewTab).onChange(async (value) => {
this.plugin.settings.replaceMobileNewTab = value;
await this.plugin.saveSettings();
this.plugin.updateMobileButton();
});
});
});
if (this.plugin.settings.useDifferentFileForNewTab) {
mobileGroup.addSetting((setting) => {
setting.setName("Separate mobile new tab").setDesc("Use a different new tab file on mobile devices").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.newTabSeparateMobile).onChange(async (value) => {
this.plugin.settings.newTabSeparateMobile = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
});
});
if (this.plugin.settings.newTabSeparateMobile) {
const mobileType = this.plugin.settings.mobileNewTabType || "File" /* File */;
const mobileValue = this.plugin.settings.mobileNewTabValue || "";
mobileGroup.addSetting((setting) => {
setting.setName("Mobile new tab type").setDesc("What to open for new tabs on mobile").addDropdown((dropdown) => {
let pluginDisabled = false;
for (const type of Object.values(HomeBaseType)) {
if (!this.plugin.hasRequiredPlugin(type)) {
if (type === mobileType) {
pluginDisabled = true;
dropdown.addOption(type, type);
} else {
dropdown.selectEl.createEl("option", {
text: type,
attr: { disabled: "true" }
});
continue;
}
} else {
dropdown.addOption(type, type);
}
}
dropdown.setValue(mobileType).onChange(async (value) => {
this.plugin.settings.mobileNewTabType = value;
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
if (pluginDisabled) {
setting.descEl.createDiv({
text: "The required plugin has not been enabled or configured for this type.",
cls: "mod-warning"
});
}
});
});
if (!UNCHANGEABLE_TYPES.includes(mobileType)) {
mobileGroup.addSetting((setting) => {
let desc = "";
let placeholder = "";
if (mobileType === "File" /* File */) {
desc = "The file to open for new tabs on mobile";
placeholder = "Path to mobile new tab file";
} else if (mobileType === "Workspace" /* Workspace */) {
desc = "The workspace to load for new tabs on mobile";
placeholder = "Workspace name";
} else if (mobileType === "Random in folder" /* RandomFolder */) {
desc = "The folder to pick a random file from for new tabs on mobile";
placeholder = "Folder path";
} else if (mobileType === "Journal" /* Journal */) {
desc = "The journal name for new tabs on mobile";
placeholder = "Journal name";
}
setting.setName(mobileType === "File" /* File */ ? "Mobile new tab file" : mobileType === "Workspace" /* Workspace */ ? "Mobile new tab workspace" : mobileType === "Random in folder" /* RandomFolder */ ? "Mobile new tab folder" : mobileType === "Journal" /* Journal */ ? "Mobile new tab journal" : "Mobile new tab value").setDesc(desc).addText((text) => {
if (mobileType === "File" /* File */) {
new FilePathSuggest(this.app, text.inputEl);
} else if (mobileType === "Workspace" /* Workspace */) {
new WorkspaceSuggest(this.app, text.inputEl);
} else if (mobileType === "Random in folder" /* RandomFolder */) {
new FolderSuggest(this.app, text.inputEl);
}
text.setPlaceholder(placeholder).setValue(mobileValue || "").onChange(async (value) => {
this.plugin.settings.mobileNewTabValue = value;
await this.plugin.saveSettings();
});
});
});
}
}
}
const automationGroup = new import_obsidian4.SettingGroup(containerEl).setHeading("Automation");
automationGroup.addSetting((setting) => {
const commandId = this.plugin.settings.commandOnOpen;
const command = commandId ? getCommandById(this.app, commandId) : void 0;
const displayValue = command ? command.name : commandId;
setting.setName("Command on open").setDesc("Run an Obsidian command when opening home base").addText((text) => {
new CommandSuggest(this.app, text.inputEl);
text.setPlaceholder("Search for a command...").setValue(displayValue || "").onChange(async (value) => {
this.plugin.settings.commandOnOpen = value;
await this.plugin.saveSettings();
});
}).addExtraButton((btn) => {
btn.setIcon("x").setTooltip("Clear command").onClick(async () => {
this.plugin.settings.commandOnOpen = "";
await this.plugin.saveSettings();
const scrollContainer = containerEl.closest(".vertical-tab-content") || containerEl.closest(".settings-content") || containerEl.parentElement;
const scrollTop = (scrollContainer == null ? void 0 : scrollContainer.scrollTop) || 0;
this.display();
requestAnimationFrame(() => {
if (scrollContainer) {
scrollContainer.scrollTop = scrollTop;
}
});
});
});
});
automationGroup.addSetting((setting) => {
setting.setName("Wait for git sync").setDesc("Wait before creating periodic or journal notes to allow git sync to finish pulling existing notes. Only applies when a note doesn't already exist.").addToggle((toggle) => {
toggle.setValue(this.plugin.settings.waitForGitSync).onChange(async (value) => {
this.plugin.settings.waitForGitSync = value;
await this.plugin.saveSettings();
this.display();
});
});
});
if (this.plugin.settings.waitForGitSync) {
automationGroup.addSetting((setting) => {
setting.setName("Git sync timeout (seconds)").setDesc("How long to wait for git sync to finish before creating a new note").addText((text) => {
var _a;
text.inputEl.type = "number";
text.setPlaceholder("3").setValue(((_a = this.plugin.settings.gitSyncTimeout) == null ? void 0 : _a.toString()) || "3").onChange(async (value) => {
const numValue = parseInt(value);
if (!isNaN(numValue) && numValue >= 0) {
this.plugin.settings.gitSyncTimeout = numValue;
await this.plugin.saveSettings();
}
});
});
});
}
}
};
// src/services/home-service.ts
var import_obsidian7 = require("obsidian");
// src/utils/file-utils.ts
var import_obsidian5 = require("obsidian");
var SUPPORTED_EXTENSIONS2 = ["md", "mdx", "canvas", "base", "kanban"];
function isSupportedExtension(extension) {
return SUPPORTED_EXTENSIONS2.includes(extension);
}
function isMarkdownLike(file) {
const ext = file.extension.toLowerCase();
return ext === "md" || ext === "mdx";
}
function getFileByPath(app, path) {
const exactMatch = app.vault.getAbstractFileByPath(path);
if (exactMatch instanceof import_obsidian5.TFile) {
return exactMatch;
}
const file = app.metadataCache.getFirstLinkpathDest(path, "/");
return file;
}
function trimFileExtension(path) {
const lastDot = path.lastIndexOf(".");
if (lastDot > 0) {
const ext = path.slice(lastDot + 1).toLowerCase();
if (SUPPORTED_EXTENSIONS2.includes(ext)) {
return path.slice(0, lastDot);
}
}
return path;
}
function pathsEqual(path1, path2) {
const norm1 = trimFileExtension(path1).toLowerCase();
const norm2 = trimFileExtension(path2).toLowerCase();
return norm1 === norm2;
}
function leafHasFile(leaf, filePath) {
var _a, _b;
const state = (_b = (_a = leaf.view) == null ? void 0 : _a.getState) == null ? void 0 : _b.call(_a);
const leafFile = state == null ? void 0 : state.file;
if (!leafFile) return false;
return pathsEqual(leafFile, filePath);
}
// src/utils/homebase-resolver.ts
var import_obsidian6 = require("obsidian");
var import_obsidian_daily_notes_interface = __toESM(require_main(), 1);
function randomFile(app, root) {
let files = [];
if (root) {
const resolvedRoot = app.vault.getFolderByPath(root);
if (resolvedRoot) {
files = getFilesInFolder(resolvedRoot);
} else {
const allFiles = app.vault.getFiles();
const pattern = root.toLowerCase();
files = allFiles.filter((f) => {
const fileName = f.name.toLowerCase();
return fileName === pattern || fileName === pattern.replace(/\.md$/, "");
});
}
} else {
files = app.vault.getFiles();
}
files = files.filter((f) => ["md", "canvas", "base"].includes(f.extension));
if (files.length) {
const index = Math.floor(Math.random() * files.length);
return files[index] || null;
}
return null;
}
function getFilesInFolder(folder) {
let files = [];
for (const item of folder.children) {
if (item instanceof import_obsidian6.TFile) {
files.push(item);
} else if (item instanceof import_obsidian6.TFolder) {
files.push(...getFilesInFolder(item));
}
}
return files;
}
function trimFile(file) {
if (!file) return "";
return file.extension === "md" ? file.path.slice(0, -3) : file.path;
}
var PERIODIC_INFO = {
["Daily Note" /* DailyNote */]: {
noun: "day",
adjective: "daily",
create: import_obsidian_daily_notes_interface.createDailyNote,
get: import_obsidian_daily_notes_interface.getDailyNote,
getAll: import_obsidian_daily_notes_interface.getAllDailyNotes
},
["Weekly Note" /* WeeklyNote */]: {
noun: "week",
adjective: "weekly",
create: import_obsidian_daily_notes_interface.createWeeklyNote,
get: import_obsidian_daily_notes_interface.getWeeklyNote,
getAll: import_obsidian_daily_notes_interface.getAllWeeklyNotes
},
["Monthly Note" /* MonthlyNote */]: {
noun: "month",
adjective: "monthly",
create: import_obsidian_daily_notes_interface.createMonthlyNote,
get: import_obsidian_daily_notes_interface.getMonthlyNote,
getAll: import_obsidian_daily_notes_interface.getAllMonthlyNotes
},
["Quarterly Note" /* QuarterlyNote */]: {
noun: "quarter",
adjective: "quarterly",
create: import_obsidian_daily_notes_interface.createQuarterlyNote,
get: import_obsidian_daily_notes_interface.getQuarterlyNote,
getAll: import_obsidian_daily_notes_interface.getAllQuarterlyNotes
},
["Yearly Note" /* YearlyNote */]: {
noun: "year",
adjective: "yearly",
create: import_obsidian_daily_notes_interface.createYearlyNote,
get: import_obsidian_daily_notes_interface.getYearlyNote,
getAll: import_obsidian_daily_notes_interface.getAllYearlyNotes
},
["File" /* File */]: null,
["Random file" /* Random */]: null,
["Random in folder" /* RandomFolder */]: null,
["Workspace" /* Workspace */]: null,
["Graph view" /* Graph */]: null,
["Nothing" /* None */]: null,
["Journal" /* Journal */]: null,
["New note" /* NewNote */]: null
};
async function getPeriodicNote(kind, plugin) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
if (!window.moment) {
return null;
}
const info = PERIODIC_INFO[kind];
if (!info) {
return null;
}
const date = (0, import_obsidian6.moment)().startOf(info.noun);
const communityPlugins = ((_a = plugin.app.plugins) == null ? void 0 : _a.plugins) || {};
const periodicNotesPlugin = communityPlugins["periodic-notes"];
const isLegacy = !periodicNotesPlugin || (((_b = periodicNotesPlugin.manifest) == null ? void 0 : _b.version) || "0").startsWith("0");
let note = null;
if (isLegacy) {
let all = info.getAll();
note = info.get(date, all);
if (!note && plugin.settings.waitForGitSync) {
new import_obsidian6.Notice(`Home Base: Waiting for git sync (${plugin.settings.gitSyncTimeout}s)...`, 5e3);
await delay(plugin.settings.gitSyncTimeout * 1e3);
all = info.getAll();
note = info.get(date, all);
}
if (!note) {
note = await info.create(date);
}
} else {
(_d = (_c = periodicNotesPlugin.cache) == null ? void 0 : _c.initialize) == null ? void 0 : _d.call(_c);
note = (_f = (_e = periodicNotesPlugin.getPeriodicNote) == null ? void 0 : _e.call(periodicNotesPlugin, info.noun, date)) != null ? _f : null;
if (!note && plugin.settings.waitForGitSync) {
new import_obsidian6.Notice(`Home Base: Waiting for git sync (${plugin.settings.gitSyncTimeout}s)...`, 5e3);
await delay(plugin.settings.gitSyncTimeout * 1e3);
(_h = (_g = periodicNotesPlugin.cache) == null ? void 0 : _g.initialize) == null ? void 0 : _h.call(_g);
note = (_j = (_i = periodicNotesPlugin.getPeriodicNote) == null ? void 0 : _i.call(periodicNotesPlugin, info.noun, date)) != null ? _j : null;
}
if (!note) {
note = (_l = await ((_k = periodicNotesPlugin.createPeriodicNote) == null ? void 0 : _k.call(periodicNotesPlugin, info.noun, date))) != null ? _l : null;
}
}
return note ? trimFile(note) : null;
}
async function getJournalNote(journalName, plugin) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
const communityPlugins = ((_a = plugin.app.plugins) == null ? void 0 : _a.plugins) || {};
const journals = communityPlugins["journals"];
if (!journals) return null;
try {
const journal = (_b = journals.getJournal) == null ? void 0 : _b.call(journals, journalName);
if (!journal) return null;
const origAutoCreate = (_d = (_c = journal.config) == null ? void 0 : _c.value) == null ? void 0 : _d.autoCreate;
(_e = journals.reprocessNotes) == null ? void 0 : _e.call(journals);
if ((_f = journal.config) == null ? void 0 : _f.value) {
journal.config.value.autoCreate = true;
}
await ((_g = journal.autoCreate) == null ? void 0 : _g.call(journal));
if ((_h = journal.config) == null ? void 0 : _h.value) {
journal.config.value.autoCreate = origAutoCreate;
}
const today = (0, import_obsidian6.moment)().locale("custom-journal-locale").startOf("day");
let note = (_i = journal.get) == null ? void 0 : _i.call(journal, today);
if (!note && plugin.settings.waitForGitSync) {
new import_obsidian6.Notice(`Home Base: Waiting for git sync (${plugin.settings.gitSyncTimeout}s)...`, 5e3);
await delay(plugin.settings.gitSyncTimeout * 1e3);
(_j = journals.reprocessNotes) == null ? void 0 : _j.call(journals);
note = (_k = journal.get) == null ? void 0 : _k.call(journal, today);
}
if (!note) return null;
const path = (_l = journal.getNotePath) == null ? void 0 : _l.call(journal, note);
return path ? path.replace(/\.md$/, "") : null;
} catch (e) {
return null;
}
}
function resolvePathSync(type, value, app) {
switch (type) {
case "File" /* File */:
return value || null;
case "Daily Note" /* DailyNote */:
case "Weekly Note" /* WeeklyNote */:
case "Monthly Note" /* MonthlyNote */:
case "Quarterly Note" /* QuarterlyNote */:
case "Yearly Note" /* YearlyNote */: {
const info = PERIODIC_INFO[type];
if (info) {
const date = (0, import_obsidian6.moment)().startOf(info.noun);
const all = info.getAll();
const note = info.get(date, all);
return note ? trimFile(note) : null;
}
return null;
}
default:
return type === "Random in folder" /* RandomFolder */ || type === "New note" /* NewNote */ ? null : value || null;
}
}
async function computeHomeBasePath(type, value, plugin) {
switch (type) {
case "File" /* File */:
return value || null;
case "Random file" /* Random */: {
const file = randomFile(plugin.app);
return file ? trimFile(file) : null;
}
case "Random in folder" /* RandomFolder */: {
const file = randomFile(plugin.app, value);
return file ? trimFile(file) : null;
}
case "Daily Note" /* DailyNote */:
case "Weekly Note" /* WeeklyNote */:
case "Monthly Note" /* MonthlyNote */:
case "Quarterly Note" /* QuarterlyNote */:
case "Yearly Note" /* YearlyNote */:
return await getPeriodicNote(type, plugin);
case "Journal" /* Journal */:
return await getJournalNote(value, plugin);
case "New note" /* NewNote */: {
const fileManager = plugin.app.fileManager;
if (fileManager.createNewFile) {
const file = await fileManager.createNewFile(plugin.app.vault.getRoot(), value || "Untitled");
return file ? trimFile(file) : null;
}
return null;
}
case "Workspace" /* Workspace */:
case "Graph view" /* Graph */:
case "Nothing" /* None */:
return null;
default:
return value || null;
}
}
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// src/services/home-service.ts
var LEAF_TYPES = ["markdown", "canvas", "bases", "kanban"];
var DETACH_DELAY = 100;
var GRAPH_INIT_DELAY = 200;
var GRAPH_COMMAND_FALLBACK_DELAY = 300;
function equalsCaseless(path1, path2) {
const normalize = (p) => p.toLowerCase().replace(/\.md$/, "");
return normalize(path1) === normalize(path2);
}
var HomeBaseService = class {
constructor(plugin) {
this.ghostLeaves = /* @__PURE__ */ new WeakSet();
this.plugin = plugin;
this.app = plugin.app;
}
/**
* Open home base with a specific mode (for startup/manual opens)
*/
async openHomeBaseWithMode(mode, runCommand = true) {
var _a;
const homeBaseSettings = this.plugin.getHomeBaseSettings();
if (homeBaseSettings.type === "Workspace" /* Workspace */) {
return this.openWorkspace(homeBaseSettings.value);
}
if (homeBaseSettings.type === "Graph view" /* Graph */) {
if (this.plugin.settings.showStickyHomeIcon) {
return this.openHomeBaseInGhostTab({ runCommand });
}
return this.openGraph();
}
if (homeBaseSettings.type === "Nothing" /* None */) {
if (runCommand) {
this.runCommandOnOpen();
}
return true;
}
const resolvedPath = await computeHomeBasePath(
homeBaseSettings.type,
homeBaseSettings.value,
this.plugin
);
if (!resolvedPath) {
return false;
}
let file = this.app.metadataCache.getFirstLinkpathDest(resolvedPath, "/");
if (!file) {
file = getFileByPath(this.app, resolvedPath);
}
if (!file) {
const untrimmedPath = resolvedPath.endsWith(".md") ? resolvedPath : `${resolvedPath}.md`;
file = getFileByPath(this.app, untrimmedPath);
if (!file && homeBaseSettings.type === "File" /* File */) {
return false;
}
}
if (!file) {
return false;
}
if (mode === "replace-all") {
await this.detachAllLeaves();
} else if (mode === "replace-last") {
const activeLeaf = (_a = this.app.workspace.getActiveViewOfType(import_obsidian7.View)) == null ? void 0 : _a.leaf;
if (activeLeaf) {
const viewState = activeLeaf.getViewState();
if (viewState.pinned !== true) {
void activeLeaf.detach();
await new Promise((resolve) => setTimeout(resolve, DETACH_DELAY));
}
}
}
const existingLeaf = this.findExistingHomeBaseLeaf(file);
if (existingLeaf && mode !== "replace-all") {
const viewState = existingLeaf.getViewState();
if (viewState.pinned === true && this.plugin.settings.showStickyHomeIcon) {
} else {
this.app.workspace.setActiveLeaf(existingLeaf);
await this.configureView(existingLeaf, file);
if (runCommand) {
this.runCommandOnOpen();
}
return true;
}
}
const newLeaf = mode === "retain" ? this.app.workspace.getLeaf("tab") : this.app.workspace.getLeaf(false);
if (!newLeaf) {
return false;
}
await newLeaf.openFile(file);
this.app.workspace.setActiveLeaf(newLeaf);
await this.configureView(newLeaf, file);
if (runCommand) {
this.runCommandOnOpen();
}
return true;
}
/**
* Open workspace
*/
async openWorkspace(workspaceName) {
var _a, _b, _c;
const workspacePlugin = (_b = (_a = this.app.internalPlugins) == null ? void 0 : _a.plugins) == null ? void 0 : _b.workspaces;
if (!(workspacePlugin == null ? void 0 : workspacePlugin.enabled) || !((_c = workspacePlugin.instance) == null ? void 0 : _c.loadWorkspace)) {
return false;
}
workspacePlugin.instance.loadWorkspace(workspaceName);
await new Promise((resolve) => setTimeout(resolve, DETACH_DELAY));
return true;
}
/**
* Open graph view
*/
async openGraph() {
var _a, _b;
await ((_b = (_a = this.app.commands) == null ? void 0 : _a.executeCommandById) == null ? void 0 : _b.call(_a, "graph:open"));
return true;
}
// Removed deprecated openGraphInGhostTab - now integrated into openHomeBaseInGhostTab
/**
* Open the home base file
* @param options Options for opening
*/
async openHomeBase(options = {}) {
const { runCommand = true } = options;
const mode = this.plugin.settings.manualOpenMode;
return this.openHomeBaseWithMode(mode, runCommand);
}
/**
* Open home base in an empty leaf (for new tab replacement)
*/
async openInLeaf(leaf) {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
return this.openInLeafWithSettings(leaf, homeBaseSettings);
}
/**
* Open a file in an empty leaf with custom settings
* @param leaf The leaf to open the file in
* @param settings Settings object with type and value
* @param isNewTab Whether this is for new tab replacement (skips pinning/ghost tab logic)
*/
async openInLeafWithSettings(leaf, settings, isNewTab = false) {
if (settings.type === "Workspace" /* Workspace */) {
await this.openWorkspace(settings.value);
return true;
}
if (settings.type === "Graph view" /* Graph */) {
await this.openGraph();
return true;
}
if (settings.type === "Nothing" /* None */) {
this.runCommandOnOpen();
return true;
}
const resolvedPath = await computeHomeBasePath(
settings.type,
settings.value,
this.plugin
);
if (!resolvedPath) {
console.warn("[Home Base] Could not resolve path for new tab:", settings.type, settings.value);
return false;
}
let file = this.app.metadataCache.getFirstLinkpathDest(resolvedPath, "/");
if (!file) {
file = getFileByPath(this.app, resolvedPath);
}
if (!file && !resolvedPath.endsWith(".md") && !resolvedPath.endsWith(".canvas") && !resolvedPath.endsWith(".base")) {
const untrimmedPath = `${resolvedPath}.md`;
file = getFileByPath(this.app, untrimmedPath);
}
if (!file) {
console.warn("[Home Base] File not found for new tab:", resolvedPath);
return false;
}
if (isNewTab) {
console.debug("[Home Base] openInLeafWithSettings: isNewTab=true, bypassing ghost tab logic", {
file: file.path,
settings
});
await leaf.openFile(file);
await this.configureView(leaf, file);
this.runCommandOnOpen();
return true;
}
const isTrulyEmpty = !leaf.view || leaf.view.getViewType() === "empty";
if (this.plugin.settings.showStickyHomeIcon && isTrulyEmpty) {
const isRandom = settings.type === "Random file" /* Random */ || settings.type === "Random in folder" /* RandomFolder */ || settings.type === "Daily Note" /* DailyNote */ || settings.type === "Weekly Note" /* WeeklyNote */ || settings.type === "Monthly Note" /* MonthlyNote */ || settings.type === "Yearly Note" /* YearlyNote */;
const ghostTab = this.findGhostTab(file, isRandom);
if (ghostTab) {
void leaf.detach();
this.app.workspace.setActiveLeaf(ghostTab);
await this.configureView(ghostTab, file);
this.runCommandOnOpen();
return true;
}
this.ghostLeaves.add(leaf);
if (!isRandom) {
leaf.setPinned(true);
}
}
await leaf.openFile(file);
await this.configureView(leaf, file);
this.runCommandOnOpen();
return true;
}
/**
* Configure the view mode for a leaf
*/
async configureView(leaf, file) {
const settings = this.plugin.settings;
const view = leaf.view;
if (!isMarkdownLike(file) || !(view instanceof import_obsidian7.MarkdownView)) {
return;
}
const state = view.getState();
if (settings.revertView) {
this.lastView = new WeakRef(view);
}
if (settings.autoScroll) {
const count = view.editor.lineCount();
if (state.mode === "preview") {
view.previewMode.applyScroll(count - 4);
} else {
view.editor.setCursor(count);
view.editor.focus();
}
}
if (settings.openViewMode !== "default") {
switch (settings.openViewMode) {
case "preview":
state.mode = "preview";
break;
case "source":
state.mode = "source";
state.source = true;
break;
case "live":
state.mode = "source";
state.source = false;
break;
}
await leaf.setViewState({
type: "markdown",
state
});
}
}
/**
* Revert view to default when navigating away from home base
*/
async revertView() {
const settings = this.plugin.settings;
if (!settings.revertView || !this.lastView || settings.openViewMode === "default") {
return;
}
const view = this.lastView.deref();
if (!view) {
this.lastView = void 0;
return;
}
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const resolvedPath = await computeHomeBasePath(
homeBaseSettings.type,
homeBaseSettings.value,
this.plugin
);
if (!resolvedPath) {
this.lastView = void 0;
return;
}
const currentFile = view.file;
if (currentFile && equalsCaseless(trimFile(currentFile), resolvedPath)) {
return;
}
const state = view.getState();
const config = this.app.vault.config;
const mode = (config == null ? void 0 : config.defaultViewMode) || "source";
const source = (config == null ? void 0 : config.livePreview) !== void 0 ? !config.livePreview : false;
if (view.leaf.getViewState().type === "markdown" && (mode !== state.mode || source !== state.source)) {
state.mode = mode;
state.source = source;
await view.leaf.setViewState({ type: "markdown", state, active: true });
}
this.lastView = void 0;
}
/**
* Run the configured command after opening
*/
runCommandOnOpen() {
const commandId = this.plugin.settings.commandOnOpen;
if (commandId) {
setTimeout(() => {
executeCommand(this.app, commandId);
}, 100);
}
}
/**
* Find an existing leaf that has the home base file open
*/
findExistingHomeBaseLeaf(file) {
if (!file) return null;
const homeBasePath = file.path;
const leaves = LEAF_TYPES.flatMap(
(type) => this.app.workspace.getLeavesOfType(type)
);
for (const leaf of leaves) {
if (leafHasFile(leaf, homeBasePath)) {
return leaf;
}
}
return null;
}
/**
* Check if a leaf is a ghost tab
* Ghost tab is identified by being in our internal ghostLeaves set
* Only tabs specifically created for the sticky icon are ghost tabs
*/
isGhostLeaf(leaf) {
return this.ghostLeaves.has(leaf);
}
/**
* Find the ghost tab (the one opened via sticky icon)
* Ghost tab is identified by being in our internal ghostLeaves set
* Only returns existing ghost tabs, doesn't create new ones
*/
findGhostTab(file, isRandom = false) {
if (!file) return null;
const homeBasePath = file.path;
const leaves = [];
this.app.workspace.iterateAllLeaves((leaf) => {
var _a;
const viewType = (_a = leaf.view) == null ? void 0 : _a.getViewType();
if (viewType && LEAF_TYPES.includes(viewType)) {
leaves.push(leaf);
}
});
for (const leaf of leaves) {
if (this.ghostLeaves.has(leaf) && leafHasFile(leaf, homeBasePath)) {
return leaf;
}
}
return null;
}
/**
* Open home base in ghost tab (for sticky icon)
* Ghost tab is pinned and hidden (if setting enabled)
* Only one ghost tab should exist at a time
* Works for file-based types and Graph view
* Note: Random types don't pin (since file changes each time)
* Note: Workspace and None don't work (workspace changes layout, None doesn't open anything)
*/
async openHomeBaseInGhostTab(options = {}) {
const { runCommand = true } = options;
const homeBaseSettings = this.plugin.getHomeBaseSettings();
if (homeBaseSettings.type === "Workspace" /* Workspace */ || homeBaseSettings.type === "Nothing" /* None */) {
return this.openHomeBaseWithMode("retain", runCommand);
}
if (homeBaseSettings.type === "Graph view" /* Graph */) {
let ghostTab2 = this.findGraphGhostTab();
if (ghostTab2) {
this.ghostLeaves.add(ghostTab2);
ghostTab2.setPinned(true);
this.app.workspace.setActiveLeaf(ghostTab2, { focus: !this.isSettingsModalOpen() });
if (runCommand) this.runCommandOnOpen();
return true;
}
const newLeaf = this.app.workspace.getLeaf("tab");
if (newLeaf) {
await newLeaf.setViewState({ type: "graph", state: {} });
await new Promise((resolve) => setTimeout(resolve, GRAPH_INIT_DELAY));
this.ghostLeaves.add(newLeaf);
newLeaf.setPinned(true);
this.app.workspace.setActiveLeaf(newLeaf, { focus: !this.isSettingsModalOpen() });
if (runCommand) this.runCommandOnOpen();
return true;
}
await this.openGraph();
await new Promise((resolve) => setTimeout(resolve, GRAPH_COMMAND_FALLBACK_DELAY));
ghostTab2 = this.findGraphGhostTab();
if (ghostTab2) {
this.ghostLeaves.add(ghostTab2);
ghostTab2.setPinned(true);
this.app.workspace.setActiveLeaf(ghostTab2, { focus: !this.isSettingsModalOpen() });
if (runCommand) this.runCommandOnOpen();
return true;
}
return false;
}
const isRandom = homeBaseSettings.type === "Random file" /* Random */ || homeBaseSettings.type === "Random in folder" /* RandomFolder */ || homeBaseSettings.type === "Daily Note" /* DailyNote */ || homeBaseSettings.type === "Weekly Note" /* WeeklyNote */ || homeBaseSettings.type === "Monthly Note" /* MonthlyNote */ || homeBaseSettings.type === "Yearly Note" /* YearlyNote */;
if (this.isSettingsModalOpen()) {
return false;
}
const resolvedPath = await computeHomeBasePath(
homeBaseSettings.type,
homeBaseSettings.value,
this.plugin
);
if (!resolvedPath) {
return false;
}
let file = this.app.metadataCache.getFirstLinkpathDest(resolvedPath, "/");
if (!file) {
file = getFileByPath(this.app, resolvedPath);
}
if (!file && !resolvedPath.endsWith(".md") && !resolvedPath.endsWith(".canvas") && !resolvedPath.endsWith(".base")) {
const untrimmedPath = `${resolvedPath}.md`;
file = getFileByPath(this.app, untrimmedPath);
}
if (!file) {
return false;
}
const ghostTab = this.findGhostTab(file, isRandom);
console.debug("[Home Base] openHomeBaseInGhostTab:", {
file: file.path,
ghostTabFound: !!ghostTab,
isRandom,
zenMode: document.body.classList.contains("zenmode-active")
});
if (ghostTab) {
const shouldFocus2 = !this.isSettingsModalOpen();
this.app.workspace.setActiveLeaf(ghostTab, { focus: shouldFocus2 });
await this.configureView(ghostTab, file);
if (runCommand) {
this.runCommandOnOpen();
}
return true;
}
const newGhostTab = this.app.workspace.getLeaf("tab");
this.ghostLeaves.add(newGhostTab);
await newGhostTab.openFile(file);
if (!isRandom) {
newGhostTab.setPinned(true);
}
setTimeout(() => {
this.plugin.stickyTabService.updateTabHeaders();
}, 50);
const shouldFocus = !this.isSettingsModalOpen();
this.app.workspace.setActiveLeaf(newGhostTab, { focus: shouldFocus });
await this.configureView(newGhostTab, file);
if (runCommand) {
this.runCommandOnOpen();
}
return true;
}
/**
* Find an empty leaf
*/
findEmptyLeaf() {
const leaves = this.app.workspace.getLeavesOfType("empty");
return leaves[0] || null;
}
/**
* Get the home base file
*/
getHomeBaseFile() {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const path = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.app);
if (!path) return null;
return getFileByPath(this.app, path);
}
/**
* Fast detach all leaves using changeLayout (like homepage plugin)
* This is much faster than iterating and detaching leaves individually
*/
async detachAllLeaves() {
var _a, _b;
const layout = this.app.workspace.getLayout();
layout.main = {
"id": "5324373015726ba8",
"type": "split",
"children": [{
"id": "4509724f8bf84da7",
"type": "tabs",
"children": [{
"id": "e7a7b303c61786dc",
"type": "leaf",
"state": { "type": "empty", "state": {}, "icon": "lucide-file", "title": "New tab" }
}]
}],
"direction": "vertical"
};
layout.active = "e7a7b303c61786dc";
await this.app.workspace.changeLayout(layout);
if (import_obsidian7.Platform.isMobile) {
(_b = (_a = this.app.workspace.rightSplit) == null ? void 0 : _a.updateInfo) == null ? void 0 : _b.call(_a);
}
}
/**
* Close all leaves in the main workspace except the specified one
* Simplified approach: iterate all leaves and close those in main workspace
*/
async closeAllLeavesExcept(exceptLeaf) {
const leavesToClose = [];
this.app.workspace.iterateAllLeaves((leaf) => {
if (leaf === exceptLeaf) {
return;
}
const view = leaf.view;
let container = null;
if (view) {
const viewAny = view;
container = viewAny.containerEl || null;
}
if (!container) {
const leafAny = leaf;
container = leafAny.containerEl || null;
}
if (container) {
const rootWorkspace = container.closest(".workspace-split.mod-vertical.mod-root");
const leftSidebar = container.closest(".workspace-split.mod-left-split");
const rightSidebar = container.closest(".workspace-split.mod-right-split");
if (rootWorkspace && !leftSidebar && !rightSidebar) {
leavesToClose.push(leaf);
}
} else {
if (exceptLeaf === null) {
try {
const viewState = leaf.getViewState();
if (viewState) {
leavesToClose.push(leaf);
}
} catch (e) {
}
}
}
});
for (const leaf of leavesToClose) {
void leaf.detach();
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
/**
* Find an existing graph leaf that should be treated as a ghost tab
*/
findGraphGhostTab() {
let graphLeaves = [];
this.app.workspace.iterateAllLeaves((leaf) => {
var _a;
if (((_a = leaf.view) == null ? void 0 : _a.getViewType()) === "graph") {
graphLeaves.push(leaf);
}
});
const pinned = graphLeaves.find((l) => l.getViewState().pinned === true);
if (pinned) return pinned;
if (graphLeaves.length === 1 && graphLeaves[0]) return graphLeaves[0];
return null;
}
/**
* Check if the focused tab is the home base
*/
isFocusedOnHomeBase() {
var _a, _b;
const activeLeaf = (_a = this.app.workspace.getActiveViewOfType(import_obsidian7.View)) == null ? void 0 : _a.leaf;
if (!activeLeaf) return false;
const homeBaseSettings = this.plugin.getHomeBaseSettings();
if (homeBaseSettings.type === "Graph view" /* Graph */) {
return ((_b = activeLeaf.view) == null ? void 0 : _b.getViewType()) === "graph";
}
const homeBaseFile = this.getHomeBaseFile();
if (!homeBaseFile) return false;
return leafHasFile(activeLeaf, homeBaseFile.path);
}
/**
* Check if home base file exists
*/
homeBaseExists() {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const path = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.app);
if (!path) return false;
return getFileByPath(this.app, path) !== null;
}
/**
* Get the native Obsidian open behavior setting (from app.json)
* @returns The native setting value or undefined if not supported/found
*/
getNativeOpenBehavior() {
const config = this.app.vault.config;
if (!config) return void 0;
return config.openBehavior;
}
/**
* Check if the settings modal is currently open
*/
isSettingsModalOpen() {
const settingsModal = document.querySelector(".modal-container.mod-settings") || document.querySelector(".modal.mod-settings") || document.querySelector(".vertical-tab-content");
if (!settingsModal) {
const allModals = document.querySelectorAll(".modal-container");
for (const modal of Array.from(allModals)) {
if (modal.querySelector(".vertical-tab-content") || modal.querySelector(".settings-content") || modal.classList.contains("mod-settings")) {
return true;
}
}
}
return settingsModal !== null;
}
/**
* Set the active file as home base
*/
async setActiveFileAsHomeBase() {
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) return false;
if (!isSupportedExtension(activeFile.extension.toLowerCase())) {
return false;
}
if (this.plugin.settings.separateMobile && import_obsidian7.Platform.isMobile) {
this.plugin.settings.mobileHomeBaseType = "File" /* File */;
this.plugin.settings.mobileHomeBaseValue = activeFile.path;
} else {
this.plugin.settings.homeBaseType = "File" /* File */;
this.plugin.settings.homeBaseValue = activeFile.path;
}
await this.plugin.saveSettings();
return true;
}
/**
* Check if active file can be set as home base
*/
canSetActiveFileAsHomeBase() {
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) return false;
return isSupportedExtension(activeFile.extension.toLowerCase());
}
/**
* Restore ghost leaves from previous session
* This identifies pinned home base tabs that should be treated as ghost leaves
*/
restoreGhostLeaves() {
if (!this.plugin.settings.showStickyHomeIcon) {
return;
}
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const homeBasePath = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.app);
if (!homeBasePath && homeBaseSettings.type !== "Graph view" /* Graph */) return;
const isRandom = homeBaseSettings.type === "Random file" /* Random */ || homeBaseSettings.type === "Random in folder" /* RandomFolder */ || homeBaseSettings.type === "Daily Note" /* DailyNote */ || homeBaseSettings.type === "Weekly Note" /* WeeklyNote */ || homeBaseSettings.type === "Monthly Note" /* MonthlyNote */ || homeBaseSettings.type === "Yearly Note" /* YearlyNote */;
if (isRandom) return;
if (homeBaseSettings.type === "Graph view" /* Graph */) {
const ghostTab = this.findGraphGhostTab();
if (ghostTab && ghostTab.getViewState().pinned === true) {
this.ghostLeaves.add(ghostTab);
}
return;
}
const leaves = [];
this.app.workspace.iterateAllLeaves((leaf) => {
var _a;
const viewType = (_a = leaf.view) == null ? void 0 : _a.getViewType();
if (viewType && LEAF_TYPES.includes(viewType)) {
leaves.push(leaf);
}
});
for (const leaf of leaves) {
if (homeBasePath && leafHasFile(leaf, homeBasePath)) {
const viewState = leaf.getViewState();
if (viewState.pinned === true && !this.ghostLeaves.has(leaf)) {
this.ghostLeaves.add(leaf);
break;
}
}
}
}
};
// src/services/new-tab-service.ts
var import_obsidian8 = require("obsidian");
var STARTUP_RESTORE_DELAY = 500;
var DETACH_SETTLE_DELAY = 200;
var PLUGIN_RACE_DELAY = 50;
var NewTabService = class {
constructor(plugin) {
this.existingLeaves = /* @__PURE__ */ new WeakSet();
this.isStartup = true;
this.startupCompleted = false;
this.plugin = plugin;
this.app = plugin.app;
}
/**
* Track all existing leaves (for new tab detection)
* This must be called even when startup is handled elsewhere
*/
trackExistingLeaves() {
this.app.workspace.iterateAllLeaves((leaf) => {
this.existingLeaves.add(leaf);
});
this.startupCompleted = true;
this.isStartup = false;
}
/**
* Initialize the service - called when layout is ready
*/
initialize() {
this.trackExistingLeaves();
void this.handleStartup();
}
/**
* Handle app startup - open home base if needed
* Only called on actual app startup, not plugin reloads
*/
async handleStartup() {
const settings = this.plugin.settings;
const homeBaseSettings = this.plugin.getHomeBaseSettings();
if (!settings.openOnStartup || !homeBaseSettings.value && homeBaseSettings.type === "File" /* File */) {
this.startupCompleted = true;
this.isStartup = false;
return;
}
if (await this.hasUrlParams()) {
this.startupCompleted = true;
this.isStartup = false;
return;
}
await new Promise((resolve) => setTimeout(resolve, STARTUP_RESTORE_DELAY));
if (settings.openMode === "replace-all") {
let exceptLeaf = null;
if (!settings.hideReleaseNotes) {
const allLeaves = this.app.workspace.getLeavesOfType("markdown");
for (const leaf of allLeaves) {
const view = leaf.view;
const markdownView = view;
if (markdownView.file) {
const file = markdownView.file;
const configDir = this.app.vault.configDir;
if (file.path.includes("release") || file.path.includes(configDir)) {
const container = markdownView.containerEl;
if (container && container.querySelector(".release-notes")) {
exceptLeaf = leaf;
break;
}
}
}
}
}
await this.plugin.homeService.closeAllLeavesExcept(exceptLeaf);
await new Promise((resolve) => setTimeout(resolve, DETACH_SETTLE_DELAY));
}
if (settings.showStickyHomeIcon) {
await this.plugin.homeService.openHomeBaseInGhostTab({
runCommand: true
});
} else {
await this.plugin.homeService.openHomeBase({
replaceActiveLeaf: false,
runCommand: true
});
}
this.startupCompleted = true;
this.isStartup = false;
}
/**
* Check if the settings modal is currently open
*/
isSettingsModalOpen() {
const settingsModal = document.querySelector(".modal-container.mod-settings") || document.querySelector(".modal.mod-settings") || document.querySelector(".vertical-tab-content");
if (!settingsModal) {
const allModals = document.querySelectorAll(".modal-container");
for (const modal of Array.from(allModals)) {
if (modal.querySelector(".vertical-tab-content") || modal.querySelector(".settings-content") || modal.classList.contains("mod-settings")) {
return true;
}
}
}
return settingsModal !== null;
}
/**
* Check for URL parameters that indicate Obsidian was opened via a link
* Based on obsidian-homepage implementation
*/
async hasUrlParams() {
var _a;
const windowAny = window;
const capacitor = windowAny.Capacitor;
if ((_a = capacitor == null ? void 0 : capacitor.Plugins) == null ? void 0 : _a.App) {
try {
const launchUrl = await capacitor.Plugins.App.getLaunchUrl();
if (launchUrl == null ? void 0 : launchUrl.url) {
const url = new URL(launchUrl.url);
const params = Array.from(url.searchParams.keys());
const action = url.hostname;
if (["open", "advanced-uri"].includes(action) && ["file", "filepath", "workspace"].some((e) => params.includes(e))) {
return true;
}
}
} catch (e) {
}
}
const obsAct = windowAny.OBS_ACT;
if (obsAct) {
const params = Object.keys(obsAct);
const action = obsAct.action;
if (action && ["open", "advanced-uri"].includes(action) && ["file", "filepath", "workspace"].some((e) => params.includes(e))) {
return true;
}
}
return false;
}
/**
* Handle layout change event - check for new empty tabs
* Based on new-tab-default-page implementation
*/
handleLayoutChange() {
if (this.isStartup || !this.startupCompleted) {
return;
}
this.app.workspace.iterateAllLeaves((leaf) => {
if (this.existingLeaves.has(leaf)) {
return;
}
this.existingLeaves.add(leaf);
if (!this.isEmptyTab(leaf)) {
return;
}
const isOnlyTab = this.isOnlyTab(leaf);
if (isOnlyTab && this.plugin.settings.openWhenAllTabsClosed === true) {
void this.replaceEmptyTab(leaf, true);
return;
}
if (this.plugin.settings.replaceNewTab !== true) {
return;
}
if (this.plugin.settings.replaceNewTab !== true) {
return;
}
if (this.plugin.settings.newTabMode === "only-when-empty") {
if (!isOnlyTab) {
return;
}
}
void this.replaceEmptyTab(leaf, false);
});
}
/**
* Check if a leaf is an empty tab
* IMPORTANT: Only returns true if the leaf is truly empty (no file opened)
* If a file is already opened in the leaf, it's not empty and should NOT be replaced
*/
isEmptyTab(leaf) {
if (!leaf.view) return true;
if (leaf.view.getViewType() !== "empty") {
return false;
}
const viewState = leaf.getViewState();
if (viewState && viewState.file) {
return false;
}
return true;
}
/**
* Check if this is the only tab in the main workspace
* Only counts root leaves (main workspace tabs), not sidebar tabs
* Based on obsidian-disable-tabs pattern using iterateRootLeaves
*/
isOnlyTab(leaf) {
let tabCount = 0;
this.app.workspace.iterateRootLeaves((l) => {
tabCount++;
});
const result = tabCount === 1;
console.debug("[Home Base] isOnlyTab:", {
leaf,
tabCount,
isOnlyTab: result
});
return result;
}
/**
* Replace an empty tab with the home base or new tab file
* @param leaf The leaf to replace
* @param isAllTabsClosed Whether this is triggered by "all tabs closed" (true) or "new tab replacement" (false)
*/
async replaceEmptyTab(leaf, isAllTabsClosed = false) {
if (!isAllTabsClosed) {
if (this.plugin.settings.replaceNewTab !== true) {
console.debug("[Home Base] replaceEmptyTab: replaceNewTab is not true, aborting");
return;
}
}
await new Promise((resolve) => setTimeout(resolve, PLUGIN_RACE_DELAY));
if (!isAllTabsClosed) {
if (this.plugin.settings.replaceNewTab !== true) {
console.debug("[Home Base] replaceEmptyTab: replaceNewTab changed during delay, aborting");
return;
}
} else {
if (this.plugin.settings.openWhenAllTabsClosed !== true) {
console.debug("[Home Base] replaceEmptyTab: openWhenAllTabsClosed changed during delay, aborting");
return;
}
}
if (!this.isEmptyTab(leaf)) {
console.debug("[Home Base] replaceEmptyTab: Tab is no longer empty, skipping");
return;
}
const settings = isAllTabsClosed ? this.plugin.getHomeBaseSettings() : this.plugin.getNewTabSettings();
console.debug("[Home Base] replaceEmptyTab:", {
leaf,
isAllTabsClosed,
settings,
replaceNewTab: this.plugin.settings.replaceNewTab,
newTabMode: this.plugin.settings.newTabMode,
useDifferentFileForNewTab: this.plugin.settings.useDifferentFileForNewTab
});
const success = await this.plugin.homeService.openInLeafWithSettings(leaf, settings, true);
if (!success) {
console.warn("[Home Base] Failed to open file:", settings);
} else {
console.debug("[Home Base] replaceEmptyTab: Successfully opened file");
}
}
/**
* Force check for empty workspace and open home base
*/
async openIfEmpty() {
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian8.View);
const activeLeaf = activeView == null ? void 0 : activeView.leaf;
if (!activeLeaf) return;
if (this.isEmptyTab(activeLeaf) && this.isOnlyTab(activeLeaf)) {
await this.plugin.homeService.openInLeaf(activeLeaf);
}
}
};
// src/services/sticky-tab-service.ts
var import_obsidian9 = require("obsidian");
var TAB_HEADER_OPEN_DELAY = 150;
var ICON_PLACEMENT_CHECK_INTERVAL = 100;
var WINDOW_OPEN_CONTAINER_DELAY = 100;
var STICKY_ICON_CLASS = "home-base-sticky-icon";
var STICKY_ICON_ACTIVE_CLASS = "home-base-sticky-icon-active";
var StickyTabService = class {
constructor(plugin) {
this.stickyIconEl = null;
this.layoutChangeHandler = null;
this.tabHeaderUpdateTimeout = null;
this.sidebarObserver = null;
this.tabHeaderObserver = null;
this.plugin = plugin;
}
/**
* Update the sticky tab icon based on settings
*/
update() {
if (import_obsidian9.Platform.isMobile) {
this.remove();
this.updateTabHeaders();
this.updateWorkspaceClass(false);
return;
}
if (this.plugin.settings.showStickyHomeIcon) {
if (this.stickyIconEl) {
const iconName = this.plugin.settings.stickyIconName || "home";
(0, import_obsidian9.setIcon)(this.stickyIconEl, iconName);
} else {
this.create();
}
this.updateWorkspaceClass(true);
} else {
this.remove();
this.updateTabHeaders();
this.updateWorkspaceClass(false);
}
}
/**
* Add/remove CSS class on all workspaces to conditionally apply styles
*/
updateWorkspaceClass(enabled) {
const applyToDocument = (doc) => {
const mainWorkspace = doc.querySelector(".workspace-split.mod-vertical.mod-root");
if (!mainWorkspace) return;
if (enabled) {
mainWorkspace.classList.add("home-base-sticky-icon-enabled");
} else {
mainWorkspace.classList.remove("home-base-sticky-icon-enabled");
}
};
applyToDocument(document);
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
var _a, _b;
const doc = (_b = (_a = leaf.view) == null ? void 0 : _a.containerEl) == null ? void 0 : _b.ownerDocument;
if (doc && doc !== document) {
applyToDocument(doc);
}
});
}
/**
* Create the sticky home icon
*/
create() {
this.remove();
this.stickyIconEl = document.createElement("div");
this.stickyIconEl.className = `${STICKY_ICON_CLASS} clickable-icon`;
this.stickyIconEl.setAttribute("aria-label", "Open home base");
this.stickyIconEl.setAttribute("data-tooltip-position", "bottom");
const iconName = this.plugin.settings.stickyIconName || "home";
(0, import_obsidian9.setIcon)(this.stickyIconEl, iconName);
this.stickyIconEl.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
void this.plugin.homeService.openHomeBaseInGhostTab({
runCommand: true
}).then(() => {
setTimeout(() => {
this.updateTabHeaders();
}, TAB_HEADER_OPEN_DELAY);
});
});
this.stickyIconEl.addEventListener("contextmenu", (e) => {
e.preventDefault();
e.stopPropagation();
const menu = new import_obsidian9.Menu();
menu.addItem((item) => {
item.setTitle("Close home base").setIcon("x").onClick(() => {
void this.closeHomeBase(true);
});
});
menu.addItem((item) => {
item.setTitle("Change icon").setIcon("lucide-image-plus").onClick(() => {
const picker = new IconPicker(
this.plugin.app,
this.plugin.settings.stickyIconName,
(icon) => {
void (async () => {
this.plugin.settings.stickyIconName = icon;
await this.plugin.saveSettings();
if (this.stickyIconEl) {
(0, import_obsidian9.setIcon)(this.stickyIconEl, icon || "home");
}
})();
}
);
picker.open();
});
});
menu.showAtMouseEvent(e);
});
const ensureIconInPlace = () => {
if (!this.stickyIconEl) return;
const mainWorkspace2 = document.querySelector(".workspace-split.mod-vertical.mod-root");
if (!mainWorkspace2) return;
const tabHeaderContainerInner = mainWorkspace2.querySelector(".workspace-tab-header-container-inner");
if (!tabHeaderContainerInner) return;
const allIcons = tabHeaderContainerInner.querySelectorAll(`.${STICKY_ICON_CLASS}`);
allIcons.forEach((icon) => {
if (icon !== this.stickyIconEl) {
icon.remove();
}
});
if (tabHeaderContainerInner.contains(this.stickyIconEl)) {
return;
}
tabHeaderContainerInner.insertBefore(this.stickyIconEl, tabHeaderContainerInner.firstChild);
this.updateActiveState();
this.updateTabHeaders();
this.updateWorkspaceClass(true);
this.updateIconPositionForSidebar();
this.watchSidebarState();
};
ensureIconInPlace();
const checkInterval = setInterval(() => {
if (!this.stickyIconEl || !this.plugin.settings.showStickyHomeIcon) {
clearInterval(checkInterval);
return;
}
ensureIconInPlace();
}, ICON_PLACEMENT_CHECK_INTERVAL);
this.stickyIconEl._checkInterval = checkInterval;
if (!this.layoutChangeHandler) {
this.layoutChangeHandler = () => {
if (this.stickyIconEl && this.plugin.settings.showStickyHomeIcon) {
ensureIconInPlace();
this.updateTabHeaders();
}
};
this.plugin.registerEvent(
this.plugin.app.workspace.on("layout-change", this.layoutChangeHandler)
);
}
const mainWorkspace = document.querySelector(".workspace-split.mod-vertical.mod-root");
if (mainWorkspace) {
const containerObserver = new MutationObserver(() => {
if (!this.stickyIconEl || !this.plugin.settings.showStickyHomeIcon) return;
const tabHeaderContainerInner = mainWorkspace.querySelector(".workspace-tab-header-container-inner");
if (tabHeaderContainerInner) {
const allIcons = tabHeaderContainerInner.querySelectorAll(`.${STICKY_ICON_CLASS}`);
allIcons.forEach((icon) => {
if (icon !== this.stickyIconEl) {
icon.remove();
}
});
if (!tabHeaderContainerInner.contains(this.stickyIconEl)) {
tabHeaderContainerInner.insertBefore(this.stickyIconEl, tabHeaderContainerInner.firstChild);
this.updateActiveState();
}
}
});
containerObserver.observe(mainWorkspace, {
childList: true,
subtree: true
// Watch subtree to catch tab container recreation
});
this.stickyIconEl._containerObserver = containerObserver;
}
this.setupTabHeaderObserver();
}
/**
* Set up MutationObserver to watch for tab header changes and remove ghost tabs immediately
* This prevents the flash when tabs are opened/closed
*/
setupTabHeaderObserver() {
if (this.tabHeaderObserver) {
return;
}
this.tabHeaderObserver = new MutationObserver((mutations) => {
if (!this.plugin.settings.showStickyHomeIcon || !this.plugin.settings.hideHomeTabHeader) {
return;
}
let hasNewHeaders = false;
for (const mutation of mutations) {
if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
for (const node of Array.from(mutation.addedNodes)) {
if (node instanceof HTMLElement && node.classList.contains("workspace-tab-header")) {
hasNewHeaders = true;
break;
}
}
}
if (hasNewHeaders) break;
}
if (hasNewHeaders) {
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
const view = leaf.view;
let container = null;
if (view) {
const viewAny = view;
container = viewAny.containerEl || null;
}
if (!container) {
const leafAny = leaf;
container = leafAny.containerEl || null;
}
if (container) {
const rootWorkspace = container.closest(".workspace-split.mod-vertical.mod-root");
const leftSidebar = container.closest(".workspace-split.mod-left-split");
const rightSidebar = container.closest(".workspace-split.mod-right-split");
if (rootWorkspace && !leftSidebar && !rightSidebar) {
if (this.plugin.homeService.isGhostLeaf(leaf)) {
const tabHeader = this.getTabHeaderForLeaf(leaf);
if (tabHeader && tabHeader.parentElement) {
const parent = tabHeader.parentElement;
if (parent && parent.classList.contains("workspace-tab-header-container-inner")) {
const tabHeaderExtended = tabHeader;
if (parent.contains(tabHeader)) {
tabHeaderExtended._homeBaseParent = parent;
tabHeaderExtended._homeBaseNextSibling = tabHeader.nextSibling;
tabHeader.remove();
}
}
}
}
}
}
});
}
});
const observeContainer = (container) => {
var _a;
(_a = this.tabHeaderObserver) == null ? void 0 : _a.observe(container, {
childList: true,
subtree: false
});
};
const observeAllWindows = () => {
const containers = document.querySelectorAll(".workspace-tab-header-container-inner");
containers.forEach(observeContainer);
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
var _a, _b;
const doc = (_b = (_a = leaf.view) == null ? void 0 : _a.containerEl) == null ? void 0 : _b.ownerDocument;
if (doc && doc !== document) {
const windowContainers = doc.querySelectorAll(".workspace-tab-header-container-inner");
windowContainers.forEach(observeContainer);
}
});
};
observeAllWindows();
const setupWorkspaceObserver = (win) => {
const doc = win.document;
const workspaceObserver = new MutationObserver(() => {
const newContainers = doc.querySelectorAll(".workspace-tab-header-container-inner");
newContainers.forEach(observeContainer);
});
const mainWorkspace = doc.querySelector(".workspace-split.mod-vertical.mod-root");
if (mainWorkspace) {
workspaceObserver.observe(mainWorkspace, {
childList: true,
subtree: true
});
}
};
setupWorkspaceObserver(window);
this.plugin.registerEvent(
this.plugin.app.workspace.on("window-open", (win) => {
const actualWindow = win.win;
if (actualWindow instanceof Window) {
setupWorkspaceObserver(actualWindow);
setTimeout(observeAllWindows, WINDOW_OPEN_CONTAINER_DELAY);
}
})
);
}
/**
* Remove the sticky home icon
*/
remove() {
if (this.tabHeaderUpdateTimeout) {
clearTimeout(this.tabHeaderUpdateTimeout);
this.tabHeaderUpdateTimeout = null;
}
if (this.stickyIconEl && this.stickyIconEl._checkInterval) {
clearInterval(this.stickyIconEl._checkInterval);
}
if (this.stickyIconEl && this.stickyIconEl._containerObserver) {
this.stickyIconEl._containerObserver.disconnect();
}
if (this.sidebarObserver) {
this.sidebarObserver.disconnect();
this.sidebarObserver = null;
}
if (this.tabHeaderObserver) {
this.tabHeaderObserver.disconnect();
this.tabHeaderObserver = null;
}
this.updateWorkspaceClass(false);
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
const view = leaf.view;
let container = null;
if (view) {
const viewAny = view;
container = viewAny.containerEl || null;
}
if (!container) {
const leafAny = leaf;
container = leafAny.containerEl || null;
}
if (container) {
const rootWorkspace = container.closest(".workspace-split.mod-vertical.mod-root");
const leftSidebar = container.closest(".workspace-split.mod-left-split");
const rightSidebar = container.closest(".workspace-split.mod-right-split");
if (rootWorkspace && !leftSidebar && !rightSidebar) {
const tabHeader = this.getTabHeaderForLeaf(leaf);
if (tabHeader) {
tabHeader.classList.remove("is-home-base-tab");
tabHeader.removeAttribute("data-home-base-ghost");
tabHeader.removeAttribute("aria-hidden");
const tabHeaderExtended = tabHeader;
if (tabHeaderExtended._homeBaseParent && !tabHeaderExtended._homeBaseParent.contains(tabHeader)) {
const parent = tabHeaderExtended._homeBaseParent;
const nextSibling = tabHeaderExtended._homeBaseNextSibling;
if (parent) {
if (nextSibling && nextSibling.parentElement === parent) {
parent.insertBefore(tabHeader, nextSibling);
} else {
parent.appendChild(tabHeader);
}
}
delete tabHeaderExtended._homeBaseParent;
delete tabHeaderExtended._homeBaseNextSibling;
}
}
}
}
});
if (this.stickyIconEl) {
if (this.stickyIconEl.parentElement) {
this.stickyIconEl.remove();
}
this.stickyIconEl = null;
}
const cleanupOrphans = (doc) => {
doc.querySelectorAll(`.${STICKY_ICON_CLASS}`).forEach((el) => {
const stickyEl = el;
if (stickyEl._checkInterval) {
clearInterval(stickyEl._checkInterval);
}
if (stickyEl._containerObserver) {
stickyEl._containerObserver.disconnect();
}
el.remove();
});
};
cleanupOrphans(document);
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
var _a, _b;
const doc = (_b = (_a = leaf.view) == null ? void 0 : _a.containerEl) == null ? void 0 : _b.ownerDocument;
if (doc && doc !== document) {
cleanupOrphans(doc);
}
});
}
/**
* Update the active state of the sticky icon
*/
updateActiveState() {
if (!this.stickyIconEl) return;
const isActive = this.plugin.homeService.isFocusedOnHomeBase();
if (isActive) {
this.stickyIconEl.classList.add(STICKY_ICON_ACTIVE_CLASS);
} else {
this.stickyIconEl.classList.remove(STICKY_ICON_ACTIVE_CLASS);
}
this.updateTabHeaders();
}
/**
* Check if the left sidebar is collapsed
* Based on obsidian-oxygen-settings implementation
*/
isLeftSidebarCollapsed() {
const leftSidebar = document.querySelector(".workspace-split.mod-left-split") || document.querySelector(".mod-left-split");
if (!leftSidebar) return false;
return leftSidebar.classList.contains("is-sidedock-collapsed");
}
/**
* Update icon position based on sidebar state
* Note: With inline positioning, icon flows naturally with tabs, so no special positioning needed
*/
updateIconPositionForSidebar() {
}
/**
* Update icon visibility based on tab bar visibility
* REMOVED: JavaScript-based visibility checking was causing issues
* Now relies entirely on CSS which is more reliable
*/
updateIconVisibility() {
}
/**
* Watch for sidebar state changes and update icon position
* Based on obsidian-oxygen-settings implementation
*/
watchSidebarState() {
if (this.sidebarObserver) {
this.sidebarObserver.disconnect();
}
const leftSidebar = document.querySelector(".workspace-split.mod-left-split") || document.querySelector(".mod-left-split");
if (!leftSidebar) return;
this.sidebarObserver = new MutationObserver((mutations) => {
let shouldUpdate = false;
mutations.forEach((mutation) => {
if (mutation.type === "attributes" && mutation.attributeName === "class") {
shouldUpdate = true;
}
});
if (shouldUpdate) {
this.updateIconPositionForSidebar();
}
});
this.sidebarObserver.observe(leftSidebar, {
attributes: true,
attributeFilter: ["class"]
});
}
/**
* Watch for tab bar visibility changes (Oxygen theme auto-hide, focus mode, etc.)
* REMOVED: No longer needed - CSS handles all visibility automatically
*/
watchTabBarVisibility() {
}
/**
* Toggle the sticky icon visibility
*/
async toggle() {
this.plugin.settings.showStickyHomeIcon = !this.plugin.settings.showStickyHomeIcon;
await this.plugin.saveSettings();
this.update();
this.updateTabHeaders();
}
/**
* Update tab headers to hide/show ghost tab
* Only works when sticky icon is enabled
* Removed debounce - must be immediate to prevent flash
*/
updateTabHeaders() {
if (this.tabHeaderUpdateTimeout) {
clearTimeout(this.tabHeaderUpdateTimeout);
this.tabHeaderUpdateTimeout = null;
}
this._doUpdateTabHeaders();
}
/**
* Internal method that actually updates the tab headers
*/
_doUpdateTabHeaders() {
if (!this.plugin.settings.showStickyHomeIcon || !this.plugin.settings.hideHomeTabHeader) {
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
const tabHeader = this.getTabHeaderForLeaf(leaf);
if (tabHeader) {
tabHeader.classList.remove("is-home-base-tab");
tabHeader.removeAttribute("data-home-base-ghost");
tabHeader.removeAttribute("aria-hidden");
const tabHeaderExtended = tabHeader;
if (tabHeaderExtended._homeBaseParent && !tabHeaderExtended._homeBaseParent.contains(tabHeader)) {
const parent = tabHeaderExtended._homeBaseParent;
const nextSibling = tabHeaderExtended._homeBaseNextSibling;
if (parent) {
if (nextSibling && nextSibling.parentElement === parent) {
parent.insertBefore(tabHeader, nextSibling);
} else {
parent.appendChild(tabHeader);
}
}
delete tabHeaderExtended._homeBaseParent;
delete tabHeaderExtended._homeBaseNextSibling;
}
}
});
return;
}
const ghostTabHeadersToRemove = [];
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
const view = leaf.view;
let container = null;
if (view) {
const viewAny = view;
container = viewAny.containerEl || null;
}
if (!container) {
const leafAny = leaf;
container = leafAny.containerEl || null;
}
if (container) {
const rootWorkspace = container.closest(".workspace-split.mod-vertical.mod-root");
const leftSidebar = container.closest(".workspace-split.mod-left-split");
const rightSidebar = container.closest(".workspace-split.mod-right-split");
if (rootWorkspace && !leftSidebar && !rightSidebar) {
if (this.plugin.homeService.isGhostLeaf(leaf)) {
const tabHeader = this.getTabHeaderForLeaf(leaf);
if (tabHeader && tabHeader.parentElement) {
ghostTabHeadersToRemove.push({ tabHeader, leaf });
}
}
}
}
});
ghostTabHeadersToRemove.forEach(({ tabHeader }) => {
const parent = tabHeader.parentElement;
if (parent && parent.classList.contains("workspace-tab-header-container-inner")) {
const tabHeaderExtended = tabHeader;
if (parent.contains(tabHeader)) {
tabHeaderExtended._homeBaseParent = parent;
tabHeaderExtended._homeBaseNextSibling = tabHeader.nextSibling;
tabHeader.remove();
}
}
});
requestAnimationFrame(() => {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const homeBasePath = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.plugin.app);
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
var _a;
const view = leaf.view;
let container = null;
if (view) {
const viewAny = view;
container = viewAny.containerEl || null;
}
if (!container) {
const leafAny = leaf;
container = leafAny.containerEl || null;
}
if (container) {
const rootWorkspace = container.closest(".workspace-split.mod-vertical.mod-root");
const leftSidebar = container.closest(".workspace-split.mod-left-split");
const rightSidebar = container.closest(".workspace-split.mod-right-split");
if (rootWorkspace && !leftSidebar && !rightSidebar) {
const isGhostTab = this.plugin.homeService.isGhostLeaf(leaf);
const tabHeader = this.getTabHeaderForLeaf(leaf);
if (!tabHeader) return;
const tabHeaderExtended = tabHeader;
const isRemoved = tabHeaderExtended._homeBaseParent && !tabHeaderExtended._homeBaseParent.contains(tabHeader);
if (!isGhostTab) {
if (isRemoved) {
const parent = tabHeaderExtended._homeBaseParent;
const nextSibling = tabHeaderExtended._homeBaseNextSibling;
if (parent) {
if (nextSibling && nextSibling.parentElement === parent) {
parent.insertBefore(tabHeader, nextSibling);
} else {
parent.appendChild(tabHeader);
}
delete tabHeaderExtended._homeBaseParent;
delete tabHeaderExtended._homeBaseNextSibling;
}
}
const isGraphHome = homeBaseSettings.type === "Graph view" /* Graph */ && ((_a = leaf.view) == null ? void 0 : _a.getViewType()) === "graph";
if (homeBasePath && leafHasFile(leaf, homeBasePath) || isGraphHome) {
tabHeader.classList.add("is-home-base-tab");
} else {
tabHeader.classList.remove("is-home-base-tab");
}
tabHeader.removeAttribute("data-home-base-ghost");
tabHeader.removeAttribute("aria-hidden");
}
}
}
});
});
}
/**
* Get the tab header element for a given leaf
*/
getTabHeaderForLeaf(leaf) {
var _a, _b, _c;
const leafAny = leaf;
if (leafAny.tabHeaderEl) {
return leafAny.tabHeaderEl;
}
const viewType = (_a = leaf.view) == null ? void 0 : _a.getViewType();
if (!viewType) return null;
const doc = ((_c = (_b = leaf.view) == null ? void 0 : _b.containerEl) == null ? void 0 : _c.ownerDocument) || document;
const activeLeaf = this.plugin.app.workspace.getMostRecentLeaf();
const isActive = leaf === activeLeaf;
const tabHeaders = doc.querySelectorAll(`.workspace-tab-header[data-type="${viewType}"]`);
if (isActive) {
const activeHeader = doc.querySelector(".workspace-tab-header.is-active");
if (activeHeader && activeHeader.getAttribute("data-type") === viewType) {
return activeHeader;
}
}
for (const header of Array.from(tabHeaders)) {
const headerEl = header;
const headerElWithLeaf = headerEl;
const headerLeaf = headerElWithLeaf.leaf;
if (headerLeaf === leaf) {
return headerEl;
}
}
if (tabHeaders.length === 1) {
return tabHeaders[0];
}
if (isActive) {
const activeHeader = doc.querySelector(".workspace-tab-header.is-active");
if (activeHeader) {
return activeHeader;
}
}
return null;
}
/**
* Close the home base tab
* When called from context menu: Only closes the ghost tab (the "occupied" slot)
* Other home base tabs are left alone
*/
closeHomeBase(actuallyClose = false) {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const homeBasePath = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.plugin.app);
const ghostTabs = [];
const allHomeBaseLeaves = [];
this.plugin.app.workspace.iterateAllLeaves((leaf) => {
const isGhost = this.plugin.homeService.isGhostLeaf(leaf);
if (isGhost) {
ghostTabs.push(leaf);
}
if (homeBasePath && leafHasFile(leaf, homeBasePath)) {
allHomeBaseLeaves.push(leaf);
}
});
if (this.plugin.settings.showStickyHomeIcon) {
for (const ghostTab of ghostTabs) {
void ghostTab.detach();
}
} else {
for (const leaf of allHomeBaseLeaves) {
void leaf.detach();
}
}
this.updateTabHeaders();
}
/**
* Pin the home base tab
*/
pinHomeBaseTab() {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const homeBasePath = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.plugin.app);
if (!homeBasePath) return;
const homeBaseFile = getFileByPath(this.plugin.app, homeBasePath);
if (!homeBaseFile) return;
const homeBaseLeaf = this.plugin.homeService.findExistingHomeBaseLeaf(homeBaseFile);
if (homeBaseLeaf) {
homeBaseLeaf.setPinned(true);
}
}
/**
* Unpin the home base tab
*/
unpinHomeBaseTab() {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const homeBasePath = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.plugin.app);
if (!homeBasePath) return;
const homeBaseFile = getFileByPath(this.plugin.app, homeBasePath);
if (!homeBaseFile) return;
const homeBaseLeaf = this.plugin.homeService.findExistingHomeBaseLeaf(homeBaseFile);
if (homeBaseLeaf) {
homeBaseLeaf.setPinned(false);
}
}
/**
* Check if the home base tab is pinned
*/
isHomeBaseTabPinned() {
const homeBaseSettings = this.plugin.getHomeBaseSettings();
const homeBasePath = resolvePathSync(homeBaseSettings.type, homeBaseSettings.value, this.plugin.app);
if (!homeBasePath) return false;
const homeBaseFile = getFileByPath(this.plugin.app, homeBasePath);
if (!homeBaseFile) return false;
const homeBaseLeaf = this.plugin.homeService.findExistingHomeBaseLeaf(homeBaseFile);
if (!homeBaseLeaf) return false;
const viewState = homeBaseLeaf.getViewState();
return viewState.pinned === true;
}
};
// src/services/mobile-button-service.ts
var import_obsidian10 = require("obsidian");
var MOBILE_HOME_CLASS = "home-base-mobile-enabled";
var MobileButtonService = class {
constructor(plugin) {
this.plugin = plugin;
}
/**
* Update the mobile button based on settings
*/
update() {
if (!import_obsidian10.Platform.isMobile) {
this.remove();
return;
}
if (this.plugin.settings.replaceMobileNewTab) {
this.apply();
} else {
this.remove();
}
}
/**
* Apply the mobile button replacement
*/
apply() {
document.body.classList.add(MOBILE_HOME_CLASS);
}
/**
* Remove the mobile button replacement
*/
remove() {
document.body.classList.remove(MOBILE_HOME_CLASS);
}
};
// src/migration.ts
async function migrateLegacySettings(plugin) {
let needsSave = false;
const settings = plugin.settings;
if (settings.homeBasePath && !plugin.settings.homeBaseValue) {
plugin.settings.homeBaseType = "File" /* File */;
plugin.settings.homeBaseValue = settings.homeBasePath;
needsSave = true;
}
if (settings.keepExistingTabs !== void 0) {
if (plugin.settings.openMode === DEFAULT_SETTINGS.openMode) {
plugin.settings.openMode = settings.keepExistingTabs ? "retain" : "replace-all";
needsSave = true;
}
delete settings.keepExistingTabs;
needsSave = true;
}
if (settings.mobileHomeBasePath && !plugin.settings.mobileHomeBaseValue) {
plugin.settings.mobileHomeBaseType = "File" /* File */;
plugin.settings.mobileHomeBaseValue = settings.mobileHomeBasePath;
needsSave = true;
}
if (settings.mobileHomeBasePath !== void 0) {
delete settings.mobileHomeBasePath;
needsSave = true;
}
if (settings.homeBasePath !== void 0) {
delete settings.homeBasePath;
needsSave = true;
}
if (needsSave) {
await plugin.saveSettings();
}
}
// src/main.ts
var HOME_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"/><path d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>`;
var DOM_READY_DELAY = 100;
var TAB_HEADER_UPDATE_DELAY = 150;
var FILE_OPEN_ANIMATION_DELAY = 100;
var TAB_SWITCH_ANIMATION_DELAY = 100;
var STARTUP_COMPLETE_DELAY = 1e3;
var HomeBasePlugin = class extends import_obsidian11.Plugin {
constructor() {
super(...arguments);
// Release notes tracking
this.newRelease = false;
// Track if patched opening behavior already ran
this.openingBehaviorRan = false;
// Track if we're currently in startup (to prevent handleOpenWhenEmpty from firing)
this.isStartup = true;
}
async onload() {
await this.loadSettings();
await migrateLegacySettings(this);
this.patchOpeningBehavior();
(0, import_obsidian11.addIcon)("home-base", HOME_ICON);
this.homeService = new HomeBaseService(this);
this.newTabService = new NewTabService(this);
this.stickyTabService = new StickyTabService(this);
this.mobileButtonService = new MobileButtonService(this);
this.addRibbonIcon("home", "Open home base", () => {
void this.homeService.openHomeBase({
replaceActiveLeaf: false,
runCommand: true
});
});
this.registerCommands();
this.addSettingTab(new HomeBaseSettingTab(this.app, this));
this.app.workspace.onLayoutReady(() => {
setTimeout(() => {
if (this.isSettingsModalOpen()) {
this.updateStickyTabIcon();
this.updateMobileButton();
this.stickyTabService.updateTabHeaders();
return;
}
if (!this.openingBehaviorRan) {
this.newTabService.initialize();
} else {
this.newTabService.trackExistingLeaves();
}
this.homeService.restoreGhostLeaves();
setTimeout(() => {
this.isStartup = false;
}, STARTUP_COMPLETE_DELAY);
this.updateStickyTabIcon();
this.updateMobileButton();
this.stickyTabService.updateTabHeaders();
}, DOM_READY_DELAY);
});
this.registerEvent(
this.app.workspace.on("layout-change", async () => {
this.newTabService.handleLayoutChange();
if (this.settings.revertView) {
await this.homeService.revertView();
}
setTimeout(() => {
this.stickyTabService.updateActiveState();
this.stickyTabService.updateTabHeaders();
this.stickyTabService.updateIconPositionForSidebar();
}, TAB_HEADER_UPDATE_DELAY);
})
);
this.registerEvent(
this.app.workspace.on("file-open", () => {
setTimeout(() => {
this.stickyTabService.updateActiveState();
this.stickyTabService.updateTabHeaders();
}, FILE_OPEN_ANIMATION_DELAY);
})
);
this.registerEvent(
this.app.workspace.on("active-leaf-change", () => {
setTimeout(() => {
this.stickyTabService.updateTabHeaders();
}, TAB_SWITCH_ANIMATION_DELAY);
})
);
}
onunload() {
this.unpatchReleaseNotes();
this.unpatchOpeningBehavior();
this.stickyTabService.remove();
this.mobileButtonService.remove();
}
/**
* Register plugin commands
*/
registerCommands() {
this.addCommand({
id: "open",
name: "Open",
callback: () => {
const homeBaseSettings = this.getHomeBaseSettings();
if (!homeBaseSettings.value && homeBaseSettings.type === "File" /* File */) {
new import_obsidian11.Notice("No home base configured. Set one in settings.");
return;
}
void this.homeService.openHomeBase({
replaceActiveLeaf: false,
runCommand: true
});
}
});
this.addCommand({
id: "set-current-file",
name: "Set current file as home",
checkCallback: (checking) => {
if (!this.homeService.canSetActiveFileAsHomeBase()) {
return false;
}
if (!checking) {
void this.homeService.setActiveFileAsHomeBase().then((success) => {
if (success) {
const activeFile = this.app.workspace.getActiveFile();
new import_obsidian11.Notice(`Home base set to "${activeFile == null ? void 0 : activeFile.name}"`);
}
});
}
return true;
}
});
this.addCommand({
id: "toggle-sticky-icon",
name: "Toggle sticky home icon",
callback: async () => {
await this.stickyTabService.toggle();
const state = this.settings.showStickyHomeIcon ? "enabled" : "disabled";
new import_obsidian11.Notice(`Sticky home icon ${state}`);
}
});
this.addCommand({
id: "close",
name: "Close",
callback: () => {
this.stickyTabService.closeHomeBase();
}
});
}
/**
* Load plugin settings
*/
async loadSettings() {
const data = await this.loadData();
this.settings = Object.assign({}, DEFAULT_SETTINGS, data != null ? data : {});
}
/**
* Get the active home base settings (mobile or desktop)
*/
getHomeBaseSettings() {
if (this.settings.separateMobile && import_obsidian11.Platform.isMobile) {
return {
type: this.settings.mobileHomeBaseType || "File" /* File */,
value: this.settings.mobileHomeBaseValue || ""
};
}
return {
type: this.settings.homeBaseType || "File" /* File */,
value: this.settings.homeBaseValue || ""
};
}
/**
* Get the active new tab settings (mobile or desktop)
* Falls back to home base settings if useDifferentFileForNewTab is disabled
*/
getNewTabSettings() {
if (!this.settings.useDifferentFileForNewTab) {
return this.getHomeBaseSettings();
}
if (this.settings.newTabSeparateMobile && import_obsidian11.Platform.isMobile) {
return {
type: this.settings.mobileNewTabType || "File" /* File */,
value: this.settings.mobileNewTabValue || ""
};
}
return {
type: this.settings.newTabType || "File" /* File */,
value: this.settings.newTabValue || ""
};
}
/**
* Save plugin settings
*/
async saveSettings() {
await this.saveData(this.settings);
}
/**
* Update the sticky tab icon based on current settings
*/
updateStickyTabIcon() {
this.stickyTabService.update();
this.stickyTabService.updateTabHeaders();
}
/**
* Update the mobile button based on current settings
*/
updateMobileButton() {
this.mobileButtonService.update();
}
/**
* Check if we should skip startup logic (e.g., plugin reload, settings modal open)
* This prevents destructive behavior when the plugin is reloaded or settings are open
*/
shouldSkipStartupLogic() {
if (this.isSettingsModalOpen()) {
return true;
}
const hasOpenFiles = this.app.workspace.getLeavesOfType("markdown").length > 0 || this.app.workspace.getLeavesOfType("canvas").length > 0 || this.app.workspace.getLeavesOfType("bases").length > 0 || this.app.workspace.getLeavesOfType("empty").length > 0;
return hasOpenFiles;
}
/**
* Check if the settings modal is currently open
*/
isSettingsModalOpen() {
const settingsModal = document.querySelector(".modal-container.mod-settings") || document.querySelector(".modal.mod-settings") || document.querySelector(".vertical-tab-content");
if (!settingsModal) {
const allModals = document.querySelectorAll(".modal-container");
for (const modal of Array.from(allModals)) {
if (modal.querySelector(".vertical-tab-content") || modal.querySelector(".settings-content") || modal.classList.contains("mod-settings")) {
return true;
}
}
}
return settingsModal !== null;
}
/**
* Patch runOpeningBehavior for fast startup (like homepage plugin)
*/
patchOpeningBehavior() {
try {
this.app.nvOrig_runOpeningBehavior = this.app.runOpeningBehavior;
this.app.runOpeningBehavior = async (path) => {
const openInitially = this.settings.openOnStartup && !this.hasUrlParams();
if (openInitially) {
this.openingBehaviorRan = true;
const mode = this.settings.openMode;
if (mode === "replace-all") {
await this.homeService.detachAllLeaves();
}
if (this.settings.showStickyHomeIcon) {
void this.homeService.openHomeBaseInGhostTab({
runCommand: true
});
} else {
void this.homeService.openHomeBaseWithMode(mode, true);
}
} else {
if (this.app.nvOrig_runOpeningBehavior) {
await this.app.nvOrig_runOpeningBehavior(path);
}
}
this.unpatchReleaseNotes();
};
} catch (e) {
console.warn("[Home Base] Failed to patch opening behavior:", e);
}
}
/**
* Unpatch runOpeningBehavior
*/
unpatchOpeningBehavior() {
if (this.app.nvOrig_runOpeningBehavior) {
this.app.runOpeningBehavior = this.app.nvOrig_runOpeningBehavior;
}
}
/**
* Patch showReleaseNotes to track new releases
*/
patchReleaseNotes() {
try {
const appAny = this.app;
appAny.nvOrig_showReleaseNotes = appAny.showReleaseNotes;
appAny.showReleaseNotes = () => {
this.newRelease = true;
};
} catch (e) {
console.warn("[Home Base] Failed to patch release notes:", e);
}
}
/**
* Unpatch showReleaseNotes
*/
unpatchReleaseNotes() {
var _a;
const appAny = this.app;
if (this.newRelease && !this.settings.hideReleaseNotes) {
(_a = appAny.nvOrig_showReleaseNotes) == null ? void 0 : _a.call(appAny);
}
if (appAny.nvOrig_showReleaseNotes) {
appAny.showReleaseNotes = appAny.nvOrig_showReleaseNotes;
}
}
/**
* Check if a home base type has its required plugin enabled
*/
hasRequiredPlugin(type) {
var _a, _b, _c, _d, _e, _f, _g, _h;
switch (type) {
case "Workspace" /* Workspace */:
return ((_c = (_b = (_a = this.app.internalPlugins) == null ? void 0 : _a.plugins) == null ? void 0 : _b.workspaces) == null ? void 0 : _c.enabled) === true;
case "Graph view" /* Graph */:
return ((_f = (_e = (_d = this.app.internalPlugins) == null ? void 0 : _d.plugins) == null ? void 0 : _e.graph) == null ? void 0 : _f.enabled) === true;
case "Journal" /* Journal */:
return !!((_h = (_g = this.app.plugins) == null ? void 0 : _g.plugins) == null ? void 0 : _h["journals"]);
case "Daily Note" /* DailyNote */:
case "Weekly Note" /* WeeklyNote */:
case "Monthly Note" /* MonthlyNote */:
case "Quarterly Note" /* QuarterlyNote */:
case "Yearly Note" /* YearlyNote */:
return this.hasRequiredPeriodicity(type);
default:
return true;
}
}
/**
* Check if periodic notes are available for the given type
*/
hasRequiredPeriodicity(type) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
if (type === "Daily Note" /* DailyNote */) {
const coreDailyNotes = ((_c = (_b = (_a = this.app.internalPlugins) == null ? void 0 : _a.plugins) == null ? void 0 : _b["daily-notes"]) == null ? void 0 : _c.enabled) === true;
if (coreDailyNotes) {
return true;
}
const periodicNotes2 = (_e = (_d = this.app.plugins) == null ? void 0 : _d.plugins) == null ? void 0 : _e["periodic-notes"];
if (periodicNotes2) {
const version2 = ((_f = periodicNotes2 == null ? void 0 : periodicNotes2.manifest) == null ? void 0 : _f.version) || "0";
const isLegacy2 = version2.startsWith("0");
if (isLegacy2) {
return ((_h = (_g = periodicNotes2 == null ? void 0 : periodicNotes2.settings) == null ? void 0 : _g["daily"]) == null ? void 0 : _h.enabled) === true;
} else {
const calendarSet = (_j = (_i = periodicNotes2 == null ? void 0 : periodicNotes2.calendarSetManager) == null ? void 0 : _i.getActiveSet) == null ? void 0 : _j.call(_i);
return ((_k = calendarSet == null ? void 0 : calendarSet["day"]) == null ? void 0 : _k.enabled) === true;
}
}
return false;
}
const periodicNotes = (_m = (_l = this.app.plugins) == null ? void 0 : _l.plugins) == null ? void 0 : _m["periodic-notes"];
if (!periodicNotes) return false;
const version = ((_n = periodicNotes == null ? void 0 : periodicNotes.manifest) == null ? void 0 : _n.version) || "0";
const isLegacy = version.startsWith("0");
if (isLegacy) {
const periodMap = {
["Weekly Note" /* WeeklyNote */]: "weekly",
["Monthly Note" /* MonthlyNote */]: "monthly",
["Quarterly Note" /* QuarterlyNote */]: "quarterly",
["Yearly Note" /* YearlyNote */]: "yearly"
};
const adjective = periodMap[type];
if (!adjective) return false;
return ((_p = (_o = periodicNotes == null ? void 0 : periodicNotes.settings) == null ? void 0 : _o[adjective]) == null ? void 0 : _p.enabled) === true;
} else {
const nounMap = {
["Weekly Note" /* WeeklyNote */]: "week",
["Monthly Note" /* MonthlyNote */]: "month",
["Quarterly Note" /* QuarterlyNote */]: "quarter",
["Yearly Note" /* YearlyNote */]: "year"
};
const noun = nounMap[type];
if (!noun) return false;
const calendarSet = (_r = (_q = periodicNotes == null ? void 0 : periodicNotes.calendarSetManager) == null ? void 0 : _q.getActiveSet) == null ? void 0 : _r.call(_q);
return ((_s = calendarSet == null ? void 0 : calendarSet[noun]) == null ? void 0 : _s.enabled) === true;
}
}
/**
* Check if URL params indicate a file/workspace should be opened (skip homepage)
*/
hasUrlParams() {
if (typeof window !== "undefined" && window.OBS_ACT) {
const params = Object.keys(window.OBS_ACT);
const action = window.OBS_ACT.action;
return action !== void 0 && ["open", "advanced-uri"].includes(action) && ["file", "filepath", "workspace"].some((e) => params.includes(e));
}
return false;
}
// "Open when empty" feature removed - redundant with "New tab replacement: only when empty"
// Since Obsidian auto-creates an empty tab when you close the last one, they do the same thing
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibm9kZV9tb2R1bGVzLy5wbnBtL29ic2lkaWFuLWRhaWx5LW5vdGVzLWludGVyZl84ZDlmYzI0YjBmYzYyMmQ5NGZkMTFmYTVlOWIxODViMy9ub2RlX21vZHVsZXMvb2JzaWRpYW4tZGFpbHktbm90ZXMtaW50ZXJmYWNlL2Rpc3QvbWFpbi5qcyIsICJzcmMvbWFpbi50cyIsICJzcmMvc2V0dGluZ3MudHMiLCAic3JjL3VpL3NldHRpbmdzLXRhYi50cyIsICJzcmMvdWkvZmlsZS1zdWdnZXN0LnRzIiwgInNyYy91aS9jb21tYW5kLXN1Z2dlc3QudHMiLCAic3JjL3VpL2ljb24tcGlja2VyLnRzIiwgInNyYy9zZXJ2aWNlcy9ob21lLXNlcnZpY2UudHMiLCAic3JjL3V0aWxzL2ZpbGUtdXRpbHMudHMiLCAic3JjL3V0aWxzL2hvbWViYXNlLXJlc29sdmVyLnRzIiwgInNyYy9zZXJ2aWNlcy9uZXctdGFiLXNlcnZpY2UudHMiLCAic3JjL3NlcnZpY2VzL3N0aWNreS10YWItc2VydmljZS50cyIsICJzcmMvc2VydmljZXMvbW9iaWxlLWJ1dHRvbi1zZXJ2aWNlLnRzIiwgInNyYy9taWdyYXRpb24udHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcblxudmFyIG9ic2lkaWFuID0gcmVxdWlyZSgnb2JzaWRpYW4nKTtcblxuY29uc3QgREVGQVVMVF9EQUlMWV9OT1RFX0ZPUk1BVCA9IFwiWVlZWS1NTS1ERFwiO1xuY29uc3QgREVGQVVMVF9XRUVLTFlfTk9URV9GT1JNQVQgPSBcImdnZ2ctW1ddd3dcIjtcbmNvbnN0IERFRkFVTFRfTU9OVEhMWV9OT1RFX0ZPUk1BVCA9IFwiWVlZWS1NTVwiO1xuY29uc3QgREVGQVVMVF9RVUFSVEVSTFlfTk9URV9GT1JNQVQgPSBcIllZWVktW1FdUVwiO1xuY29uc3QgREVGQVVMVF9ZRUFSTFlfTk9URV9GT1JNQVQgPSBcIllZWVlcIjtcblxuZnVuY3Rpb24gc2hvdWxkVXNlUGVyaW9kaWNOb3Rlc1NldHRpbmdzKHBlcmlvZGljaXR5KSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbiAgICBjb25zdCBwZXJpb2RpY05vdGVzID0gd2luZG93LmFwcC5wbHVnaW5zLmdldFBsdWdpbihcInBlcmlvZGljLW5vdGVzXCIpO1xuICAgIHJldHVybiBwZXJpb2RpY05vdGVzICYmIHBlcmlvZGljTm90ZXMuc2V0dGluZ3M/LltwZXJpb2RpY2l0eV0/LmVuYWJsZWQ7XG59XG4vKipcbiAqIFJlYWQgdGhlIHVzZXIgc2V0dGluZ3MgZm9yIHRoZSBgZGFpbHktbm90ZXNgIHBsdWdpblxuICogdG8ga2VlcCBiZWhhdmlvciBvZiBjcmVhdGluZyBhIG5ldyBub3RlIGluLXN5bmMuXG4gKi9cbmZ1bmN0aW9uIGdldERhaWx5Tm90ZVNldHRpbmdzKCkge1xuICAgIHRyeSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gICAgICAgIGNvbnN0IHsgaW50ZXJuYWxQbHVnaW5zLCBwbHVnaW5zIH0gPSB3aW5kb3cuYXBwO1xuICAgICAgICBpZiAoc2hvdWxkVXNlUGVyaW9kaWNOb3Rlc1NldHRpbmdzKFwiZGFpbHlcIikpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgZm9ybWF0LCBmb2xkZXIsIHRlbXBsYXRlIH0gPSBwbHVnaW5zLmdldFBsdWdpbihcInBlcmlvZGljLW5vdGVzXCIpPy5zZXR0aW5ncz8uZGFpbHkgfHwge307XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGZvcm1hdDogZm9ybWF0IHx8IERFRkFVTFRfREFJTFlfTk9URV9GT1JNQVQsXG4gICAgICAgICAgICAgICAgZm9sZGVyOiBmb2xkZXI/LnRyaW0oKSB8fCBcIlwiLFxuICAgICAgICAgICAgICAgIHRlbXBsYXRlOiB0ZW1wbGF0ZT8udHJpbSgpIHx8IFwiXCIsXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHsgZm9sZGVyLCBmb3JtYXQsIHRlbXBsYXRlIH0gPSBpbnRlcm5hbFBsdWdpbnMuZ2V0UGx1Z2luQnlJZChcImRhaWx5LW5vdGVzXCIpPy5pbnN0YW5jZT8ub3B0aW9ucyB8fCB7fTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGZvcm1hdDogZm9ybWF0IHx8IERFRkFVTFRfREFJTFlfTk9URV9GT1JNQVQsXG4gICAgICAgICAgICBmb2xkZXI6IGZvbGRlcj8udHJpbSgpIHx8IFwiXCIsXG4gICAgICAgICAgICB0ZW1wbGF0ZTogdGVtcGxhdGU/LnRyaW0oKSB8fCBcIlwiLFxuICAgICAgICB9O1xuICAgIH1cbiAgICBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhcIk5vIGN1c3RvbSBkYWlseSBub3RlIHNldHRpbmdzIGZvdW5kIVwiLCBlcnIpO1xuICAgIH1cbn1cbi8qKlxuICogUmVhZCB0aGUgdXNlciBzZXR0aW5ncyBmb3IgdGhlIGB3ZWVrbHktbm90ZXNgIHBsdWdpblxuICogdG8ga2VlcCBiZWhhdmlvciBvZiBjcmVhdGluZyBhIG5ldyBub3RlIGluLXN5bmMuXG4gKi9cbmZ1bmN0aW9uIGdldFdlZWtseU5vdGVTZXR0aW5ncygpIHtcbiAgICB0cnkge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICAgICAgICBjb25zdCBwbHVnaW5NYW5hZ2VyID0gd2luZG93LmFwcC5wbHVnaW5zO1xuICAgICAgICBjb25zdCBjYWxlbmRhclNldHRpbmdzID0gcGx1Z2luTWFuYWdlci5nZXRQbHVnaW4oXCJjYWxlbmRhclwiKT8ub3B0aW9ucztcbiAgICAgICAgY29uc3QgcGVyaW9kaWNOb3Rlc1NldHRpbmdzID0gcGx1Z2luTWFuYWdlci5nZXRQbHVnaW4oXCJwZXJpb2RpYy1ub3Rlc1wiKT8uc2V0dGluZ3M/LndlZWtseTtcbiAgICAgICAgaWYgKHNob3VsZFVzZVBlcmlvZGljTm90ZXNTZXR0aW5ncyhcIndlZWtseVwiKSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBmb3JtYXQ6IHBlcmlvZGljTm90ZXNTZXR0aW5ncy5mb3JtYXQgfHwgREVGQVVMVF9XRUVLTFlfTk9URV9GT1JNQVQsXG4gICAgICAgICAgICAgICAgZm9sZGVyOiBwZXJpb2RpY05vdGVzU2V0dGluZ3MuZm9sZGVyPy50cmltKCkgfHwgXCJcIixcbiAgICAgICAgICAgICAgICB0Z