1268 lines
59 KiB
JavaScript
1268 lines
59 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
|
|
// src/main.js
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => main_default
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian3 = require("obsidian");
|
|
|
|
// src/modals.js
|
|
var import_obsidian = require("obsidian");
|
|
var ReleaseNotesModal = class extends import_obsidian.Modal {
|
|
constructor(app, plugin, version, releaseNotes2) {
|
|
super(app);
|
|
this.plugin = plugin;
|
|
this.version = version;
|
|
this.releaseNotes = releaseNotes2;
|
|
}
|
|
async onOpen() {
|
|
const { contentEl } = this;
|
|
contentEl.empty();
|
|
contentEl.createEl("h2", { text: `Welcome to \u{1F9B6} Rich Foot v${this.version}` });
|
|
contentEl.createEl("p", {
|
|
text: "After each update you'll be prompted with the release notes. You can disable this in the plugin settings.",
|
|
cls: "release-notes-instructions"
|
|
});
|
|
const promotionalLinks = contentEl.createEl("div");
|
|
promotionalLinks.style.display = "flex";
|
|
promotionalLinks.style.flexDirection = "row";
|
|
promotionalLinks.style.justifyContent = "space-around";
|
|
const equilllabsLink = promotionalLinks.createEl("a", {
|
|
href: "https://www.equilllabs.com",
|
|
target: "_blank"
|
|
});
|
|
equilllabsLink.createEl("img", {
|
|
attr: {
|
|
height: "36",
|
|
style: "border:0px;height:36px;",
|
|
src: "https://raw.githubusercontent.com/jparkerweb/pixel-banner/refs/heads/main/img/equilllabs.png?raw=true",
|
|
border: "0",
|
|
alt: "eQuill-Labs"
|
|
}
|
|
});
|
|
const discordLink = promotionalLinks.createEl("a", {
|
|
href: "https://discord.gg/sp8AQQhMJ7",
|
|
target: "_blank"
|
|
});
|
|
discordLink.createEl("img", {
|
|
attr: {
|
|
height: "36",
|
|
style: "border:0px;height:36px;",
|
|
src: "https://raw.githubusercontent.com/jparkerweb/pixel-banner/refs/heads/main/img/discord.png?raw=true",
|
|
border: "0",
|
|
alt: "Discord"
|
|
}
|
|
});
|
|
const kofiLink = promotionalLinks.createEl("a", {
|
|
href: "https://ko-fi.com/Z8Z212UMBI",
|
|
target: "_blank"
|
|
});
|
|
kofiLink.createEl("img", {
|
|
attr: {
|
|
height: "36",
|
|
style: "border:0px;height:36px;",
|
|
src: "https://raw.githubusercontent.com/jparkerweb/pixel-banner/refs/heads/main/img/support.png?raw=true",
|
|
border: "0",
|
|
alt: "Buy Me a Coffee at ko-fi.com"
|
|
}
|
|
});
|
|
const notesContainer = contentEl.createDiv("release-notes-container");
|
|
await import_obsidian.MarkdownRenderer.renderMarkdown(
|
|
this.releaseNotes,
|
|
notesContainer,
|
|
"",
|
|
this.plugin,
|
|
this
|
|
);
|
|
contentEl.createEl("div", { cls: "release-notes-spacer" }).style.height = "20px";
|
|
new import_obsidian.Setting(contentEl).addButton((btn) => btn.setButtonText("Close").onClick(() => this.close()));
|
|
}
|
|
onClose() {
|
|
const { contentEl } = this;
|
|
contentEl.empty();
|
|
}
|
|
};
|
|
|
|
// virtual-module:virtual:release-notes
|
|
var releaseNotes = '<h2>\u{1F6D1} Exclude Me Please</h2>\n<h2>[1.10.9] - 2025-01-25</h2>\n<h3>\u{1F41B} Fixed</h3>\n<ul>\n<li>Addressed issue with Rich Foot being duplicated when a note was opened in a "new window"</li>\n</ul>\n<h2>[1.10.8] - 2025-01-25</h2>\n<h3>\u2728 Added</h3>\n<ul>\n<li>Outlink collections now include embedded notes</li>\n</ul>\n<h3>\u{1F41B} Fixed</h3>\n<ul>\n<li>Fixed issue with Rich Foot not being applied in Reading Mode if the note has an embedded note</li>\n</ul>\n<h3>[1.10.7] - 2025-01-13</h3>\n<h4>\u{1F4E6} Updated</h4>\n<ul>\n<li>Updated <code>css</code> variables to support the <code>Minimal</code> theme</li>\n</ul>\n<h3>[1.10.6] - 2025-01-10</h3>\n<h4>\u{1F4E6} Updated</h4>\n<ul>\n<li>Adjusted <code>css</code> padding values to be compatible with <code>Typewriter Scroll</code> plugin</li>\n</ul>\n<h3>[1.10.5] - 2024-12-26</h3>\n<h4>\u{1F4E6} Updated</h4>\n<ul>\n<li>Support for more date formats in <code>frontmatter</code> created/modified fields (ISO, space-separated, and just date)</li>\n</ul>\n<h3>[1.10.4] - 2024-12-23</h3>\n<h4>\u{1F41B} Fixed</h4>\n<ul>\n<li>Fixed issue with Rich Foot not loading all user defined colors when Obsidian is restarted</li>\n</ul>\n<h3>[1.10.3] - 2024-12-14</h3>\n<h4>\u{1F41B} Fixed</h4>\n<ul>\n<li>Improved parent selector matching to properly detect and exclude Rich Foot when specified selectors are present in the view or its parent elements</li>\n</ul>\n<h3>[1.10.2] - 2024-12-11</h3>\n<h4>\u{1F41B} Fixed</h4>\n<ul>\n<li>Missing <code>Excluded Folders</code> section in the settings</li>\n</ul>\n<h3>[1.10.1] - 2024-12-10</h3>\n<h4>\u{1F41B} Fixed</h4>\n<ul>\n<li>Extra padding on the bottom of the editor in Canvas / Kanban Cards</li>\n</ul>\n<h3>[1.10.0] - 2024-12-08</h3>\n<h4>\u2728 Added</h4>\n<ul>\n<li>Exclusion rule via <code>frontmatter</code> field</li>\n<li>Custom exclusions using specified DOM parent selectors for advanced control</li>\n</ul>\n<p><a href="https://raw.githubusercontent.com/jparkerweb/ref/refs/heads/main/equill-labs/rich-foot/rich-foot-v1.10.0.jpg"><img src="https://raw.githubusercontent.com/jparkerweb/ref/refs/heads/main/equill-labs/rich-foot/rich-foot-v1.10.0.jpg" alt="screenshot"></a></p>\n';
|
|
|
|
// src/settings.js
|
|
var import_obsidian2 = require("obsidian");
|
|
|
|
// src/utils.js
|
|
function rgbToHex(color) {
|
|
if (color.startsWith("hsl")) {
|
|
const temp = document.createElement("div");
|
|
temp.style.color = color;
|
|
document.body.appendChild(temp);
|
|
color = getComputedStyle(temp).color;
|
|
document.body.removeChild(temp);
|
|
}
|
|
const rgb = color.match(/\d+/g);
|
|
if (!rgb || rgb.length < 3) return "#000000";
|
|
const [r, g, b] = rgb.slice(0, 3).map((x) => {
|
|
const val = Math.min(255, Math.max(0, Math.round(parseFloat(x))));
|
|
return val.toString(16).padStart(2, "0");
|
|
});
|
|
return `#${r}${g}${b}`;
|
|
}
|
|
function formatDate(date, format) {
|
|
const d = new Date(date);
|
|
const year = d.getFullYear();
|
|
const month = d.getMonth();
|
|
const day = d.getDate();
|
|
const weekday = d.getDay();
|
|
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
const monthsShort = months.map((m) => m.slice(0, 3));
|
|
const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
const weekdaysShort = weekdays.map((w) => w.slice(0, 3));
|
|
const pad = (num) => num.toString().padStart(2, "0");
|
|
const tokens = {
|
|
"dddd": weekdays[weekday],
|
|
"ddd": weekdaysShort[weekday],
|
|
"dd": pad(day),
|
|
"d": day.toString(),
|
|
"mmmm": months[month],
|
|
"mmm": monthsShort[month],
|
|
"mm": pad(month + 1),
|
|
"m": (month + 1).toString(),
|
|
"yyyy": year.toString(),
|
|
"yy": year.toString().slice(-2)
|
|
};
|
|
const sortedTokens = Object.keys(tokens).sort((a, b) => b.length - a.length);
|
|
let result = format.toLowerCase();
|
|
const replacements = /* @__PURE__ */ new Map();
|
|
sortedTokens.forEach((token, index) => {
|
|
const placeholder = `__${index}__`;
|
|
replacements.set(placeholder, tokens[token]);
|
|
result = result.replace(new RegExp(token, "gi"), placeholder);
|
|
});
|
|
replacements.forEach((value, placeholder) => {
|
|
result = result.replace(new RegExp(placeholder, "g"), value);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
// src/settings.js
|
|
var DEFAULT_SETTINGS = {
|
|
borderWidth: 1,
|
|
borderStyle: "dashed",
|
|
borderOpacity: 1,
|
|
borderRadius: 15,
|
|
datesOpacity: 1,
|
|
linksOpacity: 1,
|
|
showReleaseNotes: true,
|
|
excludedFolders: [],
|
|
dateColor: "var(--text-accent)",
|
|
borderColor: "var(--text-accent)",
|
|
linkColor: "var(--link-color)",
|
|
linkBackgroundColor: "var(--tag-background)",
|
|
linkBorderColor: "rgba(255, 255, 255, 0.204)",
|
|
customCreatedDateProp: "",
|
|
customModifiedDateProp: "",
|
|
dateDisplayFormat: "mmmm dd, yyyy",
|
|
showBacklinks: true,
|
|
showOutlinks: true,
|
|
showDates: true,
|
|
combineLinks: false,
|
|
updateDelay: 3e3,
|
|
excludedParentSelectors: [],
|
|
frontmatterExclusionField: ""
|
|
};
|
|
var RichFootSettingTab = class extends import_obsidian2.PluginSettingTab {
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
this.excludedParentSelectors = [];
|
|
this.frontmatterExclusionField = "";
|
|
}
|
|
display() {
|
|
var _a, _b;
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
containerEl.addClass("rich-foot-settings");
|
|
containerEl.createEl("div", { cls: "rich-foot-info", text: "\u{1F9B6} Rich Foot adds a footer to your notes with useful information such as backlinks, creation date, and last modified date. Use the settings below to customize the appearance." });
|
|
containerEl.createEl("h3", { text: "Visibility Settings" });
|
|
new import_obsidian2.Setting(containerEl).setName("Show Backlinks").setDesc("Show backlinks in the footer").addToggle((toggle) => toggle.setValue(this.plugin.settings.showBacklinks).onChange(async (value) => {
|
|
this.plugin.settings.showBacklinks = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Show Outlinks").setDesc("Show outgoing links in the footer").addToggle((toggle) => toggle.setValue(this.plugin.settings.showOutlinks).onChange(async (value) => {
|
|
this.plugin.settings.showOutlinks = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Show Combine Links").setDesc("Show backlinks and outlinks in a single combined section (overrides show backlinks and show outlinks settings)").addToggle((toggle) => toggle.setValue(this.plugin.settings.combineLinks).onChange(async (value) => {
|
|
this.plugin.settings.combineLinks = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Rich-foot update delay").setDesc("Delay in milliseconds before updating the rich-foot in edit mode (lower values may impact performance)").addText((text) => text.setPlaceholder("3000").setValue(String(this.plugin.settings.updateDelay)).onChange(async (value) => {
|
|
const numValue = Math.floor(Number(value));
|
|
if (!isNaN(numValue) && numValue > 0) {
|
|
this.plugin.settings.updateDelay = numValue;
|
|
await this.plugin.saveSettings();
|
|
const updateRichFootCallback = this.plugin.debouncedUpdateRichFoot.callback;
|
|
if (updateRichFootCallback) {
|
|
this.plugin.debouncedUpdateRichFoot = (0, import_obsidian2.debounce)(updateRichFootCallback, numValue, true);
|
|
this.plugin.debouncedUpdateRichFoot.callback = updateRichFootCallback;
|
|
}
|
|
}
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
const defaultDelay = DEFAULT_SETTINGS.updateDelay;
|
|
this.plugin.settings.updateDelay = defaultDelay;
|
|
await this.plugin.saveSettings();
|
|
const textComponent = containerEl.querySelector('.setting-item:last-child input[type="text"]');
|
|
if (textComponent) {
|
|
textComponent.value = String(defaultDelay);
|
|
}
|
|
const updateRichFootCallback = this.plugin.debouncedUpdateRichFoot.callback;
|
|
if (updateRichFootCallback) {
|
|
this.plugin.debouncedUpdateRichFoot = (0, import_obsidian2.debounce)(updateRichFootCallback, defaultDelay, true);
|
|
this.plugin.debouncedUpdateRichFoot.callback = updateRichFootCallback;
|
|
}
|
|
}));
|
|
containerEl.createEl("hr");
|
|
containerEl.createEl("h3", { text: "Date Settings" });
|
|
new import_obsidian2.Setting(containerEl).setName("Show Dates").setDesc("Show creation and modification dates in the footer").addToggle((toggle) => toggle.setValue(this.plugin.settings.showDates).onChange(async (value) => {
|
|
this.plugin.settings.showDates = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Date Display Format").setDesc("Choose how dates should be displayed in the footer").addDropdown((dropdown) => {
|
|
const today = /* @__PURE__ */ new Date();
|
|
const formats = [
|
|
"mm/dd/yyyy",
|
|
"dd/mm/yyyy",
|
|
"yyyy-mm-dd",
|
|
"mmm dd, yyyy",
|
|
"dd mmm yyyy",
|
|
"mmmm dd, yyyy",
|
|
"ddd, mmm dd, yyyy",
|
|
"dddd, mmmm dd, yyyy",
|
|
"mm/dd/yy",
|
|
"dd/mm/yy",
|
|
"yy-mm-dd",
|
|
"m/d/yy"
|
|
];
|
|
formats.forEach((format) => {
|
|
const example = formatDate(today, format);
|
|
dropdown.addOption(format, `${format} (${example})`);
|
|
});
|
|
dropdown.setValue(this.plugin.settings.dateDisplayFormat).onChange(async (value) => {
|
|
this.plugin.settings.dateDisplayFormat = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
});
|
|
});
|
|
new import_obsidian2.Setting(containerEl).setName("Custom Created Date Property").setDesc("Specify a frontmatter property to use for creation date (leave empty to use file creation date)").addText((text) => {
|
|
text.setValue(this.plugin.settings.customCreatedDateProp).onChange(async (value) => {
|
|
this.plugin.settings.customCreatedDateProp = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
});
|
|
this.createdDateInput = text;
|
|
return text;
|
|
}).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.customCreatedDateProp = "";
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
this.createdDateInput.setValue("");
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Custom Modified Date Property").setDesc("Specify a frontmatter property to use for modification date (leave empty to use file modification date)").addText((text) => {
|
|
text.setValue(this.plugin.settings.customModifiedDateProp).onChange(async (value) => {
|
|
this.plugin.settings.customModifiedDateProp = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
});
|
|
this.modifiedDateInput = text;
|
|
return text;
|
|
}).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.customModifiedDateProp = "";
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
this.modifiedDateInput.setValue("");
|
|
}));
|
|
containerEl.createEl("hr");
|
|
containerEl.createEl("h3", { text: "Style Settings" });
|
|
new import_obsidian2.Setting(containerEl).setName("Border Width").setDesc("Adjust the width of the footer border (1-10px)").addSlider((slider) => slider.setLimits(1, 10, 1).setValue(this.plugin.settings.borderWidth).setDynamicTooltip().onChange(async (value) => {
|
|
this.plugin.settings.borderWidth = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.borderWidth = DEFAULT_SETTINGS.borderWidth;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const slider = this.containerEl.querySelector('input[type="range"]');
|
|
if (slider) slider.value = DEFAULT_SETTINGS.borderWidth;
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Border Style").setDesc("Choose the style of the footer border").addDropdown((dropdown) => dropdown.addOptions({
|
|
"solid": "Solid",
|
|
"dashed": "Dashed",
|
|
"dotted": "Dotted",
|
|
"double": "Double",
|
|
"groove": "Groove",
|
|
"ridge": "Ridge",
|
|
"inset": "Inset",
|
|
"outset": "Outset"
|
|
}).setValue(this.plugin.settings.borderStyle).onChange(async (value) => {
|
|
this.plugin.settings.borderStyle = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.borderStyle = DEFAULT_SETTINGS.borderStyle;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const dropdown = this.containerEl.querySelector("select");
|
|
if (dropdown) dropdown.value = DEFAULT_SETTINGS.borderStyle;
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Border Opacity").setDesc("Adjust the opacity of the footer border (0-1)").addSlider((slider) => slider.setLimits(0, 1, 0.1).setValue(this.plugin.settings.borderOpacity).setDynamicTooltip().onChange(async (value) => {
|
|
this.plugin.settings.borderOpacity = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.borderOpacity = DEFAULT_SETTINGS.borderOpacity;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const slider = button.buttonEl.parentElement.parentElement.querySelector('input[type="range"]');
|
|
if (slider) slider.value = DEFAULT_SETTINGS.borderOpacity;
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Border Color").setDesc("Choose the color for the footer border").addColorPicker((color) => color.setValue(this.plugin.settings.borderColor.startsWith("var(--") ? (() => {
|
|
const temp = document.createElement("div");
|
|
temp.style.borderColor = "var(--text-accent)";
|
|
document.body.appendChild(temp);
|
|
const color2 = getComputedStyle(temp).borderColor;
|
|
document.body.removeChild(temp);
|
|
return rgbToHex(color2);
|
|
})() : this.plugin.settings.borderColor).onChange(async (value) => {
|
|
this.plugin.settings.borderColor = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.borderColor = DEFAULT_SETTINGS.borderColor;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const colorPicker = button.buttonEl.parentElement.parentElement.querySelector('input[type="color"]');
|
|
if (colorPicker) {
|
|
const temp = document.createElement("div");
|
|
temp.style.borderColor = "var(--text-accent)";
|
|
document.body.appendChild(temp);
|
|
const color = getComputedStyle(temp).borderColor;
|
|
document.body.removeChild(temp);
|
|
colorPicker.value = rgbToHex(color);
|
|
}
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Link Border Radius").setDesc("Adjust the border radius of Backlinks and Outlinks (0-15px)").addSlider((slider) => slider.setLimits(0, 15, 1).setValue(this.plugin.settings.borderRadius).setDynamicTooltip().onChange(async (value) => {
|
|
this.plugin.settings.borderRadius = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.borderRadius = DEFAULT_SETTINGS.borderRadius;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const slider = button.buttonEl.parentElement.parentElement.querySelector('input[type="range"]');
|
|
if (slider) slider.value = DEFAULT_SETTINGS.borderRadius;
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Links Opacity").setDesc("Adjust the opacity of Backlinks and Outlinks (0-1)").addSlider((slider) => slider.setLimits(0, 1, 0.1).setValue(this.plugin.settings.linksOpacity).setDynamicTooltip().onChange(async (value) => {
|
|
this.plugin.settings.linksOpacity = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.linksOpacity = DEFAULT_SETTINGS.linksOpacity;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const slider = button.buttonEl.parentElement.parentElement.querySelector('input[type="range"]');
|
|
if (slider) slider.value = DEFAULT_SETTINGS.linksOpacity;
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Link Text Color").setDesc("Choose the color for link text").addColorPicker((color) => color.setValue(this.plugin.settings.linkColor.startsWith("var(--") ? (() => {
|
|
const temp = document.createElement("div");
|
|
temp.style.color = "var(--link-color)";
|
|
document.body.appendChild(temp);
|
|
const color2 = getComputedStyle(temp).color;
|
|
document.body.removeChild(temp);
|
|
return rgbToHex(color2);
|
|
})() : this.plugin.settings.linkColor).onChange(async (value) => {
|
|
this.plugin.settings.linkColor = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.linkColor = DEFAULT_SETTINGS.linkColor;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const colorPicker = button.buttonEl.parentElement.parentElement.querySelector('input[type="color"]');
|
|
if (colorPicker) {
|
|
const temp = document.createElement("div");
|
|
temp.style.color = "var(--link-color)";
|
|
document.body.appendChild(temp);
|
|
const color = getComputedStyle(temp).color;
|
|
document.body.removeChild(temp);
|
|
colorPicker.value = rgbToHex(color);
|
|
}
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Link Background Color").setDesc("Choose the background color for links").addColorPicker((color) => color.setValue(this.plugin.settings.linkBackgroundColor.startsWith("var(--") ? (() => {
|
|
const temp = document.createElement("div");
|
|
temp.style.backgroundColor = "var(--tag-background)";
|
|
document.body.appendChild(temp);
|
|
const color2 = getComputedStyle(temp).backgroundColor;
|
|
document.body.removeChild(temp);
|
|
return rgbToHex(color2);
|
|
})() : this.plugin.settings.linkBackgroundColor).onChange(async (value) => {
|
|
this.plugin.settings.linkBackgroundColor = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.linkBackgroundColor = DEFAULT_SETTINGS.linkBackgroundColor;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const colorPicker = button.buttonEl.parentElement.parentElement.querySelector('input[type="color"]');
|
|
if (colorPicker) {
|
|
const temp = document.createElement("div");
|
|
temp.style.backgroundColor = "var(--tag-background)";
|
|
document.body.appendChild(temp);
|
|
const color = getComputedStyle(temp).backgroundColor;
|
|
document.body.removeChild(temp);
|
|
colorPicker.value = rgbToHex(color);
|
|
}
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Link Border Color").setDesc("Choose the border color for links").addColorPicker((color) => color.setValue(this.plugin.settings.linkBorderColor.startsWith("rgba(") ? (() => {
|
|
const temp = document.createElement("div");
|
|
temp.style.borderColor = this.plugin.settings.linkBorderColor;
|
|
document.body.appendChild(temp);
|
|
const color2 = getComputedStyle(temp).borderColor;
|
|
document.body.removeChild(temp);
|
|
return rgbToHex(color2);
|
|
})() : this.plugin.settings.linkBorderColor).onChange(async (value) => {
|
|
this.plugin.settings.linkBorderColor = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.linkBorderColor = DEFAULT_SETTINGS.linkBorderColor;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const colorPicker = button.buttonEl.parentElement.parentElement.querySelector('input[type="color"]');
|
|
if (colorPicker) {
|
|
const temp = document.createElement("div");
|
|
temp.style.borderColor = DEFAULT_SETTINGS.linkBorderColor;
|
|
document.body.appendChild(temp);
|
|
const color = getComputedStyle(temp).borderColor;
|
|
document.body.removeChild(temp);
|
|
colorPicker.value = rgbToHex(color);
|
|
}
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Dates Opacity").setDesc("Adjust the opacity of the Created / Modified Dates (0-1)").addSlider((slider) => slider.setLimits(0, 1, 0.1).setValue(this.plugin.settings.datesOpacity).setDynamicTooltip().onChange(async (value) => {
|
|
this.plugin.settings.datesOpacity = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.datesOpacity = DEFAULT_SETTINGS.datesOpacity;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const slider = button.buttonEl.parentElement.parentElement.querySelector('input[type="range"]');
|
|
if (slider) slider.value = DEFAULT_SETTINGS.datesOpacity;
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("Date Color").setDesc("Choose the color for Created / Modified Dates").addColorPicker((color) => color.setValue(this.plugin.settings.dateColor.startsWith("var(--") ? (() => {
|
|
const temp = document.createElement("div");
|
|
temp.style.color = "var(--text-accent)";
|
|
document.body.appendChild(temp);
|
|
const color2 = getComputedStyle(temp).color;
|
|
document.body.removeChild(temp);
|
|
return rgbToHex(color2);
|
|
})() : this.plugin.settings.dateColor).onChange(async (value) => {
|
|
this.plugin.settings.dateColor = value;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.dateColor = DEFAULT_SETTINGS.dateColor;
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const colorPicker = button.buttonEl.parentElement.parentElement.querySelector('input[type="color"]');
|
|
if (colorPicker) {
|
|
const temp = document.createElement("div");
|
|
temp.style.color = "var(--text-accent)";
|
|
document.body.appendChild(temp);
|
|
const color = getComputedStyle(temp).color;
|
|
document.body.removeChild(temp);
|
|
colorPicker.value = rgbToHex(color);
|
|
}
|
|
}));
|
|
containerEl.createEl("hr");
|
|
containerEl.createEl("h3", { text: "Exclusion Rules" });
|
|
containerEl.createEl("h3", { text: "Excluded Folders" });
|
|
containerEl.createEl("p", {
|
|
text: "Notes in excluded folders (and their subfolders) will not display the Rich Foot footer. This is useful for system folders or areas where you don't want footer information to appear.",
|
|
cls: "setting-item-description"
|
|
});
|
|
const excludedFoldersContainer = containerEl.createDiv("excluded-folders-container");
|
|
if ((_a = this.plugin.settings) == null ? void 0 : _a.excludedFolders) {
|
|
this.plugin.settings.excludedFolders.forEach((folder, index) => {
|
|
const folderDiv = excludedFoldersContainer.createDiv("excluded-folder-item");
|
|
folderDiv.createSpan({ text: folder });
|
|
const deleteButton = folderDiv.createEl("button", {
|
|
text: "Delete",
|
|
cls: "excluded-folder-delete"
|
|
});
|
|
deleteButton.addEventListener("click", async () => {
|
|
this.plugin.settings.excludedFolders.splice(index, 1);
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
}
|
|
const newFolderSetting = new import_obsidian2.Setting(containerEl).setName("Add excluded folder").setDesc("Enter a folder path or browse to select").addText((text) => text.setPlaceholder("folder/subfolder")).addButton((button) => button.setButtonText("Browse").onClick(async () => {
|
|
const folder = await this.browseForFolder();
|
|
if (folder) {
|
|
const textComponent = newFolderSetting.components[0];
|
|
textComponent.setValue(folder);
|
|
}
|
|
})).addButton((button) => button.setButtonText("Add").onClick(async () => {
|
|
const textComponent = newFolderSetting.components[0];
|
|
const newFolder = textComponent.getValue().trim();
|
|
if (newFolder && !this.plugin.settings.excludedFolders.includes(newFolder)) {
|
|
this.plugin.settings.excludedFolders.push(newFolder);
|
|
await this.plugin.saveSettings();
|
|
textComponent.setValue("");
|
|
this.display();
|
|
}
|
|
}));
|
|
containerEl.createEl("h4", { text: "Exclude Rich Foot via Frontmatter" });
|
|
new import_obsidian2.Setting(containerEl).setName("Frontmatter Exclusion Field").setDesc("If this frontmatter field exists and has a truthy value (true, yes, 1, on), Rich Foot will not be shown on that note").addText((text) => text.setPlaceholder("e.g., exclude-rich-foot").setValue(this.plugin.settings.frontmatterExclusionField).onChange(async (value) => {
|
|
this.plugin.settings.frontmatterExclusionField = value.trim();
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
})).addButton((button) => button.setButtonText("Reset").onClick(async () => {
|
|
this.plugin.settings.frontmatterExclusionField = "";
|
|
await this.plugin.saveSettings();
|
|
await this.plugin.updateRichFoot();
|
|
const textComponent = button.buttonEl.parentElement.parentElement.querySelector('input[type="text"]');
|
|
if (textComponent) textComponent.value = "";
|
|
}));
|
|
containerEl.createEl("h4", { text: "Excluded Parent Selectors" });
|
|
containerEl.createEl("p", {
|
|
text: "Rich Foot will not be added to notes that have any of these parent selectors in their DOM hierarchy. Useful for compatibility with other plugins.",
|
|
cls: "setting-item-description"
|
|
});
|
|
const excludedSelectorsContainer = containerEl.createDiv("excluded-selectors-container");
|
|
if ((_b = this.plugin.settings) == null ? void 0 : _b.excludedParentSelectors) {
|
|
this.plugin.settings.excludedParentSelectors.forEach((selector, index) => {
|
|
const selectorDiv = excludedSelectorsContainer.createDiv("excluded-selector-item");
|
|
selectorDiv.createSpan({ text: selector });
|
|
const deleteButton = selectorDiv.createEl("button", {
|
|
text: "Delete",
|
|
cls: "excluded-selector-delete"
|
|
});
|
|
deleteButton.addEventListener("click", async () => {
|
|
this.plugin.settings.excludedParentSelectors.splice(index, 1);
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
}
|
|
const newSelectorSetting = new import_obsidian2.Setting(containerEl).setName("Add excluded parent selector").setDesc('Enter a CSS selector (e.g., .some-class, #some-id, [data-type="special"])').addText((text) => text.setPlaceholder("Enter selector").onChange(() => {
|
|
try {
|
|
document.querySelector(text.getValue());
|
|
text.inputEl.style.color = "";
|
|
} catch (e) {
|
|
text.inputEl.style.color = "var(--text-error)";
|
|
}
|
|
})).addButton((button) => button.setButtonText("Add").onClick(async () => {
|
|
const textComponent = newSelectorSetting.components[0];
|
|
const newSelector = textComponent.getValue().trim();
|
|
if (!newSelector) return;
|
|
try {
|
|
document.querySelector(newSelector);
|
|
} catch (e) {
|
|
new Notice("Invalid CSS selector");
|
|
return;
|
|
}
|
|
if (!this.plugin.settings.excludedParentSelectors.includes(newSelector)) {
|
|
this.plugin.settings.excludedParentSelectors.push(newSelector);
|
|
await this.plugin.saveSettings();
|
|
textComponent.setValue("");
|
|
this.display();
|
|
}
|
|
}));
|
|
containerEl.createEl("h3", { text: "Example Screenshot", cls: "rich-foot-example-title" });
|
|
const exampleDiv = containerEl.createDiv({ cls: "rich-foot-example" });
|
|
exampleDiv.createEl("img", {
|
|
attr: {
|
|
src: "https://raw.githubusercontent.com/jparkerweb/rich-foot/refs/heads/main/rich-foot.jpg",
|
|
alt: "Rich Foot Example"
|
|
}
|
|
});
|
|
new import_obsidian2.Setting(containerEl).setName("Show Release Notes").setDesc("Show release notes after plugin updates").addToggle((toggle) => toggle.setValue(this.plugin.settings.showReleaseNotes).onChange(async (value) => {
|
|
this.plugin.settings.showReleaseNotes = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian2.Setting(containerEl).setName("View Release Notes").setDesc("View release notes for the current version").addButton((button) => button.setButtonText("View Release Notes").onClick(async () => {
|
|
const notes = await this.plugin.getReleaseNotes(this.plugin.manifest.version);
|
|
new ReleaseNotesModal(this.app, this.plugin, this.plugin.manifest.version, notes).open();
|
|
}));
|
|
}
|
|
async browseForFolder() {
|
|
const folders = this.app.vault.getAllLoadedFiles().filter((file) => file.children).map((folder) => folder.path);
|
|
return new Promise((resolve) => {
|
|
const modal = new FolderSuggestModal(this.app, folders, (result) => {
|
|
resolve(result);
|
|
});
|
|
modal.open();
|
|
});
|
|
}
|
|
};
|
|
var FolderSuggestModal = class extends import_obsidian2.FuzzySuggestModal {
|
|
constructor(app, folders, onChoose) {
|
|
super(app);
|
|
this.folders = folders;
|
|
this.onChoose = onChoose;
|
|
}
|
|
getItems() {
|
|
return this.folders;
|
|
}
|
|
getItemText(item) {
|
|
return item;
|
|
}
|
|
onChooseItem(item, evt) {
|
|
this.onChoose(item);
|
|
}
|
|
};
|
|
|
|
// src/main.js
|
|
var RichFootPlugin = class extends import_obsidian3.Plugin {
|
|
async onload() {
|
|
await this.loadSettings();
|
|
document.documentElement.style.setProperty("--rich-foot-border-width", `${this.settings.borderWidth}px`);
|
|
document.documentElement.style.setProperty("--rich-foot-border-style", this.settings.borderStyle);
|
|
document.documentElement.style.setProperty("--rich-foot-border-opacity", this.settings.borderOpacity);
|
|
document.documentElement.style.setProperty("--rich-foot-border-radius", `${this.settings.borderRadius}px`);
|
|
document.documentElement.style.setProperty("--rich-foot-dates-opacity", this.settings.datesOpacity);
|
|
document.documentElement.style.setProperty("--rich-foot-links-opacity", this.settings.linksOpacity);
|
|
document.documentElement.style.setProperty("--rich-foot-date-color", this.settings.dateColor);
|
|
document.documentElement.style.setProperty("--rich-foot-border-color", this.settings.borderColor);
|
|
document.documentElement.style.setProperty("--rich-foot-link-color", this.settings.linkColor);
|
|
document.documentElement.style.setProperty("--rich-foot-link-background", this.settings.linkBackgroundColor);
|
|
document.documentElement.style.setProperty("--rich-foot-link-border-color", this.settings.linkBorderColor);
|
|
await this.checkVersion();
|
|
const updateRichFootCallback = async () => {
|
|
const activeLeaf = this.app.workspace.activeLeaf;
|
|
try {
|
|
await this.addRichFoot(activeLeaf.view);
|
|
this.adjustFooterPadding();
|
|
} catch (error) {
|
|
console.error("Error in debouncedUpdateRichFoot:", error);
|
|
}
|
|
};
|
|
this.debouncedUpdateRichFoot = (0, import_obsidian3.debounce)(updateRichFootCallback, this.settings.updateDelay, true);
|
|
this.debouncedUpdateRichFoot.callback = updateRichFootCallback;
|
|
this.immediateUpdateRichFoot = async () => {
|
|
const activeLeaf = this.app.workspace.activeLeaf;
|
|
try {
|
|
await this.addRichFoot(activeLeaf.view);
|
|
this.adjustFooterPadding();
|
|
} catch (error) {
|
|
console.error("Error in immediateUpdateRichFoot:", error);
|
|
}
|
|
};
|
|
this.addSettingTab(new RichFootSettingTab(this.app, this));
|
|
this.registerEvent(
|
|
this.app.metadataCache.on("changed", (file) => {
|
|
const cache = this.app.metadataCache.getFileCache(file);
|
|
if (cache == null ? void 0 : cache.frontmatter) {
|
|
const customCreatedProp = this.settings.customCreatedDateProp;
|
|
const customModifiedProp = this.settings.customModifiedDateProp;
|
|
if (customCreatedProp && customCreatedProp in cache.frontmatter || customModifiedProp && customModifiedProp in cache.frontmatter) {
|
|
if (this.isEditMode()) {
|
|
this.debouncedUpdateRichFoot();
|
|
} else {
|
|
this.immediateUpdateRichFoot();
|
|
}
|
|
}
|
|
}
|
|
})
|
|
);
|
|
this.app.workspace.onLayoutReady(() => {
|
|
this.registerEvent(
|
|
this.app.workspace.on("layout-change", async () => {
|
|
await this.immediateUpdateRichFoot();
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("active-leaf-change", async () => {
|
|
if (this.isEditMode()) {
|
|
await this.debouncedUpdateRichFoot();
|
|
} else {
|
|
await this.immediateUpdateRichFoot();
|
|
}
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("file-open", async () => {
|
|
await this.immediateUpdateRichFoot();
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("mode-change", async (event) => {
|
|
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
|
|
if (activeView) {
|
|
await this.immediateUpdateRichFoot();
|
|
}
|
|
})
|
|
);
|
|
this.registerEvent(
|
|
this.app.workspace.on("editor-change", async () => {
|
|
await this.debouncedUpdateRichFoot();
|
|
})
|
|
);
|
|
this.immediateUpdateRichFoot();
|
|
});
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
document.documentElement.style.setProperty("--rich-foot-date-color", this.settings.dateColor);
|
|
if (!Array.isArray(this.settings.excludedFolders)) {
|
|
this.settings.excludedFolders = [];
|
|
}
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
async checkVersion() {
|
|
const currentVersion = this.manifest.version;
|
|
const lastVersion = this.settings.lastVersion;
|
|
const shouldShow = this.settings.showReleaseNotes && (!lastVersion || lastVersion !== currentVersion);
|
|
if (shouldShow) {
|
|
const releaseNotes2 = await this.getReleaseNotes(currentVersion);
|
|
new ReleaseNotesModal(this.app, this, currentVersion, releaseNotes2).open();
|
|
this.settings.lastVersion = currentVersion;
|
|
await this.saveSettings();
|
|
}
|
|
}
|
|
async getReleaseNotes(version) {
|
|
return releaseNotes;
|
|
}
|
|
async updateRichFoot() {
|
|
document.documentElement.style.setProperty("--rich-foot-border-width", `${this.settings.borderWidth}px`);
|
|
document.documentElement.style.setProperty("--rich-foot-border-style", this.settings.borderStyle);
|
|
document.documentElement.style.setProperty("--rich-foot-border-opacity", this.settings.borderOpacity);
|
|
document.documentElement.style.setProperty("--rich-foot-border-radius", `${this.settings.borderRadius}px`);
|
|
document.documentElement.style.setProperty("--rich-foot-dates-opacity", this.settings.datesOpacity);
|
|
document.documentElement.style.setProperty("--rich-foot-links-opacity", this.settings.linksOpacity);
|
|
document.documentElement.style.setProperty("--rich-foot-date-color", this.settings.dateColor);
|
|
document.documentElement.style.setProperty("--rich-foot-border-color", this.settings.borderColor);
|
|
document.documentElement.style.setProperty("--rich-foot-link-color", this.settings.linkColor);
|
|
document.documentElement.style.setProperty("--rich-foot-link-background", this.settings.linkBackgroundColor);
|
|
document.documentElement.style.setProperty("--rich-foot-link-border-color", this.settings.linkBorderColor);
|
|
const activeLeaf = this.app.workspace.activeLeaf;
|
|
if ((activeLeaf == null ? void 0 : activeLeaf.view) instanceof import_obsidian3.MarkdownView) {
|
|
await this.addRichFoot(activeLeaf.view);
|
|
}
|
|
}
|
|
async addRichFoot(view) {
|
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
try {
|
|
const file = view.file;
|
|
if (!file || !file.path) {
|
|
return;
|
|
}
|
|
if (this.shouldExcludeFile(file.path)) {
|
|
const existingRichFoots2 = view.contentEl.querySelectorAll(".rich-foot");
|
|
existingRichFoots2.forEach((el) => el.remove());
|
|
return;
|
|
}
|
|
const content = view.contentEl;
|
|
let container;
|
|
if (((_b = (_a = view.getMode) == null ? void 0 : _a.call(view)) != null ? _b : view.mode) === "preview") {
|
|
const previewSections = content.querySelectorAll(".markdown-preview-section");
|
|
for (const section of previewSections) {
|
|
if (!section.closest(".internal-embed")) {
|
|
container = section;
|
|
break;
|
|
}
|
|
}
|
|
} else if (((_d = (_c = view.getMode) == null ? void 0 : _c.call(view)) != null ? _d : view.mode) === "source" || ((_f = (_e = view.getMode) == null ? void 0 : _e.call(view)) != null ? _f : view.mode) === "live") {
|
|
container = content.querySelector(".cm-sizer");
|
|
}
|
|
if (!container) {
|
|
return;
|
|
}
|
|
if ((_h = (_g = this.settings) == null ? void 0 : _g.excludedParentSelectors) == null ? void 0 : _h.some((selector) => {
|
|
try {
|
|
const matchingElements = content.querySelectorAll(selector);
|
|
return Array.from(matchingElements).some(
|
|
(el) => el === container || el.contains(container)
|
|
);
|
|
} catch (e) {
|
|
console.error(`Invalid selector in Rich Foot settings: ${selector}`);
|
|
return false;
|
|
}
|
|
})) {
|
|
const existingRichFoots2 = view.contentEl.querySelectorAll(".rich-foot");
|
|
existingRichFoots2.forEach((el) => el.remove());
|
|
return;
|
|
}
|
|
const existingRichFoots = view.contentEl.querySelectorAll(".rich-foot");
|
|
existingRichFoots.forEach((el) => el.remove());
|
|
this.disconnectObservers();
|
|
const richFoot = await this.createRichFoot(file);
|
|
const newCheck = view.contentEl.querySelectorAll(".rich-foot");
|
|
if (newCheck.length > 0) {
|
|
newCheck.forEach((el) => el.remove());
|
|
}
|
|
container.appendChild(richFoot);
|
|
this.observeContainer(container);
|
|
} catch (error) {
|
|
console.error("Error in addRichFoot:", error);
|
|
}
|
|
}
|
|
removeExistingRichFoot(container) {
|
|
var _a;
|
|
const existingRichFoot = container.querySelector(".rich-foot");
|
|
if (existingRichFoot) {
|
|
existingRichFoot.remove();
|
|
}
|
|
const cmEditor = container.closest(".cm-editor");
|
|
if (cmEditor) {
|
|
const cmSizer = cmEditor.querySelector(".cm-sizer");
|
|
if (cmSizer) {
|
|
const richFootInSizer = cmSizer.querySelector(".rich-foot");
|
|
if (richFootInSizer) {
|
|
richFootInSizer.remove();
|
|
}
|
|
}
|
|
}
|
|
const previewSection = (_a = container.closest(".markdown-reading-view")) == null ? void 0 : _a.querySelector(".markdown-preview-section");
|
|
if (previewSection) {
|
|
const richFootInPreview = previewSection.querySelector(".rich-foot");
|
|
if (richFootInPreview) {
|
|
richFootInPreview.remove();
|
|
}
|
|
}
|
|
}
|
|
disconnectObservers() {
|
|
if (this.contentObserver) {
|
|
this.contentObserver.disconnect();
|
|
}
|
|
if (this.containerObserver) {
|
|
this.containerObserver.disconnect();
|
|
}
|
|
}
|
|
observeContainer(container) {
|
|
if (this.containerObserver) {
|
|
this.containerObserver.disconnect();
|
|
}
|
|
this.containerObserver = new MutationObserver(async (mutations) => {
|
|
var _a;
|
|
const richFoot = container.querySelector(".rich-foot");
|
|
if (!richFoot) {
|
|
const view = (_a = this.app.workspace.activeLeaf) == null ? void 0 : _a.view;
|
|
if (view instanceof import_obsidian3.MarkdownView) {
|
|
try {
|
|
await this.addRichFoot(view);
|
|
} catch (error) {
|
|
console.error("Error in mutation observer:", error);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
this.containerObserver.observe(container, { childList: true, subtree: true });
|
|
}
|
|
async createRichFoot(file) {
|
|
const richFoot = createDiv({ cls: "rich-foot rich-foot--hidden" });
|
|
richFoot.createDiv({ cls: "rich-foot--dashed-line" });
|
|
const backlinksData = this.app.metadataCache.getBacklinksForFile(file);
|
|
const outlinks = await this.getOutlinks(file);
|
|
if (this.settings.combineLinks) {
|
|
if ((backlinksData == null ? void 0 : backlinksData.data) && backlinksData.data.size > 0 || outlinks.size > 0) {
|
|
const linksDiv = richFoot.createDiv({ cls: "rich-foot--links" });
|
|
const linksUl = linksDiv.createEl("ul");
|
|
const processedLinks = /* @__PURE__ */ new Set();
|
|
if (backlinksData == null ? void 0 : backlinksData.data) {
|
|
for (const [linkPath, linkData] of backlinksData.data) {
|
|
if (!linkPath.endsWith(".md")) continue;
|
|
processedLinks.add(linkPath);
|
|
const li = linksUl.createEl("li");
|
|
const link = li.createEl("a", {
|
|
href: linkPath,
|
|
text: linkPath.split("/").pop().slice(0, -3),
|
|
cls: this.isEditMode() ? "cm-hmd-internal-link cm-underline" : "internal-link"
|
|
});
|
|
link.dataset.href = linkPath;
|
|
link.dataset.sourcePath = file.path;
|
|
link.dataset.isBacklink = "true";
|
|
if (outlinks.has(linkPath)) {
|
|
link.dataset.isOutlink = "true";
|
|
}
|
|
this.setupLinkBehavior(link, linkPath, file);
|
|
}
|
|
}
|
|
for (const linkPath of outlinks) {
|
|
if (processedLinks.has(linkPath)) continue;
|
|
const li = linksUl.createEl("li");
|
|
const link = li.createEl("a", {
|
|
href: linkPath,
|
|
text: linkPath.split("/").pop().slice(0, -3),
|
|
cls: this.isEditMode() ? "cm-hmd-internal-link cm-underline" : "internal-link"
|
|
});
|
|
link.dataset.href = linkPath;
|
|
link.dataset.sourcePath = file.path;
|
|
link.dataset.isOutlink = "true";
|
|
this.setupLinkBehavior(link, linkPath, file);
|
|
}
|
|
if (linksUl.childElementCount === 0) {
|
|
linksDiv.remove();
|
|
}
|
|
}
|
|
} else {
|
|
if (this.settings.showBacklinks && (backlinksData == null ? void 0 : backlinksData.data) && backlinksData.data.size > 0) {
|
|
const backlinksDiv = richFoot.createDiv({ cls: "rich-foot--backlinks" });
|
|
const backlinksUl = backlinksDiv.createEl("ul");
|
|
for (const [linkPath, linkData] of backlinksData.data) {
|
|
if (!linkPath.endsWith(".md")) continue;
|
|
const li = backlinksUl.createEl("li");
|
|
const link = li.createEl("a", {
|
|
href: linkPath,
|
|
text: linkPath.split("/").pop().slice(0, -3),
|
|
cls: this.isEditMode() ? "cm-hmd-internal-link cm-underline" : "internal-link"
|
|
});
|
|
link.dataset.href = linkPath;
|
|
link.dataset.sourcePath = file.path;
|
|
this.setupLinkBehavior(link, linkPath, file);
|
|
}
|
|
if (backlinksUl.childElementCount === 0) {
|
|
backlinksDiv.remove();
|
|
}
|
|
}
|
|
if (this.settings.showOutlinks && outlinks.size > 0) {
|
|
const outlinksDiv = richFoot.createDiv({ cls: "rich-foot--outlinks" });
|
|
const outlinksUl = outlinksDiv.createEl("ul");
|
|
for (const linkPath of outlinks) {
|
|
const li = outlinksUl.createEl("li");
|
|
const link = li.createEl("a", {
|
|
href: linkPath,
|
|
text: linkPath.split("/").pop().slice(0, -3),
|
|
cls: this.isEditMode() ? "cm-hmd-internal-link cm-underline" : "internal-link"
|
|
});
|
|
link.dataset.href = linkPath;
|
|
link.dataset.sourcePath = file.path;
|
|
this.setupLinkBehavior(link, linkPath, file);
|
|
}
|
|
if (outlinksUl.childElementCount === 0) {
|
|
outlinksDiv.remove();
|
|
}
|
|
}
|
|
}
|
|
if (this.settings.showDates) {
|
|
const datesWrapper = richFoot.createDiv({ cls: "rich-foot--dates-wrapper" });
|
|
const cache = this.app.metadataCache.getFileCache(file);
|
|
const frontmatter = cache == null ? void 0 : cache.frontmatter;
|
|
let modifiedDate;
|
|
if (this.settings.customModifiedDateProp && frontmatter && frontmatter[this.settings.customModifiedDateProp]) {
|
|
modifiedDate = frontmatter[this.settings.customModifiedDateProp];
|
|
let isValidDate = false;
|
|
let tempDate = modifiedDate;
|
|
if (!isNaN(Date.parse(tempDate))) {
|
|
isValidDate = true;
|
|
}
|
|
if (!isValidDate) {
|
|
let count = 0;
|
|
tempDate = modifiedDate.replace(/\./g, (match) => {
|
|
count++;
|
|
return count <= 2 ? "-" : match;
|
|
});
|
|
if (!isNaN(Date.parse(tempDate))) {
|
|
isValidDate = true;
|
|
}
|
|
}
|
|
if (!isValidDate) {
|
|
let count = 0;
|
|
tempDate = modifiedDate.replace(/\//g, (match) => {
|
|
count++;
|
|
return count <= 2 ? "-" : match;
|
|
});
|
|
if (!isNaN(Date.parse(tempDate))) {
|
|
isValidDate = true;
|
|
}
|
|
}
|
|
if (isValidDate) {
|
|
if (!tempDate.includes("T") && !tempDate.includes(" ")) {
|
|
tempDate = `${tempDate}T00:00:00`;
|
|
}
|
|
const dateObj = new Date(tempDate);
|
|
modifiedDate = formatDate(dateObj, this.settings.dateDisplayFormat);
|
|
} else {
|
|
modifiedDate = modifiedDate;
|
|
}
|
|
} else {
|
|
modifiedDate = new Date(file.stat.mtime);
|
|
modifiedDate = formatDate(modifiedDate, this.settings.dateDisplayFormat);
|
|
}
|
|
datesWrapper.createDiv({
|
|
cls: "rich-foot--modified-date",
|
|
text: `${modifiedDate}`
|
|
});
|
|
let createdDate;
|
|
if (this.settings.customCreatedDateProp && frontmatter && frontmatter[this.settings.customCreatedDateProp]) {
|
|
createdDate = frontmatter[this.settings.customCreatedDateProp];
|
|
let isValidDate = false;
|
|
let tempDate = createdDate;
|
|
if (!isNaN(Date.parse(tempDate))) {
|
|
isValidDate = true;
|
|
}
|
|
if (!isValidDate) {
|
|
let count = 0;
|
|
tempDate = createdDate.replace(/\./g, (match) => {
|
|
count++;
|
|
return count <= 2 ? "-" : match;
|
|
});
|
|
if (!isNaN(Date.parse(tempDate))) {
|
|
isValidDate = true;
|
|
}
|
|
}
|
|
if (!isValidDate) {
|
|
let count = 0;
|
|
tempDate = createdDate.replace(/\//g, (match) => {
|
|
count++;
|
|
return count <= 2 ? "-" : match;
|
|
});
|
|
if (!isNaN(Date.parse(tempDate))) {
|
|
isValidDate = true;
|
|
}
|
|
}
|
|
if (isValidDate) {
|
|
if (!tempDate.includes("T") && !tempDate.includes(" ")) {
|
|
tempDate = `${tempDate}T00:00:00`;
|
|
}
|
|
const dateObj = new Date(tempDate);
|
|
createdDate = formatDate(dateObj, this.settings.dateDisplayFormat);
|
|
} else {
|
|
createdDate = createdDate;
|
|
}
|
|
} else {
|
|
createdDate = new Date(file.stat.ctime);
|
|
createdDate = formatDate(createdDate, this.settings.dateDisplayFormat);
|
|
}
|
|
datesWrapper.createDiv({
|
|
cls: "rich-foot--created-date",
|
|
text: `${createdDate}`
|
|
});
|
|
}
|
|
setTimeout(() => {
|
|
richFoot.removeClass("rich-foot--hidden");
|
|
}, 10);
|
|
return richFoot;
|
|
}
|
|
async getOutlinks(file) {
|
|
const cache = this.app.metadataCache.getFileCache(file);
|
|
const links = /* @__PURE__ */ new Set();
|
|
if (cache == null ? void 0 : cache.links) {
|
|
for (const link of cache.links) {
|
|
const linkPath = link.link.split("#")[0];
|
|
const targetFile = this.app.metadataCache.getFirstLinkpathDest(linkPath, file.path);
|
|
if (targetFile && targetFile.extension === "md") {
|
|
links.add(targetFile.path);
|
|
}
|
|
}
|
|
}
|
|
if (cache == null ? void 0 : cache.embeds) {
|
|
for (const embed of cache.embeds) {
|
|
const linkPath = embed.link.split("#")[0];
|
|
const targetFile = this.app.metadataCache.getFirstLinkpathDest(linkPath, file.path);
|
|
if (targetFile && targetFile.extension === "md") {
|
|
links.add(targetFile.path);
|
|
}
|
|
}
|
|
}
|
|
if (cache == null ? void 0 : cache.frontmatterLinks) {
|
|
for (const link of cache.frontmatterLinks) {
|
|
const linkPath = link.link.split("#")[0];
|
|
const targetFile = this.app.metadataCache.getFirstLinkpathDest(linkPath, file.path);
|
|
if (targetFile && targetFile.extension === "md") {
|
|
links.add(targetFile.path);
|
|
}
|
|
}
|
|
}
|
|
if (cache == null ? void 0 : cache.blocks) {
|
|
for (const block of Object.values(cache.blocks)) {
|
|
if (block.type === "footnote") {
|
|
const wikiLinkRegex = /\[\[(.*?)\]\]/g;
|
|
let wikiMatch;
|
|
while ((wikiMatch = wikiLinkRegex.exec(block.text)) !== null) {
|
|
const linkText = wikiMatch[1];
|
|
const linkPath = linkText.split("#")[0];
|
|
const targetFile = this.app.metadataCache.getFirstLinkpathDest(linkPath, file.path);
|
|
if (targetFile && targetFile.extension === "md") {
|
|
links.add(targetFile.path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const fileContent = await this.app.vault.read(file);
|
|
const inlineFootnoteRegex = /\^\[((?:[^\[\]]|\[(?:[^\[\]]|\[[^\[\]]*\])*\])*)\]/g;
|
|
const refFootnoteRegex = /\[\^[^\]]+\]:\s*((?:[^\[\]]|\[(?:[^\[\]]|\[[^\[\]]*\])*\])*)/g;
|
|
let match;
|
|
while ((match = inlineFootnoteRegex.exec(fileContent)) !== null) {
|
|
const footnoteContent = match[1];
|
|
await this.processFootnoteContent(footnoteContent, file, links);
|
|
}
|
|
while ((match = refFootnoteRegex.exec(fileContent)) !== null) {
|
|
const footnoteContent = match[1];
|
|
await this.processFootnoteContent(footnoteContent, file, links);
|
|
}
|
|
return links;
|
|
}
|
|
async processFootnoteContent(content, file, links) {
|
|
const wikiLinkRegex = /\[\[(.*?)\]\]/g;
|
|
let wikiMatch;
|
|
while ((wikiMatch = wikiLinkRegex.exec(content)) !== null) {
|
|
const linkText = wikiMatch[1].trim();
|
|
const linkPath = linkText.split("#")[0];
|
|
const targetFile = this.app.metadataCache.getFirstLinkpathDest(linkPath, file.path);
|
|
if (targetFile && targetFile.extension === "md") {
|
|
links.add(targetFile.path);
|
|
}
|
|
}
|
|
}
|
|
setupLinkBehavior(link, linkPath, file) {
|
|
if (this.isEditMode()) {
|
|
let hoverTimeout = null;
|
|
link.addEventListener("mouseover", (mouseEvent) => {
|
|
var _a;
|
|
const pagePreviewPlugin = this.app.internalPlugins.plugins["page-preview"];
|
|
if (!(pagePreviewPlugin == null ? void 0 : pagePreviewPlugin.enabled)) {
|
|
return;
|
|
}
|
|
if (hoverTimeout) {
|
|
clearTimeout(hoverTimeout);
|
|
hoverTimeout = null;
|
|
}
|
|
const previewPlugin = (_a = this.app.internalPlugins.plugins["page-preview"]) == null ? void 0 : _a.instance;
|
|
if (previewPlugin == null ? void 0 : previewPlugin.onLinkHover) {
|
|
previewPlugin.onLinkHover(mouseEvent, link, linkPath, file.path);
|
|
}
|
|
});
|
|
link.addEventListener("mouseout", (mouseEvent) => {
|
|
if (hoverTimeout) {
|
|
clearTimeout(hoverTimeout);
|
|
}
|
|
hoverTimeout = setTimeout(() => {
|
|
var _a;
|
|
const previewPlugin = (_a = this.app.internalPlugins.plugins["page-preview"]) == null ? void 0 : _a.instance;
|
|
const hoverParent = (previewPlugin == null ? void 0 : previewPlugin.hoverParent) || document.body;
|
|
const previews = hoverParent.querySelectorAll(".hover-popup");
|
|
previews.forEach((preview) => preview.remove());
|
|
hoverTimeout = null;
|
|
}, 50);
|
|
});
|
|
}
|
|
link.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
this.app.workspace.openLinkText(linkPath, file.path);
|
|
});
|
|
}
|
|
onunload() {
|
|
this.disconnectObservers();
|
|
document.querySelectorAll(".rich-foot").forEach((el) => el.remove());
|
|
this.app.workspace.off("layout-change", this.updateRichFoot);
|
|
this.app.workspace.off("active-leaf-change", this.updateRichFoot);
|
|
this.app.workspace.off("file-open", this.updateRichFoot);
|
|
this.app.workspace.off("editor-change", this.updateRichFoot);
|
|
}
|
|
// check if a file should be excluded
|
|
shouldExcludeFile(filePath) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
if ((_b = (_a = this.settings) == null ? void 0 : _a.excludedFolders) == null ? void 0 : _b.some((folder) => filePath.startsWith(folder))) {
|
|
return true;
|
|
}
|
|
const file = this.app.vault.getAbstractFileByPath(filePath);
|
|
if (file && this.settings.frontmatterExclusionField) {
|
|
const cache = this.app.metadataCache.getFileCache(file);
|
|
const frontmatterValue = (_c = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _c[this.settings.frontmatterExclusionField];
|
|
if (this.isTruthy(frontmatterValue)) {
|
|
return true;
|
|
}
|
|
}
|
|
const activeLeaf = this.app.workspace.activeLeaf;
|
|
if ((_d = activeLeaf == null ? void 0 : activeLeaf.view) == null ? void 0 : _d.containerEl) {
|
|
return (_f = (_e = this.settings) == null ? void 0 : _e.excludedParentSelectors) == null ? void 0 : _f.some((selector) => {
|
|
var _a2, _b2;
|
|
try {
|
|
let element = activeLeaf.view.containerEl;
|
|
while (element) {
|
|
if ((_a2 = element.matches) == null ? void 0 : _a2.call(element, selector)) {
|
|
return true;
|
|
}
|
|
if ((_b2 = element.querySelector) == null ? void 0 : _b2.call(element, selector)) {
|
|
return true;
|
|
}
|
|
element = element.parentElement;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
console.error(`Invalid selector in Rich Foot settings: ${selector}`);
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
isEditMode() {
|
|
var _a, _b;
|
|
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
|
|
if (!activeView) return false;
|
|
return ((_b = (_a = activeView.getMode) == null ? void 0 : _a.call(activeView)) != null ? _b : activeView.mode) === "source";
|
|
}
|
|
// adjust footer padding
|
|
adjustFooterPadding() {
|
|
setTimeout(() => {
|
|
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
|
|
if (!activeView) return;
|
|
const readingView = activeView.contentEl.querySelector(".markdown-reading-view");
|
|
if (!readingView) return;
|
|
const preview = readingView.querySelector(".markdown-preview-view");
|
|
const previewSizer = readingView.querySelector(".markdown-preview-sizer");
|
|
const footer = readingView.querySelector(".markdown-preview-sizer > .rich-foot");
|
|
if (!preview || !previewSizer || !footer) return;
|
|
readingView.style.setProperty("--rich-foot-top-padding", "0px");
|
|
const contentHeight = previewSizer.offsetHeight - footer.offsetHeight;
|
|
const availableSpace = preview.offsetHeight - contentHeight - footer.offsetHeight - 85;
|
|
if (availableSpace > 20) {
|
|
readingView.style.setProperty("--rich-foot-top-padding", `${availableSpace}px`);
|
|
readingView.style.setProperty("--rich-foot-margin-bottom", "0");
|
|
} else {
|
|
readingView.style.setProperty("--rich-foot-top-padding", "10px");
|
|
readingView.style.setProperty("--rich-foot-margin-bottom", "20px");
|
|
}
|
|
}, 100);
|
|
}
|
|
isTruthy(value) {
|
|
if (!value) return false;
|
|
const truthyValues = ["true", "yes", "1", "on"];
|
|
return truthyValues.includes(String(value).toLowerCase());
|
|
}
|
|
};
|
|
var main_default = RichFootPlugin;
|
|
|
|
/* nosourcemap */ |