@lifecorner/angular-ui-kit 3.21.0 → 3.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +11 -11
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +6 -6
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/theme-contrast.spec.ts +239 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-log-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/log-viewer/log-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/log-viewer/log-viewer.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n DestroyRef,\n ElementRef,\n Injector,\n NgZone,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { Observable, Subscription } from 'rxjs';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_TRASH } from '@lifecorner/angular-ui-kit/icons';\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type LogViewerVariant = 'terminal' | 'log';\n\nexport interface LogLine {\n text: string;\n level?: LogLevel;\n timestamp?: Date;\n source?: string;\n meta?: Record<string, unknown>;\n}\n\n/** SGR code → CSS class for the ANSI subset the viewer renders. */\nconst ANSI_CLASSES: Record<string, string> = {\n '1': 'ansi-bold',\n '3': 'ansi-italic',\n '30': 'ansi-black',\n '31': 'ansi-red',\n '32': 'ansi-green',\n '33': 'ansi-yellow',\n '34': 'ansi-blue',\n '35': 'ansi-magenta',\n '36': 'ansi-cyan',\n '37': 'ansi-white',\n '90': 'ansi-gray',\n '91': 'ansi-bright-red',\n '92': 'ansi-bright-green',\n '93': 'ansi-bright-yellow',\n '94': 'ansi-bright-blue',\n '95': 'ansi-bright-magenta',\n '96': 'ansi-bright-cyan',\n '97': 'ansi-bright-white',\n};\n\n/** Row height fallback when `--lc-log-viewer-line-height` can't be read (SSR, tests). */\nconst DEFAULT_LINE_HEIGHT = 22;\n\n/** Distance from the bottom (px) still counted as \"at the bottom\". */\nconst AT_BOTTOM_THRESHOLD = 30;\n\n/**\n * Streaming log / terminal viewer component.\n *\n * Supports controlled (lines input) and streaming (stream$ observable) modes,\n * virtualized rendering for large buffers, ANSI color parsing, auto-scroll,\n * and filtering.\n *\n * The virtual window is sized from the *measured* height of the scroll area\n * (ResizeObserver), so any `height` — `400px`, `50vh`, `100%` inside a flex\n * parent — renders a full window. Row height comes from the CSS custom\n * property `--lc-log-viewer-line-height` (default 22px), read once after the\n * first render; override it on the host to change the row density.\n *\n * @example\n * ```html\n * <lc-log-viewer [stream$]=\"logs$\" autoScroll height=\"600px\" variant=\"terminal\" />\n * ```\n */\n@Component({\n selector: 'lc-log-viewer',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_TRASH])],\n templateUrl: './log-viewer.component.html',\n styleUrls: ['./log-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class LogViewerComponent {\n private static nextId = 0;\n /** Id of the search input (links its visually-hidden label). */\n protected readonly searchInputId = `lc-log-viewer-search-${++LogViewerComponent.nextId}`;\n\n private readonly ngZone = inject(NgZone);\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n private readonly scrollContainer = viewChild.required<ElementRef<HTMLElement>>('scrollContainer');\n private readonly searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');\n private streamSub?: Subscription;\n\n /** Controlled mode: array of log lines */\n readonly lines = input<LogLine[]>([]);\n\n /** Streaming mode: observable of log lines */\n readonly stream$ = input<Observable<LogLine | LogLine[]>>();\n\n /** Maximum lines to keep in buffer */\n readonly maxLines = input<number>(10_000);\n\n /**\n * Follow new lines: scroll to the bottom whenever the visible line count\n * changes — but only while the reader is already at the bottom, so scrolling\n * up to inspect older output is never fought.\n */\n readonly autoScroll = input<boolean>(true);\n\n /** Show timestamps column */\n readonly showTimestamps = input<boolean>(true);\n\n /** Show line numbers */\n readonly showLineNumbers = input<boolean>(false);\n\n /** Parse ANSI color codes */\n readonly ansiColors = input<boolean>(true);\n\n /** Filter by log levels */\n readonly levelFilter = input<LogLevel[]>();\n\n /** Search query to highlight */\n readonly searchQuery = input<string>();\n\n /** Container height */\n readonly height = input<string>('400px');\n\n /** Visual variant */\n readonly variant = input<LogViewerVariant>('log');\n\n /**\n * Render lines as interactive rows (`role=\"button\"`, focusable, Enter/Space\n * emit `lineClick`). Set this whenever you bind `(lineClick)` so keyboard\n * users can activate a line too; a plain click emits either way.\n */\n readonly clickableLines = input<boolean>(false);\n\n /** BCP 47 locale of the line timestamps (24-hour clock). */\n readonly locale = input('en-US');\n\n /** Tooltip of the pause button while streaming. */\n readonly pauseLabel = input('Pause');\n\n /** Tooltip of the pause button while paused. */\n readonly resumeLabel = input('Resume');\n\n /** Accessible name of the pause button while streaming. */\n readonly pauseAriaLabel = input('Pause stream');\n\n /** Accessible name of the pause button while paused. */\n readonly resumeAriaLabel = input('Resume stream');\n\n /** Tooltip of the clear button. */\n readonly clearLabel = input('Clear');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear log');\n\n /** Tooltip of the copy button. */\n readonly copyLabel = input('Copy all');\n\n /** Accessible name of the copy button. */\n readonly copyAriaLabel = input('Copy all lines');\n\n /** Tooltip of the search toggle. */\n readonly searchLabel = input('Search (/)');\n\n /** Accessible name of the search toggle. */\n readonly searchAriaLabel = input('Search log');\n\n /** Label of the search field (visually hidden). */\n readonly searchInputLabel = input('Search logs');\n\n /** Placeholder of the search field. */\n readonly searchPlaceholder = input('Search logs...');\n\n /** Unit after the error count in the level filter. */\n readonly errorsLabel = input('errors');\n\n /** Unit after the warning count in the level filter. */\n readonly warningsLabel = input('warnings');\n\n /** Unit after the line count. */\n readonly linesLabel = input('lines');\n\n /** Accessible name of the log region. */\n readonly outputAriaLabel = input('Log output');\n\n /** Label of the jump-to-bottom button. */\n readonly jumpToBottomLabel = input('Jump to bottom');\n\n /** Emitted when a line is clicked (or activated with Enter/Space) */\n readonly lineClick = output<LogLine>();\n\n /**\n * Emitted with the copied text after \"Copy all\" — also when the Clipboard\n * API is unavailable (insecure context, denied permission), so consumers can\n * offer their own fallback.\n */\n readonly copyAll = output<string>();\n\n /** Emitted when scroll state changes */\n readonly scrollStateChange = output<{ atBottom: boolean }>();\n\n /** Internal buffer of all lines */\n protected buffer = signal<LogLine[]>([]);\n\n /** Whether stream is paused */\n protected paused = signal(false);\n\n /** Whether user is at bottom of scroll */\n protected atBottom = signal(true);\n\n /** Internal search input */\n protected internalSearch = signal('');\n\n /** Internal level filter */\n protected internalLevelFilter = signal<Set<LogLevel>>(new Set());\n\n /** Show search bar */\n protected showSearch = signal(false);\n\n /** Effective search query */\n protected effectiveSearch = computed(() => {\n return this.searchQuery() || this.internalSearch();\n });\n\n /** Filtered lines for display */\n protected filteredLines = computed(() => {\n let lines = this.buffer();\n const levels = this.levelFilter();\n const internalFilter = this.internalLevelFilter();\n\n // Apply level filter\n if (levels?.length) {\n lines = lines.filter((l) => l.level && levels.includes(l.level));\n } else if (internalFilter.size > 0) {\n lines = lines.filter((l) => l.level && internalFilter.has(l.level));\n }\n\n return lines;\n });\n\n // ── Virtual window ────────────────────────────────────────────────────\n\n protected scrollTop = signal(0);\n\n /**\n * Measured `clientHeight` of the scroll area (excludes toolbar and search\n * bar by construction). 0 until the first ResizeObserver callback.\n */\n private readonly viewportHeight = signal(0);\n\n /** Row height in px — `--lc-log-viewer-line-height`, read once after render. */\n protected readonly lineHeight = signal(DEFAULT_LINE_HEIGHT);\n\n protected visibleRange = computed(() => {\n const lineHeight = this.lineHeight();\n const measured = this.viewportHeight();\n // Before the first measurement fall back to the declared height (minus a\n // nominal toolbar) so the first frame already shows a plausible window.\n const viewHeight = measured > 0 ? measured : (parseInt(this.height(), 10) || 400) - 40;\n const total = this.filteredLines().length;\n const start = Math.floor(this.scrollTop() / lineHeight);\n const visible = Math.ceil(viewHeight / lineHeight) + 2;\n return {\n start: Math.max(0, start - 1),\n end: Math.min(total, start + visible + 1),\n total,\n totalHeight: total * lineHeight,\n };\n });\n\n protected visibleLines = computed(() => {\n const range = this.visibleRange();\n return this.filteredLines().slice(range.start, range.end).map((line, i) => ({\n ...line,\n _index: range.start + i,\n }));\n });\n\n protected containerClasses = computed(() => {\n return `lc-log-viewer lc-log-viewer--${this.variant()}`;\n });\n\n /** Line counts per level for toolbar */\n protected levelCounts = computed(() => {\n const buf = this.buffer();\n const counts = { debug: 0, info: 0, warn: 0, error: 0 };\n for (const line of buf) {\n if (line.level && line.level in counts) counts[line.level as keyof typeof counts]++;\n }\n return counts;\n });\n\n constructor() {\n // Controlled mode: mirror `lines` into the buffer — unconditionally, so\n // `[lines]=\"[]\"` clears the view. `maxLines` is read untracked: changing\n // it must not wipe a streamed buffer through this effect.\n effect(() => {\n const input = this.lines();\n untracked(() => this.buffer.set(this.trim(input, this.maxLines())));\n });\n\n // Streaming mode. The subscribe body runs untracked: BehaviorSubject /\n // ReplaySubject / of() emit synchronously inside this effect, and reading\n // `paused()` / `maxLines()` there would make them dependencies — toggling\n // Pause would then re-subscribe and replay duplicates.\n effect(() => {\n const stream = this.stream$();\n this.streamSub?.unsubscribe();\n this.streamSub = undefined;\n if (!stream) return;\n untracked(() => {\n this.streamSub = stream.subscribe((data) => this.append(Array.isArray(data) ? data : [data]));\n });\n });\n this.destroyRef.onDestroy(() => this.streamSub?.unsubscribe());\n\n // Follow mode: whenever the visible lines change (new lines, filter),\n // stick to the bottom — but only if the reader is already there.\n effect(() => {\n this.filteredLines();\n if (!this.autoScroll() || !untracked(this.atBottom)) return;\n afterNextRender(() => this.scrollToBottom(), { injector: this.injector });\n });\n\n afterNextRender(() => {\n const el = this.scrollContainer().nativeElement;\n\n // Row height from CSS (once) so the virtual window matches the rows.\n const declared = parseFloat(getComputedStyle(el).getPropertyValue('--lc-log-viewer-line-height'));\n if (declared > 0) this.lineHeight.set(declared);\n\n // Passive scroll tracking outside the zone.\n const onScroll = () => this.onScroll();\n this.ngZone.runOutsideAngular(() => el.addEventListener('scroll', onScroll, { passive: true }));\n this.destroyRef.onDestroy(() => el.removeEventListener('scroll', onScroll));\n\n // Measure the scroll area so `height=\"100%\"`, `50vh`, flex parents … all\n // yield a correctly sized window (not one derived from parseInt(height)).\n this.viewportHeight.set(el.clientHeight);\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => this.viewportHeight.set(el.clientHeight));\n observer.observe(el);\n this.destroyRef.onDestroy(() => observer.disconnect());\n });\n }\n\n private trim(lines: LogLine[], max: number): LogLine[] {\n return lines.length > max ? lines.slice(-max) : lines;\n }\n\n /** Append streamed lines to the buffer (no-op while paused). */\n private append(newLines: LogLine[]): void {\n if (untracked(this.paused)) return;\n const max = untracked(this.maxLines);\n this.buffer.update((buf) => this.trim([...buf, ...newLines], max));\n }\n\n protected onScroll(): void {\n const el = this.scrollContainer().nativeElement;\n this.scrollTop.set(el.scrollTop);\n const isAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < AT_BOTTOM_THRESHOLD;\n if (this.atBottom() !== isAtBottom) {\n this.ngZone.run(() => {\n this.atBottom.set(isAtBottom);\n this.scrollStateChange.emit({ atBottom: isAtBottom });\n });\n }\n }\n\n protected scrollToBottom(): void {\n const el = this.scrollContainer().nativeElement;\n el.scrollTop = el.scrollHeight;\n this.atBottom.set(true);\n }\n\n protected togglePause(): void {\n this.paused.update((v) => !v);\n }\n\n protected clearBuffer(): void {\n this.buffer.set([]);\n }\n\n protected async onCopyAll(): Promise<void> {\n const text = this.filteredLines()\n .map((l) => {\n const ts = l.timestamp ? `[${l.timestamp.toISOString()}] ` : '';\n const lvl = l.level ? `[${l.level.toUpperCase()}] ` : '';\n return `${ts}${lvl}${l.text}`;\n })\n .join('\\n');\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n // Clipboard API unavailable (insecure context / permission denied):\n // still emit so the consumer can fall back to its own copy path.\n }\n this.copyAll.emit(text);\n }\n\n protected toggleSearch(): void {\n this.showSearch.update((v) => !v);\n if (this.showSearch()) {\n // The input is `@if`-rendered — focus it once it exists (`autofocus`\n // is unreliable for dynamically inserted elements).\n afterNextRender(() => this.searchInput()?.nativeElement.focus(), { injector: this.injector });\n } else {\n this.internalSearch.set('');\n }\n }\n\n protected toggleLevelFilter(level: LogLevel): void {\n this.internalLevelFilter.update((set) => {\n const newSet = new Set(set);\n if (newSet.has(level)) {\n newSet.delete(level);\n } else {\n newSet.add(level);\n }\n return newSet;\n });\n }\n\n protected onLineClick(line: LogLine): void {\n this.lineClick.emit(line);\n }\n\n /**\n * Enter/Space activate an interactive line (see `clickableLines`). Like a\n * native button, Enter fires on keydown and Space on keyup (keydown only\n * prevents the page scroll) — so a consumer that moves focus in its\n * `lineClick` handler doesn't get a stray Space keyup on the new target.\n */\n protected onLineKeydown(event: KeyboardEvent, line: LogLine): void {\n if (!this.clickableLines()) return;\n if (event.key === 'Enter') {\n event.preventDefault();\n this.lineClick.emit(line);\n } else if (event.key === ' ') {\n event.preventDefault();\n }\n }\n\n protected onLineKeyup(event: KeyboardEvent, line: LogLine): void {\n if (this.clickableLines() && event.key === ' ') {\n event.preventDefault();\n this.lineClick.emit(line);\n }\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === '/' && !this.showSearch()) {\n event.preventDefault();\n this.toggleSearch();\n }\n if (event.key === 'g' && !event.shiftKey && !this.showSearch()) {\n this.scrollContainer().nativeElement.scrollTo(0, 0);\n }\n if (event.key === 'G' && !this.showSearch()) {\n this.scrollToBottom();\n }\n }\n\n protected formatTimestamp(date?: Date): string {\n if (!date) return '';\n return date.toLocaleTimeString(this.locale(), { hour12: false } as Intl.DateTimeFormatOptions);\n }\n\n /**\n * Render a line's text as HTML: ANSI SGR codes become `<span class=\"ansi-…\">`\n * wrappers, everything else is escaped, and search matches are wrapped in\n * `<mark>`. Highlighting is applied per *text* segment — before the spans\n * are assembled — so a query can never match inside a tag or an entity\n * (`e` used to hit `class=\"ansi-red\"`, `&` / `<` never matched at all).\n */\n protected renderLine(text: string): string {\n const query = this.effectiveSearch();\n const matcher = query\n ? new RegExp(`(${query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`, 'gi')\n : null;\n // Split the raw text on the query (capturing group → odd parts are the\n // matches), escape every part, wrap the matches.\n const renderText = (segment: string): string =>\n matcher\n ? segment\n .split(matcher)\n .map((part, i) =>\n i % 2 === 1\n ? `<mark class=\"lc-log-viewer__match\">${this.escapeHtml(part)}</mark>`\n : this.escapeHtml(part),\n )\n .join('')\n : this.escapeHtml(segment);\n\n if (!this.ansiColors()) return renderText(text);\n\n let html = '';\n let openSpans = 0;\n let cursor = 0;\n // SGR sequences: ESC [ <params> m — params are `;`-separated (may be empty = reset).\n // eslint-disable-next-line no-control-regex -- matching the ESC byte is the point here\n const re = /\\x1b\\[([\\d;]*)m/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n html += renderText(text.slice(cursor, m.index));\n cursor = m.index + m[0].length;\n for (const code of (m[1] || '0').split(';')) {\n if (code === '0' || code === '') {\n // Reset closes everything that is open.\n html += '</span>'.repeat(openSpans);\n openSpans = 0;\n } else if (ANSI_CLASSES[code]) {\n html += `<span class=\"${ANSI_CLASSES[code]}\">`;\n openSpans++;\n }\n // Unrecognised codes are stripped.\n }\n }\n html += renderText(text.slice(cursor));\n return html + '</span>'.repeat(openSpans);\n }\n\n private escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n }\n\n protected getInputValue(event: Event): string {\n return (event.target as HTMLInputElement).value;\n }\n}\n","<div\n [class]=\"containerClasses()\"\n [style.height]=\"height()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Toolbar -->\n <div class=\"lc-log-viewer__toolbar\">\n <div class=\"lc-log-viewer__toolbar-left\">\n @if (stream$()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"paused()\"\n (click)=\"togglePause()\"\n [title]=\"paused() ? resumeLabel() : pauseLabel()\"\n [attr.aria-label]=\"paused() ? resumeAriaLabel() : pauseAriaLabel()\"\n [attr.aria-pressed]=\"paused()\"\n >\n <lc-icon [name]=\"paused() ? 'player-play' : 'player-pause'\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"clearBuffer()\"\n [attr.title]=\"clearLabel()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"trash\" size=\"xs\" [decorative]=\"true\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"onCopyAll()\"\n [attr.title]=\"copyLabel()\"\n [attr.aria-label]=\"copyAriaLabel()\"\n >\n <lc-icon name=\"clipboard-document\" size=\"xs\" [decorative]=\"true\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"showSearch()\"\n (click)=\"toggleSearch()\"\n [attr.title]=\"searchLabel()\"\n [attr.aria-label]=\"searchAriaLabel()\"\n [attr.aria-expanded]=\"showSearch()\"\n >\n <lc-icon name=\"magnifying-glass\" size=\"xs\" [decorative]=\"true\" />\n </button>\n </div>\n\n <div class=\"lc-log-viewer__toolbar-right\">\n <!-- Level filter pills -->\n @if (levelCounts().error > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--error\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('error')\"\n [attr.aria-pressed]=\"internalLevelFilter().has('error')\"\n (click)=\"toggleLevelFilter('error')\"\n >\n {{ levelCounts().error }} {{ errorsLabel() }}\n </button>\n }\n @if (levelCounts().warn > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--warn\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('warn')\"\n [attr.aria-pressed]=\"internalLevelFilter().has('warn')\"\n (click)=\"toggleLevelFilter('warn')\"\n >\n {{ levelCounts().warn }} {{ warningsLabel() }}\n </button>\n }\n <span class=\"lc-log-viewer__line-count\">{{ filteredLines().length }} {{ linesLabel() }}</span>\n </div>\n </div>\n\n <!-- Search bar -->\n @if (showSearch()) {\n <div class=\"lc-log-viewer__search\">\n <lc-icon name=\"magnifying-glass\" size=\"xs\" [decorative]=\"true\" />\n <label class=\"lc-log-viewer__sr-only\" [for]=\"searchInputId\">{{ searchInputLabel() }}</label>\n <input\n #searchInput\n type=\"text\"\n [id]=\"searchInputId\"\n class=\"lc-log-viewer__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"internalSearch()\"\n (input)=\"internalSearch.set(getInputValue($event))\"\n />\n </div>\n }\n\n <!--\n Log content (virtualized). `role=\"log\"` names the region; live\n announcements are switched off because rows enter/leave the DOM on every\n scroll of the virtual window — announcing them would be pure noise.\n -->\n <div\n #scrollContainer\n class=\"lc-log-viewer__content\"\n role=\"log\"\n aria-live=\"off\"\n [attr.aria-label]=\"outputAriaLabel()\"\n >\n <div\n class=\"lc-log-viewer__spacer\"\n [style.height.px]=\"visibleRange().totalHeight\"\n >\n <div\n class=\"lc-log-viewer__window\"\n [style.transform]=\"'translateY(' + (visibleRange().start * lineHeight()) + 'px)'\"\n >\n @for (line of visibleLines(); track line._index) {\n <div\n class=\"lc-log-viewer__line\"\n [class.lc-log-viewer__line--clickable]=\"clickableLines()\"\n [class.lc-log-viewer__line--debug]=\"line.level === 'debug'\"\n [class.lc-log-viewer__line--info]=\"line.level === 'info'\"\n [class.lc-log-viewer__line--warn]=\"line.level === 'warn'\"\n [class.lc-log-viewer__line--error]=\"line.level === 'error'\"\n [attr.role]=\"clickableLines() ? 'button' : null\"\n [attr.tabindex]=\"clickableLines() ? 0 : null\"\n (click)=\"onLineClick(line)\"\n (keydown)=\"onLineKeydown($event, line)\"\n (keyup)=\"onLineKeyup($event, line)\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-log-viewer__line-num\">{{ line._index + 1 }}</span>\n }\n @if (showTimestamps() && line.timestamp) {\n <span class=\"lc-log-viewer__timestamp\">{{ formatTimestamp(line.timestamp) }}</span>\n }\n @if (line.level) {\n <span class=\"lc-log-viewer__level\">{{ line.level.toUpperCase().padEnd(5) }}</span>\n }\n @if (line.source) {\n <span class=\"lc-log-viewer__source\">[{{ line.source }}]</span>\n }\n <span\n class=\"lc-log-viewer__text\"\n [innerHTML]=\"renderLine(line.text)\"\n ></span>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Jump to bottom button -->\n @if (!atBottom()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__jump-bottom\"\n (click)=\"scrollToBottom()\"\n >\n <lc-icon name=\"arrow-down\" size=\"xs\" [decorative]=\"true\" />\n {{ jumpToBottomLabel() }}\n </button>\n }\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-log-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/log-viewer/log-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/log-viewer/log-viewer.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n DestroyRef,\n ElementRef,\n Injector,\n NgZone,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { Observable, Subscription } from 'rxjs';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_TRASH } from '@lifecorner/angular-ui-kit/icons';\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type LogViewerVariant = 'terminal' | 'log';\n\nexport interface LogLine {\n text: string;\n level?: LogLevel;\n timestamp?: Date;\n source?: string;\n meta?: Record<string, unknown>;\n}\n\n/** SGR code → CSS class for the ANSI subset the viewer renders. */\nconst ANSI_CLASSES: Record<string, string> = {\n '1': 'ansi-bold',\n '3': 'ansi-italic',\n '30': 'ansi-black',\n '31': 'ansi-red',\n '32': 'ansi-green',\n '33': 'ansi-yellow',\n '34': 'ansi-blue',\n '35': 'ansi-magenta',\n '36': 'ansi-cyan',\n '37': 'ansi-white',\n '90': 'ansi-gray',\n '91': 'ansi-bright-red',\n '92': 'ansi-bright-green',\n '93': 'ansi-bright-yellow',\n '94': 'ansi-bright-blue',\n '95': 'ansi-bright-magenta',\n '96': 'ansi-bright-cyan',\n '97': 'ansi-bright-white',\n};\n\n/** Row height fallback when `--lc-log-viewer-line-height` can't be read (SSR, tests). */\nconst DEFAULT_LINE_HEIGHT = 22;\n\n/** Distance from the bottom (px) still counted as \"at the bottom\". */\nconst AT_BOTTOM_THRESHOLD = 30;\n\n/**\n * Streaming log / terminal viewer component.\n *\n * Supports controlled (lines input) and streaming (stream$ observable) modes,\n * virtualized rendering for large buffers, ANSI color parsing, auto-scroll,\n * and filtering.\n *\n * The virtual window is sized from the *measured* height of the scroll area\n * (ResizeObserver), so any `height` — `400px`, `50vh`, `100%` inside a flex\n * parent — renders a full window. Row height comes from the CSS custom\n * property `--lc-log-viewer-line-height` (default 22px), read once after the\n * first render; override it on the host to change the row density.\n *\n * @example\n * ```html\n * <lc-log-viewer [stream$]=\"logs$\" autoScroll height=\"600px\" variant=\"terminal\" />\n * ```\n */\n@Component({\n selector: 'lc-log-viewer',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_TRASH])],\n templateUrl: './log-viewer.component.html',\n styleUrls: ['./log-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class LogViewerComponent {\n private static nextId = 0;\n /** Id of the search input (links its visually-hidden label). */\n protected readonly searchInputId = `lc-log-viewer-search-${++LogViewerComponent.nextId}`;\n\n private readonly ngZone = inject(NgZone);\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n private readonly scrollContainer = viewChild.required<ElementRef<HTMLElement>>('scrollContainer');\n private readonly searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');\n private streamSub?: Subscription;\n\n /** Controlled mode: array of log lines */\n readonly lines = input<LogLine[]>([]);\n\n /** Streaming mode: observable of log lines */\n readonly stream$ = input<Observable<LogLine | LogLine[]>>();\n\n /** Maximum lines to keep in buffer */\n readonly maxLines = input<number>(10_000);\n\n /**\n * Follow new lines: scroll to the bottom whenever the visible line count\n * changes — but only while the reader is already at the bottom, so scrolling\n * up to inspect older output is never fought.\n */\n readonly autoScroll = input<boolean>(true);\n\n /** Show timestamps column */\n readonly showTimestamps = input<boolean>(true);\n\n /** Show line numbers */\n readonly showLineNumbers = input<boolean>(false);\n\n /** Parse ANSI color codes */\n readonly ansiColors = input<boolean>(true);\n\n /** Filter by log levels */\n readonly levelFilter = input<LogLevel[]>();\n\n /** Search query to highlight */\n readonly searchQuery = input<string>();\n\n /** Container height */\n readonly height = input<string>('400px');\n\n /** Visual variant */\n readonly variant = input<LogViewerVariant>('log');\n\n /**\n * Render lines as interactive rows (`role=\"button\"`, focusable, Enter/Space\n * emit `lineClick`). Set this whenever you bind `(lineClick)` so keyboard\n * users can activate a line too; a plain click emits either way.\n */\n readonly clickableLines = input<boolean>(false);\n\n /** BCP 47 locale of the line timestamps (24-hour clock). */\n readonly locale = input('en-US');\n\n /** Tooltip of the pause button while streaming. */\n readonly pauseLabel = input('Pause');\n\n /** Tooltip of the pause button while paused. */\n readonly resumeLabel = input('Resume');\n\n /** Accessible name of the pause button while streaming. */\n readonly pauseAriaLabel = input('Pause stream');\n\n /** Accessible name of the pause button while paused. */\n readonly resumeAriaLabel = input('Resume stream');\n\n /** Tooltip of the clear button. */\n readonly clearLabel = input('Clear');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear log');\n\n /** Tooltip of the copy button. */\n readonly copyLabel = input('Copy all');\n\n /** Accessible name of the copy button. */\n readonly copyAriaLabel = input('Copy all lines');\n\n /** Tooltip of the search toggle. */\n readonly searchLabel = input('Search (/)');\n\n /** Accessible name of the search toggle. */\n readonly searchAriaLabel = input('Search log');\n\n /** Label of the search field (visually hidden). */\n readonly searchInputLabel = input('Search logs');\n\n /** Placeholder of the search field. */\n readonly searchPlaceholder = input('Search logs...');\n\n /** Unit after the error count in the level filter. */\n readonly errorsLabel = input('errors');\n\n /** Unit after the warning count in the level filter. */\n readonly warningsLabel = input('warnings');\n\n /** Unit after the line count. */\n readonly linesLabel = input('lines');\n\n /** Accessible name of the log region. */\n readonly outputAriaLabel = input('Log output');\n\n /** Label of the jump-to-bottom button. */\n readonly jumpToBottomLabel = input('Jump to bottom');\n\n /** Emitted when a line is clicked (or activated with Enter/Space) */\n readonly lineClick = output<LogLine>();\n\n /**\n * Emitted with the copied text after \"Copy all\" — also when the Clipboard\n * API is unavailable (insecure context, denied permission), so consumers can\n * offer their own fallback.\n */\n readonly copyAll = output<string>();\n\n /** Emitted when scroll state changes */\n readonly scrollStateChange = output<{ atBottom: boolean }>();\n\n /** Internal buffer of all lines */\n protected buffer = signal<LogLine[]>([]);\n\n /** Whether stream is paused */\n protected paused = signal(false);\n\n /** Whether user is at bottom of scroll */\n protected atBottom = signal(true);\n\n /** Internal search input */\n protected internalSearch = signal('');\n\n /** Internal level filter */\n protected internalLevelFilter = signal<Set<LogLevel>>(new Set());\n\n /** Show search bar */\n protected showSearch = signal(false);\n\n /** Effective search query */\n protected effectiveSearch = computed(() => {\n return this.searchQuery() || this.internalSearch();\n });\n\n /** Filtered lines for display */\n protected filteredLines = computed(() => {\n let lines = this.buffer();\n const levels = this.levelFilter();\n const internalFilter = this.internalLevelFilter();\n\n // Apply level filter\n if (levels?.length) {\n lines = lines.filter((l) => l.level && levels.includes(l.level));\n } else if (internalFilter.size > 0) {\n lines = lines.filter((l) => l.level && internalFilter.has(l.level));\n }\n\n return lines;\n });\n\n // ── Virtual window ────────────────────────────────────────────────────\n\n protected scrollTop = signal(0);\n\n /**\n * Measured `clientHeight` of the scroll area (excludes toolbar and search\n * bar by construction). 0 until the first ResizeObserver callback.\n */\n private readonly viewportHeight = signal(0);\n\n /** Row height in px — `--lc-log-viewer-line-height`, read once after render. */\n protected readonly lineHeight = signal(DEFAULT_LINE_HEIGHT);\n\n protected visibleRange = computed(() => {\n const lineHeight = this.lineHeight();\n const measured = this.viewportHeight();\n // Before the first measurement fall back to the declared height (minus a\n // nominal toolbar) so the first frame already shows a plausible window.\n const viewHeight = measured > 0 ? measured : (parseInt(this.height(), 10) || 400) - 40;\n const total = this.filteredLines().length;\n const start = Math.floor(this.scrollTop() / lineHeight);\n const visible = Math.ceil(viewHeight / lineHeight) + 2;\n return {\n start: Math.max(0, start - 1),\n end: Math.min(total, start + visible + 1),\n total,\n totalHeight: total * lineHeight,\n };\n });\n\n protected visibleLines = computed(() => {\n const range = this.visibleRange();\n return this.filteredLines().slice(range.start, range.end).map((line, i) => ({\n ...line,\n _index: range.start + i,\n }));\n });\n\n protected containerClasses = computed(() => {\n return `lc-log-viewer lc-log-viewer--${this.variant()}`;\n });\n\n /** Line counts per level for toolbar */\n protected levelCounts = computed(() => {\n const buf = this.buffer();\n const counts = { debug: 0, info: 0, warn: 0, error: 0 };\n for (const line of buf) {\n if (line.level && line.level in counts) counts[line.level as keyof typeof counts]++;\n }\n return counts;\n });\n\n constructor() {\n // Controlled mode: mirror `lines` into the buffer — unconditionally, so\n // `[lines]=\"[]\"` clears the view. `maxLines` is read untracked: changing\n // it must not wipe a streamed buffer through this effect.\n effect(() => {\n const input = this.lines();\n untracked(() => this.buffer.set(this.trim(input, this.maxLines())));\n });\n\n // Streaming mode. The subscribe body runs untracked: BehaviorSubject /\n // ReplaySubject / of() emit synchronously inside this effect, and reading\n // `paused()` / `maxLines()` there would make them dependencies — toggling\n // Pause would then re-subscribe and replay duplicates.\n effect(() => {\n const stream = this.stream$();\n this.streamSub?.unsubscribe();\n this.streamSub = undefined;\n if (!stream) return;\n untracked(() => {\n this.streamSub = stream.subscribe((data) => this.append(Array.isArray(data) ? data : [data]));\n });\n });\n this.destroyRef.onDestroy(() => this.streamSub?.unsubscribe());\n\n // Follow mode: whenever the visible lines change (new lines, filter),\n // stick to the bottom — but only if the reader is already there.\n effect(() => {\n this.filteredLines();\n if (!this.autoScroll() || !untracked(this.atBottom)) return;\n afterNextRender(() => this.scrollToBottom(), { injector: this.injector });\n });\n\n afterNextRender(() => {\n const el = this.scrollContainer().nativeElement;\n\n // Row height from CSS (once) so the virtual window matches the rows.\n const declared = parseFloat(getComputedStyle(el).getPropertyValue('--lc-log-viewer-line-height'));\n if (declared > 0) this.lineHeight.set(declared);\n\n // Passive scroll tracking outside the zone.\n const onScroll = () => this.onScroll();\n this.ngZone.runOutsideAngular(() => el.addEventListener('scroll', onScroll, { passive: true }));\n this.destroyRef.onDestroy(() => el.removeEventListener('scroll', onScroll));\n\n // Measure the scroll area so `height=\"100%\"`, `50vh`, flex parents … all\n // yield a correctly sized window (not one derived from parseInt(height)).\n this.viewportHeight.set(el.clientHeight);\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => this.viewportHeight.set(el.clientHeight));\n observer.observe(el);\n this.destroyRef.onDestroy(() => observer.disconnect());\n });\n }\n\n private trim(lines: LogLine[], max: number): LogLine[] {\n return lines.length > max ? lines.slice(-max) : lines;\n }\n\n /** Append streamed lines to the buffer (no-op while paused). */\n private append(newLines: LogLine[]): void {\n if (untracked(this.paused)) return;\n const max = untracked(this.maxLines);\n this.buffer.update((buf) => this.trim([...buf, ...newLines], max));\n }\n\n protected onScroll(): void {\n const el = this.scrollContainer().nativeElement;\n this.scrollTop.set(el.scrollTop);\n const isAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < AT_BOTTOM_THRESHOLD;\n if (this.atBottom() !== isAtBottom) {\n this.ngZone.run(() => {\n this.atBottom.set(isAtBottom);\n this.scrollStateChange.emit({ atBottom: isAtBottom });\n });\n }\n }\n\n protected scrollToBottom(): void {\n const el = this.scrollContainer().nativeElement;\n el.scrollTop = el.scrollHeight;\n this.atBottom.set(true);\n }\n\n protected togglePause(): void {\n this.paused.update((v) => !v);\n }\n\n protected clearBuffer(): void {\n this.buffer.set([]);\n }\n\n protected async onCopyAll(): Promise<void> {\n const text = this.filteredLines()\n .map((l) => {\n const ts = l.timestamp ? `[${l.timestamp.toISOString()}] ` : '';\n const lvl = l.level ? `[${l.level.toUpperCase()}] ` : '';\n return `${ts}${lvl}${l.text}`;\n })\n .join('\\n');\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n // Clipboard API unavailable (insecure context / permission denied):\n // still emit so the consumer can fall back to its own copy path.\n }\n this.copyAll.emit(text);\n }\n\n protected toggleSearch(): void {\n this.showSearch.update((v) => !v);\n if (this.showSearch()) {\n // The input is `@if`-rendered — focus it once it exists (`autofocus`\n // is unreliable for dynamically inserted elements).\n afterNextRender(() => this.searchInput()?.nativeElement.focus(), { injector: this.injector });\n } else {\n this.internalSearch.set('');\n }\n }\n\n protected toggleLevelFilter(level: LogLevel): void {\n this.internalLevelFilter.update((set) => {\n const newSet = new Set(set);\n if (newSet.has(level)) {\n newSet.delete(level);\n } else {\n newSet.add(level);\n }\n return newSet;\n });\n }\n\n protected onLineClick(line: LogLine): void {\n this.lineClick.emit(line);\n }\n\n /**\n * Enter/Space activate an interactive line (see `clickableLines`). Like a\n * native button, Enter fires on keydown and Space on keyup (keydown only\n * prevents the page scroll) — so a consumer that moves focus in its\n * `lineClick` handler doesn't get a stray Space keyup on the new target.\n */\n protected onLineKeydown(event: KeyboardEvent, line: LogLine): void {\n if (!this.clickableLines()) return;\n if (event.key === 'Enter') {\n event.preventDefault();\n this.lineClick.emit(line);\n } else if (event.key === ' ') {\n event.preventDefault();\n }\n }\n\n protected onLineKeyup(event: KeyboardEvent, line: LogLine): void {\n if (this.clickableLines() && event.key === ' ') {\n event.preventDefault();\n this.lineClick.emit(line);\n }\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === '/' && !this.showSearch()) {\n event.preventDefault();\n this.toggleSearch();\n }\n if (event.key === 'g' && !event.shiftKey && !this.showSearch()) {\n this.scrollContainer().nativeElement.scrollTo(0, 0);\n }\n if (event.key === 'G' && !this.showSearch()) {\n this.scrollToBottom();\n }\n }\n\n protected formatTimestamp(date?: Date): string {\n if (!date) return '';\n return date.toLocaleTimeString(this.locale(), { hour12: false } as Intl.DateTimeFormatOptions);\n }\n\n /**\n * Render a line's text as HTML: ANSI SGR codes become `<span class=\"ansi-…\">`\n * wrappers, everything else is escaped, and search matches are wrapped in\n * `<mark>`. Highlighting is applied per *text* segment — before the spans\n * are assembled — so a query can never match inside a tag or an entity\n * (`e` used to hit `class=\"ansi-red\"`, `&` / `<` never matched at all).\n */\n protected renderLine(text: string): string {\n const query = this.effectiveSearch();\n const matcher = query\n ? new RegExp(`(${query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`, 'gi')\n : null;\n // Split the raw text on the query (capturing group → odd parts are the\n // matches), escape every part, wrap the matches.\n const renderText = (segment: string): string =>\n matcher\n ? segment\n .split(matcher)\n .map((part, i) =>\n i % 2 === 1\n ? `<mark class=\"lc-log-viewer__match\">${this.escapeHtml(part)}</mark>`\n : this.escapeHtml(part),\n )\n .join('')\n : this.escapeHtml(segment);\n\n if (!this.ansiColors()) return renderText(text);\n\n let html = '';\n let openSpans = 0;\n let cursor = 0;\n // SGR sequences: ESC [ <params> m — params are `;`-separated (may be empty = reset).\n // eslint-disable-next-line no-control-regex -- matching the ESC byte is the point here\n const re = /\\x1b\\[([\\d;]*)m/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n html += renderText(text.slice(cursor, m.index));\n cursor = m.index + m[0].length;\n for (const code of (m[1] || '0').split(';')) {\n if (code === '0' || code === '') {\n // Reset closes everything that is open.\n html += '</span>'.repeat(openSpans);\n openSpans = 0;\n } else if (ANSI_CLASSES[code]) {\n html += `<span class=\"${ANSI_CLASSES[code]}\">`;\n openSpans++;\n }\n // Unrecognised codes are stripped.\n }\n }\n html += renderText(text.slice(cursor));\n return html + '</span>'.repeat(openSpans);\n }\n\n private escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n }\n\n protected getInputValue(event: Event): string {\n return (event.target as HTMLInputElement).value;\n }\n}\n","<div\n [class]=\"containerClasses()\"\n [style.height]=\"height()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Toolbar -->\n <div class=\"lc-log-viewer__toolbar\">\n <div class=\"lc-log-viewer__toolbar-left\">\n @if (stream$()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"paused()\"\n (click)=\"togglePause()\"\n [title]=\"paused() ? resumeLabel() : pauseLabel()\"\n [attr.aria-label]=\"paused() ? resumeAriaLabel() : pauseAriaLabel()\"\n [attr.aria-pressed]=\"paused()\"\n >\n <lc-icon [name]=\"paused() ? 'player-play' : 'player-pause'\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"clearBuffer()\"\n [attr.title]=\"clearLabel()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"trash\" size=\"xs\" [decorative]=\"true\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"onCopyAll()\"\n [attr.title]=\"copyLabel()\"\n [attr.aria-label]=\"copyAriaLabel()\"\n >\n <lc-icon name=\"clipboard-document\" size=\"xs\" [decorative]=\"true\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"showSearch()\"\n (click)=\"toggleSearch()\"\n [attr.title]=\"searchLabel()\"\n [attr.aria-label]=\"searchAriaLabel()\"\n [attr.aria-expanded]=\"showSearch()\"\n >\n <lc-icon name=\"magnifying-glass\" size=\"xs\" [decorative]=\"true\" />\n </button>\n </div>\n\n <div class=\"lc-log-viewer__toolbar-right\">\n <!-- Level filter pills -->\n @if (levelCounts().error > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--error\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('error')\"\n [attr.aria-pressed]=\"internalLevelFilter().has('error')\"\n (click)=\"toggleLevelFilter('error')\"\n >\n {{ levelCounts().error }} {{ errorsLabel() }}\n </button>\n }\n @if (levelCounts().warn > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--warn\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('warn')\"\n [attr.aria-pressed]=\"internalLevelFilter().has('warn')\"\n (click)=\"toggleLevelFilter('warn')\"\n >\n {{ levelCounts().warn }} {{ warningsLabel() }}\n </button>\n }\n <span class=\"lc-log-viewer__line-count\">{{ filteredLines().length }} {{ linesLabel() }}</span>\n </div>\n </div>\n\n <!-- Search bar -->\n @if (showSearch()) {\n <div class=\"lc-log-viewer__search\">\n <lc-icon name=\"magnifying-glass\" size=\"xs\" [decorative]=\"true\" />\n <label class=\"lc-log-viewer__sr-only\" [for]=\"searchInputId\">{{ searchInputLabel() }}</label>\n <input\n #searchInput\n type=\"text\"\n [id]=\"searchInputId\"\n class=\"lc-log-viewer__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"internalSearch()\"\n (input)=\"internalSearch.set(getInputValue($event))\"\n />\n </div>\n }\n\n <!--\n Log content (virtualized). `role=\"log\"` names the region; live\n announcements are switched off because rows enter/leave the DOM on every\n scroll of the virtual window — announcing them would be pure noise.\n -->\n <div\n #scrollContainer\n class=\"lc-log-viewer__content\"\n role=\"log\"\n aria-live=\"off\"\n [attr.aria-label]=\"outputAriaLabel()\"\n >\n <div\n class=\"lc-log-viewer__spacer\"\n [style.height.px]=\"visibleRange().totalHeight\"\n >\n <div\n class=\"lc-log-viewer__window\"\n [style.transform]=\"'translateY(' + (visibleRange().start * lineHeight()) + 'px)'\"\n >\n @for (line of visibleLines(); track line._index) {\n <div\n class=\"lc-log-viewer__line\"\n [class.lc-log-viewer__line--clickable]=\"clickableLines()\"\n [class.lc-log-viewer__line--debug]=\"line.level === 'debug'\"\n [class.lc-log-viewer__line--info]=\"line.level === 'info'\"\n [class.lc-log-viewer__line--warn]=\"line.level === 'warn'\"\n [class.lc-log-viewer__line--error]=\"line.level === 'error'\"\n [attr.role]=\"clickableLines() ? 'button' : null\"\n [attr.tabindex]=\"clickableLines() ? 0 : null\"\n (click)=\"onLineClick(line)\"\n (keydown)=\"onLineKeydown($event, line)\"\n (keyup)=\"onLineKeyup($event, line)\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-log-viewer__line-num\">{{ line._index + 1 }}</span>\n }\n @if (showTimestamps() && line.timestamp) {\n <span class=\"lc-log-viewer__timestamp\">{{ formatTimestamp(line.timestamp) }}</span>\n }\n @if (line.level) {\n <span class=\"lc-log-viewer__level\">{{ line.level.toUpperCase().padEnd(5) }}</span>\n }\n @if (line.source) {\n <span class=\"lc-log-viewer__source\">[{{ line.source }}]</span>\n }\n <span\n class=\"lc-log-viewer__text\"\n [innerHTML]=\"renderLine(line.text)\"\n ></span>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Jump to bottom button -->\n @if (!atBottom()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__jump-bottom\"\n (click)=\"scrollToBottom()\"\n >\n <lc-icon name=\"arrow-down\" size=\"xs\" [decorative]=\"true\" />\n {{ jumpToBottomLabel() }}\n </button>\n }\n</div>\n"],"mappings":"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"}
|
|
@@ -552,7 +552,7 @@ var MarkdownComponent = class MarkdownComponent {
|
|
|
552
552
|
},
|
|
553
553
|
ngImport: i0,
|
|
554
554
|
template: "<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- delegated handler for clicks on rendered links, which are focusable anchors -->\n<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">{{ mermaidLoadingLabel() }}</div>\n }\n }\n }\n</div>\n",
|
|
555
|
-
styles: [".lc-markdown{color:var(--color-text-primary);font-family:var(--font-family-sans);font-size:var(--font-size-base, 1rem);line-height:var(--line-height-relaxed, 1.75);hyphens:var(--lc-markdown-hyphens, auto);overflow-wrap:var(--lc-markdown-overflow-wrap, anywhere)}.lc-markdown h1,.lc-markdown h2,.lc-markdown h3,.lc-markdown h4,.lc-markdown h5,.lc-markdown h6{color:var(--color-text-primary);font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-tight, 1.25);margin:1.5em 0 .5em}.lc-markdown h1:first-child,.lc-markdown h2:first-child,.lc-markdown h3:first-child,.lc-markdown h4:first-child,.lc-markdown h5:first-child,.lc-markdown h6:first-child{margin-top:0}.lc-markdown h1{font-size:var(--lc-markdown-h1, 2rem)}.lc-markdown h2{font-size:var(--lc-markdown-h2, 1.5rem)}.lc-markdown h3{font-size:var(--lc-markdown-h3, 1.25rem)}.lc-markdown h4{font-size:var(--lc-markdown-h4, 1.125rem)}.lc-markdown h5{font-size:var(--lc-markdown-h5, 1rem)}.lc-markdown h6{font-size:var(--lc-markdown-h6, .875rem)}.lc-markdown p{margin:0 0 1em}.lc-markdown a{color:var(--lc-markdown-link-color, var(--color-link, #1a6a79));text-decoration:var(--lc-markdown-link-decoration, underline);hyphens:none;text-decoration-thickness:1px;text-underline-offset:.18em}.lc-markdown a:hover{color:var(--lc-markdown-link-hover-color, var(--color-link-hover, #144f5b));text-decoration-line:underline;text-decoration-thickness:2px}.lc-markdown a:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:2px}.lc-markdown strong{font-weight:var(--font-weight-semibold, 600)}.lc-markdown code{background-color:var(--color-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-radius-sm, .25rem);font-family:var(--font-family-mono, monospace);font-size:.875em;hyphens:none}.lc-markdown pre{margin:1em 0;overflow-x:auto;hyphens:none;overflow-wrap:normal}.lc-markdown pre code{background:none;padding:0;border-radius:0}.lc-markdown blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--color-primary-300, #93c5fd);background-color:var(--color-surface-2);color:var(--color-text-secondary, #4b5563)}.lc-markdown blockquote p{margin:0}.lc-markdown ul,.lc-markdown ol{margin:0 0 1em;padding-left:1.5em}.lc-markdown ul{list-style:disc}.lc-markdown ol{list-style:decimal}.lc-markdown li{margin:.25em 0}.lc-markdown hr{border:none;border-top:1px solid var(--color-divider, #e5e7eb);margin:1.5em 0}.lc-markdown img{max-width:100%;height:auto;border-radius:var(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch;hyphens:manual;overflow-wrap:break-word}.lc-markdown__table{width:100%;border-collapse:collapse;font-size:var(--font-size-sm, .875rem)}.lc-markdown__table th,.lc-markdown__table td{padding:.5rem .75rem;border:1px solid var(--color-divider, #e5e7eb);text-align:left}.lc-markdown__table th{background-color:var(--color-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown .lc-markdown__anchor{color:var(--color-text-tertiary);text-decoration:none;margin-right:.25em;opacity:0;transition:opacity .15s ease}h1:hover .lc-markdown__anchor,h2:hover .lc-markdown__anchor,h3:hover .lc-markdown__anchor,h4:hover .lc-markdown__anchor,h5:hover .lc-markdown__anchor,h6:hover .lc-markdown__anchor{opacity:1}.lc-markdown lc-code-block{display:block;margin:1em 0;hyphens:none;overflow-wrap:normal}.lc-markdown lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-error);font-size:var(--font-size-sm, .875rem)}.lc-markdown--compact{font-size:var(--font-size-sm, .875rem);line-height:var(--line-height-normal, 1.5)}.lc-markdown--compact h1{font-size:1.5rem}.lc-markdown--compact h2{font-size:1.25rem}.lc-markdown--compact h3{font-size:1.125rem}.lc-markdown--compact h4{font-size:1rem}.lc-markdown--compact h5,.lc-markdown--compact h6{font-size:.875rem}.lc-markdown--compact h1,.lc-markdown--compact h2,.lc-markdown--compact h3,.lc-markdown--compact h4,.lc-markdown--compact h5,.lc-markdown--compact h6{margin:1em 0 .25em}.lc-markdown--compact p{margin:0 0 .5em}.lc-markdown--compact blockquote{margin:.5em 0;padding:.25em .75em}.lc-markdown--compact ul,.lc-markdown--compact ol{margin:0 0 .5em}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat hr{margin:8px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"],
|
|
555
|
+
styles: [".lc-markdown{color:var(--color-text-primary);font-family:var(--font-family-sans);font-size:var(--font-size-base, 1rem);line-height:var(--line-height-relaxed, 1.75);hyphens:var(--lc-markdown-hyphens, auto);overflow-wrap:var(--lc-markdown-overflow-wrap, anywhere)}.lc-markdown h1,.lc-markdown h2,.lc-markdown h3,.lc-markdown h4,.lc-markdown h5,.lc-markdown h6{color:var(--color-text-primary);font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-tight, 1.25);margin:1.5em 0 .5em}.lc-markdown h1:first-child,.lc-markdown h2:first-child,.lc-markdown h3:first-child,.lc-markdown h4:first-child,.lc-markdown h5:first-child,.lc-markdown h6:first-child{margin-top:0}.lc-markdown h1{font-size:var(--lc-markdown-h1, 2rem)}.lc-markdown h2{font-size:var(--lc-markdown-h2, 1.5rem)}.lc-markdown h3{font-size:var(--lc-markdown-h3, 1.25rem)}.lc-markdown h4{font-size:var(--lc-markdown-h4, 1.125rem)}.lc-markdown h5{font-size:var(--lc-markdown-h5, 1rem)}.lc-markdown h6{font-size:var(--lc-markdown-h6, .875rem)}.lc-markdown p{margin:0 0 1em}.lc-markdown a{color:var(--lc-markdown-link-color, var(--color-link, #1a6a79));text-decoration:var(--lc-markdown-link-decoration, underline);hyphens:none;text-decoration-thickness:1px;text-underline-offset:.18em}.lc-markdown a:hover{color:var(--lc-markdown-link-hover-color, var(--color-link-hover, #144f5b));text-decoration-line:underline;text-decoration-thickness:2px}.lc-markdown a:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:2px}.lc-markdown strong{font-weight:var(--font-weight-semibold, 600)}.lc-markdown code{background-color:var(--color-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-radius-sm, .25rem);font-family:var(--font-family-mono, monospace);font-size:.875em;hyphens:none}.lc-markdown pre{margin:1em 0;overflow-x:auto;hyphens:none;overflow-wrap:normal}.lc-markdown pre code{background:none;padding:0;border-radius:0}.lc-markdown blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--color-primary-300, #93c5fd);background-color:var(--color-surface-2);color:var(--color-text-secondary, #4b5563)}.lc-markdown blockquote p{margin:0}.lc-markdown ul,.lc-markdown ol{margin:0 0 1em;padding-left:1.5em}.lc-markdown ul{list-style:disc}.lc-markdown ol{list-style:decimal}.lc-markdown li{margin:.25em 0}.lc-markdown hr{border:none;border-top:1px solid var(--color-divider, #e5e7eb);margin:1.5em 0}.lc-markdown img{max-width:100%;height:auto;border-radius:var(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch;hyphens:manual;overflow-wrap:break-word}.lc-markdown__table{width:100%;border-collapse:collapse;font-size:var(--font-size-sm, .875rem)}.lc-markdown__table th,.lc-markdown__table td{padding:.5rem .75rem;border:1px solid var(--color-divider, #e5e7eb);text-align:left}.lc-markdown__table th{background-color:var(--color-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown .lc-markdown__anchor{color:var(--color-text-tertiary);text-decoration:none;margin-right:.25em;opacity:0;transition:opacity .15s ease}h1:hover .lc-markdown__anchor,h2:hover .lc-markdown__anchor,h3:hover .lc-markdown__anchor,h4:hover .lc-markdown__anchor,h5:hover .lc-markdown__anchor,h6:hover .lc-markdown__anchor{opacity:1}.lc-markdown lc-code-block{display:block;margin:1em 0;hyphens:none;overflow-wrap:normal}.lc-markdown lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-on-error-subtle);font-size:var(--font-size-sm, .875rem)}.lc-markdown--compact{font-size:var(--font-size-sm, .875rem);line-height:var(--line-height-normal, 1.5)}.lc-markdown--compact h1{font-size:1.5rem}.lc-markdown--compact h2{font-size:1.25rem}.lc-markdown--compact h3{font-size:1.125rem}.lc-markdown--compact h4{font-size:1rem}.lc-markdown--compact h5,.lc-markdown--compact h6{font-size:.875rem}.lc-markdown--compact h1,.lc-markdown--compact h2,.lc-markdown--compact h3,.lc-markdown--compact h4,.lc-markdown--compact h5,.lc-markdown--compact h6{margin:1em 0 .25em}.lc-markdown--compact p{margin:0 0 .5em}.lc-markdown--compact blockquote{margin:.5em 0;padding:.25em .75em}.lc-markdown--compact ul,.lc-markdown--compact ol{margin:0 0 .5em}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat hr{margin:8px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"],
|
|
556
556
|
dependencies: [{
|
|
557
557
|
kind: "component",
|
|
558
558
|
type: CodeBlockComponent,
|
|
@@ -590,7 +590,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
590
590
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
591
591
|
encapsulation: ViewEncapsulation.None,
|
|
592
592
|
template: "<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- delegated handler for clicks on rendered links, which are focusable anchors -->\n<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">{{ mermaidLoadingLabel() }}</div>\n }\n }\n }\n</div>\n",
|
|
593
|
-
styles: [".lc-markdown{color:var(--color-text-primary);font-family:var(--font-family-sans);font-size:var(--font-size-base, 1rem);line-height:var(--line-height-relaxed, 1.75);hyphens:var(--lc-markdown-hyphens, auto);overflow-wrap:var(--lc-markdown-overflow-wrap, anywhere)}.lc-markdown h1,.lc-markdown h2,.lc-markdown h3,.lc-markdown h4,.lc-markdown h5,.lc-markdown h6{color:var(--color-text-primary);font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-tight, 1.25);margin:1.5em 0 .5em}.lc-markdown h1:first-child,.lc-markdown h2:first-child,.lc-markdown h3:first-child,.lc-markdown h4:first-child,.lc-markdown h5:first-child,.lc-markdown h6:first-child{margin-top:0}.lc-markdown h1{font-size:var(--lc-markdown-h1, 2rem)}.lc-markdown h2{font-size:var(--lc-markdown-h2, 1.5rem)}.lc-markdown h3{font-size:var(--lc-markdown-h3, 1.25rem)}.lc-markdown h4{font-size:var(--lc-markdown-h4, 1.125rem)}.lc-markdown h5{font-size:var(--lc-markdown-h5, 1rem)}.lc-markdown h6{font-size:var(--lc-markdown-h6, .875rem)}.lc-markdown p{margin:0 0 1em}.lc-markdown a{color:var(--lc-markdown-link-color, var(--color-link, #1a6a79));text-decoration:var(--lc-markdown-link-decoration, underline);hyphens:none;text-decoration-thickness:1px;text-underline-offset:.18em}.lc-markdown a:hover{color:var(--lc-markdown-link-hover-color, var(--color-link-hover, #144f5b));text-decoration-line:underline;text-decoration-thickness:2px}.lc-markdown a:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:2px}.lc-markdown strong{font-weight:var(--font-weight-semibold, 600)}.lc-markdown code{background-color:var(--color-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-radius-sm, .25rem);font-family:var(--font-family-mono, monospace);font-size:.875em;hyphens:none}.lc-markdown pre{margin:1em 0;overflow-x:auto;hyphens:none;overflow-wrap:normal}.lc-markdown pre code{background:none;padding:0;border-radius:0}.lc-markdown blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--color-primary-300, #93c5fd);background-color:var(--color-surface-2);color:var(--color-text-secondary, #4b5563)}.lc-markdown blockquote p{margin:0}.lc-markdown ul,.lc-markdown ol{margin:0 0 1em;padding-left:1.5em}.lc-markdown ul{list-style:disc}.lc-markdown ol{list-style:decimal}.lc-markdown li{margin:.25em 0}.lc-markdown hr{border:none;border-top:1px solid var(--color-divider, #e5e7eb);margin:1.5em 0}.lc-markdown img{max-width:100%;height:auto;border-radius:var(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch;hyphens:manual;overflow-wrap:break-word}.lc-markdown__table{width:100%;border-collapse:collapse;font-size:var(--font-size-sm, .875rem)}.lc-markdown__table th,.lc-markdown__table td{padding:.5rem .75rem;border:1px solid var(--color-divider, #e5e7eb);text-align:left}.lc-markdown__table th{background-color:var(--color-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown .lc-markdown__anchor{color:var(--color-text-tertiary);text-decoration:none;margin-right:.25em;opacity:0;transition:opacity .15s ease}h1:hover .lc-markdown__anchor,h2:hover .lc-markdown__anchor,h3:hover .lc-markdown__anchor,h4:hover .lc-markdown__anchor,h5:hover .lc-markdown__anchor,h6:hover .lc-markdown__anchor{opacity:1}.lc-markdown lc-code-block{display:block;margin:1em 0;hyphens:none;overflow-wrap:normal}.lc-markdown lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-error);font-size:var(--font-size-sm, .875rem)}.lc-markdown--compact{font-size:var(--font-size-sm, .875rem);line-height:var(--line-height-normal, 1.5)}.lc-markdown--compact h1{font-size:1.5rem}.lc-markdown--compact h2{font-size:1.25rem}.lc-markdown--compact h3{font-size:1.125rem}.lc-markdown--compact h4{font-size:1rem}.lc-markdown--compact h5,.lc-markdown--compact h6{font-size:.875rem}.lc-markdown--compact h1,.lc-markdown--compact h2,.lc-markdown--compact h3,.lc-markdown--compact h4,.lc-markdown--compact h5,.lc-markdown--compact h6{margin:1em 0 .25em}.lc-markdown--compact p{margin:0 0 .5em}.lc-markdown--compact blockquote{margin:.5em 0;padding:.25em .75em}.lc-markdown--compact ul,.lc-markdown--compact ol{margin:0 0 .5em}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat hr{margin:8px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"]
|
|
593
|
+
styles: [".lc-markdown{color:var(--color-text-primary);font-family:var(--font-family-sans);font-size:var(--font-size-base, 1rem);line-height:var(--line-height-relaxed, 1.75);hyphens:var(--lc-markdown-hyphens, auto);overflow-wrap:var(--lc-markdown-overflow-wrap, anywhere)}.lc-markdown h1,.lc-markdown h2,.lc-markdown h3,.lc-markdown h4,.lc-markdown h5,.lc-markdown h6{color:var(--color-text-primary);font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-tight, 1.25);margin:1.5em 0 .5em}.lc-markdown h1:first-child,.lc-markdown h2:first-child,.lc-markdown h3:first-child,.lc-markdown h4:first-child,.lc-markdown h5:first-child,.lc-markdown h6:first-child{margin-top:0}.lc-markdown h1{font-size:var(--lc-markdown-h1, 2rem)}.lc-markdown h2{font-size:var(--lc-markdown-h2, 1.5rem)}.lc-markdown h3{font-size:var(--lc-markdown-h3, 1.25rem)}.lc-markdown h4{font-size:var(--lc-markdown-h4, 1.125rem)}.lc-markdown h5{font-size:var(--lc-markdown-h5, 1rem)}.lc-markdown h6{font-size:var(--lc-markdown-h6, .875rem)}.lc-markdown p{margin:0 0 1em}.lc-markdown a{color:var(--lc-markdown-link-color, var(--color-link, #1a6a79));text-decoration:var(--lc-markdown-link-decoration, underline);hyphens:none;text-decoration-thickness:1px;text-underline-offset:.18em}.lc-markdown a:hover{color:var(--lc-markdown-link-hover-color, var(--color-link-hover, #144f5b));text-decoration-line:underline;text-decoration-thickness:2px}.lc-markdown a:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:2px}.lc-markdown strong{font-weight:var(--font-weight-semibold, 600)}.lc-markdown code{background-color:var(--color-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-radius-sm, .25rem);font-family:var(--font-family-mono, monospace);font-size:.875em;hyphens:none}.lc-markdown pre{margin:1em 0;overflow-x:auto;hyphens:none;overflow-wrap:normal}.lc-markdown pre code{background:none;padding:0;border-radius:0}.lc-markdown blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--color-primary-300, #93c5fd);background-color:var(--color-surface-2);color:var(--color-text-secondary, #4b5563)}.lc-markdown blockquote p{margin:0}.lc-markdown ul,.lc-markdown ol{margin:0 0 1em;padding-left:1.5em}.lc-markdown ul{list-style:disc}.lc-markdown ol{list-style:decimal}.lc-markdown li{margin:.25em 0}.lc-markdown hr{border:none;border-top:1px solid var(--color-divider, #e5e7eb);margin:1.5em 0}.lc-markdown img{max-width:100%;height:auto;border-radius:var(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch;hyphens:manual;overflow-wrap:break-word}.lc-markdown__table{width:100%;border-collapse:collapse;font-size:var(--font-size-sm, .875rem)}.lc-markdown__table th,.lc-markdown__table td{padding:.5rem .75rem;border:1px solid var(--color-divider, #e5e7eb);text-align:left}.lc-markdown__table th{background-color:var(--color-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown .lc-markdown__anchor{color:var(--color-text-tertiary);text-decoration:none;margin-right:.25em;opacity:0;transition:opacity .15s ease}h1:hover .lc-markdown__anchor,h2:hover .lc-markdown__anchor,h3:hover .lc-markdown__anchor,h4:hover .lc-markdown__anchor,h5:hover .lc-markdown__anchor,h6:hover .lc-markdown__anchor{opacity:1}.lc-markdown lc-code-block{display:block;margin:1em 0;hyphens:none;overflow-wrap:normal}.lc-markdown lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-on-error-subtle);font-size:var(--font-size-sm, .875rem)}.lc-markdown--compact{font-size:var(--font-size-sm, .875rem);line-height:var(--line-height-normal, 1.5)}.lc-markdown--compact h1{font-size:1.5rem}.lc-markdown--compact h2{font-size:1.25rem}.lc-markdown--compact h3{font-size:1.125rem}.lc-markdown--compact h4{font-size:1rem}.lc-markdown--compact h5,.lc-markdown--compact h6{font-size:.875rem}.lc-markdown--compact h1,.lc-markdown--compact h2,.lc-markdown--compact h3,.lc-markdown--compact h4,.lc-markdown--compact h5,.lc-markdown--compact h6{margin:1em 0 .25em}.lc-markdown--compact p{margin:0 0 .5em}.lc-markdown--compact blockquote{margin:.5em 0;padding:.25em .75em}.lc-markdown--compact ul,.lc-markdown--compact ol{margin:0 0 .5em}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat hr{margin:8px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"]
|
|
594
594
|
}]
|
|
595
595
|
}],
|
|
596
596
|
ctorParameters: () => [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-markdown.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/markdown/markdown.component.ts","../../../../libs/angular-ui-kit/src/lib/markdown/markdown.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n computed,\n signal,\n effect,\n untracked,\n inject,\n ElementRef,\n OnDestroy,\n SecurityContext,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { HttpClient } from '@angular/common/http';\nimport { Subscription } from 'rxjs';\nimport { CodeBlockComponent, CodeBlockLanguage } from '@lifecorner/angular-ui-kit/code-block';\nimport { isSafeImageUrl, isSafeLinkUrl } from '@lifecorner/angular-ui-kit/internal/url';\n\nexport interface MarkdownHeading {\n level: number;\n text: string;\n id: string;\n}\n\nexport interface MarkdownLinkClick {\n href: string;\n event: MouseEvent;\n}\n\nexport interface MarkdownRendered {\n headings: MarkdownHeading[];\n}\n\nexport interface MarkdownChangesHighlighted {\n /** Number of changed/added blocks highlighted in the current render. */\n changedBlocks: number;\n}\n\nexport interface RenderPart {\n type: 'html' | 'code' | 'mermaid';\n index: number;\n safeHtml?: SafeHtml;\n code?: string;\n lang?: CodeBlockLanguage;\n}\n\n/**\n * Stand-in for a fenced code block while the rest of the markdown is processed.\n * Bracketed in NUL characters, which the source is stripped of first — so no\n * text a user writes can ever spell a placeholder and be mistaken for a block.\n * (The earlier `<!--CODE_BLOCK_N-->` form was: an escaped literal comment in\n * the text matched it and dereferenced a block that wasn't there.)\n */\nconst BLOCK_MARK = '\\u0000';\nconst blockPlaceholder = (idx: number): string => `${BLOCK_MARK}LCBLOCK${idx}${BLOCK_MARK}`;\n// eslint-disable-next-line no-control-regex -- the NUL bracket is the point: unforgeable\nconst BLOCK_PLACEHOLDER = /\\u0000LCBLOCK(\\d+)\\u0000/g;\n\n/** Instances numbered so their mermaid render ids never collide on one page. */\nlet markdownInstances = 0;\n\n/**\n * Markdown renderer component.\n *\n * Renders GitHub-Flavored Markdown (GFM) to sanitized HTML with\n * optional syntax highlighting via `<lc-code-block>`.\n *\n * Supports headings (+ anchors), paragraphs, ordered/unordered lists, task lists\n * (`- [ ]` / `- [x]` → accessible checkbox), blockquotes, horizontal rules,\n * images, GFM tables (column alignment + horizontal scroll), bold/italic/\n * strikethrough, inline code, fenced code (incl. `mermaid`), explicit links and\n * autolinks (bare URLs / `www.` / emails, never inside code).\n *\n * Optionally highlights *changed* blocks in place: pass the pre-edit markdown as\n * `previousContent` and set `highlightChanges` — added/edited blocks (diffed at\n * block / list-item level) gain a left accent bar + subtle tint, can auto-fade\n * (`changeHighlightFadeMs`) and scroll into view (`scrollToFirstChange`).\n *\n * @example\n * ```html\n * <lc-markdown [content]=\"'# Hello World'\" />\n * <lc-markdown [src]=\"'/docs/readme.md'\" />\n * <lc-markdown\n * [content]=\"current\" [previousContent]=\"prev\"\n * [highlightChanges]=\"true\" [changeHighlightFadeMs]=\"3000\" />\n * ```\n */\n@Component({\n selector: 'lc-markdown',\n standalone: true,\n imports: [CodeBlockComponent],\n templateUrl: './markdown.component.html',\n styleUrls: ['./markdown.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class MarkdownComponent implements OnDestroy {\n private readonly sanitizer = inject(DomSanitizer);\n private readonly instanceId = ++markdownInstances;\n private readonly http = inject(HttpClient);\n private readonly host = inject(ElementRef<HTMLElement>);\n private httpSub?: Subscription;\n private fadeTimer?: ReturnType<typeof setTimeout>;\n private scrollTimer?: ReturnType<typeof setTimeout>;\n private mermaidApiPromise?: Promise<any>;\n private mermaidInitialized = false;\n private mermaidRenderRun = 0;\n\n /** URL or path to load markdown from */\n readonly src = input<string>();\n\n /** Raw markdown string */\n readonly content = input<string>();\n\n /** Display variant */\n readonly variant = input<'default' | 'compact' | 'chat'>('default');\n\n /** Target for links */\n readonly linkTarget = input<'_self' | '_blank'>('_self');\n\n /** Whether to sanitize HTML output */\n readonly sanitize = input<boolean>(true);\n\n /** Whether to use code-block for fenced code */\n readonly syntaxHighlight = input<boolean>(true);\n\n /** Whether to show anchor links on headings */\n readonly showHeadingAnchors = input<boolean>(false);\n\n /** Base URL for resolving relative links/images */\n readonly baseUrl = input<string>();\n\n // -- Change highlighting --\n /** Enable change highlighting (requires `previousContent` to compute a diff). */\n readonly highlightChanges = input<boolean>(false);\n\n /**\n * The prior Markdown. When it differs from `content`, the changed/added blocks\n * in `content` are highlighted. The caller passes the pre-edit markdown.\n */\n readonly previousContent = input<string>();\n\n /** Auto-fade the highlight after N ms. 0 / undefined ⇒ persist until content changes. */\n readonly changeHighlightFadeMs = input<number>();\n\n /** Scroll the first changed block into view when highlights appear. */\n readonly scrollToFirstChange = input<boolean>(false);\n\n /** Screen-reader marker put in front of each highlighted block (`highlightChanges`). */\n readonly changedMarkerLabel = input('(geändert)');\n\n /** Announced after the count when one block changed: `1 Abschnitt geändert`. */\n readonly changedSummaryOneLabel = input('Abschnitt geändert');\n\n /** Announced after the count when several blocks changed: `3 Abschnitte geändert`. */\n readonly changedSummaryManyLabel = input('Abschnitte geändert');\n\n /** Shown while a Mermaid diagram renders. */\n readonly mermaidLoadingLabel = input('Rendering Mermaid diagram...');\n\n /** Shown above the source when a Mermaid diagram fails. */\n readonly mermaidErrorLabel = input('Mermaid diagram could not be rendered.');\n\n /** Shown above the source when the optional `mermaid` package cannot be loaded. */\n readonly mermaidUnavailableLabel = input('Mermaid runtime is not available.');\n\n /** Emitted when a link is clicked */\n readonly linkClick = output<MarkdownLinkClick>();\n\n /** Emitted after rendering with heading TOC */\n readonly rendered = output<MarkdownRendered>();\n\n /** Emitted after a render that produced change highlights. */\n readonly changesHighlighted = output<MarkdownChangesHighlighted>();\n\n /** Internal resolved markdown source */\n protected resolvedMarkdown = signal<string>('');\n\n /** Parsed result (computed once from resolvedMarkdown) */\n private parsed = computed(() => {\n const md = this.resolvedMarkdown();\n if (!md) {\n return {\n html: '',\n blocks: [] as { kind: 'code' | 'mermaid'; lang: string; code: string }[],\n headings: [] as MarkdownHeading[],\n };\n }\n return this.parseMarkdown(md);\n });\n\n /**\n * Rendered HTML after applying change highlights. When highlighting is off (or\n * there is no differing `previousContent`) this returns the parsed HTML\n * unchanged, so the non-highlight path is byte-for-byte identical to before.\n */\n private readonly highlightResult = computed<{ html: string; count: number }>(() => {\n const { html } = this.parsed();\n const prev = this.previousContent();\n if (\n !html ||\n !this.highlightChanges() ||\n prev == null ||\n prev === this.resolvedMarkdown()\n ) {\n return { html, count: 0 };\n }\n return this.applyChangeHighlights(html, prev);\n });\n\n /** Number of changed/added blocks highlighted in the current render. */\n protected readonly changedCount = computed(() => this.highlightResult().count);\n\n /** Visually-hidden polite summary announced when highlights appear. */\n protected readonly changeSummary = computed(() => {\n const n = this.changedCount();\n if (n <= 0) return '';\n return `${n} ${n === 1 ? this.changedSummaryOneLabel() : this.changedSummaryManyLabel()}`;\n });\n\n /** Whether the current highlights have faded out (after `changeHighlightFadeMs`). */\n protected readonly highlightsFaded = signal(false);\n\n /** Computed render parts (HTML chunks + code blocks interleaved) */\n protected renderParts = computed<RenderPart[]>(() => {\n const html = this.highlightResult().html;\n const { blocks } = this.parsed();\n if (!html) return [];\n\n if (this.syntaxHighlight() && blocks.length > 0) {\n return this.splitIntoParts(html, blocks);\n }\n\n // No code blocks — single HTML part\n return [{\n type: 'html',\n index: 0,\n safeHtml: this.sanitizer.bypassSecurityTrustHtml(this.sanitizeHtml(html, { next: 0 })),\n }];\n });\n\n /**\n * The HTML as it may go into the DOM. With `sanitize` on (the default) that is\n * Angular's HTML sanitizer's output — which drops every `id`, since `id` isn't\n * on its attribute allow-list. The heading ids are put back afterwards: they\n * are what anchors and the `rendered` TOC point at, and each is a `[\\w-]`\n * slug this parser produced itself, so re-adding them reopens nothing. The\n * cursor walks the parsed headings in document order across the parts.\n */\n private sanitizeHtml(html: string, cursor: { next: number }): string {\n const headings = this.headings();\n if (!this.sanitize()) {\n cursor.next += (html.match(/<h[1-6]\\b/g) ?? []).length;\n return html;\n }\n const sanitized = this.sanitizer.sanitize(SecurityContext.HTML, html) || '';\n return sanitized.replace(/<h([1-6])(\\s[^>]*)?>/g, (tag, level: string, attrs = '') => {\n const heading = headings[cursor.next++];\n if (!heading || /\\sid=/.test(attrs)) return tag;\n return `<h${level} id=\"${heading.id}\"${attrs}>`;\n });\n }\n\n protected containerClasses = computed(() => {\n let classes = `lc-markdown lc-markdown--${this.variant()}`;\n if (this.highlightsFaded()) classes += ' lc-markdown--faded';\n return classes;\n });\n\n protected mermaidSvgs = signal<Record<number, SafeHtml>>({});\n protected mermaidErrors = signal<Record<number, string>>({});\n\n private headings = computed(() => this.parsed().headings);\n\n constructor() {\n // Load content from src when src changes\n effect(() => {\n const src = this.src();\n if (src) {\n this.loadFromUrl(src);\n }\n });\n\n // Use raw content when provided\n effect(() => {\n const content = this.content();\n if (content !== undefined) {\n this.resolvedMarkdown.set(content);\n }\n });\n\n // Emit rendered event whenever a render produced a (possibly empty) TOC\n effect(() => {\n const h = this.headings();\n untracked(() => this.rendered.emit({ headings: h }));\n });\n\n // Render Mermaid code fences as inline SVG diagrams.\n effect(() => {\n void this.renderMermaidParts(this.renderParts());\n });\n\n // React to a render that produced change highlights: emit, schedule fade,\n // and optionally scroll the first changed block into view.\n effect(() => {\n const count = this.highlightResult().count;\n untracked(() => this.onHighlightResult(count));\n });\n }\n\n ngOnDestroy(): void {\n this.httpSub?.unsubscribe();\n clearTimeout(this.fadeTimer);\n clearTimeout(this.scrollTimer);\n }\n\n protected onLinkClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n const anchor = target.closest('a');\n if (anchor) {\n this.linkClick.emit({ href: anchor.href, event });\n }\n }\n\n // -- Change highlighting -----------------------------------------------------\n\n /** Side-effects for a render: emit, (re)arm the fade timer, optional scroll. */\n private onHighlightResult(count: number): void {\n clearTimeout(this.fadeTimer);\n clearTimeout(this.scrollTimer);\n // A fresh render starts un-faded (re-entrant: never reuse stale fade state).\n this.highlightsFaded.set(false);\n if (count <= 0) return;\n\n this.changesHighlighted.emit({ changedBlocks: count });\n\n const fadeMs = this.changeHighlightFadeMs();\n if (fadeMs && fadeMs > 0) {\n this.fadeTimer = setTimeout(() => this.highlightsFaded.set(true), fadeMs);\n }\n if (this.scrollToFirstChange()) {\n // Defer until the highlighted HTML has been written to the DOM.\n this.scrollTimer = setTimeout(() => this.scrollToFirstChanged(), 0);\n }\n }\n\n private scrollToFirstChanged(): void {\n if (typeof document === 'undefined') return;\n const el = this.host.nativeElement.querySelector('.lc-markdown__block--changed');\n el?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }\n\n /**\n * Wraps the changed/added top-level blocks of `html` with\n * `.lc-markdown__block--changed`. A block is \"changed\" when its normalized\n * text is not present among the blocks of `prevMarkdown`. Lists are diffed\n * per `<li>` and tables per `<tr>` so a single edited item highlights alone.\n */\n private applyChangeHighlights(\n html: string,\n prevMarkdown: string\n ): { html: string; count: number } {\n if (typeof document === 'undefined') return { html, count: 0 };\n\n const prevKeys = this.collectBlockKeys(this.parseMarkdown(prevMarkdown).html);\n const root = this.htmlToElement(html);\n if (!root) return { html, count: 0 };\n\n let count = 0;\n const markIfChanged = (el: Element): void => {\n const key = this.blockTextKey(el.textContent ?? '');\n if (!key || prevKeys.has(key)) return;\n el.classList.add('lc-markdown__block--changed');\n const sr = document.createElement('span');\n sr.className = 'lc-markdown__sr-only';\n sr.textContent = `${this.changedMarkerLabel()} `;\n el.insertBefore(sr, el.firstChild);\n count++;\n };\n\n this.eachLogicalBlock(root, markIfChanged);\n return { html: root.innerHTML, count };\n }\n\n /** Normalized text set of the logical blocks within an HTML fragment. */\n private collectBlockKeys(html: string): Set<string> {\n const keys = new Set<string>();\n const root = this.htmlToElement(html);\n if (!root) return keys;\n this.eachLogicalBlock(root, (el) => {\n const key = this.blockTextKey(el.textContent ?? '');\n if (key) keys.add(key);\n });\n return keys;\n }\n\n /**\n * Visits each diffable block under `root`: top-level elements, but descending\n * into `<ul>`/`<ol>` (per `<li>`) and `<table>` (per `<tr>`).\n */\n private eachLogicalBlock(root: Element, visit: (el: Element) => void): void {\n for (const node of Array.from(root.childNodes)) {\n if (node.nodeType !== 1) continue; // elements only (skips code placeholders)\n const el = node as Element;\n const tag = el.tagName;\n if (tag === 'UL' || tag === 'OL') {\n for (const li of Array.from(el.children)) {\n if (li.tagName === 'LI') visit(li);\n }\n } else if (tag === 'TABLE' || el.querySelector('table')) {\n // Tables (incl. the scroll wrapper div) diff per row.\n for (const tr of Array.from(el.querySelectorAll('tr'))) {\n visit(tr);\n }\n } else {\n visit(el);\n }\n }\n }\n\n private blockTextKey(text: string): string {\n return text.replace(/\\s+/g, ' ').trim().toLowerCase();\n }\n\n private htmlToElement(html: string): HTMLElement | null {\n if (typeof document === 'undefined') return null;\n const div = document.createElement('div');\n div.innerHTML = html;\n return div;\n }\n\n private loadFromUrl(url: string): void {\n this.httpSub?.unsubscribe();\n this.httpSub = this.http.get(url, { responseType: 'text' }).subscribe({\n next: (text) => this.resolvedMarkdown.set(text),\n error: () => this.resolvedMarkdown.set(`*Failed to load ${url}*`),\n });\n }\n\n private splitIntoParts(\n html: string,\n blocks: { kind: 'code' | 'mermaid'; lang: string; code: string }[]\n ): RenderPart[] {\n const parts: RenderPart[] = [];\n const regex = new RegExp(BLOCK_PLACEHOLDER.source, 'g');\n const cursor = { next: 0 };\n let lastIndex = 0;\n let partIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(html)) !== null) {\n const before = html.slice(lastIndex, match.index);\n if (before.trim()) {\n parts.push({\n type: 'html',\n index: partIndex++,\n safeHtml: this.sanitizer.bypassSecurityTrustHtml(this.sanitizeHtml(before, cursor)),\n });\n }\n const block = blocks[parseInt(match[1], 10)];\n lastIndex = match.index + match[0].length;\n // Unforgeable placeholder, so this can only be a bug — but never a crash.\n if (!block) continue;\n if (block.kind === 'mermaid') {\n parts.push({\n type: 'mermaid',\n index: partIndex++,\n code: block.code,\n });\n } else {\n parts.push({\n type: 'code',\n index: partIndex++,\n code: block.code,\n lang: block.lang as CodeBlockLanguage,\n });\n }\n }\n\n const remaining = html.slice(lastIndex);\n if (remaining.trim()) {\n parts.push({\n type: 'html',\n index: partIndex++,\n safeHtml: this.sanitizer.bypassSecurityTrustHtml(this.sanitizeHtml(remaining, cursor)),\n });\n }\n\n return parts;\n }\n\n private parseMarkdown(md: string): {\n html: string;\n blocks: { kind: 'code' | 'mermaid'; lang: string; code: string }[];\n headings: MarkdownHeading[];\n } {\n const blocks: { kind: 'code' | 'mermaid'; lang: string; code: string }[] = [];\n const headings: MarkdownHeading[] = [];\n const baseUrl = this.baseUrl();\n const linkTarget = this.linkTarget();\n const showAnchors = this.showHeadingAnchors();\n\n // 1. Extract fenced code blocks → placeholders (see BLOCK_MARK: the source\n // is stripped of the marker character first, so it can't forge one)\n // eslint-disable-next-line no-control-regex -- strip the placeholder marker (see BLOCK_MARK)\n let processed = md.replace(/\\u0000/g, '').replace(\n /```(\\w*)\\n([\\s\\S]*?)```/g,\n (_match, lang: string, code: string) => {\n const idx = blocks.length;\n const normalizedLang = (lang || 'text').toLowerCase();\n blocks.push({\n kind: normalizedLang === 'mermaid' ? 'mermaid' : 'code',\n lang: normalizedLang,\n code: code.trimEnd(),\n });\n return blockPlaceholder(idx);\n }\n );\n\n // 2. Escape HTML entities — quotes included, so nothing the parser later\n // interpolates into an attribute (link/image URLs, alt text) can close it.\n processed = this.escapeHtml(processed);\n\n // 3. Headings (# to ######)\n processed = processed.replace(\n /^(#{1,6})\\s+(.+)$/gm,\n (_match, hashes: string, text: string) => {\n const level = hashes.length;\n const id = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-');\n headings.push({ level, text, id });\n const anchor = showAnchors\n ? `<a href=\"#${id}\" class=\"lc-markdown__anchor\" aria-hidden=\"true\">#</a>`\n : '';\n return `<h${level} id=\"${id}\">${anchor}${text}</h${level}>`;\n }\n );\n\n // 4. Horizontal rules\n processed = processed.replace(/^---+$/gm, '<hr>');\n\n // 5. Blockquotes\n processed = processed.replace(\n /^(?:>)\\s?(.*)$/gm,\n '<blockquote>$1</blockquote>'\n );\n // Merge consecutive blockquotes\n processed = processed.replace(\n /<\\/blockquote>\\n<blockquote>/g,\n '\\n'\n );\n\n // 6. Tables (GFM)\n processed = this.parseTables(processed);\n\n // 7. Task lists. Angular's HTML sanitizer strips <input>, so the checkbox is\n // rendered as a styled <span role=\"checkbox\"> (which survives sanitization and\n // carries accessible checked/disabled state) rather than a form control.\n processed = processed.replace(\n /^[-*]\\s+\\[[xX]\\]\\s+(.*)$/gm,\n '<li class=\"lc-markdown__task-item\">' +\n '<span class=\"lc-markdown__checkbox lc-markdown__checkbox--checked\" role=\"checkbox\" aria-checked=\"true\" aria-disabled=\"true\"></span>' +\n '<span class=\"lc-markdown__task-label\">$1</span></li>'\n );\n processed = processed.replace(\n /^[-*]\\s+\\[ \\]\\s+(.*)$/gm,\n '<li class=\"lc-markdown__task-item\">' +\n '<span class=\"lc-markdown__checkbox\" role=\"checkbox\" aria-checked=\"false\" aria-disabled=\"true\"></span>' +\n '<span class=\"lc-markdown__task-label\">$1</span></li>'\n );\n\n // 8. Unordered lists\n processed = processed.replace(\n /^[-*]\\s+(.*)$/gm,\n '<li>$1</li>'\n );\n // Wrap consecutive <li> in <ul>\n processed = processed.replace(\n /((?:<li[^>]*>.*<\\/li>\\n?)+)/g,\n '<ul>$1</ul>'\n );\n\n // 9. Ordered lists — marked while wrapping, so a run of them gets its <ol>\n // and isn't confused with the <li>s already inside <ul>s above.\n processed = processed.replace(\n /^\\d+\\.\\s+(.*)$/gm,\n '<li data-ol>$1</li>'\n );\n processed = processed.replace(\n /((?:<li data-ol>.*<\\/li>\\n?)+)/g,\n (run) => `<ol>${run.replace(/ data-ol/g, '')}</ol>`\n );\n\n // 10. Inline code\n processed = processed.replace(\n /`([^`]+)`/g,\n '<code>$1</code>'\n );\n\n // 11. Bold + italic combos\n processed = processed.replace(\n /\\*\\*\\*(.+?)\\*\\*\\*/g,\n '<strong><em>$1</em></strong>'\n );\n processed = processed.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');\n processed = processed.replace(/\\*(.+?)\\*/g, '<em>$1</em>');\n\n // 12. Strikethrough (GFM)\n processed = processed.replace(/~~(.+?)~~/g, '<del>$1</del>');\n\n // 13. Images. Only allow-listed URL schemes become a `src` (see\n // safe-url.ts); anything else — `javascript:`, unknown schemes — is\n // rendered as its alt text. This holds regardless of `sanitize`.\n processed = processed.replace(\n /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g,\n (_match, alt: string, src: string) => {\n if (!isSafeImageUrl(src)) return alt;\n const resolvedSrc = baseUrl && !src.startsWith('http') ? `${baseUrl}/${src}` : src;\n return `<img src=\"${resolvedSrc}\" alt=\"${alt}\" class=\"lc-markdown__img\">`;\n }\n );\n\n // 14. Links — same rule: an unsafe URL leaves plain text, not a link.\n processed = processed.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n (_match, text: string, href: string) => {\n if (!isSafeLinkUrl(href)) return text;\n const resolvedHref = baseUrl && !href.startsWith('http') && !href.startsWith('#')\n ? `${baseUrl}/${href}`\n : href;\n return `<a href=\"${resolvedHref}\" target=\"${linkTarget}\" rel=\"noopener\">${text}</a>`;\n }\n );\n\n // 14b. Autolinks — linkify bare URLs / www / emails in plain text. Runs after\n // explicit links and inline code so it can skip anything already inside an\n // <a> or <code> (and never touches tag attributes).\n processed = this.autolink(processed, linkTarget);\n\n // 15. Paragraphs — wrap standalone lines\n processed = processed\n .split('\\n\\n')\n .map((block) => {\n const trimmed = block.trim();\n if (!trimmed) return '';\n if (\n trimmed.startsWith('<h') ||\n trimmed.startsWith('<ul') ||\n trimmed.startsWith('<ol') ||\n trimmed.startsWith('<blockquote') ||\n trimmed.startsWith('<hr') ||\n trimmed.startsWith('<table') ||\n trimmed.startsWith('<div') ||\n trimmed.startsWith(BLOCK_MARK)\n ) {\n return trimmed;\n }\n return `<p>${trimmed.replace(/\\n/g, '<br>')}</p>`;\n })\n .join('\\n');\n\n // 16. Restore code block placeholders\n if (this.syntaxHighlight()) {\n // Leave placeholders — the template will render <lc-code-block> for each\n // No replacement needed here\n } else {\n processed = processed.replace(\n new RegExp(BLOCK_PLACEHOLDER.source, 'g'),\n (_match, idx: string) => {\n const block = blocks[parseInt(idx, 10)];\n if (!block) return '';\n return `<pre><code class=\"language-${block.lang}\">${this.escapeHtml(block.code)}</code></pre>`;\n }\n );\n }\n\n return { html: processed, blocks, headings };\n }\n\n private parseTables(text: string): string {\n return text.replace(\n /^(\\|.+\\|)\\n(\\|[-| :]+\\|)\\n((?:\\|.+\\|\\n?)*)/gm,\n (_match, header: string, separator: string, body: string) => {\n // Per-column alignment from the delimiter row: :--- left, :--: center,\n // ---: right. Surfaced as classes (the sanitizer strips inline `style`).\n const aligns = this.splitTableRow(separator).map((cell) => {\n const left = cell.startsWith(':');\n const right = cell.endsWith(':');\n if (left && right) return 'center';\n if (right) return 'right';\n if (left) return 'left';\n return '';\n });\n const alignClass = (col: number): string =>\n aligns[col] ? ` class=\"lc-markdown__cell--${aligns[col]}\"` : '';\n\n const headers = this.splitTableRow(header)\n .map((cell, i) => `<th scope=\"col\"${alignClass(i)}>${cell.trim()}</th>`)\n .join('');\n\n const rows = body\n .trim()\n .split('\\n')\n .map((row: string) => {\n const cells = this.splitTableRow(row)\n .map((cell, i) => `<td${alignClass(i)}>${cell.trim()}</td>`)\n .join('');\n return `<tr>${cells}</tr>`;\n })\n .join('');\n\n // Wrapped so wide tables scroll horizontally instead of breaking layout.\n return (\n `<div class=\"lc-markdown__table-wrap\">` +\n `<table class=\"lc-markdown__table\"><thead><tr>${headers}</tr></thead><tbody>${rows}</tbody></table>` +\n `</div>`\n );\n }\n );\n }\n\n /** Splits a pipe-table row into trimmed cells, dropping the leading/trailing edges. */\n private splitTableRow(row: string): string[] {\n return row\n .trim()\n .replace(/^\\|/, '')\n .replace(/\\|$/, '')\n .split('|')\n .map((cell) => cell.trim());\n }\n\n private escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n }\n\n /**\n * Linkifies bare URLs (`http(s)://…`, `www.…`) and email addresses in text,\n * leaving anything inside an existing `<a>` or `<code>` (and all tag\n * attributes) untouched. The HTML is tokenized into tags vs. text so only\n * top-level text runs are linkified.\n */\n private autolink(html: string, linkTarget: string): string {\n const url = /(?:https?:\\/\\/|www\\.)[^\\s<]*[^\\s<.,:;!?)\\]}'\"]/;\n const email = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/;\n const pattern = new RegExp(`${url.source}|${email.source}`, 'g');\n\n let depth = 0; // inside <a> or <code>\n return html\n .split(/(<[^>]+>)/g)\n .map((segment) => {\n if (segment.startsWith('<')) {\n if (/^<(a|code)[\\s>]/i.test(segment)) depth++;\n else if (/^<\\/(a|code)>/i.test(segment)) depth = Math.max(0, depth - 1);\n return segment;\n }\n if (depth > 0 || !segment) return segment;\n return segment.replace(pattern, (token) => {\n const isEmail = token.includes('@') && !/^(?:https?:\\/\\/|www\\.)/.test(token);\n const href = isEmail\n ? `mailto:${token}`\n : token.startsWith('www.')\n ? `https://${token}`\n : token;\n return `<a href=\"${href}\" target=\"${linkTarget}\" rel=\"noopener\">${token}</a>`;\n });\n })\n .join('');\n }\n\n protected mermaidSvgFor(index: number): SafeHtml | null {\n return this.mermaidSvgs()[index] ?? null;\n }\n\n protected mermaidErrorFor(index: number): string | null {\n return this.mermaidErrors()[index] ?? null;\n }\n\n private async renderMermaidParts(parts: RenderPart[]): Promise<void> {\n const mermaidParts = parts.filter((part) => part.type === 'mermaid' && !!part.code);\n this.mermaidSvgs.set({});\n this.mermaidErrors.set({});\n\n if (mermaidParts.length === 0) {\n return;\n }\n\n const runId = ++this.mermaidRenderRun;\n\n try {\n const mermaidApi = await this.getMermaidApi();\n if (runId !== this.mermaidRenderRun) return;\n\n const nextSvgs: Record<number, SafeHtml> = {};\n const nextErrors: Record<number, string> = {};\n\n for (const part of mermaidParts) {\n try {\n // Per instance, per run, per part: mermaid's render() removes any\n // element already carrying the id it is given, so an id shared with\n // another <lc-markdown> on the page would delete that one's diagram.\n const renderId = `lc-md-mermaid-${this.instanceId}-${runId}-${part.index}`;\n const { svg } = await mermaidApi.render(renderId, part.code!);\n if (runId !== this.mermaidRenderRun) return;\n // Mermaid emits self-contained SVG with internal <style> rules.\n // Angular HTML sanitization strips critical style declarations,\n // which leaves only labels visible (no edges/shapes). We trust\n // Mermaid output here and rely on Mermaid's strict security mode.\n nextSvgs[part.index] = this.sanitizer.bypassSecurityTrustHtml(svg);\n } catch {\n nextErrors[part.index] = this.mermaidErrorLabel();\n }\n }\n\n if (runId !== this.mermaidRenderRun) return;\n this.mermaidSvgs.set(nextSvgs);\n this.mermaidErrors.set(nextErrors);\n } catch {\n if (runId !== this.mermaidRenderRun) return;\n const fallbackErrors: Record<number, string> = {};\n for (const part of mermaidParts) {\n fallbackErrors[part.index] = this.mermaidUnavailableLabel();\n }\n this.mermaidErrors.set(fallbackErrors);\n }\n }\n\n private async getMermaidApi(): Promise<any> {\n if (!this.mermaidApiPromise) {\n this.mermaidApiPromise = import('mermaid').then((module) => {\n const api = module.default ?? module;\n if (!this.mermaidInitialized) {\n api.initialize({\n startOnLoad: false,\n securityLevel: 'strict',\n });\n this.mermaidInitialized = true;\n }\n return api;\n });\n }\n return this.mermaidApiPromise;\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- delegated handler for clicks on rendered links, which are focusable anchors -->\n<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">{{ mermaidLoadingLabel() }}</div>\n }\n }\n }\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-markdown.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/markdown/markdown.component.ts","../../../../libs/angular-ui-kit/src/lib/markdown/markdown.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n computed,\n signal,\n effect,\n untracked,\n inject,\n ElementRef,\n OnDestroy,\n SecurityContext,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { HttpClient } from '@angular/common/http';\nimport { Subscription } from 'rxjs';\nimport { CodeBlockComponent, CodeBlockLanguage } from '@lifecorner/angular-ui-kit/code-block';\nimport { isSafeImageUrl, isSafeLinkUrl } from '@lifecorner/angular-ui-kit/internal/url';\n\nexport interface MarkdownHeading {\n level: number;\n text: string;\n id: string;\n}\n\nexport interface MarkdownLinkClick {\n href: string;\n event: MouseEvent;\n}\n\nexport interface MarkdownRendered {\n headings: MarkdownHeading[];\n}\n\nexport interface MarkdownChangesHighlighted {\n /** Number of changed/added blocks highlighted in the current render. */\n changedBlocks: number;\n}\n\nexport interface RenderPart {\n type: 'html' | 'code' | 'mermaid';\n index: number;\n safeHtml?: SafeHtml;\n code?: string;\n lang?: CodeBlockLanguage;\n}\n\n/**\n * Stand-in for a fenced code block while the rest of the markdown is processed.\n * Bracketed in NUL characters, which the source is stripped of first — so no\n * text a user writes can ever spell a placeholder and be mistaken for a block.\n * (The earlier `<!--CODE_BLOCK_N-->` form was: an escaped literal comment in\n * the text matched it and dereferenced a block that wasn't there.)\n */\nconst BLOCK_MARK = '\\u0000';\nconst blockPlaceholder = (idx: number): string => `${BLOCK_MARK}LCBLOCK${idx}${BLOCK_MARK}`;\n// eslint-disable-next-line no-control-regex -- the NUL bracket is the point: unforgeable\nconst BLOCK_PLACEHOLDER = /\\u0000LCBLOCK(\\d+)\\u0000/g;\n\n/** Instances numbered so their mermaid render ids never collide on one page. */\nlet markdownInstances = 0;\n\n/**\n * Markdown renderer component.\n *\n * Renders GitHub-Flavored Markdown (GFM) to sanitized HTML with\n * optional syntax highlighting via `<lc-code-block>`.\n *\n * Supports headings (+ anchors), paragraphs, ordered/unordered lists, task lists\n * (`- [ ]` / `- [x]` → accessible checkbox), blockquotes, horizontal rules,\n * images, GFM tables (column alignment + horizontal scroll), bold/italic/\n * strikethrough, inline code, fenced code (incl. `mermaid`), explicit links and\n * autolinks (bare URLs / `www.` / emails, never inside code).\n *\n * Optionally highlights *changed* blocks in place: pass the pre-edit markdown as\n * `previousContent` and set `highlightChanges` — added/edited blocks (diffed at\n * block / list-item level) gain a left accent bar + subtle tint, can auto-fade\n * (`changeHighlightFadeMs`) and scroll into view (`scrollToFirstChange`).\n *\n * @example\n * ```html\n * <lc-markdown [content]=\"'# Hello World'\" />\n * <lc-markdown [src]=\"'/docs/readme.md'\" />\n * <lc-markdown\n * [content]=\"current\" [previousContent]=\"prev\"\n * [highlightChanges]=\"true\" [changeHighlightFadeMs]=\"3000\" />\n * ```\n */\n@Component({\n selector: 'lc-markdown',\n standalone: true,\n imports: [CodeBlockComponent],\n templateUrl: './markdown.component.html',\n styleUrls: ['./markdown.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class MarkdownComponent implements OnDestroy {\n private readonly sanitizer = inject(DomSanitizer);\n private readonly instanceId = ++markdownInstances;\n private readonly http = inject(HttpClient);\n private readonly host = inject(ElementRef<HTMLElement>);\n private httpSub?: Subscription;\n private fadeTimer?: ReturnType<typeof setTimeout>;\n private scrollTimer?: ReturnType<typeof setTimeout>;\n private mermaidApiPromise?: Promise<any>;\n private mermaidInitialized = false;\n private mermaidRenderRun = 0;\n\n /** URL or path to load markdown from */\n readonly src = input<string>();\n\n /** Raw markdown string */\n readonly content = input<string>();\n\n /** Display variant */\n readonly variant = input<'default' | 'compact' | 'chat'>('default');\n\n /** Target for links */\n readonly linkTarget = input<'_self' | '_blank'>('_self');\n\n /** Whether to sanitize HTML output */\n readonly sanitize = input<boolean>(true);\n\n /** Whether to use code-block for fenced code */\n readonly syntaxHighlight = input<boolean>(true);\n\n /** Whether to show anchor links on headings */\n readonly showHeadingAnchors = input<boolean>(false);\n\n /** Base URL for resolving relative links/images */\n readonly baseUrl = input<string>();\n\n // -- Change highlighting --\n /** Enable change highlighting (requires `previousContent` to compute a diff). */\n readonly highlightChanges = input<boolean>(false);\n\n /**\n * The prior Markdown. When it differs from `content`, the changed/added blocks\n * in `content` are highlighted. The caller passes the pre-edit markdown.\n */\n readonly previousContent = input<string>();\n\n /** Auto-fade the highlight after N ms. 0 / undefined ⇒ persist until content changes. */\n readonly changeHighlightFadeMs = input<number>();\n\n /** Scroll the first changed block into view when highlights appear. */\n readonly scrollToFirstChange = input<boolean>(false);\n\n /** Screen-reader marker put in front of each highlighted block (`highlightChanges`). */\n readonly changedMarkerLabel = input('(geändert)');\n\n /** Announced after the count when one block changed: `1 Abschnitt geändert`. */\n readonly changedSummaryOneLabel = input('Abschnitt geändert');\n\n /** Announced after the count when several blocks changed: `3 Abschnitte geändert`. */\n readonly changedSummaryManyLabel = input('Abschnitte geändert');\n\n /** Shown while a Mermaid diagram renders. */\n readonly mermaidLoadingLabel = input('Rendering Mermaid diagram...');\n\n /** Shown above the source when a Mermaid diagram fails. */\n readonly mermaidErrorLabel = input('Mermaid diagram could not be rendered.');\n\n /** Shown above the source when the optional `mermaid` package cannot be loaded. */\n readonly mermaidUnavailableLabel = input('Mermaid runtime is not available.');\n\n /** Emitted when a link is clicked */\n readonly linkClick = output<MarkdownLinkClick>();\n\n /** Emitted after rendering with heading TOC */\n readonly rendered = output<MarkdownRendered>();\n\n /** Emitted after a render that produced change highlights. */\n readonly changesHighlighted = output<MarkdownChangesHighlighted>();\n\n /** Internal resolved markdown source */\n protected resolvedMarkdown = signal<string>('');\n\n /** Parsed result (computed once from resolvedMarkdown) */\n private parsed = computed(() => {\n const md = this.resolvedMarkdown();\n if (!md) {\n return {\n html: '',\n blocks: [] as { kind: 'code' | 'mermaid'; lang: string; code: string }[],\n headings: [] as MarkdownHeading[],\n };\n }\n return this.parseMarkdown(md);\n });\n\n /**\n * Rendered HTML after applying change highlights. When highlighting is off (or\n * there is no differing `previousContent`) this returns the parsed HTML\n * unchanged, so the non-highlight path is byte-for-byte identical to before.\n */\n private readonly highlightResult = computed<{ html: string; count: number }>(() => {\n const { html } = this.parsed();\n const prev = this.previousContent();\n if (\n !html ||\n !this.highlightChanges() ||\n prev == null ||\n prev === this.resolvedMarkdown()\n ) {\n return { html, count: 0 };\n }\n return this.applyChangeHighlights(html, prev);\n });\n\n /** Number of changed/added blocks highlighted in the current render. */\n protected readonly changedCount = computed(() => this.highlightResult().count);\n\n /** Visually-hidden polite summary announced when highlights appear. */\n protected readonly changeSummary = computed(() => {\n const n = this.changedCount();\n if (n <= 0) return '';\n return `${n} ${n === 1 ? this.changedSummaryOneLabel() : this.changedSummaryManyLabel()}`;\n });\n\n /** Whether the current highlights have faded out (after `changeHighlightFadeMs`). */\n protected readonly highlightsFaded = signal(false);\n\n /** Computed render parts (HTML chunks + code blocks interleaved) */\n protected renderParts = computed<RenderPart[]>(() => {\n const html = this.highlightResult().html;\n const { blocks } = this.parsed();\n if (!html) return [];\n\n if (this.syntaxHighlight() && blocks.length > 0) {\n return this.splitIntoParts(html, blocks);\n }\n\n // No code blocks — single HTML part\n return [{\n type: 'html',\n index: 0,\n safeHtml: this.sanitizer.bypassSecurityTrustHtml(this.sanitizeHtml(html, { next: 0 })),\n }];\n });\n\n /**\n * The HTML as it may go into the DOM. With `sanitize` on (the default) that is\n * Angular's HTML sanitizer's output — which drops every `id`, since `id` isn't\n * on its attribute allow-list. The heading ids are put back afterwards: they\n * are what anchors and the `rendered` TOC point at, and each is a `[\\w-]`\n * slug this parser produced itself, so re-adding them reopens nothing. The\n * cursor walks the parsed headings in document order across the parts.\n */\n private sanitizeHtml(html: string, cursor: { next: number }): string {\n const headings = this.headings();\n if (!this.sanitize()) {\n cursor.next += (html.match(/<h[1-6]\\b/g) ?? []).length;\n return html;\n }\n const sanitized = this.sanitizer.sanitize(SecurityContext.HTML, html) || '';\n return sanitized.replace(/<h([1-6])(\\s[^>]*)?>/g, (tag, level: string, attrs = '') => {\n const heading = headings[cursor.next++];\n if (!heading || /\\sid=/.test(attrs)) return tag;\n return `<h${level} id=\"${heading.id}\"${attrs}>`;\n });\n }\n\n protected containerClasses = computed(() => {\n let classes = `lc-markdown lc-markdown--${this.variant()}`;\n if (this.highlightsFaded()) classes += ' lc-markdown--faded';\n return classes;\n });\n\n protected mermaidSvgs = signal<Record<number, SafeHtml>>({});\n protected mermaidErrors = signal<Record<number, string>>({});\n\n private headings = computed(() => this.parsed().headings);\n\n constructor() {\n // Load content from src when src changes\n effect(() => {\n const src = this.src();\n if (src) {\n this.loadFromUrl(src);\n }\n });\n\n // Use raw content when provided\n effect(() => {\n const content = this.content();\n if (content !== undefined) {\n this.resolvedMarkdown.set(content);\n }\n });\n\n // Emit rendered event whenever a render produced a (possibly empty) TOC\n effect(() => {\n const h = this.headings();\n untracked(() => this.rendered.emit({ headings: h }));\n });\n\n // Render Mermaid code fences as inline SVG diagrams.\n effect(() => {\n void this.renderMermaidParts(this.renderParts());\n });\n\n // React to a render that produced change highlights: emit, schedule fade,\n // and optionally scroll the first changed block into view.\n effect(() => {\n const count = this.highlightResult().count;\n untracked(() => this.onHighlightResult(count));\n });\n }\n\n ngOnDestroy(): void {\n this.httpSub?.unsubscribe();\n clearTimeout(this.fadeTimer);\n clearTimeout(this.scrollTimer);\n }\n\n protected onLinkClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n const anchor = target.closest('a');\n if (anchor) {\n this.linkClick.emit({ href: anchor.href, event });\n }\n }\n\n // -- Change highlighting -----------------------------------------------------\n\n /** Side-effects for a render: emit, (re)arm the fade timer, optional scroll. */\n private onHighlightResult(count: number): void {\n clearTimeout(this.fadeTimer);\n clearTimeout(this.scrollTimer);\n // A fresh render starts un-faded (re-entrant: never reuse stale fade state).\n this.highlightsFaded.set(false);\n if (count <= 0) return;\n\n this.changesHighlighted.emit({ changedBlocks: count });\n\n const fadeMs = this.changeHighlightFadeMs();\n if (fadeMs && fadeMs > 0) {\n this.fadeTimer = setTimeout(() => this.highlightsFaded.set(true), fadeMs);\n }\n if (this.scrollToFirstChange()) {\n // Defer until the highlighted HTML has been written to the DOM.\n this.scrollTimer = setTimeout(() => this.scrollToFirstChanged(), 0);\n }\n }\n\n private scrollToFirstChanged(): void {\n if (typeof document === 'undefined') return;\n const el = this.host.nativeElement.querySelector('.lc-markdown__block--changed');\n el?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n }\n\n /**\n * Wraps the changed/added top-level blocks of `html` with\n * `.lc-markdown__block--changed`. A block is \"changed\" when its normalized\n * text is not present among the blocks of `prevMarkdown`. Lists are diffed\n * per `<li>` and tables per `<tr>` so a single edited item highlights alone.\n */\n private applyChangeHighlights(\n html: string,\n prevMarkdown: string\n ): { html: string; count: number } {\n if (typeof document === 'undefined') return { html, count: 0 };\n\n const prevKeys = this.collectBlockKeys(this.parseMarkdown(prevMarkdown).html);\n const root = this.htmlToElement(html);\n if (!root) return { html, count: 0 };\n\n let count = 0;\n const markIfChanged = (el: Element): void => {\n const key = this.blockTextKey(el.textContent ?? '');\n if (!key || prevKeys.has(key)) return;\n el.classList.add('lc-markdown__block--changed');\n const sr = document.createElement('span');\n sr.className = 'lc-markdown__sr-only';\n sr.textContent = `${this.changedMarkerLabel()} `;\n el.insertBefore(sr, el.firstChild);\n count++;\n };\n\n this.eachLogicalBlock(root, markIfChanged);\n return { html: root.innerHTML, count };\n }\n\n /** Normalized text set of the logical blocks within an HTML fragment. */\n private collectBlockKeys(html: string): Set<string> {\n const keys = new Set<string>();\n const root = this.htmlToElement(html);\n if (!root) return keys;\n this.eachLogicalBlock(root, (el) => {\n const key = this.blockTextKey(el.textContent ?? '');\n if (key) keys.add(key);\n });\n return keys;\n }\n\n /**\n * Visits each diffable block under `root`: top-level elements, but descending\n * into `<ul>`/`<ol>` (per `<li>`) and `<table>` (per `<tr>`).\n */\n private eachLogicalBlock(root: Element, visit: (el: Element) => void): void {\n for (const node of Array.from(root.childNodes)) {\n if (node.nodeType !== 1) continue; // elements only (skips code placeholders)\n const el = node as Element;\n const tag = el.tagName;\n if (tag === 'UL' || tag === 'OL') {\n for (const li of Array.from(el.children)) {\n if (li.tagName === 'LI') visit(li);\n }\n } else if (tag === 'TABLE' || el.querySelector('table')) {\n // Tables (incl. the scroll wrapper div) diff per row.\n for (const tr of Array.from(el.querySelectorAll('tr'))) {\n visit(tr);\n }\n } else {\n visit(el);\n }\n }\n }\n\n private blockTextKey(text: string): string {\n return text.replace(/\\s+/g, ' ').trim().toLowerCase();\n }\n\n private htmlToElement(html: string): HTMLElement | null {\n if (typeof document === 'undefined') return null;\n const div = document.createElement('div');\n div.innerHTML = html;\n return div;\n }\n\n private loadFromUrl(url: string): void {\n this.httpSub?.unsubscribe();\n this.httpSub = this.http.get(url, { responseType: 'text' }).subscribe({\n next: (text) => this.resolvedMarkdown.set(text),\n error: () => this.resolvedMarkdown.set(`*Failed to load ${url}*`),\n });\n }\n\n private splitIntoParts(\n html: string,\n blocks: { kind: 'code' | 'mermaid'; lang: string; code: string }[]\n ): RenderPart[] {\n const parts: RenderPart[] = [];\n const regex = new RegExp(BLOCK_PLACEHOLDER.source, 'g');\n const cursor = { next: 0 };\n let lastIndex = 0;\n let partIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(html)) !== null) {\n const before = html.slice(lastIndex, match.index);\n if (before.trim()) {\n parts.push({\n type: 'html',\n index: partIndex++,\n safeHtml: this.sanitizer.bypassSecurityTrustHtml(this.sanitizeHtml(before, cursor)),\n });\n }\n const block = blocks[parseInt(match[1], 10)];\n lastIndex = match.index + match[0].length;\n // Unforgeable placeholder, so this can only be a bug — but never a crash.\n if (!block) continue;\n if (block.kind === 'mermaid') {\n parts.push({\n type: 'mermaid',\n index: partIndex++,\n code: block.code,\n });\n } else {\n parts.push({\n type: 'code',\n index: partIndex++,\n code: block.code,\n lang: block.lang as CodeBlockLanguage,\n });\n }\n }\n\n const remaining = html.slice(lastIndex);\n if (remaining.trim()) {\n parts.push({\n type: 'html',\n index: partIndex++,\n safeHtml: this.sanitizer.bypassSecurityTrustHtml(this.sanitizeHtml(remaining, cursor)),\n });\n }\n\n return parts;\n }\n\n private parseMarkdown(md: string): {\n html: string;\n blocks: { kind: 'code' | 'mermaid'; lang: string; code: string }[];\n headings: MarkdownHeading[];\n } {\n const blocks: { kind: 'code' | 'mermaid'; lang: string; code: string }[] = [];\n const headings: MarkdownHeading[] = [];\n const baseUrl = this.baseUrl();\n const linkTarget = this.linkTarget();\n const showAnchors = this.showHeadingAnchors();\n\n // 1. Extract fenced code blocks → placeholders (see BLOCK_MARK: the source\n // is stripped of the marker character first, so it can't forge one)\n // eslint-disable-next-line no-control-regex -- strip the placeholder marker (see BLOCK_MARK)\n let processed = md.replace(/\\u0000/g, '').replace(\n /```(\\w*)\\n([\\s\\S]*?)```/g,\n (_match, lang: string, code: string) => {\n const idx = blocks.length;\n const normalizedLang = (lang || 'text').toLowerCase();\n blocks.push({\n kind: normalizedLang === 'mermaid' ? 'mermaid' : 'code',\n lang: normalizedLang,\n code: code.trimEnd(),\n });\n return blockPlaceholder(idx);\n }\n );\n\n // 2. Escape HTML entities — quotes included, so nothing the parser later\n // interpolates into an attribute (link/image URLs, alt text) can close it.\n processed = this.escapeHtml(processed);\n\n // 3. Headings (# to ######)\n processed = processed.replace(\n /^(#{1,6})\\s+(.+)$/gm,\n (_match, hashes: string, text: string) => {\n const level = hashes.length;\n const id = text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-');\n headings.push({ level, text, id });\n const anchor = showAnchors\n ? `<a href=\"#${id}\" class=\"lc-markdown__anchor\" aria-hidden=\"true\">#</a>`\n : '';\n return `<h${level} id=\"${id}\">${anchor}${text}</h${level}>`;\n }\n );\n\n // 4. Horizontal rules\n processed = processed.replace(/^---+$/gm, '<hr>');\n\n // 5. Blockquotes\n processed = processed.replace(\n /^(?:>)\\s?(.*)$/gm,\n '<blockquote>$1</blockquote>'\n );\n // Merge consecutive blockquotes\n processed = processed.replace(\n /<\\/blockquote>\\n<blockquote>/g,\n '\\n'\n );\n\n // 6. Tables (GFM)\n processed = this.parseTables(processed);\n\n // 7. Task lists. Angular's HTML sanitizer strips <input>, so the checkbox is\n // rendered as a styled <span role=\"checkbox\"> (which survives sanitization and\n // carries accessible checked/disabled state) rather than a form control.\n processed = processed.replace(\n /^[-*]\\s+\\[[xX]\\]\\s+(.*)$/gm,\n '<li class=\"lc-markdown__task-item\">' +\n '<span class=\"lc-markdown__checkbox lc-markdown__checkbox--checked\" role=\"checkbox\" aria-checked=\"true\" aria-disabled=\"true\"></span>' +\n '<span class=\"lc-markdown__task-label\">$1</span></li>'\n );\n processed = processed.replace(\n /^[-*]\\s+\\[ \\]\\s+(.*)$/gm,\n '<li class=\"lc-markdown__task-item\">' +\n '<span class=\"lc-markdown__checkbox\" role=\"checkbox\" aria-checked=\"false\" aria-disabled=\"true\"></span>' +\n '<span class=\"lc-markdown__task-label\">$1</span></li>'\n );\n\n // 8. Unordered lists\n processed = processed.replace(\n /^[-*]\\s+(.*)$/gm,\n '<li>$1</li>'\n );\n // Wrap consecutive <li> in <ul>\n processed = processed.replace(\n /((?:<li[^>]*>.*<\\/li>\\n?)+)/g,\n '<ul>$1</ul>'\n );\n\n // 9. Ordered lists — marked while wrapping, so a run of them gets its <ol>\n // and isn't confused with the <li>s already inside <ul>s above.\n processed = processed.replace(\n /^\\d+\\.\\s+(.*)$/gm,\n '<li data-ol>$1</li>'\n );\n processed = processed.replace(\n /((?:<li data-ol>.*<\\/li>\\n?)+)/g,\n (run) => `<ol>${run.replace(/ data-ol/g, '')}</ol>`\n );\n\n // 10. Inline code\n processed = processed.replace(\n /`([^`]+)`/g,\n '<code>$1</code>'\n );\n\n // 11. Bold + italic combos\n processed = processed.replace(\n /\\*\\*\\*(.+?)\\*\\*\\*/g,\n '<strong><em>$1</em></strong>'\n );\n processed = processed.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');\n processed = processed.replace(/\\*(.+?)\\*/g, '<em>$1</em>');\n\n // 12. Strikethrough (GFM)\n processed = processed.replace(/~~(.+?)~~/g, '<del>$1</del>');\n\n // 13. Images. Only allow-listed URL schemes become a `src` (see\n // safe-url.ts); anything else — `javascript:`, unknown schemes — is\n // rendered as its alt text. This holds regardless of `sanitize`.\n processed = processed.replace(\n /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g,\n (_match, alt: string, src: string) => {\n if (!isSafeImageUrl(src)) return alt;\n const resolvedSrc = baseUrl && !src.startsWith('http') ? `${baseUrl}/${src}` : src;\n return `<img src=\"${resolvedSrc}\" alt=\"${alt}\" class=\"lc-markdown__img\">`;\n }\n );\n\n // 14. Links — same rule: an unsafe URL leaves plain text, not a link.\n processed = processed.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n (_match, text: string, href: string) => {\n if (!isSafeLinkUrl(href)) return text;\n const resolvedHref = baseUrl && !href.startsWith('http') && !href.startsWith('#')\n ? `${baseUrl}/${href}`\n : href;\n return `<a href=\"${resolvedHref}\" target=\"${linkTarget}\" rel=\"noopener\">${text}</a>`;\n }\n );\n\n // 14b. Autolinks — linkify bare URLs / www / emails in plain text. Runs after\n // explicit links and inline code so it can skip anything already inside an\n // <a> or <code> (and never touches tag attributes).\n processed = this.autolink(processed, linkTarget);\n\n // 15. Paragraphs — wrap standalone lines\n processed = processed\n .split('\\n\\n')\n .map((block) => {\n const trimmed = block.trim();\n if (!trimmed) return '';\n if (\n trimmed.startsWith('<h') ||\n trimmed.startsWith('<ul') ||\n trimmed.startsWith('<ol') ||\n trimmed.startsWith('<blockquote') ||\n trimmed.startsWith('<hr') ||\n trimmed.startsWith('<table') ||\n trimmed.startsWith('<div') ||\n trimmed.startsWith(BLOCK_MARK)\n ) {\n return trimmed;\n }\n return `<p>${trimmed.replace(/\\n/g, '<br>')}</p>`;\n })\n .join('\\n');\n\n // 16. Restore code block placeholders\n if (this.syntaxHighlight()) {\n // Leave placeholders — the template will render <lc-code-block> for each\n // No replacement needed here\n } else {\n processed = processed.replace(\n new RegExp(BLOCK_PLACEHOLDER.source, 'g'),\n (_match, idx: string) => {\n const block = blocks[parseInt(idx, 10)];\n if (!block) return '';\n return `<pre><code class=\"language-${block.lang}\">${this.escapeHtml(block.code)}</code></pre>`;\n }\n );\n }\n\n return { html: processed, blocks, headings };\n }\n\n private parseTables(text: string): string {\n return text.replace(\n /^(\\|.+\\|)\\n(\\|[-| :]+\\|)\\n((?:\\|.+\\|\\n?)*)/gm,\n (_match, header: string, separator: string, body: string) => {\n // Per-column alignment from the delimiter row: :--- left, :--: center,\n // ---: right. Surfaced as classes (the sanitizer strips inline `style`).\n const aligns = this.splitTableRow(separator).map((cell) => {\n const left = cell.startsWith(':');\n const right = cell.endsWith(':');\n if (left && right) return 'center';\n if (right) return 'right';\n if (left) return 'left';\n return '';\n });\n const alignClass = (col: number): string =>\n aligns[col] ? ` class=\"lc-markdown__cell--${aligns[col]}\"` : '';\n\n const headers = this.splitTableRow(header)\n .map((cell, i) => `<th scope=\"col\"${alignClass(i)}>${cell.trim()}</th>`)\n .join('');\n\n const rows = body\n .trim()\n .split('\\n')\n .map((row: string) => {\n const cells = this.splitTableRow(row)\n .map((cell, i) => `<td${alignClass(i)}>${cell.trim()}</td>`)\n .join('');\n return `<tr>${cells}</tr>`;\n })\n .join('');\n\n // Wrapped so wide tables scroll horizontally instead of breaking layout.\n return (\n `<div class=\"lc-markdown__table-wrap\">` +\n `<table class=\"lc-markdown__table\"><thead><tr>${headers}</tr></thead><tbody>${rows}</tbody></table>` +\n `</div>`\n );\n }\n );\n }\n\n /** Splits a pipe-table row into trimmed cells, dropping the leading/trailing edges. */\n private splitTableRow(row: string): string[] {\n return row\n .trim()\n .replace(/^\\|/, '')\n .replace(/\\|$/, '')\n .split('|')\n .map((cell) => cell.trim());\n }\n\n private escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n }\n\n /**\n * Linkifies bare URLs (`http(s)://…`, `www.…`) and email addresses in text,\n * leaving anything inside an existing `<a>` or `<code>` (and all tag\n * attributes) untouched. The HTML is tokenized into tags vs. text so only\n * top-level text runs are linkified.\n */\n private autolink(html: string, linkTarget: string): string {\n const url = /(?:https?:\\/\\/|www\\.)[^\\s<]*[^\\s<.,:;!?)\\]}'\"]/;\n const email = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/;\n const pattern = new RegExp(`${url.source}|${email.source}`, 'g');\n\n let depth = 0; // inside <a> or <code>\n return html\n .split(/(<[^>]+>)/g)\n .map((segment) => {\n if (segment.startsWith('<')) {\n if (/^<(a|code)[\\s>]/i.test(segment)) depth++;\n else if (/^<\\/(a|code)>/i.test(segment)) depth = Math.max(0, depth - 1);\n return segment;\n }\n if (depth > 0 || !segment) return segment;\n return segment.replace(pattern, (token) => {\n const isEmail = token.includes('@') && !/^(?:https?:\\/\\/|www\\.)/.test(token);\n const href = isEmail\n ? `mailto:${token}`\n : token.startsWith('www.')\n ? `https://${token}`\n : token;\n return `<a href=\"${href}\" target=\"${linkTarget}\" rel=\"noopener\">${token}</a>`;\n });\n })\n .join('');\n }\n\n protected mermaidSvgFor(index: number): SafeHtml | null {\n return this.mermaidSvgs()[index] ?? null;\n }\n\n protected mermaidErrorFor(index: number): string | null {\n return this.mermaidErrors()[index] ?? null;\n }\n\n private async renderMermaidParts(parts: RenderPart[]): Promise<void> {\n const mermaidParts = parts.filter((part) => part.type === 'mermaid' && !!part.code);\n this.mermaidSvgs.set({});\n this.mermaidErrors.set({});\n\n if (mermaidParts.length === 0) {\n return;\n }\n\n const runId = ++this.mermaidRenderRun;\n\n try {\n const mermaidApi = await this.getMermaidApi();\n if (runId !== this.mermaidRenderRun) return;\n\n const nextSvgs: Record<number, SafeHtml> = {};\n const nextErrors: Record<number, string> = {};\n\n for (const part of mermaidParts) {\n try {\n // Per instance, per run, per part: mermaid's render() removes any\n // element already carrying the id it is given, so an id shared with\n // another <lc-markdown> on the page would delete that one's diagram.\n const renderId = `lc-md-mermaid-${this.instanceId}-${runId}-${part.index}`;\n const { svg } = await mermaidApi.render(renderId, part.code!);\n if (runId !== this.mermaidRenderRun) return;\n // Mermaid emits self-contained SVG with internal <style> rules.\n // Angular HTML sanitization strips critical style declarations,\n // which leaves only labels visible (no edges/shapes). We trust\n // Mermaid output here and rely on Mermaid's strict security mode.\n nextSvgs[part.index] = this.sanitizer.bypassSecurityTrustHtml(svg);\n } catch {\n nextErrors[part.index] = this.mermaidErrorLabel();\n }\n }\n\n if (runId !== this.mermaidRenderRun) return;\n this.mermaidSvgs.set(nextSvgs);\n this.mermaidErrors.set(nextErrors);\n } catch {\n if (runId !== this.mermaidRenderRun) return;\n const fallbackErrors: Record<number, string> = {};\n for (const part of mermaidParts) {\n fallbackErrors[part.index] = this.mermaidUnavailableLabel();\n }\n this.mermaidErrors.set(fallbackErrors);\n }\n }\n\n private async getMermaidApi(): Promise<any> {\n if (!this.mermaidApiPromise) {\n this.mermaidApiPromise = import('mermaid').then((module) => {\n const api = module.default ?? module;\n if (!this.mermaidInitialized) {\n api.initialize({\n startOnLoad: false,\n securityLevel: 'strict',\n });\n this.mermaidInitialized = true;\n }\n return api;\n });\n }\n return this.mermaidApiPromise;\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- delegated handler for clicks on rendered links, which are focusable anchors -->\n<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">{{ mermaidLoadingLabel() }}</div>\n }\n }\n }\n</div>\n"],"mappings":"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"}
|