Fix lint errors and warnings: refactor styles to CSS classes, bump minAppVersion to 0.16.0, replace builtin-modules, add cm dependencies, fix popout compatibility, remove any types and this aliasing

This commit is contained in:
Tommy Bergeron 2026-05-12 14:14:40 -04:00
parent 1c0a795258
commit 805cd387a3
7 changed files with 207 additions and 237 deletions

View file

@ -1,6 +1,6 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { builtinModules } from "node:module";
const banner =
`/*
@ -31,7 +31,7 @@ const context = await esbuild.context({
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
...builtinModules],
format: "cjs",
target: "es2018",
logLevel: "info",

387
main.ts
View file

@ -1,4 +1,5 @@
import { Plugin, PluginSettingTab, App, Setting } from 'obsidian'
import { Range } from '@codemirror/state'
import { EditorView, Decoration, DecorationSet, ViewPlugin, ViewUpdate, WidgetType } from '@codemirror/view'
interface HumanReadableDatesSettings {
@ -11,74 +12,193 @@ const DEFAULT_SETTINGS: HumanReadableDatesSettings = {
class HumanReadableDateWidget extends WidgetType {
constructor(
private originalText: string,
private humanReadable: string,
private originalText: string,
private humanReadable: string,
private isLink: boolean = false,
private app?: App
) {
super();
}
toDOM() {
toDOM(view: EditorView) {
const doc = view.dom.ownerDocument;
if (this.isLink && this.app) {
// Create a clickable link for bracketed dates
const link = document.createElement('a');
link.className = 'human-readable-date internal-link';
const link = doc.createElement('a');
link.className = 'human-readable-date human-readable-date-link';
link.textContent = this.humanReadable;
link.title = `Original: ${this.originalText}`;
link.style.cursor = 'pointer';
link.style.color = 'var(--link-color)';
link.style.textDecoration = 'underline';
// Extract the date from the original text (remove [[ and ]])
const linkTarget = this.originalText.replace(/^\[\[|\]\]$/g, '');
// Handle click to navigate to the note
link.addEventListener('click', (event) => {
const linkTarget = this.originalText.replace(/^\[\||\]\]$/g, '');
link.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
// Use Obsidian's app to open the note
this.app?.workspace.openLinkText(linkTarget, '', false);
await this.app?.workspace.openLinkText(linkTarget, '', false);
});
return link;
} else {
// Regular non-link widget for plain dates
const span = document.createElement('span');
span.className = 'human-readable-date';
const span = doc.createElement('span');
span.className = 'human-readable-date human-readable-date-plain';
span.title = `Original: ${this.originalText}`;
span.style.cursor = 'text';
span.textContent = this.humanReadable;
span.style.color = 'var(--text-accent)';
span.style.fontStyle = 'italic';
return span;
}
}
}
function createDateRegex(format: string): RegExp {
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const dayPattern = `(${dayNames.join('|')})`;
const monthPattern = `(${monthNames.join('|')})`;
const dayOfMonthPattern = '(\\d{1,2})';
const yearPattern = '(\\d{4})';
const timePattern = '(?:\\s+(\\d{1,2}):(\\d{2}))?';
const pattern = `${dayPattern}\\s+${monthPattern}\\s+${dayOfMonthPattern}\\s+${yearPattern}${timePattern}`;
return new RegExp(pattern, 'g');
}
function createBracketedDateRegex(format: string): RegExp {
const datePattern = createDateRegex(format).source;
const bracketedPattern = `\\[\\[(${datePattern})\\]\\]`;
return new RegExp(bracketedPattern, 'g');
}
function parseDateString(dateString: string, format: string): Date | null {
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const dayPattern = `(${dayNames.join('|')})`;
const monthPattern = `(${monthNames.join('|')})`;
const dayOfMonthPattern = '(\\d{1,2})';
const yearPattern = '(\\d{4})';
const timePattern = '(?:\\s+(\\d{1,2}):(\\d{2}))?';
const pattern = `${dayPattern}\\s+${monthPattern}\\s+${dayOfMonthPattern}\\s+${yearPattern}${timePattern}`;
const regex = new RegExp(pattern);
const match = dateString.match(regex);
if (!match) {
return null;
}
const [, , monthName, dayStr, yearStr, hourStr, minuteStr] = match;
const monthIndex = monthNames.indexOf(monthName);
if (monthIndex === -1) {
return null;
}
const day = parseInt(dayStr, 10);
const year = parseInt(yearStr, 10);
const hour = hourStr ? parseInt(hourStr, 10) : 0;
const minute = minuteStr ? parseInt(minuteStr, 10) : 0;
return new Date(year, monthIndex, day, hour, minute);
}
function formatDateAsHumanReadable(dateString: string, format: string): string | null {
const parsedDate = parseDateString(dateString, format);
if (!parsedDate) {
return null;
}
const now = new Date();
const diffMs = parsedDate.getTime() - now.getTime();
const diffHours = diffMs / (1000 * 60 * 60);
const parsedDateMidnight = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
const nowMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const diffDays = Math.round((parsedDateMidnight.getTime() - nowMidnight.getTime()) / (1000 * 60 * 60 * 24));
const absDiffDays = Math.abs(diffDays);
const absDiffHours = Math.abs(diffHours);
let result: string;
const hasTimeComponent = parsedDate.getHours() !== 0 || parsedDate.getMinutes() !== 0;
if (hasTimeComponent && absDiffHours < 24) {
const diffMinutes = diffMs / (1000 * 60);
const absDiffMinutes = Math.abs(diffMinutes);
if (absDiffMinutes < 5) {
result = 'Now';
} else if (absDiffMinutes < 60) {
const minutesDiff = Math.round(absDiffMinutes);
if (diffMinutes < 0) {
result = minutesDiff === 1 ? '1 min ago' : `${minutesDiff} mins ago`;
} else {
result = minutesDiff === 1 ? 'In 1 min' : `In ${minutesDiff} mins`;
}
} else {
const hoursDiff = Math.round(absDiffHours);
if (diffHours < 0) {
result = hoursDiff === 1 ? '1 hour ago' : `${hoursDiff} hours ago`;
} else {
result = hoursDiff === 1 ? 'In 1 hour' : `In ${hoursDiff} hours`;
}
}
} else if (diffDays === 0) {
result = 'Today';
} else if (diffDays === 1) {
result = 'Tomorrow';
} else if (diffDays === -1) {
result = 'Yesterday';
} else if (diffDays > 1 && diffDays <= 7) {
result = `In ${diffDays} days`;
} else if (diffDays < -1 && diffDays >= -7) {
result = `${absDiffDays} days ago`;
} else if (diffDays > 7 && diffDays <= 14) {
result = 'Next week';
} else if (diffDays < -7 && diffDays >= -14) {
result = 'Last week';
} else if (diffDays > 14 && diffDays <= 30) {
const weeks = Math.ceil(diffDays / 7);
result = `In ${weeks} weeks`;
} else if (diffDays < -14 && diffDays >= -30) {
const weeks = Math.ceil(absDiffDays / 7);
result = `${weeks} weeks ago`;
} else if (diffDays > 30 && diffDays <= 365) {
const months = Math.ceil(diffDays / 30);
result = months === 1 ? 'Next month' : `In ${months} months`;
} else if (diffDays < -30 && diffDays >= -365) {
const months = Math.ceil(absDiffDays / 30);
result = months === 1 ? 'Last month' : `${months} months ago`;
} else if (diffDays > 365) {
const years = Math.ceil(diffDays / 365);
result = years === 1 ? 'Next year' : `In ${years} years`;
} else {
const years = Math.ceil(absDiffDays / 365);
result = years === 1 ? 'Last year' : `${years} years ago`;
}
return result;
}
export default class HumanReadableDates extends Plugin {
settings: HumanReadableDatesSettings;
async onload() {
await this.loadSettings();
// Add settings tab
this.addSettingTab(new HumanReadableDatesSettingTab(this.app, this));
// Register editor extension for live preview mode only
this.registerEditorExtension([
this.createLivePreviewExtension()
]);
}
onunload() {
// Plugin cleanup if needed
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as HumanReadableDatesSettings);
}
async saveSettings() {
@ -86,8 +206,10 @@ export default class HumanReadableDates extends Plugin {
}
createLivePreviewExtension() {
const self = this;
const app = this.app;
const settings = this.settings;
const format = settings.dateFormat;
return ViewPlugin.fromClass(
class {
decorations: DecorationSet = Decoration.set([]);
@ -103,40 +225,35 @@ export default class HumanReadableDates extends Plugin {
}
buildDecorations(view: EditorView): DecorationSet {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const decorations: any[] = [];
const decorations: Range<Decoration>[] = [];
const doc = view.state.doc;
const text = doc.toString();
const selection = view.state.selection.main;
// Create regex for date matching
const dateRegex = self.createDateRegex(self.settings.dateFormat);
const bracketedDateRegex = self.createBracketedDateRegex(self.settings.dateFormat);
const dateRegex = createDateRegex(format);
const bracketedDateRegex = createBracketedDateRegex(format);
// Process bracketed dates first
let match;
bracketedDateRegex.lastIndex = 0;
while ((match = bracketedDateRegex.exec(text)) !== null) {
const fullMatch = match[0];
const dateString = match[1];
const humanReadable = self.formatDateAsHumanReadable(dateString);
const humanReadable = formatDateAsHumanReadable(dateString, format);
if (humanReadable) {
const from = match.index;
const to = match.index + fullMatch.length;
// Only show overlay if cursor is not within this range
const cursorInRange = selection.from >= from && selection.from <= to;
if (!cursorInRange) {
decorations.push(
Decoration.widget({
widget: new HumanReadableDateWidget(fullMatch, humanReadable, true, self.app),
widget: new HumanReadableDateWidget(fullMatch, humanReadable, true, app),
side: 1
}).range(from)
);
// Hide the original text
decorations.push(
Decoration.mark({
attributes: { style: "opacity: 0; position: absolute; pointer-events: none;" }
@ -146,43 +263,38 @@ export default class HumanReadableDates extends Plugin {
}
}
// Process regular dates
const processedRanges: Array<{from: number, to: number}> = [];
// Track bracketed date ranges to avoid overlaps
bracketedDateRegex.lastIndex = 0;
while ((match = bracketedDateRegex.exec(text)) !== null) {
processedRanges.push({ from: match.index, to: match.index + match[0].length });
}
dateRegex.lastIndex = 0;
while ((match = dateRegex.exec(text)) !== null) {
const dateString = match[0];
const from = match.index;
const to = match.index + dateString.length;
// Check if this range overlaps with any bracketed date
const overlaps = processedRanges.some(range =>
(from >= range.from && from < range.to) ||
const overlaps = processedRanges.some(range =>
(from >= range.from && from < range.to) ||
(to > range.from && to <= range.to) ||
(from <= range.from && to >= range.to)
);
if (!overlaps) {
const humanReadable = self.formatDateAsHumanReadable(dateString);
const humanReadable = formatDateAsHumanReadable(dateString, format);
if (humanReadable) {
// Only show overlay if cursor is not within this range
const cursorInRange = selection.from >= from && selection.from <= to;
if (!cursorInRange) {
decorations.push(
Decoration.widget({
widget: new HumanReadableDateWidget(dateString, humanReadable, false, self.app),
widget: new HumanReadableDateWidget(dateString, humanReadable, false, app),
side: 1
}).range(from)
);
// Hide the original text
decorations.push(
Decoration.mark({
attributes: { style: "opacity: 0; position: absolute; pointer-events: none;" }
@ -201,147 +313,6 @@ export default class HumanReadableDates extends Plugin {
}
);
}
createDateRegex(format: string): RegExp {
// Convert the format string to a regex pattern
// Default format: ddd MMM DD YYYY HH:mm -> Fri Aug 29 2025 19:28
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const dayPattern = `(${dayNames.join('|')})`;
const monthPattern = `(${monthNames.join('|')})`;
const dayOfMonthPattern = '(\\d{1,2})';
const yearPattern = '(\\d{4})';
const timePattern = '(?:\\s+(\\d{1,2}):(\\d{2}))?'; // Optional time part
const pattern = `${dayPattern}\\s+${monthPattern}\\s+${dayOfMonthPattern}\\s+${yearPattern}${timePattern}`;
return new RegExp(pattern, 'g');
}
createBracketedDateRegex(format: string): RegExp {
// Create regex for dates in square brackets [[date]]
const datePattern = this.createDateRegex(format).source;
// Remove the 'g' flag from the source pattern and wrap in brackets
const bracketedPattern = `\\[\\[(${datePattern})\\]\\]`;
return new RegExp(bracketedPattern, 'g');
}
formatDateAsHumanReadable(dateString: string): string | null {
const parsedDate = this.parseDateString(dateString, this.settings.dateFormat);
if (!parsedDate) {
return null;
}
const now = new Date();
const diffMs = parsedDate.getTime() - now.getTime();
const diffHours = diffMs / (1000 * 60 * 60);
// Better day calculation - compare dates at midnight to avoid timezone issues
const parsedDateMidnight = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
const nowMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const diffDays = Math.round((parsedDateMidnight.getTime() - nowMidnight.getTime()) / (1000 * 60 * 60 * 24));
const absDiffDays = Math.abs(diffDays);
const absDiffHours = Math.abs(diffHours);
let result: string;
// Check if the date has time component (not just midnight)
const hasTimeComponent = parsedDate.getHours() !== 0 || parsedDate.getMinutes() !== 0;
// For dates with time components within 24 hours, show precise time
if (hasTimeComponent && absDiffHours < 24) {
const diffMinutes = diffMs / (1000 * 60);
const absDiffMinutes = Math.abs(diffMinutes);
if (absDiffMinutes < 5) {
result = 'Now'; // Less than 5 minutes
} else if (absDiffMinutes < 60) {
const minutesDiff = Math.round(absDiffMinutes);
if (diffMinutes < 0) {
result = minutesDiff === 1 ? '1 min ago' : `${minutesDiff} mins ago`;
} else {
result = minutesDiff === 1 ? 'In 1 min' : `In ${minutesDiff} mins`;
}
} else {
const hoursDiff = Math.round(absDiffHours);
if (diffHours < 0) {
result = hoursDiff === 1 ? '1 hour ago' : `${hoursDiff} hours ago`;
} else {
result = hoursDiff === 1 ? 'In 1 hour' : `In ${hoursDiff} hours`;
}
}
} else if (diffDays === 0) {
result = 'Today';
} else if (diffDays === 1) {
result = 'Tomorrow';
} else if (diffDays === -1) {
result = 'Yesterday';
} else if (diffDays > 1 && diffDays <= 7) {
result = `In ${diffDays} days`;
} else if (diffDays < -1 && diffDays >= -7) {
result = `${absDiffDays} days ago`;
} else if (diffDays > 7 && diffDays <= 14) {
result = 'Next week';
} else if (diffDays < -7 && diffDays >= -14) {
result = 'Last week';
} else if (diffDays > 14 && diffDays <= 30) {
const weeks = Math.ceil(diffDays / 7);
result = `In ${weeks} weeks`;
} else if (diffDays < -14 && diffDays >= -30) {
const weeks = Math.ceil(absDiffDays / 7);
result = `${weeks} weeks ago`;
} else if (diffDays > 30 && diffDays <= 365) {
const months = Math.ceil(diffDays / 30);
result = months === 1 ? 'Next month' : `In ${months} months`;
} else if (diffDays < -30 && diffDays >= -365) {
const months = Math.ceil(absDiffDays / 30);
result = months === 1 ? 'Last month' : `${months} months ago`;
} else if (diffDays > 365) {
const years = Math.ceil(diffDays / 365);
result = years === 1 ? 'Next year' : `In ${years} years`;
} else {
const years = Math.ceil(absDiffDays / 365);
result = years === 1 ? 'Last year' : `${years} years ago`;
}
return result;
}
parseDateString(dateString: string, format: string): Date | null {
// Use the same regex as createDateRegex but without global flag
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const dayPattern = `(${dayNames.join('|')})`;
const monthPattern = `(${monthNames.join('|')})`;
const dayOfMonthPattern = '(\\d{1,2})';
const yearPattern = '(\\d{4})';
const timePattern = '(?:\\s+(\\d{1,2}):(\\d{2}))?'; // Optional time part
const pattern = `${dayPattern}\\s+${monthPattern}\\s+${dayOfMonthPattern}\\s+${yearPattern}${timePattern}`;
const regex = new RegExp(pattern);
const match = dateString.match(regex);
if (!match) {
return null;
}
const [, , monthName, dayStr, yearStr, hourStr, minuteStr] = match;
const monthIndex = monthNames.indexOf(monthName);
if (monthIndex === -1) {
return null;
}
const day = parseInt(dayStr, 10);
const year = parseInt(yearStr, 10);
const hour = hourStr ? parseInt(hourStr, 10) : 0; // Default to midnight if no time
const minute = minuteStr ? parseInt(minuteStr, 10) : 0;
return new Date(year, monthIndex, day, hour, minute);
}
}
class HumanReadableDatesSettingTab extends PluginSettingTab {
@ -357,7 +328,9 @@ class HumanReadableDatesSettingTab extends PluginSettingTab {
containerEl.empty();
containerEl.createEl('h2', {text: 'Human Readable Dates Settings'});
new Setting(containerEl)
.setName('Human Readable Dates Settings')
.setHeading();
new Setting(containerEl)
.setName('Date format')
@ -366,23 +339,23 @@ class HumanReadableDatesSettingTab extends PluginSettingTab {
.setPlaceholder('ddd MMM DD YYYY HH:mm')
.setValue(this.plugin.settings.dateFormat)
.onChange(async (value) => {
this.plugin.settings.dateFormat = value;
await this.plugin.saveSettings();
this.plugin.settings.dateFormat = value;
await this.plugin.saveSettings();
}));
containerEl.createEl('p', {
text: 'Examples of supported formats:',
cls: 'setting-item-description'
});
const examplesList = containerEl.createEl('ul', {
cls: 'setting-item-description'
});
examplesList.createEl('li', {text: 'Fri Aug 29 2025 19:20 (with time)'});
examplesList.createEl('li', {text: 'Fri Aug 29 2025 (without time)'});
examplesList.createEl('li', {text: '[[Fri Aug 29 2025]] (in square brackets)'});
containerEl.createEl('p', {
text: 'Note: This plugin only works in Live Preview mode. Dates will show as human-readable text (e.g., "Yesterday", "Tomorrow") but revert to original format when you move your cursor over them for editing.',
cls: 'setting-item-description'

View file

@ -2,10 +2,10 @@
"id": "human-readable-dates",
"name": "Human Readable Dates",
"version": "1.0.2",
"minAppVersion": "0.15.0",
"minAppVersion": "0.16.0",
"description": "Human readable dates.",
"author": "Tommy Bergeron",
"authorUrl": "https://github.com/tbergeron",
"fundingUrl": "https://www.paypal.com/donate/?hosted_button_id=93Z9BUUD778SG",
"isDesktopOnly": false
}
}

30
package-lock.json generated
View file

@ -1,18 +1,19 @@
{
"name": "obsidian-human-readable-dates",
"name": "human-readable-dates",
"version": "1.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-human-readable-dates",
"name": "human-readable-dates",
"version": "1.0.2",
"license": "MIT",
"devDependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"tslib": "2.4.0",
@ -24,7 +25,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.1.tgz",
"integrity": "sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==",
"dev": true,
"peer": true,
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
@ -34,7 +34,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.2.tgz",
"integrity": "sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==",
"dev": true,
"peer": true,
"dependencies": {
"@codemirror/state": "^6.5.0",
"style-mod": "^4.1.0",
@ -498,8 +497,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
@ -875,18 +873,6 @@
"node": ">=8"
}
},
"node_modules/builtin-modules": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true,
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@ -2059,8 +2045,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/supports-color": {
"version": "7.2.0",
@ -2174,8 +2159,7 @@
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/which": {
"version": "2.0.2",

View file

@ -12,10 +12,11 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"tslib": "2.4.0",

View file

@ -15,3 +15,15 @@
position: relative;
z-index: 1;
}
.human-readable-date-link {
cursor: pointer;
color: var(--link-color);
text-decoration: underline;
}
.human-readable-date-plain {
cursor: text;
color: var(--text-accent);
font-style: italic;
}

View file

@ -1,4 +1,4 @@
{
"1.0.0": "0.15.0",
"1.0.2": "0.15.0"
}
"1.0.2": "0.16.0"
}