@mrsf/marp-mrsf 0.4.8
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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/browser.d.ts +6 -0
- package/dist/browser.js +317 -0
- package/dist/browser.js.map +7 -0
- package/dist/controller.d.ts +173 -0
- package/dist/controller.js +1179 -0
- package/dist/controller.js.map +7 -0
- package/dist/gutter.d.ts +50 -0
- package/dist/html.d.ts +13 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +353 -0
- package/dist/index.js.map +7 -0
- package/dist/rules/core.d.ts +17 -0
- package/dist/rules/renderer.d.ts +10 -0
- package/dist/shared.d.ts +20 -0
- package/dist/style.css +522 -0
- package/dist/types.d.ts +105 -0
- package/package.json +85 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/gutter.ts", "../src/html.ts", "../src/controller.ts"],
|
|
4
|
+
"sourcesContent": ["/* ---------------------------------------------------------------\n * AUTO-GENERATED \u2014 DO NOT EDIT\n *\n * Source: plugins/shared/src/gutter.ts\n * Run `node plugins/sync-types.mjs` to regenerate.\n * --------------------------------------------------------------- */\n\nexport type MrsfResolvedState = \"open\" | \"resolved\" | \"mixed\";\n\nexport interface MrsfGutterBadgePresentation {\n icon: string;\n countText: string;\n label: string;\n title: string;\n ariaLabel: string;\n className?: string;\n attributes?: Record<string, string>;\n}\n\nexport interface MrsfGutterBadgeContext {\n line: number;\n commentCount: number;\n threadCount: number;\n resolvedState: MrsfResolvedState;\n highestSeverity: string | null;\n isActive: boolean;\n}\n\nexport interface MrsfGutterBadgeRenderContext extends MrsfGutterBadgeContext {\n defaultPresentation: MrsfGutterBadgePresentation;\n}\n\nexport interface MrsfGutterAddButtonPresentation {\n label: string;\n title: string;\n ariaLabel: string;\n className?: string;\n attributes?: Record<string, string>;\n}\n\nexport interface MrsfGutterAddButtonContext {\n line: number;\n isActive: boolean;\n}\n\nexport interface MrsfGutterAddButtonRenderContext extends MrsfGutterAddButtonContext {\n defaultPresentation: MrsfGutterAddButtonPresentation;\n}\n\nexport interface MrsfGutterRenderers {\n badge?: (\n context: MrsfGutterBadgeRenderContext,\n ) => Partial<MrsfGutterBadgePresentation> | null | undefined;\n addButton?: (\n context: MrsfGutterAddButtonRenderContext,\n ) => Partial<MrsfGutterAddButtonPresentation> | null | undefined;\n}\n\nexport interface MrsfBadgeSource {\n commentCount: number;\n threadCount: number;\n resolvedState: MrsfResolvedState;\n}\n\nexport function formatMrsfCount(count: number, max = 9): string {\n return count > max ? `${max}+` : String(count);\n}\n\nexport function createMrsfGutterBadgePresentation(\n context: MrsfGutterBadgeContext,\n): MrsfGutterBadgePresentation {\n const icon = context.resolvedState === \"resolved\" ? \"\u2713\" : \"\uD83D\uDCAC\";\n const countText = formatMrsfCount(context.commentCount);\n const label = `${icon} ${countText}`;\n const threadSummary = context.threadCount === 1 ? \"1 thread\" : `${context.threadCount} threads`;\n const commentSummary = context.commentCount === 1 ? \"1 comment\" : `${context.commentCount} comments`;\n\n return {\n icon,\n countText,\n label,\n title: `${threadSummary}, ${commentSummary}`,\n ariaLabel: `${label} on line ${context.line}`,\n };\n}\n\nexport function createMrsfGutterAddButtonPresentation(\n _context: MrsfGutterAddButtonContext,\n): MrsfGutterAddButtonPresentation {\n return {\n label: \"Add\",\n title: \"Add comment thread\",\n ariaLabel: \"Add comment thread\",\n };\n}\n\nexport function resolveMrsfGutterBadgePresentation(\n context: MrsfGutterBadgeContext,\n renderer?: MrsfGutterRenderers[\"badge\"],\n): MrsfGutterBadgePresentation {\n const defaultPresentation = createMrsfGutterBadgePresentation(context);\n const override = renderer?.({\n ...context,\n defaultPresentation,\n });\n\n return {\n ...defaultPresentation,\n ...override,\n attributes: {\n ...(defaultPresentation.attributes ?? {}),\n ...(override?.attributes ?? {}),\n },\n };\n}\n\nexport function resolveMrsfGutterAddButtonPresentation(\n context: MrsfGutterAddButtonContext,\n renderer?: MrsfGutterRenderers[\"addButton\"],\n): MrsfGutterAddButtonPresentation {\n const defaultPresentation = createMrsfGutterAddButtonPresentation(context);\n const override = renderer?.({\n ...context,\n defaultPresentation,\n });\n\n return {\n ...defaultPresentation,\n ...override,\n attributes: {\n ...(defaultPresentation.attributes ?? {}),\n ...(override?.attributes ?? {}),\n },\n };\n}", "/* ---------------------------------------------------------------\n * AUTO-GENERATED \u2014 DO NOT EDIT\n *\n * Source: plugins/shared/src/html.ts\n * Run `node plugins/sync-types.mjs` to regenerate.\n * --------------------------------------------------------------- */\n\n/**\n * Shared HTML rendering helpers for MRSF rendering plugins.\n *\n * These produce the HTML strings used in tooltips, badges, and comment\n * rendering. Both the markdown-it and rehype plugins use these functions\n * to ensure identical visual output.\n */\n\nimport type { CommentThread, SlimComment } from \"./types.js\";\n\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\nexport function formatTime(iso: string | null): string {\n if (!iso) return \"\";\n try {\n const d = new Date(iso);\n return d.toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n } catch {\n return \"\";\n }\n}\n\nexport function renderCommentHtml(\n comment: SlimComment,\n isReply: boolean,\n interactive: boolean,\n): string {\n const resolvedClass = comment.resolved ? \" mrsf-resolved\" : \"\";\n const replyClass = isReply ? \" mrsf-reply\" : \"\";\n let html = `<div class=\"mrsf-comment${resolvedClass}${replyClass}\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\">`;\n\n // Header\n html += `<div class=\"mrsf-comment-header\">`;\n html += `<span class=\"mrsf-author\">${escapeHtml(comment.author)}</span>`;\n if (comment.timestamp) {\n html += `<span class=\"mrsf-date\">${escapeHtml(formatTime(comment.timestamp))}</span>`;\n }\n if (comment.severity) {\n html += `<span class=\"mrsf-severity mrsf-severity-${escapeHtml(comment.severity)}\">${escapeHtml(comment.severity)}</span>`;\n }\n if (comment.type) {\n html += `<span class=\"mrsf-type\">${escapeHtml(comment.type)}</span>`;\n }\n if (comment.resolved) {\n html += `<span class=\"mrsf-resolved-badge\">\u2713 resolved</span>`;\n }\n html += `</div>`;\n\n // Selected text quote (collapsible)\n if (comment.selected_text) {\n html += `<details class=\"mrsf-selected-text\"><summary class=\"mrsf-selected-text-summary\">${escapeHtml(comment.selected_text)}</summary><div class=\"mrsf-selected-text-full\">${escapeHtml(comment.selected_text)}</div></details>`;\n }\n\n // Body\n html += `<div class=\"mrsf-comment-body\">${escapeHtml(comment.text)}</div>`;\n\n // Action buttons (interactive mode)\n if (interactive) {\n const line = comment.line != null ? String(comment.line) : \"\";\n html += `<div class=\"mrsf-actions\">`;\n if (comment.resolved) {\n html += `<button class=\"mrsf-action-btn\" data-mrsf-action=\"unresolve\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\" data-mrsf-line=\"${line}\">Unresolve</button>`;\n } else {\n html += `<button class=\"mrsf-action-btn\" data-mrsf-action=\"resolve\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\" data-mrsf-line=\"${line}\">Resolve</button>`;\n }\n html += `<button class=\"mrsf-action-btn\" data-mrsf-action=\"reply\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\" data-mrsf-line=\"${line}\">Reply</button>`;\n html += `<button class=\"mrsf-action-btn\" data-mrsf-action=\"edit\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\" data-mrsf-line=\"${line}\">Edit</button>`;\n html += `<button class=\"mrsf-action-btn mrsf-action-danger\" data-mrsf-action=\"delete\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\" data-mrsf-line=\"${line}\">Delete</button>`;\n html += `</div>`;\n }\n\n html += `</div>`;\n return html;\n}\n\nexport function renderThreadHtml(thread: CommentThread, interactive: boolean): string {\n let html = `<div class=\"mrsf-thread\">`;\n html += renderCommentHtml(thread.comment, false, interactive);\n if (thread.replies.length > 0) {\n html += `<div class=\"mrsf-replies\">`;\n for (const reply of thread.replies) {\n html += renderCommentHtml(reply, true, interactive);\n }\n html += `</div>`;\n }\n html += `</div>`;\n return html;\n}\n", "/* ---------------------------------------------------------------\n * AUTO-GENERATED \u2014 DO NOT EDIT\n *\n * Source: plugins/shared/src/controller.ts\n * Run `node plugins/sync-types.mjs` to regenerate.\n * --------------------------------------------------------------- */\n\n/**\n * Sidemark \u2014 MrsfController: overlay gutter architecture.\n *\n * The controller is the runtime engine for interactive MRSF rendering.\n * It reads comment data from the DOM (embedded `<script type=\"application/mrsf+json\">`)\n * or constructor options, creates gutter overlay columns, positions\n * badges/buttons at the correct vertical offsets, and handles all\n * user interactions (tooltips, selection, action buttons).\n *\n * Usage:\n * import { MrsfController } from \"@mrsf/plugin-shared/controller\";\n *\n * const ctrl = new MrsfController(document.querySelector(\".my-content\")!, {\n * interactive: true,\n * gutterPosition: \"left\",\n * });\n * // ctrl.destroy() to clean up\n *\n * Events dispatched on document:\n * - mrsf:resolve { commentId, line, ... }\n * - mrsf:unresolve { commentId, line, ... }\n * - mrsf:reply { commentId, line, ... }\n * - mrsf:edit { commentId, line, ... }\n * - mrsf:delete { commentId, line, ... }\n * - mrsf:navigate { commentId, line, ... }\n * - mrsf:add { commentId: null, line, selectionText?, ... }\n * - mrsf:submit { action, commentId, text?, line?, ... }\n */\n\nimport type { CommentThread, SlimComment } from \"./types.js\";\nimport type { MrsfGutterRenderers } from \"./gutter.js\";\nimport {\n resolveMrsfGutterAddButtonPresentation,\n resolveMrsfGutterBadgePresentation,\n} from \"./gutter.js\";\nimport { renderThreadHtml, escapeHtml } from \"./html.js\";\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type MrsfAction = \"resolve\" | \"unresolve\" | \"reply\" | \"edit\" | \"delete\" | \"navigate\" | \"add\";\n\nexport interface MrsfActionDetail {\n commentId: string | null;\n line: number | null;\n action: MrsfAction;\n selectionText?: string | null;\n start_line?: number | null;\n end_line?: number | null;\n start_column?: number | null;\n end_column?: number | null;\n}\n\nexport interface MrsfSubmitDetail {\n action: \"add\" | \"edit\" | \"reply\" | \"resolve\" | \"unresolve\" | \"delete\";\n commentId: string | null;\n text: string;\n type?: string | null;\n severity?: \"low\" | \"medium\" | \"high\" | null;\n line?: number | null;\n end_line?: number | null;\n start_column?: number | null;\n end_column?: number | null;\n selection_text?: string | null;\n}\n\nexport interface MrsfControllerOptions {\n /** Show gutter on left or right side. Default: \"right\". */\n gutterPosition?: \"left\" | \"right\";\n /** Enable interactive actions (add, resolve, reply, etc.). Default: false. */\n interactive?: boolean;\n /** Comment data passed directly (overrides embedded script). */\n comments?: CommentThread[];\n /**\n * Render inline text highlights for comments that have `selected_text`.\n * Wraps matching text in `<mark>` elements with hover tooltips.\n * Default: true.\n */\n inlineHighlights?: boolean;\n gutterRenderers?: MrsfGutterRenderers;\n}\n\n// \u2500\u2500 MrsfController \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MrsfController {\n private container: HTMLElement;\n private opts: Required<MrsfControllerOptions>;\n private threads: Map<number, CommentThread[]> = new Map();\n private gutterLeft: HTMLDivElement | null = null;\n private gutterRight: HTMLDivElement | null = null;\n private activeTooltip: HTMLElement | null = null;\n private floatingAddButton: HTMLButtonElement | null = null;\n private overlayEl: HTMLDivElement | null = null;\n private lastSelectionText: string | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private mutationObserver: MutationObserver | null = null;\n private styleInjected = false;\n private inlineMarks: HTMLElement[] = [];\n private inlineTooltipEl: HTMLElement | null = null;\n private orphanedSection: HTMLDivElement | null = null;\n private refreshQueued = false;\n\n private handleResizeBound = this.positionGutterItems.bind(this);\n private handleMutationBound = this.handleMutations.bind(this);\n private handleClickBound = this.handleClick.bind(this);\n private handleSelectionBound = this.handleSelectionChange.bind(this);\n\n constructor(container: HTMLElement, options: MrsfControllerOptions = {}) {\n this.container = container;\n this.opts = {\n gutterPosition: options.gutterPosition ?? \"right\",\n interactive: options.interactive ?? false,\n comments: options.comments ?? [],\n inlineHighlights: options.inlineHighlights ?? true,\n gutterRenderers: options.gutterRenderers ?? {},\n };\n\n this.loadCommentData();\n this.setupOverlayStructure();\n this.renderGutterItems();\n this.positionGutterItems();\n this.renderInlineHighlights();\n this.renderOrphanedSection();\n\n controllerRegistry.add(this);\n\n // Listeners\n this.resizeObserver = new ResizeObserver(this.handleResizeBound);\n this.resizeObserver.observe(this.container);\n if (typeof MutationObserver !== \"undefined\") {\n this.mutationObserver = new MutationObserver(this.handleMutationBound);\n this.mutationObserver.observe(this.container, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n document.addEventListener(\"click\", this.handleClickBound);\n if (this.opts.interactive) {\n document.addEventListener(\"selectionchange\", this.handleSelectionBound);\n }\n }\n\n /** Remove all controller DOM and listeners. */\n destroy(): void {\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n controllerRegistry.delete(this);\n document.removeEventListener(\"click\", this.handleClickBound);\n document.removeEventListener(\"selectionchange\", this.handleSelectionBound);\n this.removeInlineHighlights();\n this.orphanedSection?.remove();\n this.gutterLeft?.remove();\n this.gutterRight?.remove();\n this.floatingAddButton?.remove();\n this.closeOverlay();\n this.container.classList.remove(\"mrsf-overlay-root\");\n }\n\n /** Recalculate gutter positions after async layout changes such as Mermaid renders. */\n refresh(): void {\n this.positionGutterItems();\n }\n\n // \u2500\u2500 Data loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private loadCommentData(): void {\n // Priority: constructor options > embedded script\n if (this.opts.comments.length > 0) {\n this.buildThreadMap(this.opts.comments);\n return;\n }\n\n const script = this.container.querySelector('script[type=\"application/mrsf+json\"]');\n if (!script?.textContent) return;\n\n try {\n const data = JSON.parse(script.textContent) as { threads?: CommentThread[] };\n if (data.threads) {\n this.buildThreadMap(data.threads);\n }\n } catch {\n // silently ignore malformed data\n }\n }\n\n private buildThreadMap(threads: CommentThread[]): void {\n this.threads.clear();\n for (const t of threads) {\n const line = t.comment.line;\n if (line == null) continue;\n const arr = this.threads.get(line) ?? [];\n arr.push(t);\n this.threads.set(line, arr);\n }\n }\n\n private findCommentById(commentId: string | null): SlimComment | null {\n if (!commentId) return null;\n\n for (const threadList of this.threads.values()) {\n for (const thread of threadList) {\n if (thread.comment.id === commentId) {\n return thread.comment;\n }\n const reply = thread.replies.find((item) => item.id === commentId);\n if (reply) {\n return reply;\n }\n }\n }\n\n return null;\n }\n\n private orderThreadsForDisplay(threads: CommentThread[]): CommentThread[] {\n return [...threads].sort((left, right) => {\n if (left.comment.resolved === right.comment.resolved) return 0;\n return left.comment.resolved ? 1 : -1;\n });\n }\n\n // \u2500\u2500 Overlay structure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private setupOverlayStructure(): void {\n this.container.classList.add(\"mrsf-overlay-root\");\n\n const pos = this.opts.gutterPosition;\n if (pos === \"left\") {\n this.gutterLeft = this.createGutter(\"mrsf-gutter-left\");\n } else {\n this.gutterRight = this.createGutter(\"mrsf-gutter-right\");\n }\n }\n\n private createGutter(cls: string): HTMLDivElement {\n const gutter = document.createElement(\"div\");\n gutter.className = `mrsf-gutter ${cls}`;\n this.container.appendChild(gutter);\n return gutter;\n }\n\n // \u2500\u2500 Gutter rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Build badge/add-button elements for each line in the gutter(s). */\n private renderGutterItems(): void {\n const lines = this.collectLines();\n const displayMap = this.collectThreadDisplayMap(lines);\n const gutter = this.primaryGutter();\n if (!gutter) return;\n\n for (const line of lines) {\n const threads = displayMap.get(line);\n if (threads && threads.length > 0) {\n const item = this.createBadgeItem(line, threads);\n gutter.appendChild(item);\n } else if (this.opts.interactive) {\n const item = this.createAddItem(line);\n gutter.appendChild(item);\n }\n }\n }\n\n private primaryGutter(): HTMLDivElement | null {\n return this.gutterLeft ?? this.gutterRight;\n }\n\n private shouldExpandRange(el: HTMLElement): boolean {\n return el.tagName === \"BLOCKQUOTE\" || el.tagName === \"PRE\";\n }\n\n private addVisibleLinesForElement(el: HTMLElement, seen: Set<number>): void {\n const line = parseInt(el.dataset.mrsfLine ?? \"\", 10);\n const startLine = parseInt(el.dataset.mrsfStartLine ?? \"\", 10);\n const endLine = parseInt(el.dataset.mrsfEndLine ?? \"\", 10);\n\n if (el.tagName === \"PRE\" && !isNaN(startLine) && !isNaN(endLine) && endLine > startLine) {\n const visibleStart = startLine + 1;\n const visibleEnd = endLine - 1;\n for (let currentLine = visibleStart; currentLine <= visibleEnd; currentLine++) {\n seen.add(currentLine);\n }\n return;\n }\n\n if (!isNaN(line)) {\n seen.add(line);\n }\n\n if (\n this.shouldExpandRange(el) &&\n !isNaN(startLine) &&\n !isNaN(endLine) &&\n endLine > startLine\n ) {\n for (let currentLine = startLine; currentLine <= endLine; currentLine++) {\n seen.add(currentLine);\n }\n }\n }\n\n /** Collect all unique line numbers from data-mrsf-line elements, expanding multi-line ranges. */\n private collectLines(): number[] {\n const els = this.container.querySelectorAll<HTMLElement>(\"[data-mrsf-line]\");\n const seen = new Set<number>();\n for (const el of els) {\n if (el.tagName === \"SCRIPT\") continue;\n this.addVisibleLinesForElement(el, seen);\n }\n\n return [...seen].sort((a, b) => a - b);\n }\n\n private resolveThreadDisplayLine(thread: CommentThread, availableLines: Set<number>): number | null {\n const startLine = thread.comment.line;\n if (startLine == null) return null;\n\n const endLine = thread.comment.end_line != null && thread.comment.end_line >= startLine\n ? thread.comment.end_line\n : startLine;\n\n for (let currentLine = startLine; currentLine <= endLine; currentLine++) {\n if (availableLines.has(currentLine)) {\n return currentLine;\n }\n }\n\n return null;\n }\n\n private collectThreadDisplayMap(lines: number[]): Map<number, CommentThread[]> {\n const availableLines = new Set(lines);\n const displayMap = new Map<number, CommentThread[]>();\n\n for (const threads of this.threads.values()) {\n for (const thread of threads) {\n const displayLine = this.resolveThreadDisplayLine(thread, availableLines);\n if (displayLine == null) continue;\n\n const existing = displayMap.get(displayLine) ?? [];\n existing.push(thread);\n displayMap.set(displayLine, existing);\n }\n }\n\n return displayMap;\n }\n\n private createBadgeItem(line: number, threads: CommentThread[]): HTMLDivElement {\n const item = document.createElement(\"div\");\n item.className = \"mrsf-gutter-item\";\n item.dataset.mrsfGutterLine = String(line);\n\n const displayThreads = this.orderThreadsForDisplay(threads);\n\n const total = threads.reduce((n, t) => n + 1 + t.replies.length, 0);\n const allResolved = threads.every((t) => t.comment.resolved);\n const highestSeverity = threads.reduce<string | null>((sev, t) => {\n if (t.comment.severity === \"high\" || sev === \"high\") return \"high\";\n if (t.comment.severity === \"medium\" || sev === \"medium\") return \"medium\";\n if (t.comment.severity === \"low\" || sev === \"low\") return \"low\";\n return sev;\n }, null);\n\n const classes = [\"mrsf-badge\"];\n if (allResolved) classes.push(\"mrsf-badge-resolved\");\n if (highestSeverity === \"high\" || highestSeverity === \"medium\") {\n classes.push(`mrsf-badge-severity-${highestSeverity}`);\n }\n\n const presentation = resolveMrsfGutterBadgePresentation(\n {\n line,\n commentCount: total,\n threadCount: threads.length,\n resolvedState: allResolved ? \"resolved\" : threads.some((thread) => thread.comment.resolved) ? \"mixed\" : \"open\",\n highestSeverity,\n isActive: this.activeTooltip?.parentElement === item,\n },\n this.opts.gutterRenderers.badge,\n );\n const badge = document.createElement(\"span\");\n badge.className = classes.join(\" \");\n if (presentation.className) {\n badge.classList.add(...presentation.className.split(/\\s+/).filter(Boolean));\n }\n badge.dataset.mrsfLine = String(line);\n badge.dataset.mrsfAction = \"navigate\";\n badge.dataset.mrsfCommentId = displayThreads[0].comment.id;\n badge.tabIndex = 0;\n badge.textContent = presentation.label;\n badge.title = presentation.title;\n badge.setAttribute(\"aria-label\", presentation.ariaLabel);\n for (const [name, value] of Object.entries(presentation.attributes ?? {})) {\n badge.setAttribute(name, value);\n }\n badge.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.toggleTooltip(item, line, displayThreads);\n });\n\n item.appendChild(badge);\n\n return item;\n }\n\n private createAddItem(line: number): HTMLDivElement {\n const item = document.createElement(\"div\");\n item.className = \"mrsf-gutter-item\";\n item.dataset.mrsfGutterLine = String(line);\n\n const addBtn = this.createAddButton(line);\n item.appendChild(addBtn);\n return item;\n }\n\n private createAddButton(line: number): HTMLButtonElement {\n const presentation = resolveMrsfGutterAddButtonPresentation(\n {\n line,\n isActive: false,\n },\n this.opts.gutterRenderers.addButton,\n );\n const btn = document.createElement(\"button\");\n btn.className = \"mrsf-gutter-add\";\n if (presentation.className) {\n btn.classList.add(...presentation.className.split(/\\s+/).filter(Boolean));\n }\n btn.type = \"button\";\n btn.dataset.mrsfAction = \"add\";\n btn.dataset.mrsfLine = String(line);\n btn.dataset.mrsfStartLine = String(line);\n btn.dataset.mrsfEndLine = String(line);\n btn.title = presentation.title;\n btn.setAttribute(\"aria-label\", presentation.ariaLabel);\n btn.textContent = presentation.label;\n for (const [name, value] of Object.entries(presentation.attributes ?? {})) {\n btn.setAttribute(name, value);\n }\n return btn;\n }\n\n // \u2500\u2500 Positioning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Measure [data-mrsf-line] elements and set gutter item Y offsets. */\n positionGutterItems(): void {\n const containerRect = this.container.getBoundingClientRect();\n\n const gutters = [this.gutterLeft, this.gutterRight].filter(Boolean) as HTMLDivElement[];\n for (const gutter of gutters) {\n const items = gutter.querySelectorAll<HTMLDivElement>(\".mrsf-gutter-item\");\n const positioned: Array<{ item: HTMLDivElement; target: HTMLElement; kind: \"badge\" | \"add\" }> = [];\n for (const item of items) {\n const line = parseInt(item.dataset.mrsfGutterLine!, 10);\n // For expanded multi-line elements, find the rendered content element whose line data contains this line.\n let target = this.findDirectElementForLine(line);\n if (!target) {\n target = this.findElementForLine(line);\n }\n if (!target) {\n item.style.display = \"none\";\n continue;\n }\n const top = this.calculateItemTop(target, line, containerRect);\n item.style.top = `${top}px`;\n item.style.display = \"\";\n positioned.push({\n item,\n target,\n kind: item.querySelector(\".mrsf-badge\") ? \"badge\" : \"add\",\n });\n }\n\n this.suppressAddItemsThatShareBadgeTargets(positioned);\n }\n }\n\n private suppressAddItemsThatShareBadgeTargets(\n positioned: Array<{ item: HTMLDivElement; target: HTMLElement; kind: \"badge\" | \"add\" }>,\n ): void {\n const badgeTargets = new Set(\n positioned\n .filter((entry) => entry.kind === \"badge\")\n .map((entry) => entry.target),\n );\n\n for (const entry of positioned) {\n if (entry.kind === \"add\" && badgeTargets.has(entry.target)) {\n entry.item.style.display = \"none\";\n }\n }\n }\n\n private handleMutations(records: MutationRecord[]): void {\n if (!records.some((record) => this.isExternalContentMutation(record))) {\n return;\n }\n this.queueRefresh();\n }\n\n private isExternalContentMutation(record: MutationRecord): boolean {\n const target = record.target instanceof Node ? record.target : null;\n if (target && this.isControllerOwnedNode(target)) {\n return false;\n }\n\n for (const node of [...record.addedNodes, ...record.removedNodes]) {\n if (!this.isControllerOwnedNode(node)) {\n return true;\n }\n }\n\n return record.type === \"characterData\";\n }\n\n private isControllerOwnedNode(node: Node): boolean {\n if (!(node instanceof Element)) {\n return false;\n }\n return Boolean(\n node.closest(\".mrsf-gutter\") ||\n node.closest(\".mrsf-orphaned-section\") ||\n node.closest(\"script[type=\\\"application/mrsf+json\\\"]\"),\n );\n }\n\n private queueRefresh(): void {\n if (this.refreshQueued) return;\n this.refreshQueued = true;\n queueMicrotask(() => {\n this.refreshQueued = false;\n this.refresh();\n });\n }\n\n private findDirectElementForLine(line: number): HTMLElement | null {\n const els = this.container.querySelectorAll<HTMLElement>(`[data-mrsf-line=\"${line}\"]`);\n for (const el of els) {\n if (el.tagName === \"SCRIPT\") continue;\n if (el.closest(\".mrsf-gutter\")) continue;\n if (el.classList.contains(\"mrsf-gutter-item\")) continue;\n return el;\n }\n return null;\n }\n\n private calculateItemTop(target: HTMLElement, line: number, containerRect: DOMRect): number {\n const targetRect = target.getBoundingClientRect();\n const rangeTop = targetRect.top - containerRect.top + this.container.scrollTop;\n\n if (target.tagName !== \"PRE\") {\n return rangeTop;\n }\n\n const startLine = parseInt(target.dataset.mrsfStartLine ?? \"\", 10);\n const endLine = parseInt(target.dataset.mrsfEndLine ?? \"\", 10);\n const visibleStart = startLine + 1;\n const visibleEnd = endLine - 1;\n const visibleLineCount = visibleEnd - visibleStart + 1;\n\n if (\n isNaN(startLine) ||\n isNaN(endLine) ||\n visibleLineCount <= 0 ||\n line < visibleStart ||\n line > visibleEnd\n ) {\n return rangeTop;\n }\n\n const styles = window.getComputedStyle(target);\n const paddingTop = parseFloat(styles.paddingTop) || 0;\n const paddingBottom = parseFloat(styles.paddingBottom) || 0;\n const contentHeight = Math.max(targetRect.height - paddingTop - paddingBottom, 0);\n\n let lineHeight = parseFloat(styles.lineHeight);\n if (!Number.isFinite(lineHeight) || lineHeight <= 0) {\n lineHeight = visibleLineCount > 0 ? contentHeight / visibleLineCount : 0;\n }\n\n return rangeTop + paddingTop + (line - visibleStart) * lineHeight;\n }\n\n /**\n * Find the element whose start-line/end-line range contains the given line.\n * Used for positioning gutter items on expanded multi-line elements.\n */\n private findElementForLine(line: number): HTMLElement | null {\n const els = this.container.querySelectorAll<HTMLElement>(\"[data-mrsf-start-line][data-mrsf-end-line]\");\n for (const el of els) {\n if (el.tagName === \"SCRIPT\") continue;\n const start = parseInt(el.dataset.mrsfStartLine!, 10);\n const end = parseInt(el.dataset.mrsfEndLine!, 10);\n if (!isNaN(start) && !isNaN(end) && line >= start && line <= end) {\n return el;\n }\n }\n return null;\n }\n\n // \u2500\u2500 Orphaned comments section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Render orphaned comments (whose line doesn't match any DOM element)\n * in a dedicated section at the bottom of the container.\n */\n private renderOrphanedSection(): void {\n const lines = this.collectLines();\n const lineSet = new Set(lines);\n const orphanedThreads: CommentThread[] = [];\n\n for (const threads of this.threads.values()) {\n for (const thread of threads) {\n if (this.resolveThreadDisplayLine(thread, lineSet) == null) {\n orphanedThreads.push(thread);\n }\n }\n }\n\n if (orphanedThreads.length === 0) return;\n\n const section = document.createElement(\"div\");\n section.className = \"mrsf-orphaned-section\";\n\n const heading = document.createElement(\"div\");\n heading.className = \"mrsf-orphaned-heading\";\n heading.textContent = `Orphaned Comments (${orphanedThreads.length})`;\n section.appendChild(heading);\n\n const interactive = this.opts.interactive;\n for (const thread of this.orderThreadsForDisplay(orphanedThreads)) {\n const wrapper = document.createElement(\"div\");\n wrapper.className = interactive\n ? \"mrsf-orphaned-thread mrsf-interactive\"\n : \"mrsf-orphaned-thread\";\n wrapper.innerHTML = renderThreadHtml(thread, interactive);\n section.appendChild(wrapper);\n }\n\n this.container.appendChild(section);\n this.orphanedSection = section;\n }\n\n // \u2500\u2500 Inline text highlights \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * For comments with `selected_text`, find the matching text in the DOM\n * and wrap it in a `<mark class=\"mrsf-inline-highlight\">` element with\n * hover/click behaviour to show the comment tooltip.\n */\n private renderInlineHighlights(): void {\n if (!this.opts.inlineHighlights) return;\n\n const lineSet = new Set(this.collectLines());\n\n for (const threads of this.threads.values()) {\n for (const thread of threads) {\n const comment = thread.comment;\n if (!comment.selected_text) continue;\n\n const displayLine = this.resolveThreadDisplayLine(thread, lineSet);\n if (displayLine == null) continue;\n\n const el = this.container.querySelector<HTMLElement>(\n `[data-mrsf-line=\"${displayLine}\"]:not(script):not(.mrsf-gutter):not(.mrsf-gutter-item)`,\n );\n if (!el) continue;\n\n this.wrapSelectedText(el, comment.selected_text, thread, displayLine);\n }\n }\n }\n\n /**\n * Strip common inline markdown syntax so `selected_text` from source\n * can be matched against rendered text content.\n */\n private static stripInlineMarkdown(text: string): string {\n let s = text;\n // Backtick code spans: `code` \u2192 code\n s = s.replace(/`([^`]+)`/g, \"$1\");\n // Bold: **text** or __text__\n s = s.replace(/\\*\\*(.+?)\\*\\*/g, \"$1\");\n s = s.replace(/__(.+?)__/g, \"$1\");\n // Italic: *text* or _text_\n s = s.replace(/\\*(.+?)\\*/g, \"$1\");\n s = s.replace(/_(.+?)_/g, \"$1\");\n // Strikethrough: ~~text~~\n s = s.replace(/~~(.+?)~~/g, \"$1\");\n return s;\n }\n\n /**\n * Walk text nodes inside `root` to find `text`, then wrap the matching\n * range in a `<mark>` element. Falls back to markdown-stripped matching.\n */\n private wrapSelectedText(\n root: HTMLElement,\n selectedText: string,\n thread: CommentThread,\n displayLine: number,\n ): void {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n let accumulated = \"\";\n const textNodes: { node: Text; start: number; end: number }[] = [];\n\n let node: Text | null;\n while ((node = walker.nextNode() as Text | null)) {\n const start = accumulated.length;\n accumulated += node.textContent || \"\";\n textNodes.push({ node, start, end: accumulated.length });\n }\n\n // Try exact match first, then stripped markdown\n let matchStart = accumulated.indexOf(selectedText);\n let matchLen = selectedText.length;\n if (matchStart === -1) {\n const stripped = MrsfController.stripInlineMarkdown(selectedText);\n if (stripped !== selectedText) {\n matchStart = accumulated.indexOf(stripped);\n matchLen = stripped.length;\n }\n }\n if (matchStart === -1) return;\n\n const matchEnd = matchStart + matchLen;\n\n // Build a Range spanning the matched text nodes\n const range = document.createRange();\n let startSet = false;\n\n for (const tn of textNodes) {\n if (!startSet && tn.end > matchStart) {\n range.setStart(tn.node, matchStart - tn.start);\n startSet = true;\n }\n if (startSet && tn.end >= matchEnd) {\n range.setEnd(tn.node, matchEnd - tn.start);\n break;\n }\n }\n\n if (!startSet) return;\n\n const mark = document.createElement(\"mark\");\n mark.className = \"mrsf-inline-highlight\";\n mark.dataset.mrsfCommentId = thread.comment.id;\n mark.dataset.mrsfLine = String(displayLine);\n\n try {\n range.surroundContents(mark);\n } catch {\n // Range crosses element boundaries \u2014 extract and re-insert\n const fragment = range.extractContents();\n mark.appendChild(fragment);\n range.insertNode(mark);\n }\n\n this.inlineMarks.push(mark);\n\n // Hover shows tooltip inline\n mark.addEventListener(\"mouseenter\", () => {\n this.showInlineTooltip(mark, thread);\n });\n mark.addEventListener(\"mouseleave\", (e) => {\n // Don't hide if moving into the tooltip itself\n const related = (e as MouseEvent).relatedTarget as HTMLElement | null;\n if (related && this.inlineTooltipEl?.contains(related)) return;\n this.scheduleHideInlineTooltip();\n });\n\n // Click toggles tooltip (for touch / accessibility)\n mark.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (this.inlineTooltipEl && this.inlineTooltipEl.dataset.mrsfForMark === thread.comment.id) {\n this.hideInlineTooltip();\n } else {\n this.showInlineTooltip(mark, thread);\n }\n });\n }\n\n private showInlineTooltip(mark: HTMLElement, thread: CommentThread): void {\n this.hideInlineTooltip();\n\n const tooltip = document.createElement(\"div\");\n tooltip.className = this.opts.interactive\n ? \"mrsf-inline-tooltip mrsf-interactive mrsf-tooltip-visible\"\n : \"mrsf-inline-tooltip mrsf-tooltip-visible\";\n tooltip.dataset.mrsfForMark = thread.comment.id;\n tooltip.innerHTML = renderThreadHtml(thread, this.opts.interactive);\n this.applyThemeVariables(tooltip);\n\n // Let user mouse into tooltip without it disappearing\n tooltip.addEventListener(\"mouseenter\", () => {\n this.cancelHideInlineTooltip();\n });\n tooltip.addEventListener(\"mouseleave\", () => {\n this.hideInlineTooltip();\n });\n\n // Append to body with fixed positioning to avoid clipping\n document.body.appendChild(tooltip);\n this.inlineTooltipEl = tooltip;\n\n // Position relative to the mark element\n const rect = mark.getBoundingClientRect();\n const margin = 4;\n const tooltipH = tooltip.offsetHeight;\n\n // Prefer below; flip above if not enough space at bottom\n if (rect.bottom + margin + tooltipH > window.innerHeight) {\n tooltip.style.top = `${rect.top - tooltipH - margin}px`;\n } else {\n tooltip.style.top = `${rect.bottom + margin}px`;\n }\n tooltip.style.left = `${Math.max(0, rect.left)}px`;\n }\n\n private applyThemeVariables(el: HTMLElement): void {\n const styles = window.getComputedStyle(this.container);\n const themeVars = [\n \"--mrsf-accent\",\n \"--mrsf-badge-bg\",\n \"--mrsf-badge-fg\",\n \"--mrsf-badge-resolved-bg\",\n \"--mrsf-add-bg\",\n \"--mrsf-add-fg\",\n \"--mrsf-add-border\",\n \"--mrsf-tooltip-bg\",\n \"--mrsf-tooltip-fg\",\n \"--mrsf-tooltip-border\",\n \"--mrsf-highlight-bg\",\n \"--mrsf-highlight-border\",\n \"--mrsf-severity-high\",\n \"--mrsf-severity-medium\",\n \"--mrsf-severity-low\",\n \"--mrsf-font-family\",\n ];\n\n for (const name of themeVars) {\n const value = styles.getPropertyValue(name).trim();\n if (value) {\n el.style.setProperty(name, value);\n }\n }\n }\n\n private hideInlineTimeout: ReturnType<typeof setTimeout> | null = null;\n\n private scheduleHideInlineTooltip(): void {\n this.hideInlineTimeout = setTimeout(() => this.hideInlineTooltip(), 120);\n }\n\n private cancelHideInlineTooltip(): void {\n if (this.hideInlineTimeout) {\n clearTimeout(this.hideInlineTimeout);\n this.hideInlineTimeout = null;\n }\n }\n\n private hideInlineTooltip(): void {\n this.cancelHideInlineTooltip();\n if (this.inlineTooltipEl) {\n this.inlineTooltipEl.remove();\n this.inlineTooltipEl = null;\n }\n }\n\n /** Remove all inline marks, unwrapping their contents back to text. */\n private removeInlineHighlights(): void {\n this.hideInlineTooltip();\n for (const mark of this.inlineMarks) {\n const parent = mark.parentNode;\n if (!parent) continue;\n while (mark.firstChild) {\n parent.insertBefore(mark.firstChild, mark);\n }\n parent.removeChild(mark);\n }\n this.inlineMarks = [];\n }\n\n // \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private toggleTooltip(anchor: HTMLElement, line: number, threads: CommentThread[]): void {\n // If already visible on this anchor, hide it\n if (this.activeTooltip && this.activeTooltip.parentElement === anchor) {\n this.hideTooltip();\n return;\n }\n this.hideTooltip();\n this.showTooltip(anchor, line, threads);\n }\n\n private showTooltip(anchor: HTMLElement, line: number, threads: CommentThread[]): void {\n const tooltip = document.createElement(\"div\");\n const interactive = this.opts.interactive;\n tooltip.className = interactive\n ? \"mrsf-tooltip mrsf-interactive mrsf-tooltip-visible\"\n : \"mrsf-tooltip mrsf-tooltip-visible\";\n tooltip.dataset.mrsfLine = String(line);\n\n let html = \"\";\n for (const thread of this.orderThreadsForDisplay(threads)) {\n html += renderThreadHtml(thread, interactive);\n }\n if (interactive) {\n html += `<div class=\"mrsf-tooltip-actions\"><button class=\"mrsf-action-btn\" data-mrsf-action=\"add\" data-mrsf-line=\"${line}\" data-mrsf-start-line=\"${line}\" data-mrsf-end-line=\"${line}\">Add comment</button></div>`;\n }\n tooltip.innerHTML = html;\n\n anchor.appendChild(tooltip);\n this.activeTooltip = tooltip;\n }\n\n private hideTooltip(): void {\n if (this.activeTooltip) {\n this.activeTooltip.remove();\n this.activeTooltip = null;\n }\n }\n\n // \u2500\u2500 Click handling \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private handleClick(e: Event): void {\n const target = (e.target as HTMLElement).closest<HTMLElement>(\"[data-mrsf-action]\");\n\n // Close tooltip when clicking outside\n if (!target && this.activeTooltip) {\n const tooltipClick = (e.target as HTMLElement).closest(\".mrsf-tooltip\");\n if (!tooltipClick) {\n this.hideTooltip();\n }\n return;\n }\n if (!target) return;\n\n const action = target.dataset.mrsfAction as MrsfAction | undefined;\n if (!action) return;\n\n const commentId = target.dataset.mrsfCommentId ?? null;\n const lineStr = target.dataset.mrsfLine;\n const line = lineStr ? parseInt(lineStr, 10) : null;\n const selectionText = target.dataset.mrsfSelection ?? this.lastSelectionText ?? null;\n\n const startLineStr = target.dataset.mrsfStartLine;\n const endLineStr = target.dataset.mrsfEndLine;\n const startColStr = target.dataset.mrsfStartColumn;\n const endColStr = target.dataset.mrsfEndColumn;\n const startLine = startLineStr ? parseInt(startLineStr, 10) : (line ?? null);\n const endLine = endLineStr ? parseInt(endLineStr, 10) : (line ?? null);\n const startColumn = startColStr ? parseInt(startColStr, 10) : null;\n const endColumn = endColStr ? parseInt(endColStr, 10) : null;\n\n e.preventDefault();\n e.stopPropagation();\n\n const detail: MrsfActionDetail = {\n commentId,\n line,\n action,\n selectionText,\n start_line: startLine,\n end_line: endLine,\n start_column: startColumn,\n end_column: endColumn,\n };\n\n if (action === \"add\" || action === \"edit\" || action === \"reply\") {\n if (action === \"add\") {\n this.hideFloatingAddButton();\n }\n this.openForm(action, detail);\n return;\n }\n\n if (action === \"resolve\" || action === \"unresolve\" || action === \"delete\") {\n this.openConfirm(action, detail);\n return;\n }\n\n document.dispatchEvent(\n new CustomEvent(`mrsf:${action}`, { detail, bubbles: true }),\n );\n }\n\n // \u2500\u2500 Selection handling \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private handleSelectionChange(): void {\n const sel = document.getSelection();\n if (!sel || sel.isCollapsed || sel.rangeCount === 0) {\n this.hideFloatingAddButton();\n return;\n }\n\n const text = sel.toString().trim();\n if (!text) {\n this.hideFloatingAddButton();\n return;\n }\n\n const range = sel.getRangeAt(0);\n if (!this.selectionBelongsToContainer(range)) {\n this.hideFloatingAddButton();\n return;\n }\n\n const rect = range.getBoundingClientRect();\n if (!rect || (rect.width === 0 && rect.height === 0)) {\n this.hideFloatingAddButton();\n return;\n }\n\n const startAnchor = this.findSelectionAnchor(range.startContainer);\n const endAnchor = this.findSelectionAnchor(range.endContainer);\n if (!startAnchor || !endAnchor) {\n this.hideFloatingAddButton();\n return;\n }\n\n const startLineStr = startAnchor?.dataset.mrsfStartLine ?? startAnchor?.dataset.mrsfLine;\n const endLineStr = endAnchor?.dataset.mrsfEndLine ?? endAnchor?.dataset.mrsfLine ?? startLineStr;\n const startLine = startLineStr ? parseInt(startLineStr, 10) : null;\n const endLine = endLineStr ? parseInt(endLineStr, 10) : startLine;\n\n const startColumn = range.startContainer.nodeType === Node.TEXT_NODE\n ? range.startOffset : null;\n const endColumn = range.endContainer.nodeType === Node.TEXT_NODE\n ? range.endOffset : null;\n\n this.showFloatingAddButton(startLine, endLine, startColumn, endColumn, rect, text);\n }\n\n private selectionBelongsToContainer(range: Range): boolean {\n return this.container.contains(range.commonAncestorContainer)\n && this.container.contains(range.startContainer)\n && this.container.contains(range.endContainer);\n }\n\n private findSelectionAnchor(node: Node): HTMLElement | null {\n const element = node instanceof Element ? node : node.parentElement;\n const anchor = element?.closest<HTMLElement>(\"[data-mrsf-line]\") ?? null;\n return anchor && this.container.contains(anchor) ? anchor : null;\n }\n\n private ensureFloatingAddButton(): HTMLButtonElement {\n if (this.floatingAddButton) return this.floatingAddButton;\n const btn = document.createElement(\"button\");\n btn.textContent = \"Add comment\";\n btn.className = \"mrsf-add-inline-button\";\n btn.dataset.mrsfAction = \"add\";\n btn.style.display = \"none\";\n btn.style.position = \"absolute\";\n btn.style.zIndex = \"1200\";\n this.container.appendChild(btn);\n this.floatingAddButton = btn;\n return btn;\n }\n\n private hideFloatingAddButton(): void {\n if (!this.floatingAddButton) return;\n this.floatingAddButton.style.display = \"none\";\n this.floatingAddButton.dataset.mrsfLine = \"\";\n this.floatingAddButton.dataset.mrsfStartLine = \"\";\n this.floatingAddButton.dataset.mrsfEndLine = \"\";\n this.lastSelectionText = null;\n }\n\n private showFloatingAddButton(\n startLine: number | null,\n endLine: number | null,\n startColumn: number | null,\n endColumn: number | null,\n rect: DOMRect,\n selectionText: string,\n ): void {\n const btn = this.ensureFloatingAddButton();\n if (startLine != null) {\n btn.dataset.mrsfLine = String(startLine);\n btn.dataset.mrsfStartLine = String(startLine);\n btn.dataset.mrsfEndLine = String(endLine ?? startLine);\n } else {\n delete btn.dataset.mrsfLine;\n delete btn.dataset.mrsfStartLine;\n delete btn.dataset.mrsfEndLine;\n }\n if (startColumn != null) {\n btn.dataset.mrsfStartColumn = String(startColumn);\n } else {\n delete btn.dataset.mrsfStartColumn;\n }\n if (endColumn != null) {\n btn.dataset.mrsfEndColumn = String(endColumn);\n } else {\n delete btn.dataset.mrsfEndColumn;\n }\n this.lastSelectionText = selectionText;\n\n const margin = 6;\n const containerRect = this.container.getBoundingClientRect();\n btn.style.visibility = \"hidden\";\n btn.style.display = \"block\";\n const width = btn.offsetWidth || 0;\n const height = btn.offsetHeight || 0;\n\n const minTop = this.container.scrollTop;\n const maxTop = Math.max(minTop, minTop + this.container.clientHeight - height);\n const minLeft = this.container.scrollLeft;\n const maxLeft = Math.max(minLeft, minLeft + this.container.clientWidth - width);\n\n const preferredTop = rect.top - containerRect.top + this.container.scrollTop - height - margin;\n const fallbackTop = rect.bottom - containerRect.top + this.container.scrollTop + margin;\n const unclampedTop = preferredTop < minTop ? fallbackTop : preferredTop;\n const unclampedLeft = rect.left - containerRect.left + this.container.scrollLeft;\n\n const top = Math.min(Math.max(unclampedTop, minTop), maxTop);\n const left = Math.min(Math.max(unclampedLeft, minLeft), maxLeft);\n\n btn.style.top = `${top}px`;\n btn.style.left = `${left}px`;\n btn.style.visibility = \"visible\";\n }\n\n // \u2500\u2500 Dialog: form (add/edit/reply) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private injectStyles(): void {\n if (this.styleInjected) return;\n const css = `\n.mrsf-overlay { position: fixed; inset: 0; background: var(--mrsf-dialog-backdrop, rgba(15, 23, 42, 0.28)); z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 12px; }\n.mrsf-dialog { background: var(--mrsf-dialog-bg, var(--mrsf-tooltip-bg, #0f172a)); color: var(--mrsf-dialog-fg, var(--mrsf-tooltip-fg, #e5eefb)); border: 1px solid var(--mrsf-dialog-border, var(--mrsf-tooltip-border, rgba(148, 163, 184, 0.35))); border-radius: 10px; width: min(420px, calc(100vw - 24px)); box-shadow: 0 18px 48px rgba(15, 23, 42, 0.24); font-family: var(--mrsf-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif); font-size: 13px; overflow: hidden; }\n.mrsf-dialog header { padding: 10px 12px; font-weight: 600; line-height: 1.35; border-bottom: 1px solid var(--mrsf-dialog-border, var(--mrsf-tooltip-border, rgba(148, 163, 184, 0.35))); }\n.mrsf-dialog form { padding: 12px; display: flex; flex-direction: column; gap: 10px; }\n.mrsf-dialog-body { padding: 12px; line-height: 1.45; }\n.mrsf-field { display: flex; flex-direction: column; gap: 4px; }\n.mrsf-field label { font-size: 12px; color: var(--mrsf-dialog-muted, color-mix(in srgb, var(--mrsf-dialog-fg, var(--mrsf-tooltip-fg, #e5eefb)) 72%, transparent)); }\n.mrsf-field input, .mrsf-field select, .mrsf-field textarea, .mrsf-field pre { background: var(--mrsf-field-bg, color-mix(in srgb, var(--mrsf-dialog-bg, var(--mrsf-tooltip-bg, #0f172a)) 88%, white)); color: var(--mrsf-dialog-fg, var(--mrsf-tooltip-fg, #e5eefb)); border: 1px solid var(--mrsf-dialog-border, var(--mrsf-tooltip-border, rgba(148, 163, 184, 0.35))); border-radius: 6px; padding: 7px 9px; font-size: 12px; line-height: 1.45; }\n.mrsf-field textarea { min-height: 76px; resize: vertical; }\n.mrsf-field select { min-height: 34px; }\n.mrsf-field pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }\n.mrsf-field input:focus, .mrsf-field select:focus, .mrsf-field textarea:focus { outline: 2px solid color-mix(in srgb, var(--mrsf-accent, #2563eb) 38%, transparent); outline-offset: 1px; }\n.mrsf-actions-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 0; padding: 10px 12px 12px; border-top: 1px solid var(--mrsf-dialog-border, var(--mrsf-tooltip-border, rgba(148, 163, 184, 0.35))); }\n.mrsf-btn { padding: 5px 10px; border-radius: 999px; border: 1px solid var(--mrsf-dialog-border, var(--mrsf-tooltip-border, rgba(148, 163, 184, 0.35))); background: var(--mrsf-button-bg, color-mix(in srgb, var(--mrsf-dialog-bg, var(--mrsf-tooltip-bg, #0f172a)) 82%, white)); color: var(--mrsf-dialog-fg, var(--mrsf-tooltip-fg, #e5eefb)); cursor: pointer; font: inherit; line-height: 1.2; }\n.mrsf-btn-primary { background: var(--mrsf-button-primary-bg, var(--mrsf-accent, #2563eb)); border-color: var(--mrsf-button-primary-bg, var(--mrsf-accent, #2563eb)); color: #fff; }\n.mrsf-helper { font-size: 11px; color: var(--mrsf-dialog-muted, color-mix(in srgb, var(--mrsf-dialog-fg, var(--mrsf-tooltip-fg, #e5eefb)) 72%, transparent)); }\n`;\n const style = document.createElement(\"style\");\n style.textContent = css;\n document.head.appendChild(style);\n this.styleInjected = true;\n }\n\n private closeOverlay(): void {\n if (this.overlayEl?.parentElement) {\n this.overlayEl.parentElement.removeChild(this.overlayEl);\n }\n this.overlayEl = null;\n }\n\n private openForm(action: \"add\" | \"edit\" | \"reply\", detail: MrsfActionDetail): void {\n if ((window as any).mrsfDisableBuiltinUi) return;\n this.injectStyles();\n this.closeOverlay();\n\n const sourceComment = action === \"edit\" ? this.findCommentById(detail.commentId) : null;\n const selText = detail.selectionText ?? sourceComment?.selected_text ?? \"\";\n const line = detail.line ?? detail.start_line ?? sourceComment?.line ?? null;\n const endLine = detail.end_line ?? detail.line ?? sourceComment?.end_line ?? sourceComment?.line ?? null;\n const startCol = detail.start_column ?? sourceComment?.start_column ?? null;\n const endCol = detail.end_column ?? sourceComment?.end_column ?? null;\n\n const overlay = document.createElement(\"div\");\n overlay.className = \"mrsf-overlay\";\n this.applyThemeVariables(overlay);\n\n const dialog = document.createElement(\"div\");\n dialog.className = \"mrsf-dialog\";\n\n const header = document.createElement(\"header\");\n header.textContent =\n action === \"add\" ? \"Add comment\" : action === \"edit\" ? \"Edit comment\" : \"Reply\";\n dialog.appendChild(header);\n\n const form = document.createElement(\"form\");\n\n const field = (labelText: string, inputEl: HTMLElement, helper?: string) => {\n const wrap = document.createElement(\"div\");\n wrap.className = \"mrsf-field\";\n const label = document.createElement(\"label\");\n label.textContent = labelText;\n wrap.appendChild(label);\n wrap.appendChild(inputEl);\n if (helper) {\n const h = document.createElement(\"div\");\n h.className = \"mrsf-helper\";\n h.textContent = helper;\n wrap.appendChild(h);\n }\n form.appendChild(wrap);\n };\n\n const textArea = document.createElement(\"textarea\");\n textArea.name = \"text\";\n textArea.required = true;\n textArea.value = action === \"edit\" ? (sourceComment?.text ?? \"\") : \"\";\n field(\"Comment text\", textArea);\n\n const typeSelect = document.createElement(\"select\");\n typeSelect.name = \"type\";\n [\"\", \"suggestion\", \"issue\", \"question\", \"accuracy\", \"style\", \"clarity\"].forEach((t) => {\n const opt = document.createElement(\"option\");\n opt.value = t;\n opt.textContent = t || \"(none)\";\n typeSelect.appendChild(opt);\n });\n typeSelect.value = action === \"edit\" ? (sourceComment?.type ?? \"\") : \"\";\n field(\"Type\", typeSelect, \"Optional\");\n\n const severitySelect = document.createElement(\"select\");\n severitySelect.name = \"severity\";\n [\"\", \"low\", \"medium\", \"high\"].forEach((s) => {\n const opt = document.createElement(\"option\");\n opt.value = s;\n opt.textContent = s || \"(none)\";\n severitySelect.appendChild(opt);\n });\n severitySelect.value = action === \"edit\" ? (sourceComment?.severity ?? \"\") : \"\";\n field(\"Severity\", severitySelect, \"Optional\");\n\n if (selText) {\n const pre = document.createElement(\"pre\");\n pre.textContent = selText;\n field(\"Selected text\", pre as unknown as HTMLElement, \"Captured automatically\");\n }\n\n const actions = document.createElement(\"div\");\n actions.className = \"mrsf-actions-row\";\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"mrsf-btn\";\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"click\", () => this.closeOverlay());\n actions.appendChild(cancelBtn);\n\n const submitBtn = document.createElement(\"button\");\n submitBtn.type = \"submit\";\n submitBtn.className = \"mrsf-btn mrsf-btn-primary\";\n submitBtn.textContent = action === \"add\" ? \"Add\" : action === \"reply\" ? \"Reply\" : \"Save\";\n actions.appendChild(submitBtn);\n\n form.appendChild(actions);\n\n form.addEventListener(\"submit\", (ev) => {\n ev.preventDefault();\n const detailOut: MrsfSubmitDetail = {\n action,\n commentId: detail.commentId,\n text: textArea.value.trim(),\n type: typeSelect.value || null,\n severity: (severitySelect.value as MrsfSubmitDetail[\"severity\"]) || null,\n line,\n end_line: endLine,\n start_column: startCol,\n end_column: endCol,\n selection_text: selText || null,\n };\n document.dispatchEvent(new CustomEvent(\"mrsf:submit\", { detail: detailOut, bubbles: true }));\n this.closeOverlay();\n });\n\n dialog.appendChild(form);\n overlay.appendChild(dialog);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) this.closeOverlay();\n });\n\n document.body.appendChild(overlay);\n this.overlayEl = overlay;\n }\n\n // \u2500\u2500 Dialog: confirm (resolve / unresolve / delete) \u2500\u2500\u2500\u2500\u2500\u2500\n\n private openConfirm(action: \"resolve\" | \"unresolve\" | \"delete\", detail: MrsfActionDetail): void {\n if ((window as any).mrsfDisableBuiltinUi) return;\n this.injectStyles();\n this.closeOverlay();\n\n const overlay = document.createElement(\"div\");\n overlay.className = \"mrsf-overlay\";\n this.applyThemeVariables(overlay);\n\n const dialog = document.createElement(\"div\");\n dialog.className = \"mrsf-dialog\";\n\n const header = document.createElement(\"header\");\n header.textContent = action === \"delete\" ? \"Delete comment\" : \"Change status\";\n dialog.appendChild(header);\n\n const body = document.createElement(\"div\");\n body.className = \"mrsf-dialog-body\";\n body.textContent =\n action === \"delete\"\n ? \"Delete this comment?\"\n : action === \"resolve\"\n ? \"Mark this comment as resolved?\"\n : \"Mark this comment as unresolved?\";\n dialog.appendChild(body);\n\n const actions = document.createElement(\"div\");\n actions.className = \"mrsf-actions-row\";\n\n const cancelBtn = document.createElement(\"button\");\n cancelBtn.type = \"button\";\n cancelBtn.className = \"mrsf-btn\";\n cancelBtn.textContent = \"Cancel\";\n cancelBtn.addEventListener(\"click\", () => this.closeOverlay());\n actions.appendChild(cancelBtn);\n\n const confirmBtn = document.createElement(\"button\");\n confirmBtn.type = \"button\";\n confirmBtn.className = \"mrsf-btn mrsf-btn-primary\";\n confirmBtn.textContent = action === \"delete\" ? \"Delete\" : \"Confirm\";\n confirmBtn.addEventListener(\"click\", () => {\n const detailOut: MrsfSubmitDetail = {\n action,\n commentId: detail.commentId,\n text: \"\",\n type: null,\n severity: null,\n line: detail.line,\n end_line: detail.end_line ?? detail.line ?? null,\n start_column: detail.start_column ?? null,\n end_column: detail.end_column ?? null,\n selection_text: detail.selectionText ?? null,\n };\n document.dispatchEvent(new CustomEvent(\"mrsf:submit\", { detail: detailOut, bubbles: true }));\n this.closeOverlay();\n });\n actions.appendChild(confirmBtn);\n\n dialog.appendChild(actions);\n\n overlay.appendChild(dialog);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) this.closeOverlay();\n });\n\n document.body.appendChild(overlay);\n this.overlayEl = overlay;\n }\n}\n\n// \u2500\u2500 Auto-init convenience \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Scan for containers with [data-mrsf-controller] and auto-init.\n\nconst controllerRegistry = new Set<MrsfController>();\nlet autoInitDone = false;\n\nexport function refreshAll(): void {\n for (const controller of controllerRegistry) {\n controller.refresh();\n }\n}\n\nexport function autoInit(): void {\n if (autoInitDone) return;\n autoInitDone = true;\n\n const containers = document.querySelectorAll<HTMLElement>(\"[data-mrsf-controller]\");\n for (const container of containers) {\n const pos = container.dataset.mrsfGutterPosition as MrsfControllerOptions[\"gutterPosition\"] | undefined;\n const interactive = container.dataset.mrsfInteractive === \"true\";\n new MrsfController(container, { gutterPosition: pos ?? \"right\", interactive });\n }\n}\n\n// Auto-init on DOMContentLoaded if in browser context\nif (typeof document !== \"undefined\") {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", autoInit);\n } else {\n autoInit();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/gutter.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type MrsfResolvedState = "open" | "resolved" | "mixed";
|
|
2
|
+
export interface MrsfGutterBadgePresentation {
|
|
3
|
+
icon: string;
|
|
4
|
+
countText: string;
|
|
5
|
+
label: string;
|
|
6
|
+
title: string;
|
|
7
|
+
ariaLabel: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
attributes?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export interface MrsfGutterBadgeContext {
|
|
12
|
+
line: number;
|
|
13
|
+
commentCount: number;
|
|
14
|
+
threadCount: number;
|
|
15
|
+
resolvedState: MrsfResolvedState;
|
|
16
|
+
highestSeverity: string | null;
|
|
17
|
+
isActive: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface MrsfGutterBadgeRenderContext extends MrsfGutterBadgeContext {
|
|
20
|
+
defaultPresentation: MrsfGutterBadgePresentation;
|
|
21
|
+
}
|
|
22
|
+
export interface MrsfGutterAddButtonPresentation {
|
|
23
|
+
label: string;
|
|
24
|
+
title: string;
|
|
25
|
+
ariaLabel: string;
|
|
26
|
+
className?: string;
|
|
27
|
+
attributes?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
export interface MrsfGutterAddButtonContext {
|
|
30
|
+
line: number;
|
|
31
|
+
isActive: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface MrsfGutterAddButtonRenderContext extends MrsfGutterAddButtonContext {
|
|
34
|
+
defaultPresentation: MrsfGutterAddButtonPresentation;
|
|
35
|
+
}
|
|
36
|
+
export interface MrsfGutterRenderers {
|
|
37
|
+
badge?: (context: MrsfGutterBadgeRenderContext) => Partial<MrsfGutterBadgePresentation> | null | undefined;
|
|
38
|
+
addButton?: (context: MrsfGutterAddButtonRenderContext) => Partial<MrsfGutterAddButtonPresentation> | null | undefined;
|
|
39
|
+
}
|
|
40
|
+
export interface MrsfBadgeSource {
|
|
41
|
+
commentCount: number;
|
|
42
|
+
threadCount: number;
|
|
43
|
+
resolvedState: MrsfResolvedState;
|
|
44
|
+
}
|
|
45
|
+
export declare function formatMrsfCount(count: number, max?: number): string;
|
|
46
|
+
export declare function createMrsfGutterBadgePresentation(context: MrsfGutterBadgeContext): MrsfGutterBadgePresentation;
|
|
47
|
+
export declare function createMrsfGutterAddButtonPresentation(_context: MrsfGutterAddButtonContext): MrsfGutterAddButtonPresentation;
|
|
48
|
+
export declare function resolveMrsfGutterBadgePresentation(context: MrsfGutterBadgeContext, renderer?: MrsfGutterRenderers["badge"]): MrsfGutterBadgePresentation;
|
|
49
|
+
export declare function resolveMrsfGutterAddButtonPresentation(context: MrsfGutterAddButtonContext, renderer?: MrsfGutterRenderers["addButton"]): MrsfGutterAddButtonPresentation;
|
|
50
|
+
//# sourceMappingURL=gutter.d.ts.map
|
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared HTML rendering helpers for MRSF rendering plugins.
|
|
3
|
+
*
|
|
4
|
+
* These produce the HTML strings used in tooltips, badges, and comment
|
|
5
|
+
* rendering. Both the markdown-it and rehype plugins use these functions
|
|
6
|
+
* to ensure identical visual output.
|
|
7
|
+
*/
|
|
8
|
+
import type { CommentThread, SlimComment } from "./types.js";
|
|
9
|
+
export declare function escapeHtml(str: string): string;
|
|
10
|
+
export declare function formatTime(iso: string | null): string;
|
|
11
|
+
export declare function renderCommentHtml(comment: SlimComment, isReply: boolean, interactive: boolean): string;
|
|
12
|
+
export declare function renderThreadHtml(thread: CommentThread, interactive: boolean): string;
|
|
13
|
+
//# sourceMappingURL=html.d.ts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MrsfMarpPlugin } from "./shared.js";
|
|
2
|
+
export type { MrsfPluginOptions, SlimComment, CommentThread, LineMap, CommentLoader } from "./types.js";
|
|
3
|
+
export type { MrsfMarpPlugin, MarpitLike, MarpitPluginContext } from "./shared.js";
|
|
4
|
+
export declare const mrsfPlugin: MrsfMarpPlugin;
|
|
5
|
+
export default mrsfPlugin;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseSidecarContent } from "@mrsf/cli";
|
|
5
|
+
|
|
6
|
+
// ../shared/dist/comments.js
|
|
7
|
+
function toSlimComments(doc) {
|
|
8
|
+
return doc.comments.map((c) => ({
|
|
9
|
+
id: c.id,
|
|
10
|
+
author: c.author || "Unknown",
|
|
11
|
+
text: c.text || "",
|
|
12
|
+
line: c.line ?? null,
|
|
13
|
+
x_page: typeof c.x_page === "number" ? c.x_page : null,
|
|
14
|
+
end_line: c.end_line ?? null,
|
|
15
|
+
start_column: c.start_column ?? null,
|
|
16
|
+
end_column: c.end_column ?? null,
|
|
17
|
+
selected_text: c.selected_text || null,
|
|
18
|
+
resolved: !!c.resolved,
|
|
19
|
+
reply_to: c.reply_to || null,
|
|
20
|
+
severity: c.severity || null,
|
|
21
|
+
type: c.type || null,
|
|
22
|
+
timestamp: c.timestamp || null
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
function groupByLine(comments) {
|
|
26
|
+
const rootComments = comments.filter((c) => !c.reply_to && c.line != null);
|
|
27
|
+
const replies = comments.filter((c) => c.reply_to);
|
|
28
|
+
const replyMap = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const r of replies) {
|
|
30
|
+
const list = replyMap.get(r.reply_to) || [];
|
|
31
|
+
list.push(r);
|
|
32
|
+
replyMap.set(r.reply_to, list);
|
|
33
|
+
}
|
|
34
|
+
const lineMap = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const c of rootComments) {
|
|
36
|
+
const line = c.line;
|
|
37
|
+
const threads = lineMap.get(line) || [];
|
|
38
|
+
threads.push({
|
|
39
|
+
comment: c,
|
|
40
|
+
replies: replyMap.get(c.id) || []
|
|
41
|
+
});
|
|
42
|
+
lineMap.set(line, threads);
|
|
43
|
+
}
|
|
44
|
+
return lineMap;
|
|
45
|
+
}
|
|
46
|
+
function resolveComments(loader, options, env) {
|
|
47
|
+
const showResolved = options.showResolved ?? true;
|
|
48
|
+
const doc = loader(options, env);
|
|
49
|
+
if (!doc || !doc.comments || doc.comments.length === 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
let comments = toSlimComments(doc);
|
|
53
|
+
if (!showResolved) {
|
|
54
|
+
comments = comments.filter((c) => !c.resolved);
|
|
55
|
+
}
|
|
56
|
+
if (comments.length === 0)
|
|
57
|
+
return null;
|
|
58
|
+
const lineMap = groupByLine(comments);
|
|
59
|
+
return { lineMap, comments };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/rules/core.ts
|
|
63
|
+
function buildPageLineMap(tokens) {
|
|
64
|
+
const pageLineMap = /* @__PURE__ */ new Map();
|
|
65
|
+
let pageNumber = 0;
|
|
66
|
+
for (const token of tokens) {
|
|
67
|
+
if (token.type !== "marpit_slide_open" || !token.map) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
pageNumber += 1;
|
|
71
|
+
pageLineMap.set(pageNumber, token.map[0] + 1);
|
|
72
|
+
}
|
|
73
|
+
return pageLineMap;
|
|
74
|
+
}
|
|
75
|
+
function mergePageScopedThreads(lineMap, comments, pageLineMap) {
|
|
76
|
+
const merged = new Map(
|
|
77
|
+
Array.from(lineMap.entries(), ([line, threads]) => [
|
|
78
|
+
line,
|
|
79
|
+
threads.map((thread) => ({
|
|
80
|
+
comment: thread.comment,
|
|
81
|
+
replies: [...thread.replies]
|
|
82
|
+
}))
|
|
83
|
+
])
|
|
84
|
+
);
|
|
85
|
+
const replyMap = /* @__PURE__ */ new Map();
|
|
86
|
+
for (const comment of comments) {
|
|
87
|
+
if (!comment.reply_to) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const replies = replyMap.get(comment.reply_to) || [];
|
|
91
|
+
replies.push(comment);
|
|
92
|
+
replyMap.set(comment.reply_to, replies);
|
|
93
|
+
}
|
|
94
|
+
for (const comment of comments) {
|
|
95
|
+
if (comment.reply_to || comment.line != null || comment.x_page == null) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const displayLine = pageLineMap.get(comment.x_page);
|
|
99
|
+
if (displayLine == null) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const threadComment = {
|
|
103
|
+
...comment,
|
|
104
|
+
line: displayLine
|
|
105
|
+
};
|
|
106
|
+
const threadReplies = (replyMap.get(comment.id) || []).map((reply) => ({
|
|
107
|
+
...reply,
|
|
108
|
+
line: reply.line ?? displayLine
|
|
109
|
+
}));
|
|
110
|
+
const threads = merged.get(displayLine) || [];
|
|
111
|
+
threads.push({
|
|
112
|
+
comment: threadComment,
|
|
113
|
+
replies: threadReplies
|
|
114
|
+
});
|
|
115
|
+
merged.set(displayLine, threads);
|
|
116
|
+
}
|
|
117
|
+
return merged;
|
|
118
|
+
}
|
|
119
|
+
function installCoreRule(md, resolveComments2, options = {}) {
|
|
120
|
+
md.core.ruler.push("mrsf_inject", (state) => {
|
|
121
|
+
const result = resolveComments2(state);
|
|
122
|
+
if (!result) return;
|
|
123
|
+
const pageLineMap = buildPageLineMap(state.tokens);
|
|
124
|
+
const lineMap = mergePageScopedThreads(result.lineMap, result.comments, pageLineMap);
|
|
125
|
+
const tokens = state.tokens;
|
|
126
|
+
const TokenCtor = state.Token;
|
|
127
|
+
const processed = /* @__PURE__ */ new Set();
|
|
128
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
129
|
+
const token = tokens[i];
|
|
130
|
+
const map = token.map;
|
|
131
|
+
if (!map) continue;
|
|
132
|
+
if (token.type === "inline") continue;
|
|
133
|
+
const startLine1 = map[0] + 1;
|
|
134
|
+
const endLine1 = map[1];
|
|
135
|
+
token.attrSet("data-mrsf-line", String(startLine1));
|
|
136
|
+
token.attrSet("data-mrsf-start-line", String(startLine1));
|
|
137
|
+
token.attrSet("data-mrsf-end-line", String(endLine1));
|
|
138
|
+
for (let line0 = map[0]; line0 < map[1]; line0++) {
|
|
139
|
+
const line = line0 + 1;
|
|
140
|
+
if (processed.has(line)) continue;
|
|
141
|
+
processed.add(line);
|
|
142
|
+
const threads = lineMap.get(line);
|
|
143
|
+
if (threads && threads.length > 0) {
|
|
144
|
+
if (options.lineHighlight) {
|
|
145
|
+
const existingClass = token.attrGet("class") || "";
|
|
146
|
+
if (!existingClass.includes("mrsf-line-highlight")) {
|
|
147
|
+
token.attrSet(
|
|
148
|
+
"class",
|
|
149
|
+
existingClass ? `${existingClass} mrsf-line-highlight` : "mrsf-line-highlight"
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
token.attrSet("data-mrsf-line", String(line));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const allThreads = [];
|
|
158
|
+
for (const threads of lineMap.values()) {
|
|
159
|
+
allThreads.push(...threads);
|
|
160
|
+
}
|
|
161
|
+
if (allThreads.length > 0) {
|
|
162
|
+
const scriptToken = new TokenCtor("mrsf_data_script", "", 0);
|
|
163
|
+
scriptToken.meta = { threads: allThreads };
|
|
164
|
+
tokens.push(scriptToken);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/rules/renderer.ts
|
|
170
|
+
function escapeAttribute(value) {
|
|
171
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
172
|
+
}
|
|
173
|
+
function installRendererRules(md, options = {}) {
|
|
174
|
+
md.renderer.rules["mrsf_data_script"] = (tokens, idx) => {
|
|
175
|
+
const { threads } = tokens[idx].meta;
|
|
176
|
+
const payload = JSON.stringify({ threads });
|
|
177
|
+
if (options.dataContainer === "element") {
|
|
178
|
+
const elementId = options.dataElementId || "mrsf-comment-data";
|
|
179
|
+
return `<div id="${escapeAttribute(elementId)}" data-mrsf-json="${escapeAttribute(payload)}" aria-hidden="true"></div>`;
|
|
180
|
+
}
|
|
181
|
+
const data = payload.replace(/</g, "\\u003c");
|
|
182
|
+
return `<script type="application/mrsf+json">${data}</script>`;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/shared.ts
|
|
187
|
+
var patchedRenderers = /* @__PURE__ */ new WeakSet();
|
|
188
|
+
function installMarkdownMrsfPlugin(md, options, loader) {
|
|
189
|
+
installCoreRule(
|
|
190
|
+
md,
|
|
191
|
+
(state) => resolveComments(loader, options, state.env),
|
|
192
|
+
{ lineHighlight: options.lineHighlight ?? false }
|
|
193
|
+
);
|
|
194
|
+
installRendererRules(md, {
|
|
195
|
+
dataContainer: options.dataContainer,
|
|
196
|
+
dataElementId: options.dataElementId
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function createMarpPlugin(loader) {
|
|
200
|
+
return function mrsfPlugin2(context, options = {}) {
|
|
201
|
+
const markdown = context.markdown ?? context.marpit?.markdown;
|
|
202
|
+
if (markdown) {
|
|
203
|
+
installMarkdownMrsfPlugin(markdown, options, loader);
|
|
204
|
+
}
|
|
205
|
+
if (!context.marpit) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
patchRender(context.marpit, options);
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function patchRender(marpit, options) {
|
|
212
|
+
if (patchedRenderers.has(marpit)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const originalRender = marpit.render.bind(marpit);
|
|
216
|
+
marpit.render = (markdown, env) => {
|
|
217
|
+
const result = originalRender(markdown, env);
|
|
218
|
+
const fallbackHtml = Array.isArray(result.html) ? originalRender(markdown, { ...env ?? {}, htmlAsArray: false }).html : null;
|
|
219
|
+
return {
|
|
220
|
+
...result,
|
|
221
|
+
html: normalizeRenderedHtml(result.html, options, typeof fallbackHtml === "string" ? fallbackHtml : null)
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
patchedRenderers.add(marpit);
|
|
225
|
+
}
|
|
226
|
+
var SCRIPT_RE = /<script type="application\/mrsf\+json">[\s\S]*?<\/script>/g;
|
|
227
|
+
function normalizeRenderedHtml(html, options, fallbackHtml = null) {
|
|
228
|
+
if (Array.isArray(html)) {
|
|
229
|
+
let payload2 = null;
|
|
230
|
+
const pages = html.map((pageHtml, index) => {
|
|
231
|
+
const extracted2 = extractPayloads(pageHtml);
|
|
232
|
+
if (extracted2.payloads.length > 0) {
|
|
233
|
+
payload2 = extracted2.payloads[extracted2.payloads.length - 1];
|
|
234
|
+
}
|
|
235
|
+
return annotatePageFragment(extracted2.html, index + 1, options);
|
|
236
|
+
});
|
|
237
|
+
if (!payload2 && fallbackHtml) {
|
|
238
|
+
const fallback = extractPayloads(fallbackHtml);
|
|
239
|
+
payload2 = fallback.payloads[fallback.payloads.length - 1] ?? null;
|
|
240
|
+
}
|
|
241
|
+
if (payload2 && pages.length > 0) {
|
|
242
|
+
pages[pages.length - 1] += payload2;
|
|
243
|
+
}
|
|
244
|
+
return pages;
|
|
245
|
+
}
|
|
246
|
+
const extracted = extractPayloads(html);
|
|
247
|
+
const annotated = annotateSectionTags(extracted.html, options);
|
|
248
|
+
if (extracted.payloads.length === 0) {
|
|
249
|
+
return annotated;
|
|
250
|
+
}
|
|
251
|
+
const payload = extracted.payloads[extracted.payloads.length - 1];
|
|
252
|
+
return insertPayloadIntoContainer(annotated, payload);
|
|
253
|
+
}
|
|
254
|
+
function extractPayloads(html) {
|
|
255
|
+
const payloads = html.match(SCRIPT_RE) ?? [];
|
|
256
|
+
return {
|
|
257
|
+
html: html.replace(SCRIPT_RE, ""),
|
|
258
|
+
payloads
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function annotatePageFragment(html, pageNumber, options) {
|
|
262
|
+
const attrName = resolvePageAttributeName(options);
|
|
263
|
+
return html.replace(/<([a-zA-Z][\w:-]*)(\s|>)/, `<$1 ${attrName}="${pageNumber}"$2`);
|
|
264
|
+
}
|
|
265
|
+
function annotateSectionTags(html, options) {
|
|
266
|
+
if (html.includes("<svg") && html.includes("data-marpit-svg")) {
|
|
267
|
+
return annotateSvgPageTags(html, options);
|
|
268
|
+
}
|
|
269
|
+
const attrName = resolvePageAttributeName(options);
|
|
270
|
+
let pageNumber = 0;
|
|
271
|
+
return html.replace(/<section(\s|>)/g, (_match, suffix) => {
|
|
272
|
+
pageNumber += 1;
|
|
273
|
+
return `<section ${attrName}="${pageNumber}"${suffix}`;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function annotateSvgPageTags(html, options) {
|
|
277
|
+
const attrName = resolvePageAttributeName(options);
|
|
278
|
+
let pageNumber = 0;
|
|
279
|
+
return html.replace(/<svg\b([^>]*)>/g, (_match, attrs) => {
|
|
280
|
+
if (!attrs.includes("data-marpit-svg")) {
|
|
281
|
+
return `<svg${attrs}>`;
|
|
282
|
+
}
|
|
283
|
+
pageNumber += 1;
|
|
284
|
+
return `<svg${attrs} ${attrName}="${pageNumber}">`;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function resolvePageAttributeName(_options) {
|
|
288
|
+
return "data-mrsf-page";
|
|
289
|
+
}
|
|
290
|
+
function insertPayloadIntoContainer(html, payload) {
|
|
291
|
+
const closingTagIndex = html.lastIndexOf("</");
|
|
292
|
+
if (closingTagIndex === -1) {
|
|
293
|
+
return html + payload;
|
|
294
|
+
}
|
|
295
|
+
return html.slice(0, closingTagIndex) + payload + html.slice(closingTagIndex);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/index.ts
|
|
299
|
+
var mrsfPlugin = createMarpPlugin((options, env) => {
|
|
300
|
+
if (options.comments) {
|
|
301
|
+
return options.comments;
|
|
302
|
+
}
|
|
303
|
+
if (options.loader) {
|
|
304
|
+
try {
|
|
305
|
+
return options.loader(options, env);
|
|
306
|
+
} catch {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (options.sidecarPath) {
|
|
311
|
+
try {
|
|
312
|
+
const abs = path.resolve(options.cwd || process.cwd(), options.sidecarPath);
|
|
313
|
+
const content = readFileSync(abs, "utf-8");
|
|
314
|
+
return parseSidecarContent(content, abs);
|
|
315
|
+
} catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (options.documentPath) {
|
|
320
|
+
try {
|
|
321
|
+
const cwd = options.cwd || process.cwd();
|
|
322
|
+
const abs = path.resolve(cwd, options.documentPath);
|
|
323
|
+
const yamlPath = abs + ".review.yaml";
|
|
324
|
+
const jsonPath = abs + ".review.json";
|
|
325
|
+
let raw = null;
|
|
326
|
+
let hint = null;
|
|
327
|
+
try {
|
|
328
|
+
raw = readFileSync(yamlPath, "utf-8");
|
|
329
|
+
hint = yamlPath;
|
|
330
|
+
} catch {
|
|
331
|
+
try {
|
|
332
|
+
raw = readFileSync(jsonPath, "utf-8");
|
|
333
|
+
hint = jsonPath;
|
|
334
|
+
} catch {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!raw) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
return parseSidecarContent(raw, hint);
|
|
342
|
+
} catch {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
});
|
|
348
|
+
var index_default = mrsfPlugin;
|
|
349
|
+
export {
|
|
350
|
+
index_default as default,
|
|
351
|
+
mrsfPlugin
|
|
352
|
+
};
|
|
353
|
+
//# sourceMappingURL=index.js.map
|