chore: update Angular cache with new TypeScript definitions
This commit is contained in:
@@ -12,7 +12,7 @@ export class MarkdownService {
|
||||
private readonly tagPaletteSize = 12;
|
||||
private tagColorCache = new Map<string, number>();
|
||||
|
||||
render(markdown: string, allNotes: Note[]): string {
|
||||
render(markdown: string, allNotes: Note[], currentNote?: Note): string {
|
||||
let html = markdown;
|
||||
|
||||
// Process code blocks first to prevent inner content from being parsed
|
||||
@@ -101,11 +101,31 @@ export class MarkdownService {
|
||||
});
|
||||
html = html.replace(/^>\s(.*)/gm, '<blockquote class="border-l-4 border-gray-300 dark:border-gray-500 pl-4 py-2 my-4 italic text-gray-700 dark:text-gray-300">$1</blockquote>');
|
||||
|
||||
// Task lists
|
||||
const transformTaskItem = (_match: string, marker: string, rawContent: string) => {
|
||||
const isChecked = marker.toLowerCase() === 'x';
|
||||
const classes = [
|
||||
'md-task-item',
|
||||
'text-obs-l-text-main',
|
||||
'dark:text-obs-d-text-main'
|
||||
];
|
||||
if (isChecked) {
|
||||
classes.push('md-task-item--done');
|
||||
}
|
||||
const content = rawContent.trim();
|
||||
const checkboxState = isChecked ? 'checked' : '';
|
||||
const ariaChecked = isChecked ? 'true' : 'false';
|
||||
return `<li class="${classes.join(' ')}"><label class="md-task"><input type="checkbox" class="md-task-checkbox" ${checkboxState} disabled aria-checked="${ariaChecked}" tabindex="-1"><span class="md-task-label-text">${content}</span></label></li>`;
|
||||
};
|
||||
|
||||
html = html.replace(/^\s*(?:[\-\*]|\d+\.)\s+\[( |x|X)\]\s+(.*)$/gm, transformTaskItem);
|
||||
|
||||
// Lists (simple implementation)
|
||||
html = html.replace(/^\s*[\-\*]\s(.*)/gm, '<li class="ml-6 list-disc">$1</li>');
|
||||
html = html.replace(/^\s*\d+\.\s(.*)/gm, '<li class="ml-6 list-decimal">$1</li>');
|
||||
html = html.replace(/<\/li>\n<li/g, '</li><li'); // Fix spacing
|
||||
html = html.replace(/(<li.*<\/li>)/gs, (match) => {
|
||||
if (match.includes('md-task-item')) return `<ul class="mb-4 md-task-list">${match}</ul>`;
|
||||
if (match.includes('list-disc')) return `<ul class="mb-4">${match}</ul>`;
|
||||
if (match.includes('list-decimal')) return `<ol class="mb-4">${match}</ol>`;
|
||||
return match;
|
||||
@@ -116,29 +136,62 @@ export class MarkdownService {
|
||||
if (inlineCode.includes('\n')) {
|
||||
return match;
|
||||
}
|
||||
return `<code class="inline-code">${this.escapeHtml(inlineCode)}</code>`;
|
||||
});
|
||||
|
||||
// Internal Links [[link|alias]] or [[link]]
|
||||
html = html.replace(/\[\[([^|\]\n]+)(?:\|([^\]\n]+))?\]\]/g, (match, link, alias) => {
|
||||
const targetText = alias || link;
|
||||
const targetId = link.toLowerCase().replace(/\s+/g, '-');
|
||||
const noteExists = allNotes.some(n => n.id === targetId || n.title === link);
|
||||
const classes = noteExists ? 'text-obs-l-accent dark:text-obs-d-accent hover:underline cursor-pointer' : 'text-red-500 dark:text-red-400 cursor-not-allowed';
|
||||
return `<a data-note-id="${targetId}" data-note-title="${link}" class="${classes}">${targetText}</a>`;
|
||||
const safeContent = this.escapeHtml(inlineCode.trim());
|
||||
return `<code class="inline-code">${safeContent}</code>`;
|
||||
});
|
||||
|
||||
// External Links [text](url)
|
||||
html = html.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer" class="text-blue-600 dark:text-blue-400 hover:underline">$1</a>');
|
||||
// External images 
|
||||
html = html.replace(/!\[(.*?)\]\(((?:https?|ftp|file):\/\/[^\s\)]*)(?:\s+"(.*?)")?\)/g, (match, alt, url, title) => {
|
||||
const safeAlt = this.escapeHtml(alt ?? '');
|
||||
const safeUrl = this.escapeHtml(url ?? '');
|
||||
const safeTitle = title ? this.escapeHtml(title) : '';
|
||||
const titleAttr = title ? ` title="${safeTitle}"` : '';
|
||||
const captionSource = title && title.trim() ? title : alt;
|
||||
const safeCaption = captionSource ? this.escapeHtml(captionSource) : '';
|
||||
const figcaption = safeCaption
|
||||
? `<figcaption class="text-center text-sm text-obs-l-text-muted dark:text-obs-d-text-muted">${safeCaption}</figcaption>`
|
||||
: '';
|
||||
return `<figure class="my-4 md-attachment-figure">
|
||||
<img src="${safeUrl}" alt="${safeAlt}"${titleAttr} loading="lazy" class="rounded-lg max-w-full h-auto mx-auto">
|
||||
${figcaption}
|
||||
</figure>`;
|
||||
});
|
||||
|
||||
// Embedded Files ![[file.png]]
|
||||
html = html.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
|
||||
return `<figure class="my-4"><img src="https://picsum.photos/600/400?random=${Math.random()}" alt="${filename}" class="rounded-lg max-w-full h-auto mx-auto"><figcaption class="text-center text-sm text-obs-l-text-muted dark:text-obs-d-text-muted mt-2">${filename}</figcaption></figure>`;
|
||||
// External links [text](url "title")
|
||||
html = html.replace(/\[(.*?)\]\(((?:https?|ftp|file|mailto):\/\/[^\s\)]*)(?:\s+"(.*?)")?\)/g, (match, label, url, title) => {
|
||||
const safeLabel = this.escapeHtml(label ?? '');
|
||||
const safeUrl = this.escapeHtml(url ?? '');
|
||||
const safeTitle = title ? this.escapeHtml(title) : '';
|
||||
const titleAttr = title ? ` title="${safeTitle}"` : '';
|
||||
return `<a href="${safeUrl}"${titleAttr} target="_blank" rel="noopener noreferrer" class="md-external-link">${safeLabel}</a>`;
|
||||
});
|
||||
|
||||
// Embedded Files ![[file.png]] using Attachements-Path from HOME.md (traité avant les liens internes)
|
||||
const homeNote = allNotes.find(n =>
|
||||
(n.fileName?.toLowerCase?.() === 'home.md') ||
|
||||
(n.originalPath?.toLowerCase?.() === 'home') ||
|
||||
(n.originalPath?.toLowerCase?.().endsWith('/home'))
|
||||
);
|
||||
const attachmentsBase = (() => {
|
||||
if (!homeNote) return '';
|
||||
const fm = homeNote.frontmatter || {};
|
||||
const key = Object.keys(fm).find(k => k?.toLowerCase?.() === 'attachements-path' || k?.toLowerCase?.() === 'attachments-path');
|
||||
const raw = key ? `${fm[key] ?? ''}` : '';
|
||||
return raw;
|
||||
})();
|
||||
html = html.replace(/!\[\[(.*?)\]\]/g, (match, rawName) => {
|
||||
const filename = `${rawName}`.trim();
|
||||
const safeAlt = this.escapeHtml(filename);
|
||||
const notePath = (currentNote?.filePath || currentNote?.originalPath || '').replace(/\\/g, '/');
|
||||
const src = `/api/attachments/resolve?name=${encodeURIComponent(filename)}¬e=${encodeURIComponent(notePath)}&base=${encodeURIComponent(attachmentsBase)}`;
|
||||
return `<figure class="my-4 md-attachment-figure">
|
||||
<img src="${src}" alt="${safeAlt}" loading="lazy" class="rounded-lg max-w-full h-auto mx-auto md-attachment-image" data-attachment-name="${safeAlt}" data-error-message="<div class='missing-attachment text-center text-sm text-red-500 dark:text-red-400'>Attachement ${this.escapeHtml(filename).replace(/'/g, ''')} introuvable</div>">
|
||||
<figcaption class="text-center text-sm text-obs-l-text-muted dark:text-obs-d-text-muted">${safeAlt}</figcaption>
|
||||
</figure>`;
|
||||
});
|
||||
|
||||
// Horizontal Rule
|
||||
html = html.replace(/^-{3,}/gm, '<hr class="my-6 border-obs-l-border dark:border-obs-d-border">');
|
||||
|
||||
// Paragraphs and line breaks
|
||||
html = html.split('\n').map(p => p.trim() === '' ? '' : `<p>${p}</p>`).join('').replace(/<\/p><p>/g, '</p><br><p>');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user