mirror of
https://github.com/tbergeron/obsidian-human-readable-dates.git
synced 2026-07-22 06:56:05 +00:00
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:
parent
1c0a795258
commit
805cd387a3
7 changed files with 207 additions and 237 deletions
|
|
@ -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
387
main.ts
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
30
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
12
styles.css
12
styles.css
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"1.0.0": "0.15.0",
|
||||
"1.0.2": "0.15.0"
|
||||
}
|
||||
"1.0.2": "0.16.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue