diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ebcf1e..c58d0cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to Rich Foot will be documented in this file. +## [1.10.5] - 2024-12-26 +### 📦 Updated +- Support for more date formats in `frontmatter` created/modified fields (ISO, space-separated, and just date) + ## [1.10.4] - 2024-12-23 ### 🐛 Fixed - Fixed issue with Rich Foot not loading all user defined colors when Obsidian is restarted diff --git a/UPDATE.md b/UPDATE.md index 876177a..f5b5775 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -1,5 +1,9 @@ ## 🛑 Exclude Me Please +### [1.10.5] - 2024-12-26 +#### 📦 Updated +- Support for more date formats in `frontmatter` created/modified fields (ISO, space-separated, and just date) + ### [1.10.4] - 2024-12-23 #### 🐛 Fixed - Fixed issue with Rich Foot not loading all user defined colors when Obsidian is restarted diff --git a/example-vault.zip b/example-vault.zip index 26e701a..b098ff7 100644 Binary files a/example-vault.zip and b/example-vault.zip differ diff --git a/example-vault/rich-foot-example/.obsidian/plugins/pexels-banner/data.json b/example-vault/rich-foot-example/.obsidian/plugins/pexels-banner/data.json index c3f643a..dfdfc36 100644 --- a/example-vault/rich-foot-example/.obsidian/plugins/pexels-banner/data.json +++ b/example-vault/rich-foot-example/.obsidian/plugins/pexels-banner/data.json @@ -51,7 +51,7 @@ "showReleaseNotes": true, "lastVersion": "2.16.2", "showRefreshIcon": true, - "showViewImageIcon": false, + "showViewImageIcon": true, "hidePixelBannerFields": true, "hidePropertiesSectionIfOnlyBanner": true, "titleColor": "var(--inline-title-color)", diff --git a/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/data.json b/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/data.json index 29f4177..b29f470 100644 --- a/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/data.json +++ b/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/data.json @@ -26,5 +26,5 @@ "[data-type=\"thino_view\"]" ], "frontmatterExclusionField": "", - "lastVersion": "1.10.3" + "lastVersion": "1.10.4" } \ No newline at end of file diff --git a/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/main.js b/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/main.js index 347b04b..2583efb 100644 --- a/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/main.js +++ b/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/main.js @@ -107,35 +107,12 @@ var ReleaseNotesModal = class extends import_obsidian.Modal { }; // virtual-module:virtual:release-notes -var releaseNotes = '

\u{1F6D1} Exclude Me Please

\n

[1.10.4] - 2024-12-23

\n

\u{1F41B} Fixed

\n\n

[1.10.3] - 2024-12-14

\n

\u{1F41B} Fixed

\n\n

[1.10.2] - 2024-12-11

\n

\u{1F41B} Fixed

\n\n

[1.10.1] - 2024-12-10

\n

\u{1F41B} Fixed

\n\n

[1.10.0] - 2024-12-08

\n

\u2728 Added

\n\n

screenshot

\n'; +var releaseNotes = '

\u{1F6D1} Exclude Me Please

\n

[1.10.5] - 2024-12-26

\n

\u{1F4E6} Updated

\n\n

[1.10.4] - 2024-12-23

\n

\u{1F41B} Fixed

\n\n

[1.10.3] - 2024-12-14

\n

\u{1F41B} Fixed

\n\n

[1.10.2] - 2024-12-11

\n

\u{1F41B} Fixed

\n\n

[1.10.1] - 2024-12-10

\n

\u{1F41B} Fixed

\n\n

[1.10.0] - 2024-12-08

\n

\u2728 Added

\n\n

screenshot

