feat: Enhance inline date calculation with improved Live Preview support, type normalization, and plugin cleanup.

This commit is contained in:
2025-12-02 22:02:36 -05:00
parent 08f75a8d8a
commit e6e791ad35
3 changed files with 75 additions and 53 deletions

View File

@@ -2,6 +2,13 @@
An Obsidian plugin that adds dynamic date calculations to your notes using `date-calc` fenced code blocks and inline code.
## Todo
- [x] Make inline work
- [ ] Add a non-verbose birthday type? Just show the age, with decimals, maybe? Like "3.7 years old"
- [ ]
## Features
### Fenced Code Blocks
@@ -96,7 +103,7 @@ to: 2025-03-01
type: diff
from: 2025-09-19 08:00
to: 2025-09-19 16:30
```
```
## Inline Usage

101
main.ts
View File

@@ -110,6 +110,11 @@ function processInlineDateCalc(raw: string, app: App, sourcePath: string): strin
else if ((cfg as any).since) type = 'since';
else if ((cfg as any).from && (cfg as any).to) type = 'diff';
}
// Normalize type aliases to match fenced code block processor
if (type === 'bday') type = 'birthday';
if (type === 'until') type = 'countdown';
if (type === 'difference') type = 'diff';
if (!type) return '';
// Render result similarly to the block processor
@@ -235,6 +240,7 @@ const dateCalcLivePreviewPlugin = (app: App) => ViewPlugin.fromClass(class {
const text = view.state.doc.toString();
// Find inline code spans that start with date-calc:
// This regex looks for inline code between backticks
const inlineCodeRegex = /`([^`]+)`/g;
let match;
@@ -258,6 +264,9 @@ const dateCalcLivePreviewPlugin = (app: App) => ViewPlugin.fromClass(class {
side: 1
});
builder.add(to, to, widget);
// Add a debug log to verify when a widget is being created
console.log(`Live Preview widget created for: ${codeContent} with result: ${result}`);
}
}
}
@@ -287,11 +296,25 @@ class DateCalcInlineRenderer extends MarkdownRenderChild {
async render() {
const result = processInlineDateCalc(this.raw, this.app, this.sourcePath);
if (result) {
console.log(`Rendering inline date-calc: ${this.raw} -> ${result}`);
// In Live Preview, just replace the code element directly
const span = document.createElement('span');
span.classList.add('date-calc-inline');
span.setAttribute('contenteditable', 'false');
span.textContent = result;
this.target.replaceWith(span);
// Ensure we properly replace the target element
try {
this.target.parentNode?.replaceChild(span, this.target);
console.log('Successfully replaced code element with result');
} catch (e) {
console.error('Failed to replace code element:', e);
// Fallback: try the original method
this.target.replaceWith(span);
}
} else {
console.log(`No result for inline date-calc: ${this.raw}`);
}
}
}
@@ -305,69 +328,44 @@ export default class MyPlugin extends Plugin {
async onload() {
await this.loadSettings();
// This creates an icon in the left ribbon.
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (_evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice('This is a notice!');
});
// Perform additional things with the ribbon
ribbonIconEl.addClass('my-plugin-ribbon-class');
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
// Add a status bar item showing the plugin is enabled
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status Bar Text');
statusBarItemEl.setText('Date Calculator Enabled');
// This adds a simple command that can be triggered anywhere
// Debugging command to test plugin functionality
this.addCommand({
id: 'open-sample-modal-simple',
name: 'Open sample modal (simple)',
id: 'date-calc-debug',
name: 'Debug Date Calculator',
callback: () => {
new SampleModal(this.app).open();
new Notice('Date Calculator plugin is working! Check console for more details.');
console.log('Date Calculator plugin debug info:');
console.log('Plugin version: 1.0.0');
console.log('Plugin enabled: true');
console.log('Current file:', this.app.workspace.getActiveFile()?.path);
}
});
// This adds an editor command that can perform some operation on the current editor instance
this.addCommand({
id: 'sample-editor-command',
name: 'Sample editor command',
editorCallback: (editor: Editor, _view: MarkdownView) => {
console.log(editor.getSelection());
editor.replaceSelection('Sample Editor Command');
}
});
// This adds a complex command that can check whether the current state of the app allows execution of the command
this.addCommand({
id: 'open-sample-modal-complex',
name: 'Open sample modal (complex)',
checkCallback: (checking: boolean) => {
// Conditions to check
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (markdownView) {
// If checking is true, we're simply "checking" if the command can be run.
// If checking is false, then we want to actually perform the operation.
if (!checking) {
new SampleModal(this.app).open();
}
// This command will only show up in Command Palette when the check function returns true
return true;
}
// Command to refresh inline calculations (useful for troubleshooting)
this.addCommand({
id: 'refresh-date-calc-inline',
name: 'Refresh Date Calculations',
callback: () => {
// Force refresh by triggering layout
this.app.workspace.trigger('layout-change');
new Notice('Date calculations refreshed!');
}
});
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this));
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
// Using this function will automatically remove the event listener when this plugin is disabled.
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
console.log('click', evt);
});
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
// Register the Live Preview editor extension for inline date-calc processing
// CRITICAL: Register Live Preview extension FIRST and ensure it's properly loaded
console.log('Registering Live Preview editor extension for date-calc...');
this.registerEditorExtension(dateCalcLivePreviewPlugin(this.app));
console.log('Date Calculator Live Preview extension registered successfully!');
// Add more comprehensive debugging
new Notice('Date Calculator loaded with Live Preview support!');
// Register the `date-calc` code block processor
this.registerMarkdownCodeBlockProcessor('date-calc', (source, el, ctx) => {
@@ -384,6 +382,7 @@ export default class MyPlugin extends Plugin {
try {
switch (type) {
case 'bday':
case 'birthday': {
// Accept from config or frontmatter fallback
const fileCache = this.app.metadataCache.getCache(ctx.sourcePath);
@@ -445,7 +444,7 @@ export default class MyPlugin extends Plugin {
msg = `next one is in ${monthsUntil} months.`;
} else if (daysUntil === 0) {
msg = `Today! Wish ${pronoun} Happy Birthday!`;
msg = `today! Wish ${pronoun} Happy Birthday!`;
} else if (daysUntil === 1) {
msg = `that's tomorrow!`;
} else {

View File

@@ -2,7 +2,7 @@
Date Calc Plugin Styles
*/
/* Inline date-calc styling for live preview - now styles actual span elements */
/* Inline date-calc styling for reading view and live preview */
.date-calc-inline {
background-color: var(--background-modifier-accent);
border-radius: 3px;
@@ -12,6 +12,22 @@ Date Calc Plugin Styles
font-size: 0.9em;
display: inline-block;
margin: 0 2px;
font-family: var(--font-text);
}
/* Specific styles for CodeMirror Live Preview widgets */
.cm-line .date-calc-inline {
background-color: var(--background-modifier-accent);
color: var(--text-accent);
padding: 2px 6px;
margin-left: 4px;
border-radius: 3px;
font-weight: bold;
font-size: 0.9em;
display: inline;
user-select: none;
cursor: default;
vertical-align: baseline;
}
/* Additional styling for fenced code blocks */