Fix counter rendering in Obsidian

- Remove start-of-line anchor from regex to match anywhere in text
- Improve text node processing to handle inline counters
- Simplify markdown post processor logic
- Remove unused editor-change event handler

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-26 10:34:33 +01:00
parent 7152027ee3
commit 634f219376

93
main.ts
View File

@@ -7,16 +7,6 @@ export default class CounterPlugin extends Plugin {
this.registerMarkdownPostProcessor((element, context) => { this.registerMarkdownPostProcessor((element, context) => {
this.processCounters(element, context); this.processCounters(element, context);
}); });
this.registerEvent(
this.app.workspace.on('editor-change', () => {
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
if (view) {
// Refresh the preview to update counters
view.previewMode.rerender(true);
}
})
);
} }
onunload() { onunload() {
@@ -24,51 +14,69 @@ export default class CounterPlugin extends Plugin {
} }
processCounters(element: HTMLElement, context: MarkdownPostProcessorContext) { processCounters(element: HTMLElement, context: MarkdownPostProcessorContext) {
const counterRegex = /^~\s*\(\s*(\d*)\s*\)\s*(.*)$/; const counterRegex = /~\s*\(\s*(\d*)\s*\)\s*(.+)/g;
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT); const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
const nodesToReplace: Array<{ node: Node; parent: Node }> = []; const nodesToReplace: Array<{ node: Node; parent: Node; replacements: Array<{type: 'text' | 'counter', content: string, value?: number, label?: string}> }> = [];
let node: Node | null; let node: Node | null;
while ((node = walker.nextNode()) !== null) { while ((node = walker.nextNode()) !== null) {
const text = node.textContent || ''; const text = node.textContent || '';
const lines = text.split('\n');
lines.forEach((line, index) => { if (counterRegex.test(text)) {
const match = line.match(counterRegex); counterRegex.lastIndex = 0;
if (match && node) { const replacements: Array<{type: 'text' | 'counter', content: string, value?: number, label?: string}> = [];
nodesToReplace.push({ node, parent: node.parentNode! }); let lastIndex = 0;
} let match;
});
}
nodesToReplace.forEach(({ node, parent }) => { while ((match = counterRegex.exec(text)) !== null) {
const text = node.textContent || ''; if (match.index > lastIndex) {
const lines = text.split('\n'); replacements.push({
const fragment = document.createDocumentFragment(); type: 'text',
content: text.substring(lastIndex, match.index)
});
}
lines.forEach((line, index) => { const value = match[1] === '' ? 0 : parseInt(match[1], 10);
const match = line.match(counterRegex);
if (match) {
const currentValue = match[1] === '' ? 0 : parseInt(match[1], 10);
const label = match[2].trim(); const label = match[2].trim();
const counterContainer = this.createCounterElement( replacements.push({
currentValue, type: 'counter',
label, content: match[0],
node, value: value,
context label: label
); });
fragment.appendChild(counterContainer); lastIndex = match.index + match[0].length;
} else {
if (line) {
fragment.appendChild(document.createTextNode(line));
}
} }
if (index < lines.length - 1) { if (lastIndex < text.length) {
fragment.appendChild(document.createTextNode('\n')); replacements.push({
type: 'text',
content: text.substring(lastIndex)
});
}
if (replacements.length > 0) {
nodesToReplace.push({ node, parent: node.parentNode!, replacements });
}
counterRegex.lastIndex = 0;
}
}
nodesToReplace.forEach(({ node, parent, replacements }) => {
const fragment = document.createDocumentFragment();
replacements.forEach(replacement => {
if (replacement.type === 'counter') {
const counterContainer = this.createCounterElement(
replacement.value!,
replacement.label!,
context
);
fragment.appendChild(counterContainer);
} else {
fragment.appendChild(document.createTextNode(replacement.content));
} }
}); });
@@ -79,7 +87,6 @@ export default class CounterPlugin extends Plugin {
createCounterElement( createCounterElement(
value: number, value: number,
label: string, label: string,
sourceNode: Node,
context: MarkdownPostProcessorContext context: MarkdownPostProcessorContext
): HTMLElement { ): HTMLElement {
const container = document.createElement('div'); const container = document.createElement('div');