\n'; // src/settings.js var import_obsidian2 = require("obsidian"); -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: "" -}; + +// src/utils.js function rgbToHex(color) { if (color.startsWith("hsl")) { const temp = document.createElement("div"); @@ -188,6 +165,33 @@ function formatDate(date, format) { }); 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); @@ -646,42 +650,6 @@ var FolderSuggestModal = class extends import_obsidian2.FuzzySuggestModal { }; // src/main.js -function formatDate2(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; - const replacements = /* @__PURE__ */ new Map(); - sortedTokens.forEach((token, index) => { - const placeholder = `__${index}__`; - replacements.set(placeholder, tokens[token]); - result = result.replace(new RegExp(token, "g"), placeholder); - }); - replacements.forEach((value, placeholder) => { - result = result.replace(new RegExp(placeholder, "g"), value); - }); - return result; -} var RichFootPlugin = class extends import_obsidian3.Plugin { async onload() { await this.loadSettings(); @@ -916,7 +884,7 @@ var RichFootPlugin = class extends import_obsidian3.Plugin { } async createRichFoot(file) { const richFoot = createDiv({ cls: "rich-foot rich-foot--hidden" }); - const richFootDashedLine = richFoot.createDiv({ cls: "rich-foot--dashed-line" }); + richFoot.createDiv({ cls: "rich-foot--dashed-line" }); const backlinksData = this.app.metadataCache.getBacklinksForFile(file); const outlinks = await this.getOutlinks(file); if (this.settings.combineLinks) { @@ -1032,16 +1000,17 @@ var RichFootPlugin = class extends import_obsidian3.Plugin { } } if (isValidDate) { - const datePart = tempDate.split("T")[0]; - const dateStr = tempDate.includes("T") ? tempDate : `${datePart}T00:00:00`; - const dateObj = new Date(dateStr); - modifiedDate = formatDate2(dateObj, this.settings.dateDisplayFormat); + 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 = formatDate2(modifiedDate, this.settings.dateDisplayFormat); + modifiedDate = formatDate(modifiedDate, this.settings.dateDisplayFormat); } datesWrapper.createDiv({ cls: "rich-foot--modified-date", @@ -1076,16 +1045,17 @@ var RichFootPlugin = class extends import_obsidian3.Plugin { } } if (isValidDate) { - const datePart = tempDate.split("T")[0]; - const dateStr = tempDate.includes("T") ? tempDate : `${datePart}T00:00:00`; - const dateObj = new Date(dateStr); - createdDate = formatDate2(dateObj, this.settings.dateDisplayFormat); + 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 = formatDate2(createdDate, this.settings.dateDisplayFormat); + createdDate = formatDate(createdDate, this.settings.dateDisplayFormat); } datesWrapper.createDiv({ cls: "rich-foot--created-date", diff --git a/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/manifest.json b/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/manifest.json index b39727c..e1989bd 100644 --- a/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/manifest.json +++ b/example-vault/rich-foot-example/.obsidian/plugins/rich-foot/manifest.json @@ -1,7 +1,7 @@ { "id": "rich-foot", "name": "Rich Foot", - "version": "1.10.4", + "version": "1.10.5", "minAppVersion": "1.5.0", "description": "Adds backlink tags and created/modified dates to the footer of your notes.", "author": "Justin Parker (eQui\\\\ Labs)", diff --git a/example-vault/rich-foot-example/.obsidian/workspace.json b/example-vault/rich-foot-example/.obsidian/workspace.json index cfeae76..34a71d4 100644 --- a/example-vault/rich-foot-example/.obsidian/workspace.json +++ b/example-vault/rich-foot-example/.obsidian/workspace.json @@ -15,7 +15,7 @@ "state": { "file": "🦶 Rich Foot.md", "mode": "preview", - "source": false, + "source": true, "backlinks": false }, "icon": "lucide-file", @@ -3378,6 +3378,236 @@ { "id": "5825c202c1412179", "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "58cbb175b1e021d0", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "1ecf112a5bef5476", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "6c7c4645339063cd", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "46d4d1d12fec7d05", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "358e834d45b842ce", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "836d8dd8e3440b19", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "cfa6590c4831d5f2", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "39008dea0d9a08f7", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "ab2478293c8deb0b", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "cc33c974312c0128", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "90ae53e2a62b7111", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "3974a8d195b7af6d", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "45eda246386b4e48", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "7faf0fc3ff5c1075", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "8ed38d383b0fef94", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "c6b64c6c59683a9d", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "fa96e498f26cdffe", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "bf6c559efc6f85ba", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "48f06b6059f3d67b", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "989c29a17fd427fa", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "01b6e00a0e3ef6bf", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "fb6f8327a3f0b8d2", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + }, + { + "id": "9ccbc65e7fda67bf", + "type": "leaf", "state": { "type": "Saved Queries View", "state": {}, @@ -3386,7 +3616,7 @@ } } ], - "currentTab": 328 + "currentTab": 351 } ], "direction": "horizontal", @@ -3406,9 +3636,15 @@ "cmdr:Open Plugin Settings: Rich Foot": false } }, - "active": "bf09cc60e5809e92", + "active": "9ccbc65e7fda67bf", "lastOpenFiles": [ "🦶 Rich Foot.md", + "images/rich-feet-3.jpg", + "images/rich-feet-2.jpg", + "images/rich-feet.jpg", + "pixel-banner-images/calendar-feet.png", + "pixel-banner-images/stuffed-links.png", + "pixel-banner-images/no-fee-allowed.png", "releases/v1.7.0 - 📆 Dates Your Way.md", "releases/v1.8.0 - 🫣 Page Preview Support.md", "releases/v1.9.0 - 🥙 Stuffed Links.md", @@ -3424,17 +3660,11 @@ "misc-notes/three.md", "misc-notes/link test.md", "misc-notes/link test 2.md", - "pixel-banner-images/no-fee-allowed.png", "test.md", - "images/rich-feet-2.jpg", - "pixel-banner-images/stuffed-links.png", - "images/rich-feet-3.jpg", - "pixel-banner-images/calendar-feet.png", "pixel-banner-images/feet-dreaming.png", "misc-notes", "Pasted image 20241129154754.png", "note with table.md", - "images/rich-feet.jpg", "images", "exclude/me too", "exclude", diff --git a/example-vault/rich-foot-example/images/rich-feet-2.jpg b/example-vault/rich-foot-example/images/rich-feet-2.jpg index 7703f64..5564ace 100644 Binary files a/example-vault/rich-foot-example/images/rich-feet-2.jpg and b/example-vault/rich-foot-example/images/rich-feet-2.jpg differ diff --git a/example-vault/rich-foot-example/images/rich-feet-3.jpg b/example-vault/rich-foot-example/images/rich-feet-3.jpg index 8c7e81d..0f999c2 100644 Binary files a/example-vault/rich-foot-example/images/rich-feet-3.jpg and b/example-vault/rich-foot-example/images/rich-feet-3.jpg differ diff --git a/example-vault/rich-foot-example/images/rich-feet.jpg b/example-vault/rich-foot-example/images/rich-feet.jpg index d4f3f2d..9de42a2 100644 Binary files a/example-vault/rich-foot-example/images/rich-feet.jpg and b/example-vault/rich-foot-example/images/rich-feet.jpg differ diff --git "a/example-vault/rich-foot-example/\360\237\246\266 Rich Foot.md" "b/example-vault/rich-foot-example/\360\237\246\266 Rich Foot.md" index 0cec460..de8aaed 100644 --- "a/example-vault/rich-foot-example/\360\237\246\266 Rich Foot.md" +++ "b/example-vault/rich-foot-example/\360\237\246\266 Rich Foot.md" @@ -1,5 +1,5 @@ --- -banner: images/rich-feet-3.jpg +banner-shuffle: images links: - "[[link test]]" --- diff --git a/manifest.json b/manifest.json index b39727c..e1989bd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "rich-foot", "name": "Rich Foot", - "version": "1.10.4", + "version": "1.10.5", "minAppVersion": "1.5.0", "description": "Adds backlink tags and created/modified dates to the footer of your notes.", "author": "Justin Parker (eQui\\\\ Labs)", diff --git a/src/main.js b/src/main.js index d8cf444..3f7aced 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,8 @@ -import { Plugin, MarkdownView, debounce, Setting } from 'obsidian'; +import { Plugin, MarkdownView, debounce } from 'obsidian'; import { ReleaseNotesModal } from './modals'; import { releaseNotes } from 'virtual:release-notes'; -import { RichFootSettingTab, FolderSuggestModal, DEFAULT_SETTINGS } from './settings'; +import { RichFootSettingTab, DEFAULT_SETTINGS } from './settings'; +import { formatDate } from './utils'; class RichFootSettings { constructor() { @@ -31,134 +32,6 @@ class RichFootSettings { } } -// Helper function to convert HSL to Hex -function hslToHex(h, s, l) { - // Evaluate calc expressions if present - const evalCalc = (expr) => { - if (typeof expr !== 'string') return expr; - if (expr.includes('calc(')) { - // Extract the expression inside calc() - const calcExpr = expr.match(/calc\((.*?)\)/)[1]; - // Basic evaluation of simple math expressions - return Function(`'use strict'; return (${calcExpr})`)(); - } - return parseFloat(expr); - }; - - h = evalCalc(h); - s = evalCalc(s); - l = evalCalc(l); - - l /= 100; - const a = s * Math.min(l, 1 - l) / 100; - const f = n => { - const k = (n + h / 30) % 12; - const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); - return Math.round(255 * color).toString(16).padStart(2, '0'); - }; - return `#${f(0)}${f(8)}${f(4)}`; -} - -// Helper function to convert RGB/RGBA to hex -function rgbToHex(color) { - // For HSLA colors, create a temporary div to convert to RGB - 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); - } - - // Extract RGB values, handling both RGB and RGBA - const rgb = color.match(/\d+/g); - if (!rgb || rgb.length < 3) return '#000000'; - - // Take only the first 3 values (RGB) and ensure they're valid hex values - const [r, g, b] = rgb.slice(0, 3).map(x => { - // Ensure value is between 0-255 - const val = Math.min(255, Math.max(0, Math.round(parseFloat(x)))); - return val.toString(16).padStart(2, '0'); - }); - - return `#${r}${g}${b}`; -} - -// Add the blendRgbaWithBackground function -function blendRgbaWithBackground(rgba, backgroundRgb) { - // Extract foreground RGBA values - const rgbaMatch = rgba.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),\s*(\d*\.?\d+)\)/); - if (!rgbaMatch) return null; - - const [ , fr, fg, fb, fa] = rgbaMatch.map(Number); // Parse to numbers - const alpha = fa !== undefined ? fa : 1; // Default alpha to 1 if not provided - - // Extract background RGB values - const rgbMatch = backgroundRgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); - if (!rgbMatch) return null; - - const [ , br, bg, bb] = rgbMatch.map(Number); // Parse to numbers - - // Blend each channel using the formula: result = fg * alpha + bg * (1 - alpha) - const r = Math.round(fr * alpha + br * (1 - alpha)); - const g = Math.round(fg * alpha + bg * (1 - alpha)); - const b = Math.round(fb * alpha + bb * (1 - alpha)); - - // Return the blended color as an RGB string - return `rgb(${r}, ${g}, ${b})`; -} - -// Add this helper function to format dates -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)); - - // Helper to pad numbers - const pad = (num) => num.toString().padStart(2, '0'); - - // Create a map of tokens to their values - 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) - }; - - // Sort tokens by length (longest first) to avoid partial matches - const sortedTokens = Object.keys(tokens).sort((a, b) => b.length - a.length); - - // Replace each token with a unique placeholder - let result = format; - const replacements = new Map(); - - sortedTokens.forEach((token, index) => { - const placeholder = `__${index}__`; - replacements.set(placeholder, tokens[token]); - result = result.replace(new RegExp(token, 'g'), placeholder); - }); - - // Replace placeholders with final values - replacements.forEach((value, placeholder) => { - result = result.replace(new RegExp(placeholder, 'g'), value); - }); - - return result; -} - class RichFootPlugin extends Plugin { async onload() { await this.loadSettings(); @@ -463,7 +336,7 @@ class RichFootPlugin extends Plugin { async createRichFoot(file) { // Remove the duplicate removal here since we're handling it in addRichFoot const richFoot = createDiv({ cls: 'rich-foot rich-foot--hidden' }); - const richFootDashedLine = richFoot.createDiv({ cls: 'rich-foot--dashed-line' }); + richFoot.createDiv({ cls: 'rich-foot--dashed-line' }); // Get both backlinks and outlinks data const backlinksData = this.app.metadataCache.getBacklinksForFile(file); @@ -610,12 +483,11 @@ class RichFootPlugin extends Plugin { } if (isValidDate) { - // Split on 'T' to handle timestamps - const datePart = tempDate.split('T')[0]; - // If there's no time component, parse in local timezone by appending T00:00:00 - const dateStr = tempDate.includes('T') ? tempDate : `${datePart}T00:00:00`; - // Create a Date object from the parts - const dateObj = new Date(dateStr); + // if tempDate doesn't have a time component, add it (using midnight in the current timezone) + 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; @@ -664,12 +536,11 @@ class RichFootPlugin extends Plugin { } if (isValidDate) { - // Split on 'T' to handle timestamps - const datePart = tempDate.split('T')[0]; - // If there's no time component, parse in local timezone by appending T00:00:00 - const dateStr = tempDate.includes('T') ? tempDate : `${datePart}T00:00:00`; - // Create a Date object from the parts - const dateObj = new Date(dateStr); + // if tempDate doesn't have a time component, add it (using midnight in the current timezone) + 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; diff --git a/src/settings.js b/src/settings.js index c632a76..164ac94 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,6 +1,6 @@ -import { App, PluginSettingTab, Setting, debounce, FuzzySuggestModal } from 'obsidian'; +import { PluginSettingTab, Setting, debounce, FuzzySuggestModal } from 'obsidian'; import { ReleaseNotesModal } from './modals'; -import { releaseNotes } from 'virtual:release-notes'; +import { rgbToHex, formatDate } from './utils'; export const DEFAULT_SETTINGS = { borderWidth: 1, @@ -28,132 +28,8 @@ export const DEFAULT_SETTINGS = { frontmatterExclusionField: '', }; -// Helper function to convert HSL to Hex -function hslToHex(h, s, l) { - // Evaluate calc expressions if present - const evalCalc = (expr) => { - if (typeof expr !== 'string') return expr; - if (expr.includes('calc(')) { - // Extract the expression inside calc() - const calcExpr = expr.match(/calc\((.*?)\)/)[1]; - // Basic evaluation of simple math expressions - return Function(`'use strict'; return (${calcExpr})`)(); - } - return parseFloat(expr); - }; - - h = evalCalc(h); - s = evalCalc(s); - l = evalCalc(l); - - l /= 100; - const a = s * Math.min(l, 1 - l) / 100; - const f = n => { - const k = (n + h / 30) % 12; - const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); - return Math.round(255 * color).toString(16).padStart(2, '0'); - }; - return `#${f(0)}${f(8)}${f(4)}`; -} - -// Helper function to convert RGB/RGBA to hex -function rgbToHex(color) { - // For HSLA colors, create a temporary div to convert to RGB - 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); - } - - // Extract RGB values, handling both RGB and RGBA - const rgb = color.match(/\d+/g); - if (!rgb || rgb.length < 3) return '#000000'; - - // Take only the first 3 values (RGB) and ensure they're valid hex values - const [r, g, b] = rgb.slice(0, 3).map(x => { - // Ensure value is between 0-255 - const val = Math.min(255, Math.max(0, Math.round(parseFloat(x)))); - return val.toString(16).padStart(2, '0'); - }); - - return `#${r}${g}${b}`; -} - -// Add the blendRgbaWithBackground function -function blendRgbaWithBackground(rgba, backgroundRgb) { - // Extract foreground RGBA values - const rgbaMatch = rgba.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),\s*(\d*\.?\d+)\)/); - if (!rgbaMatch) return null; - const [ , fr, fg, fb, fa] = rgbaMatch.map(Number); - const alpha = fa !== undefined ? fa : 1; - - // Extract background RGB values - const rgbMatch = backgroundRgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); - if (!rgbMatch) return null; - const [ , br, bg, bb] = rgbMatch.map(Number); - - // Blend each channel using the formula: result = fg * alpha + bg * (1 - alpha) - const r = Math.round(fr * alpha + br * (1 - alpha)); - const g = Math.round(fg * alpha + bg * (1 - alpha)); - const b = Math.round(fb * alpha + bb * (1 - alpha)); - - return `rgb(${r}, ${g}, ${b})`; -} - -// Add this helper function to format dates -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)); - - // Helper to pad numbers - const pad = (num) => num.toString().padStart(2, '0'); - - // Create a map of tokens to their values - 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) - }; - - // Sort tokens by length (longest first) to avoid partial matches - const sortedTokens = Object.keys(tokens).sort((a, b) => b.length - a.length); - - // Replace each token with a unique placeholder - let result = format.toLowerCase(); // Make case-insensitive - const replacements = new Map(); - - sortedTokens.forEach((token, index) => { - const placeholder = `__${index}__`; - replacements.set(placeholder, tokens[token]); - result = result.replace(new RegExp(token, 'gi'), placeholder); - }); - - // Replace placeholders with final values - replacements.forEach((value, placeholder) => { - result = result.replace(new RegExp(placeholder, 'g'), value); - }); - - return result; -} export class RichFootSettingTab extends PluginSettingTab { constructor(app, plugin) { diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..2c996d4 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,138 @@ +// ------------------------ +// -- convert HSL to Hex -- +// ------------------------ +export function hslToHex(h, s, l) { + // Evaluate calc expressions if present + const evalCalc = (expr) => { + if (typeof expr !== 'string') return expr; + if (expr.includes('calc(')) { + // Extract the expression inside calc() + const calcExpr = expr.match(/calc\((.*?)\)/)[1]; + // Basic evaluation of simple math expressions + return Function(`'use strict'; return (${calcExpr})`)(); + } + return parseFloat(expr); + }; + + h = evalCalc(h); + s = evalCalc(s); + l = evalCalc(l); + + l /= 100; + const a = s * Math.min(l, 1 - l) / 100; + const f = n => { + const k = (n + h / 30) % 12; + const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); + return Math.round(255 * color).toString(16).padStart(2, '0'); + }; + return `#${f(0)}${f(8)}${f(4)}`; +} + + +// ----------------------------- +// -- convert RGB/RGBA to hex -- +// ----------------------------- +export function rgbToHex(color) { + // For HSLA colors, create a temporary div to convert to RGB + 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); + } + + // Extract RGB values, handling both RGB and RGBA + const rgb = color.match(/\d+/g); + if (!rgb || rgb.length < 3) return '#000000'; + + // Take only the first 3 values (RGB) and ensure they're valid hex values + const [r, g, b] = rgb.slice(0, 3).map(x => { + // Ensure value is between 0-255 + const val = Math.min(255, Math.max(0, Math.round(parseFloat(x)))); + return val.toString(16).padStart(2, '0'); + }); + + return `#${r}${g}${b}`; +} + + +// ----------------------------- +// -- blendRgbaWithBackground -- +// ----------------------------- +export function blendRgbaWithBackground(rgba, backgroundRgb) { + // Extract foreground RGBA values + const rgbaMatch = rgba.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),\s*(\d*\.?\d+)\)/); + if (!rgbaMatch) return null; + + const [ , fr, fg, fb, fa] = rgbaMatch.map(Number); // Parse to numbers + const alpha = fa !== undefined ? fa : 1; // Default alpha to 1 if not provided + + // Extract background RGB values + const rgbMatch = backgroundRgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); + if (!rgbMatch) return null; + + const [ , br, bg, bb] = rgbMatch.map(Number); // Parse to numbers + + // Blend each channel using the formula: result = fg * alpha + bg * (1 - alpha) + const r = Math.round(fr * alpha + br * (1 - alpha)); + const g = Math.round(fg * alpha + bg * (1 - alpha)); + const b = Math.round(fb * alpha + bb * (1 - alpha)); + + // Return the blended color as an RGB string + return `rgb(${r}, ${g}, ${b})`; +} + + +// ------------------ +// -- format dates -- +// ------------------ +export 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)); + + // Helper to pad numbers + const pad = (num) => num.toString().padStart(2, '0'); + + // Create a map of tokens to their values + 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) + }; + + // Sort tokens by length (longest first) to avoid partial matches + const sortedTokens = Object.keys(tokens).sort((a, b) => b.length - a.length); + + // Replace each token with a unique placeholder + let result = format.toLowerCase(); // Make case-insensitive + const replacements = new Map(); + + sortedTokens.forEach((token, index) => { + const placeholder = `__${index}__`; + replacements.set(placeholder, tokens[token]); + result = result.replace(new RegExp(token, 'gi'), placeholder); + }); + + // Replace placeholders with final values + replacements.forEach((value, placeholder) => { + result = result.replace(new RegExp(placeholder, 'g'), value); + }); + + return result; +} \ No newline at end of file