@mrsf/markdown-it-mrsf 0.4.3 → 0.4.5
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/README.md +6 -1
- package/dist/controller.d.ts +12 -0
- package/dist/controller.js +127 -38
- package/dist/controller.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,18 +131,23 @@ md.use(mrsfPlugin, {
|
|
|
131
131
|
Include the optional controller to wire up the inline and gutter actions. By default it shows built-in dialogs for add, reply, edit, resolve, unresolve, and delete, then dispatches events you can persist through your own API:
|
|
132
132
|
|
|
133
133
|
```ts
|
|
134
|
-
import "@mrsf/markdown-it-mrsf/controller";
|
|
134
|
+
import { refreshAll } from "@mrsf/markdown-it-mrsf/controller";
|
|
135
135
|
|
|
136
136
|
document.addEventListener("mrsf:submit", async (e) => {
|
|
137
137
|
// { action, commentId, text?, line?, end_line?, start_column?, end_column?, selection_text? }
|
|
138
138
|
await saveComment(e.detail);
|
|
139
139
|
});
|
|
140
|
+
|
|
141
|
+
await mermaid.run();
|
|
142
|
+
refreshAll();
|
|
140
143
|
```
|
|
141
144
|
|
|
142
145
|
Events fired after user confirmation: `mrsf:add`, `mrsf:reply`, `mrsf:edit`, `mrsf:resolve`, `mrsf:unresolve`, `mrsf:delete`, `mrsf:navigate`, plus `mrsf:submit` with the full payload.
|
|
143
146
|
|
|
144
147
|
Set `window.mrsfDisableBuiltinUi = true` before loading the controller if you want to render your own UI and only consume the events.
|
|
145
148
|
|
|
149
|
+
The controller also auto-refreshes gutter positions on DOM mutations inside the rendered container, which covers common async renderers such as Mermaid. Call `refreshAll()` after third-party rendering when you need a deterministic reposition step.
|
|
150
|
+
|
|
146
151
|
## Options
|
|
147
152
|
|
|
148
153
|
| Option | Type | Default | Description |
|
package/dist/controller.d.ts
CHANGED
|
@@ -75,18 +75,25 @@ export declare class MrsfController {
|
|
|
75
75
|
private overlayEl;
|
|
76
76
|
private lastSelectionText;
|
|
77
77
|
private resizeObserver;
|
|
78
|
+
private mutationObserver;
|
|
78
79
|
private styleInjected;
|
|
79
80
|
private inlineMarks;
|
|
80
81
|
private inlineTooltipEl;
|
|
81
82
|
private orphanedSection;
|
|
83
|
+
private refreshQueued;
|
|
82
84
|
private handleResizeBound;
|
|
85
|
+
private handleMutationBound;
|
|
83
86
|
private handleClickBound;
|
|
84
87
|
private handleSelectionBound;
|
|
85
88
|
constructor(container: HTMLElement, options?: MrsfControllerOptions);
|
|
86
89
|
/** Remove all controller DOM and listeners. */
|
|
87
90
|
destroy(): void;
|
|
91
|
+
/** Recalculate gutter positions after async layout changes such as Mermaid renders. */
|
|
92
|
+
refresh(): void;
|
|
88
93
|
private loadCommentData;
|
|
89
94
|
private buildThreadMap;
|
|
95
|
+
private findCommentById;
|
|
96
|
+
private orderThreadsForDisplay;
|
|
90
97
|
private setupOverlayStructure;
|
|
91
98
|
private createGutter;
|
|
92
99
|
/** Build badge/add-button elements for each line in the gutter(s). */
|
|
@@ -102,6 +109,10 @@ export declare class MrsfController {
|
|
|
102
109
|
/** Measure [data-mrsf-line] elements and set gutter item Y offsets. */
|
|
103
110
|
positionGutterItems(): void;
|
|
104
111
|
private suppressAddItemsThatShareBadgeTargets;
|
|
112
|
+
private handleMutations;
|
|
113
|
+
private isExternalContentMutation;
|
|
114
|
+
private isControllerOwnedNode;
|
|
115
|
+
private queueRefresh;
|
|
105
116
|
private findDirectElementForLine;
|
|
106
117
|
private calculateItemTop;
|
|
107
118
|
/**
|
|
@@ -151,5 +162,6 @@ export declare class MrsfController {
|
|
|
151
162
|
private openForm;
|
|
152
163
|
private openConfirm;
|
|
153
164
|
}
|
|
165
|
+
export declare function refreshAll(): void;
|
|
154
166
|
export declare function autoInit(): void;
|
|
155
167
|
//# sourceMappingURL=controller.d.ts.map
|
package/dist/controller.js
CHANGED
|
@@ -18,8 +18,8 @@ function formatTime(iso) {
|
|
|
18
18
|
function renderCommentHtml(comment, isReply, interactive) {
|
|
19
19
|
const resolvedClass = comment.resolved ? " mrsf-resolved" : "";
|
|
20
20
|
const replyClass = isReply ? " mrsf-reply" : "";
|
|
21
|
-
let html = `<
|
|
22
|
-
html += `<
|
|
21
|
+
let html = `<div class="mrsf-comment${resolvedClass}${replyClass}" data-mrsf-comment-id="${escapeHtml(comment.id)}">`;
|
|
22
|
+
html += `<div class="mrsf-comment-header">`;
|
|
23
23
|
html += `<span class="mrsf-author">${escapeHtml(comment.author)}</span>`;
|
|
24
24
|
if (comment.timestamp) {
|
|
25
25
|
html += `<span class="mrsf-date">${escapeHtml(formatTime(comment.timestamp))}</span>`;
|
|
@@ -33,14 +33,14 @@ function renderCommentHtml(comment, isReply, interactive) {
|
|
|
33
33
|
if (comment.resolved) {
|
|
34
34
|
html += `<span class="mrsf-resolved-badge">\u2713 resolved</span>`;
|
|
35
35
|
}
|
|
36
|
-
html += `</
|
|
36
|
+
html += `</div>`;
|
|
37
37
|
if (comment.selected_text) {
|
|
38
|
-
html += `<details class="mrsf-selected-text"><summary class="mrsf-selected-text-summary">${escapeHtml(comment.selected_text)}</summary><
|
|
38
|
+
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>`;
|
|
39
39
|
}
|
|
40
|
-
html += `<
|
|
40
|
+
html += `<div class="mrsf-comment-body">${escapeHtml(comment.text)}</div>`;
|
|
41
41
|
if (interactive) {
|
|
42
42
|
const line = comment.line != null ? String(comment.line) : "";
|
|
43
|
-
html += `<
|
|
43
|
+
html += `<div class="mrsf-actions">`;
|
|
44
44
|
if (comment.resolved) {
|
|
45
45
|
html += `<button class="mrsf-action-btn" data-mrsf-action="unresolve" data-mrsf-comment-id="${escapeHtml(comment.id)}" data-mrsf-line="${line}">Unresolve</button>`;
|
|
46
46
|
} else {
|
|
@@ -49,22 +49,22 @@ function renderCommentHtml(comment, isReply, interactive) {
|
|
|
49
49
|
html += `<button class="mrsf-action-btn" data-mrsf-action="reply" data-mrsf-comment-id="${escapeHtml(comment.id)}" data-mrsf-line="${line}">Reply</button>`;
|
|
50
50
|
html += `<button class="mrsf-action-btn" data-mrsf-action="edit" data-mrsf-comment-id="${escapeHtml(comment.id)}" data-mrsf-line="${line}">Edit</button>`;
|
|
51
51
|
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>`;
|
|
52
|
-
html += `</
|
|
52
|
+
html += `</div>`;
|
|
53
53
|
}
|
|
54
|
-
html += `</
|
|
54
|
+
html += `</div>`;
|
|
55
55
|
return html;
|
|
56
56
|
}
|
|
57
57
|
function renderThreadHtml(thread, interactive) {
|
|
58
|
-
let html = `<
|
|
58
|
+
let html = `<div class="mrsf-thread">`;
|
|
59
59
|
html += renderCommentHtml(thread.comment, false, interactive);
|
|
60
60
|
if (thread.replies.length > 0) {
|
|
61
|
-
html += `<
|
|
61
|
+
html += `<div class="mrsf-replies">`;
|
|
62
62
|
for (const reply of thread.replies) {
|
|
63
63
|
html += renderCommentHtml(reply, true, interactive);
|
|
64
64
|
}
|
|
65
|
-
html += `</
|
|
65
|
+
html += `</div>`;
|
|
66
66
|
}
|
|
67
|
-
html += `</
|
|
67
|
+
html += `</div>`;
|
|
68
68
|
return html;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -80,11 +80,14 @@ var MrsfController = class _MrsfController {
|
|
|
80
80
|
overlayEl = null;
|
|
81
81
|
lastSelectionText = null;
|
|
82
82
|
resizeObserver = null;
|
|
83
|
+
mutationObserver = null;
|
|
83
84
|
styleInjected = false;
|
|
84
85
|
inlineMarks = [];
|
|
85
86
|
inlineTooltipEl = null;
|
|
86
87
|
orphanedSection = null;
|
|
88
|
+
refreshQueued = false;
|
|
87
89
|
handleResizeBound = this.positionGutterItems.bind(this);
|
|
90
|
+
handleMutationBound = this.handleMutations.bind(this);
|
|
88
91
|
handleClickBound = this.handleClick.bind(this);
|
|
89
92
|
handleSelectionBound = this.handleSelectionChange.bind(this);
|
|
90
93
|
constructor(container, options = {}) {
|
|
@@ -101,8 +104,17 @@ var MrsfController = class _MrsfController {
|
|
|
101
104
|
this.positionGutterItems();
|
|
102
105
|
this.renderInlineHighlights();
|
|
103
106
|
this.renderOrphanedSection();
|
|
107
|
+
controllerRegistry.add(this);
|
|
104
108
|
this.resizeObserver = new ResizeObserver(this.handleResizeBound);
|
|
105
109
|
this.resizeObserver.observe(this.container);
|
|
110
|
+
if (typeof MutationObserver !== "undefined") {
|
|
111
|
+
this.mutationObserver = new MutationObserver(this.handleMutationBound);
|
|
112
|
+
this.mutationObserver.observe(this.container, {
|
|
113
|
+
childList: true,
|
|
114
|
+
subtree: true,
|
|
115
|
+
characterData: true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
106
118
|
document.addEventListener("click", this.handleClickBound);
|
|
107
119
|
if (this.opts.interactive) {
|
|
108
120
|
document.addEventListener("selectionchange", this.handleSelectionBound);
|
|
@@ -111,6 +123,8 @@ var MrsfController = class _MrsfController {
|
|
|
111
123
|
/** Remove all controller DOM and listeners. */
|
|
112
124
|
destroy() {
|
|
113
125
|
this.resizeObserver?.disconnect();
|
|
126
|
+
this.mutationObserver?.disconnect();
|
|
127
|
+
controllerRegistry.delete(this);
|
|
114
128
|
document.removeEventListener("click", this.handleClickBound);
|
|
115
129
|
document.removeEventListener("selectionchange", this.handleSelectionBound);
|
|
116
130
|
this.removeInlineHighlights();
|
|
@@ -121,6 +135,10 @@ var MrsfController = class _MrsfController {
|
|
|
121
135
|
this.closeOverlay();
|
|
122
136
|
this.container.classList.remove("mrsf-overlay-root");
|
|
123
137
|
}
|
|
138
|
+
/** Recalculate gutter positions after async layout changes such as Mermaid renders. */
|
|
139
|
+
refresh() {
|
|
140
|
+
this.positionGutterItems();
|
|
141
|
+
}
|
|
124
142
|
// ── Data loading ────────────────────────────────────────
|
|
125
143
|
loadCommentData() {
|
|
126
144
|
if (this.opts.comments.length > 0) {
|
|
@@ -147,6 +165,27 @@ var MrsfController = class _MrsfController {
|
|
|
147
165
|
this.threads.set(line, arr);
|
|
148
166
|
}
|
|
149
167
|
}
|
|
168
|
+
findCommentById(commentId) {
|
|
169
|
+
if (!commentId) return null;
|
|
170
|
+
for (const threadList of this.threads.values()) {
|
|
171
|
+
for (const thread of threadList) {
|
|
172
|
+
if (thread.comment.id === commentId) {
|
|
173
|
+
return thread.comment;
|
|
174
|
+
}
|
|
175
|
+
const reply = thread.replies.find((item) => item.id === commentId);
|
|
176
|
+
if (reply) {
|
|
177
|
+
return reply;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
orderThreadsForDisplay(threads) {
|
|
184
|
+
return [...threads].sort((left, right) => {
|
|
185
|
+
if (left.comment.resolved === right.comment.resolved) return 0;
|
|
186
|
+
return left.comment.resolved ? 1 : -1;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
150
189
|
// ── Overlay structure ───────────────────────────────────
|
|
151
190
|
setupOverlayStructure() {
|
|
152
191
|
this.container.classList.add("mrsf-overlay-root");
|
|
@@ -221,6 +260,7 @@ var MrsfController = class _MrsfController {
|
|
|
221
260
|
const item = document.createElement("div");
|
|
222
261
|
item.className = "mrsf-gutter-item";
|
|
223
262
|
item.dataset.mrsfGutterLine = String(line);
|
|
263
|
+
const displayThreads = this.orderThreadsForDisplay(threads);
|
|
224
264
|
const total = threads.reduce((n, t) => n + 1 + t.replies.length, 0);
|
|
225
265
|
const allResolved = threads.every((t) => t.comment.resolved);
|
|
226
266
|
const highestSeverity = threads.reduce((sev, t) => {
|
|
@@ -239,12 +279,12 @@ var MrsfController = class _MrsfController {
|
|
|
239
279
|
badge.className = classes.join(" ");
|
|
240
280
|
badge.dataset.mrsfLine = String(line);
|
|
241
281
|
badge.dataset.mrsfAction = "navigate";
|
|
242
|
-
badge.dataset.mrsfCommentId =
|
|
282
|
+
badge.dataset.mrsfCommentId = displayThreads[0].comment.id;
|
|
243
283
|
badge.tabIndex = 0;
|
|
244
284
|
badge.textContent = `${icon} ${total}`;
|
|
245
285
|
badge.addEventListener("click", (e) => {
|
|
246
286
|
e.stopPropagation();
|
|
247
|
-
this.toggleTooltip(item, line,
|
|
287
|
+
this.toggleTooltip(item, line, displayThreads);
|
|
248
288
|
});
|
|
249
289
|
item.appendChild(badge);
|
|
250
290
|
return item;
|
|
@@ -309,6 +349,40 @@ var MrsfController = class _MrsfController {
|
|
|
309
349
|
}
|
|
310
350
|
}
|
|
311
351
|
}
|
|
352
|
+
handleMutations(records) {
|
|
353
|
+
if (!records.some((record) => this.isExternalContentMutation(record))) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.queueRefresh();
|
|
357
|
+
}
|
|
358
|
+
isExternalContentMutation(record) {
|
|
359
|
+
const target = record.target instanceof Node ? record.target : null;
|
|
360
|
+
if (target && this.isControllerOwnedNode(target)) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
for (const node of [...record.addedNodes, ...record.removedNodes]) {
|
|
364
|
+
if (!this.isControllerOwnedNode(node)) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return record.type === "characterData";
|
|
369
|
+
}
|
|
370
|
+
isControllerOwnedNode(node) {
|
|
371
|
+
if (!(node instanceof Element)) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
return Boolean(
|
|
375
|
+
node.closest(".mrsf-gutter") || node.closest(".mrsf-orphaned-section") || node.closest('script[type="application/mrsf+json"]')
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
queueRefresh() {
|
|
379
|
+
if (this.refreshQueued) return;
|
|
380
|
+
this.refreshQueued = true;
|
|
381
|
+
queueMicrotask(() => {
|
|
382
|
+
this.refreshQueued = false;
|
|
383
|
+
this.refresh();
|
|
384
|
+
});
|
|
385
|
+
}
|
|
312
386
|
findDirectElementForLine(line) {
|
|
313
387
|
const els = this.container.querySelectorAll(`[data-mrsf-line="${line}"]`);
|
|
314
388
|
for (const el of els) {
|
|
@@ -321,7 +395,7 @@ var MrsfController = class _MrsfController {
|
|
|
321
395
|
}
|
|
322
396
|
calculateItemTop(target, line, containerRect) {
|
|
323
397
|
const targetRect = target.getBoundingClientRect();
|
|
324
|
-
const rangeTop = targetRect.top - containerRect.top;
|
|
398
|
+
const rangeTop = targetRect.top - containerRect.top + this.container.scrollTop;
|
|
325
399
|
if (target.tagName !== "PRE") {
|
|
326
400
|
return rangeTop;
|
|
327
401
|
}
|
|
@@ -381,7 +455,7 @@ var MrsfController = class _MrsfController {
|
|
|
381
455
|
heading.textContent = `Orphaned Comments (${orphanedThreads.length})`;
|
|
382
456
|
section.appendChild(heading);
|
|
383
457
|
const interactive = this.opts.interactive;
|
|
384
|
-
for (const thread of orphanedThreads) {
|
|
458
|
+
for (const thread of this.orderThreadsForDisplay(orphanedThreads)) {
|
|
385
459
|
const wrapper = document.createElement("div");
|
|
386
460
|
wrapper.className = interactive ? "mrsf-orphaned-thread mrsf-interactive" : "mrsf-orphaned-thread";
|
|
387
461
|
wrapper.innerHTML = renderThreadHtml(thread, interactive);
|
|
@@ -588,7 +662,7 @@ var MrsfController = class _MrsfController {
|
|
|
588
662
|
tooltip.className = interactive ? "mrsf-tooltip mrsf-interactive mrsf-tooltip-visible" : "mrsf-tooltip mrsf-tooltip-visible";
|
|
589
663
|
tooltip.dataset.mrsfLine = String(line);
|
|
590
664
|
let html = "";
|
|
591
|
-
for (const thread of threads) {
|
|
665
|
+
for (const thread of this.orderThreadsForDisplay(threads)) {
|
|
592
666
|
html += renderThreadHtml(thread, interactive);
|
|
593
667
|
}
|
|
594
668
|
if (interactive) {
|
|
@@ -745,18 +819,22 @@ var MrsfController = class _MrsfController {
|
|
|
745
819
|
injectStyles() {
|
|
746
820
|
if (this.styleInjected) return;
|
|
747
821
|
const css = `
|
|
748
|
-
.mrsf-overlay { position: fixed; inset: 0; background: rgba(
|
|
749
|
-
.mrsf-dialog { background: var(--mrsf-dialog-bg, var(--
|
|
750
|
-
.mrsf-dialog header { padding: 12px
|
|
751
|
-
.mrsf-dialog form { padding:
|
|
752
|
-
.mrsf-
|
|
753
|
-
.mrsf-field
|
|
754
|
-
.mrsf-field
|
|
755
|
-
.mrsf-field textarea {
|
|
756
|
-
.mrsf-
|
|
757
|
-
.mrsf-
|
|
758
|
-
.mrsf-
|
|
759
|
-
.mrsf-
|
|
822
|
+
.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; }
|
|
823
|
+
.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; }
|
|
824
|
+
.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))); }
|
|
825
|
+
.mrsf-dialog form { padding: 12px; display: flex; flex-direction: column; gap: 10px; }
|
|
826
|
+
.mrsf-dialog-body { padding: 12px; line-height: 1.45; }
|
|
827
|
+
.mrsf-field { display: flex; flex-direction: column; gap: 4px; }
|
|
828
|
+
.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)); }
|
|
829
|
+
.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; }
|
|
830
|
+
.mrsf-field textarea { min-height: 76px; resize: vertical; }
|
|
831
|
+
.mrsf-field select { min-height: 34px; }
|
|
832
|
+
.mrsf-field pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
833
|
+
.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; }
|
|
834
|
+
.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))); }
|
|
835
|
+
.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; }
|
|
836
|
+
.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; }
|
|
837
|
+
.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)); }
|
|
760
838
|
`;
|
|
761
839
|
const style = document.createElement("style");
|
|
762
840
|
style.textContent = css;
|
|
@@ -773,13 +851,15 @@ var MrsfController = class _MrsfController {
|
|
|
773
851
|
if (window.mrsfDisableBuiltinUi) return;
|
|
774
852
|
this.injectStyles();
|
|
775
853
|
this.closeOverlay();
|
|
776
|
-
const
|
|
777
|
-
const
|
|
778
|
-
const
|
|
779
|
-
const
|
|
780
|
-
const
|
|
854
|
+
const sourceComment = action === "edit" ? this.findCommentById(detail.commentId) : null;
|
|
855
|
+
const selText = detail.selectionText ?? sourceComment?.selected_text ?? "";
|
|
856
|
+
const line = detail.line ?? detail.start_line ?? sourceComment?.line ?? null;
|
|
857
|
+
const endLine = detail.end_line ?? detail.line ?? sourceComment?.end_line ?? sourceComment?.line ?? null;
|
|
858
|
+
const startCol = detail.start_column ?? sourceComment?.start_column ?? null;
|
|
859
|
+
const endCol = detail.end_column ?? sourceComment?.end_column ?? null;
|
|
781
860
|
const overlay = document.createElement("div");
|
|
782
861
|
overlay.className = "mrsf-overlay";
|
|
862
|
+
this.applyThemeVariables(overlay);
|
|
783
863
|
const dialog = document.createElement("div");
|
|
784
864
|
dialog.className = "mrsf-dialog";
|
|
785
865
|
const header = document.createElement("header");
|
|
@@ -804,6 +884,7 @@ var MrsfController = class _MrsfController {
|
|
|
804
884
|
const textArea = document.createElement("textarea");
|
|
805
885
|
textArea.name = "text";
|
|
806
886
|
textArea.required = true;
|
|
887
|
+
textArea.value = action === "edit" ? sourceComment?.text ?? "" : "";
|
|
807
888
|
field("Comment text", textArea);
|
|
808
889
|
const typeSelect = document.createElement("select");
|
|
809
890
|
typeSelect.name = "type";
|
|
@@ -813,6 +894,7 @@ var MrsfController = class _MrsfController {
|
|
|
813
894
|
opt.textContent = t || "(none)";
|
|
814
895
|
typeSelect.appendChild(opt);
|
|
815
896
|
});
|
|
897
|
+
typeSelect.value = action === "edit" ? sourceComment?.type ?? "" : "";
|
|
816
898
|
field("Type", typeSelect, "Optional");
|
|
817
899
|
const severitySelect = document.createElement("select");
|
|
818
900
|
severitySelect.name = "severity";
|
|
@@ -822,12 +904,11 @@ var MrsfController = class _MrsfController {
|
|
|
822
904
|
opt.textContent = s || "(none)";
|
|
823
905
|
severitySelect.appendChild(opt);
|
|
824
906
|
});
|
|
907
|
+
severitySelect.value = action === "edit" ? sourceComment?.severity ?? "" : "";
|
|
825
908
|
field("Severity", severitySelect, "Optional");
|
|
826
909
|
if (selText) {
|
|
827
910
|
const pre = document.createElement("pre");
|
|
828
911
|
pre.textContent = selText;
|
|
829
|
-
pre.style.margin = "0";
|
|
830
|
-
pre.style.whiteSpace = "pre-wrap";
|
|
831
912
|
field("Selected text", pre, "Captured automatically");
|
|
832
913
|
}
|
|
833
914
|
const actions = document.createElement("div");
|
|
@@ -876,13 +957,14 @@ var MrsfController = class _MrsfController {
|
|
|
876
957
|
this.closeOverlay();
|
|
877
958
|
const overlay = document.createElement("div");
|
|
878
959
|
overlay.className = "mrsf-overlay";
|
|
960
|
+
this.applyThemeVariables(overlay);
|
|
879
961
|
const dialog = document.createElement("div");
|
|
880
962
|
dialog.className = "mrsf-dialog";
|
|
881
963
|
const header = document.createElement("header");
|
|
882
964
|
header.textContent = action === "delete" ? "Delete comment" : "Change status";
|
|
883
965
|
dialog.appendChild(header);
|
|
884
966
|
const body = document.createElement("div");
|
|
885
|
-
body.
|
|
967
|
+
body.className = "mrsf-dialog-body";
|
|
886
968
|
body.textContent = action === "delete" ? "Delete this comment?" : action === "resolve" ? "Mark this comment as resolved?" : "Mark this comment as unresolved?";
|
|
887
969
|
dialog.appendChild(body);
|
|
888
970
|
const actions = document.createElement("div");
|
|
@@ -923,7 +1005,13 @@ var MrsfController = class _MrsfController {
|
|
|
923
1005
|
this.overlayEl = overlay;
|
|
924
1006
|
}
|
|
925
1007
|
};
|
|
1008
|
+
var controllerRegistry = /* @__PURE__ */ new Set();
|
|
926
1009
|
var autoInitDone = false;
|
|
1010
|
+
function refreshAll() {
|
|
1011
|
+
for (const controller of controllerRegistry) {
|
|
1012
|
+
controller.refresh();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
927
1015
|
function autoInit() {
|
|
928
1016
|
if (autoInitDone) return;
|
|
929
1017
|
autoInitDone = true;
|
|
@@ -943,6 +1031,7 @@ if (typeof document !== "undefined") {
|
|
|
943
1031
|
}
|
|
944
1032
|
export {
|
|
945
1033
|
MrsfController,
|
|
946
|
-
autoInit
|
|
1034
|
+
autoInit,
|
|
1035
|
+
refreshAll
|
|
947
1036
|
};
|
|
948
1037
|
//# sourceMappingURL=controller.js.map
|
package/dist/controller.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/html.ts", "../src/controller.ts"],
|
|
4
|
-
"sourcesContent": ["/* ---------------------------------------------------------------\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 = `<span class=\"mrsf-comment${resolvedClass}${replyClass}\" data-mrsf-comment-id=\"${escapeHtml(comment.id)}\">`;\n\n // Header\n html += `<span 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 += `</span>`;\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><span class=\"mrsf-selected-text-full\">${escapeHtml(comment.selected_text)}</span></details>`;\n }\n\n // Body\n html += `<span class=\"mrsf-comment-body\">${escapeHtml(comment.text)}</span>`;\n\n // Action buttons (interactive mode)\n if (interactive) {\n const line = comment.line != null ? String(comment.line) : \"\";\n html += `<span 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 += `</span>`;\n }\n\n html += `</span>`;\n return html;\n}\n\nexport function renderThreadHtml(thread: CommentThread, interactive: boolean): string {\n let html = `<span class=\"mrsf-thread\">`;\n html += renderCommentHtml(thread.comment, false, interactive);\n if (thread.replies.length > 0) {\n html += `<span class=\"mrsf-replies\">`;\n for (const reply of thread.replies) {\n html += renderCommentHtml(reply, true, interactive);\n }\n html += `</span>`;\n }\n html += `</span>`;\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 { 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}\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 styleInjected = false;\n private inlineMarks: HTMLElement[] = [];\n private inlineTooltipEl: HTMLElement | null = null;\n private orphanedSection: HTMLDivElement | null = null;\n\n private handleResizeBound = this.positionGutterItems.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 };\n\n this.loadCommentData();\n this.setupOverlayStructure();\n this.renderGutterItems();\n this.positionGutterItems();\n this.renderInlineHighlights();\n this.renderOrphanedSection();\n\n // Listeners\n this.resizeObserver = new ResizeObserver(this.handleResizeBound);\n this.resizeObserver.observe(this.container);\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 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 // \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 // \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 gutter = this.primaryGutter();\n if (!gutter) return;\n\n for (const line of lines) {\n const threads = this.threads.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 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 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 icon = allResolved ? \"\u2713\" : \"\uD83D\uDCAC\";\n const badge = document.createElement(\"span\");\n badge.className = classes.join(\" \");\n badge.dataset.mrsfLine = String(line);\n badge.dataset.mrsfAction = \"navigate\";\n badge.dataset.mrsfCommentId = threads[0].comment.id;\n badge.tabIndex = 0;\n badge.textContent = `${icon} ${total}`;\n badge.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n this.toggleTooltip(item, line, threads);\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 btn = document.createElement(\"button\");\n btn.className = \"mrsf-gutter-add\";\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.setAttribute(\"aria-label\", \"Add comment\");\n btn.textContent = \"Add\";\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 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;\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 [line, threads] of this.threads) {\n if (!lineSet.has(line)) {\n orphanedThreads.push(...threads);\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 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 for (const [line, threads] of this.threads) {\n for (const thread of threads) {\n const comment = thread.comment;\n if (!comment.selected_text) continue;\n\n const el = this.container.querySelector<HTMLElement>(\n `[data-mrsf-line=\"${line}\"]:not(script):not(.mrsf-gutter):not(.mrsf-gutter-item)`,\n );\n if (!el) continue;\n\n this.wrapSelectedText(el, comment.selected_text, thread);\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 ): 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(thread.comment.line);\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 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 const rect = range.getBoundingClientRect();\n if (!rect || (rect.width === 0 && rect.height === 0)) {\n this.hideFloatingAddButton();\n return;\n }\n\n const startAnchor = (range.startContainer as Node).parentElement?.closest<HTMLElement>(\n \"[data-mrsf-line]\",\n );\n const endAnchor = (range.endContainer as Node).parentElement?.closest<HTMLElement>(\n \"[data-mrsf-line]\",\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 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 document.body.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 btn.style.visibility = \"hidden\";\n btn.style.display = \"block\";\n const height = btn.offsetHeight || 0;\n const top = window.scrollY + rect.top - height - margin;\n const left = window.scrollX + rect.left;\n btn.style.top = `${Math.max(0, top)}px`;\n btn.style.left = `${Math.max(0, 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: rgba(0,0,0,0.45); z-index: 2000; display: flex; align-items: center; justify-content: center; }\n.mrsf-dialog { background: var(--mrsf-dialog-bg, var(--vp-c-bg, #1e1e1e)); color: var(--mrsf-dialog-fg, var(--vp-c-text-1, #f3f3f3)); border: 1px solid var(--mrsf-dialog-border, var(--vp-c-divider, #444)); border-radius: 8px; width: min(520px, 90vw); box-shadow: 0 10px 40px rgba(0,0,0,0.35); font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; }\n.mrsf-dialog header { padding: 12px 16px; font-weight: 600; border-bottom: 1px solid var(--mrsf-dialog-border, var(--vp-c-divider, #333)); }\n.mrsf-dialog form { padding: 16px; display: flex; flex-direction: column; gap: 12px; }\n.mrsf-field { display: flex; flex-direction: column; gap: 6px; }\n.mrsf-field label { font-size: 13px; color: var(--mrsf-dialog-muted, var(--vp-c-text-2, #ccc)); }\n.mrsf-field input, .mrsf-field select, .mrsf-field textarea { background: var(--mrsf-field-bg, var(--vp-c-bg-soft, #252526)); color: var(--mrsf-dialog-fg, var(--vp-c-text-1, #f3f3f3)); border: 1px solid var(--mrsf-dialog-border, var(--vp-c-divider, #3c3c3c)); border-radius: 4px; padding: 6px 8px; font-size: 13px; }\n.mrsf-field textarea { min-height: 100px; resize: vertical; }\n.mrsf-actions-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 0; padding: 12px 16px 16px; border-top: 1px solid var(--mrsf-dialog-border, var(--vp-c-divider, #333)); }\n.mrsf-btn { padding: 6px 12px; border-radius: 4px; border: 1px solid var(--mrsf-dialog-border, var(--vp-c-divider, #3c3c3c)); background: var(--mrsf-button-bg, var(--vp-c-bg-soft, #2d2d30)); color: var(--mrsf-dialog-fg, var(--vp-c-text-1, #f3f3f3)); cursor: pointer; }\n.mrsf-btn-primary { background: var(--mrsf-button-primary-bg, #0e639c); border-color: var(--mrsf-button-primary-bg, #0e639c); color: #fff; }\n.mrsf-helper { font-size: 12px; color: var(--mrsf-dialog-muted, var(--vp-c-text-2, #aaa)); }\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 selText = detail.selectionText ?? \"\";\n const line = detail.line ?? detail.start_line ?? null;\n const endLine = detail.end_line ?? detail.line ?? null;\n const startCol = detail.start_column ?? null;\n const endCol = detail.end_column ?? null;\n\n const overlay = document.createElement(\"div\");\n overlay.className = \"mrsf-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 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 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 field(\"Severity\", severitySelect, \"Optional\");\n\n if (selText) {\n const pre = document.createElement(\"pre\");\n pre.textContent = selText;\n pre.style.margin = \"0\";\n pre.style.whiteSpace = \"pre-wrap\";\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\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.style.padding = \"16px\";\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\nlet autoInitDone = false;\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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",
|
|
4
|
+
"sourcesContent": ["/* ---------------------------------------------------------------\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 { 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}\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 };\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 gutter = this.primaryGutter();\n if (!gutter) return;\n\n for (const line of lines) {\n const threads = this.threads.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 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 icon = allResolved ? \"\u2713\" : \"\uD83D\uDCAC\";\n const badge = document.createElement(\"span\");\n badge.className = classes.join(\" \");\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 = `${icon} ${total}`;\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 btn = document.createElement(\"button\");\n btn.className = \"mrsf-gutter-add\";\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.setAttribute(\"aria-label\", \"Add comment\");\n btn.textContent = \"Add\";\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 [line, threads] of this.threads) {\n if (!lineSet.has(line)) {\n orphanedThreads.push(...threads);\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 for (const [line, threads] of this.threads) {\n for (const thread of threads) {\n const comment = thread.comment;\n if (!comment.selected_text) continue;\n\n const el = this.container.querySelector<HTMLElement>(\n `[data-mrsf-line=\"${line}\"]:not(script):not(.mrsf-gutter):not(.mrsf-gutter-item)`,\n );\n if (!el) continue;\n\n this.wrapSelectedText(el, comment.selected_text, thread);\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 ): 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(thread.comment.line);\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 const rect = range.getBoundingClientRect();\n if (!rect || (rect.width === 0 && rect.height === 0)) {\n this.hideFloatingAddButton();\n return;\n }\n\n const startAnchor = (range.startContainer as Node).parentElement?.closest<HTMLElement>(\n \"[data-mrsf-line]\",\n );\n const endAnchor = (range.endContainer as Node).parentElement?.closest<HTMLElement>(\n \"[data-mrsf-line]\",\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 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 document.body.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 btn.style.visibility = \"hidden\";\n btn.style.display = \"block\";\n const height = btn.offsetHeight || 0;\n const top = window.scrollY + rect.top - height - margin;\n const left = window.scrollX + rect.left;\n btn.style.top = `${Math.max(0, top)}px`;\n btn.style.left = `${Math.max(0, 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
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED