@mrsf/markdown-it-mrsf 0.4.2 → 0.4.4
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 +14 -16
- package/dist/controller.d.ts +11 -0
- package/dist/controller.js +109 -30
- package/dist/controller.js.map +2 -2
- package/dist/types.d.ts +1 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ Customise with CSS custom properties:
|
|
|
112
112
|
|
|
113
113
|
## Interactive Mode
|
|
114
114
|
|
|
115
|
-
Enable `interactive: true` to add action buttons
|
|
115
|
+
Enable `interactive: true` to add action buttons inside tooltips, plus:
|
|
116
116
|
|
|
117
117
|
- Gutter “Add comment” buttons for each line badge
|
|
118
118
|
- An inline “Add comment” floater that appears when users select text
|
|
@@ -128,27 +128,25 @@ md.use(mrsfPlugin, {
|
|
|
128
128
|
|
|
129
129
|
### Controller
|
|
130
130
|
|
|
131
|
-
Include the optional controller to
|
|
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
|
-
document.addEventListener("mrsf:
|
|
137
|
-
|
|
136
|
+
document.addEventListener("mrsf:submit", async (e) => {
|
|
137
|
+
// { action, commentId, text?, line?, end_line?, start_column?, end_column?, selection_text? }
|
|
138
|
+
await saveComment(e.detail);
|
|
138
139
|
});
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
document.addEventListener("mrsf:add", (e) => {
|
|
145
|
-
console.log("Add on line", e.detail.line, "selection", e.detail.selectionText);
|
|
146
|
-
});
|
|
141
|
+
await mermaid.run();
|
|
142
|
+
refreshAll();
|
|
147
143
|
```
|
|
148
144
|
|
|
149
|
-
Events
|
|
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.
|
|
146
|
+
|
|
147
|
+
Set `window.mrsfDisableBuiltinUi = true` before loading the controller if you want to render your own UI and only consume the events.
|
|
150
148
|
|
|
151
|
-
|
|
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.
|
|
152
150
|
|
|
153
151
|
## Options
|
|
154
152
|
|
|
@@ -160,7 +158,7 @@ Each event's `detail` contains `{ commentId: string | null, line: number | null,
|
|
|
160
158
|
| `sidecarPath` | `string` | — | Explicit path to sidecar file |
|
|
161
159
|
| `showResolved` | `boolean` | `true` | Whether to show resolved comments |
|
|
162
160
|
| `interactive` | `boolean` | `false` | Add action buttons for host JS integration |
|
|
163
|
-
| `gutterPosition` | `'left' \| '
|
|
161
|
+
| `gutterPosition` | `'left' \| 'right'` | `'right'` | Badge position: left margin gutter or floated right |
|
|
164
162
|
| `gutterForInline` | `boolean` | `true` | Show gutter badge for comments that also have inline highlights |
|
|
165
163
|
| `inlineHighlights` | `boolean` | `true` | Render inline text highlights for `selected_text` |
|
|
166
164
|
| `lineHighlight` | `boolean` | `false` | Add background tint on commented lines |
|
|
@@ -181,7 +179,7 @@ All rendered elements follow a strict data-attribute contract:
|
|
|
181
179
|
|
|
182
180
|
- `data-mrsf-line` — source line number (1-based)
|
|
183
181
|
- `data-mrsf-comment-id` — comment identifier
|
|
184
|
-
- `data-mrsf-action` — action type (`add`, `resolve`, `unresolve`, `reply`, `edit`, `navigate`)
|
|
182
|
+
- `data-mrsf-action` — action type (`add`, `resolve`, `unresolve`, `reply`, `edit`, `delete`, `navigate`)
|
|
185
183
|
- `data-mrsf-start-line`, `data-mrsf-end-line`, `data-mrsf-start-column`, `data-mrsf-end-column` — optional selection anchors when present
|
|
186
184
|
|
|
187
185
|
## License
|
package/dist/controller.d.ts
CHANGED
|
@@ -75,16 +75,21 @@ 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;
|
|
90
95
|
private setupOverlayStructure;
|
|
@@ -101,6 +106,11 @@ export declare class MrsfController {
|
|
|
101
106
|
private createAddButton;
|
|
102
107
|
/** Measure [data-mrsf-line] elements and set gutter item Y offsets. */
|
|
103
108
|
positionGutterItems(): void;
|
|
109
|
+
private suppressAddItemsThatShareBadgeTargets;
|
|
110
|
+
private handleMutations;
|
|
111
|
+
private isExternalContentMutation;
|
|
112
|
+
private isControllerOwnedNode;
|
|
113
|
+
private queueRefresh;
|
|
104
114
|
private findDirectElementForLine;
|
|
105
115
|
private calculateItemTop;
|
|
106
116
|
/**
|
|
@@ -150,5 +160,6 @@ export declare class MrsfController {
|
|
|
150
160
|
private openForm;
|
|
151
161
|
private openConfirm;
|
|
152
162
|
}
|
|
163
|
+
export declare function refreshAll(): void;
|
|
153
164
|
export declare function autoInit(): void;
|
|
154
165
|
//# 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) {
|
|
@@ -169,13 +187,12 @@ var MrsfController = class _MrsfController {
|
|
|
169
187
|
const lines = this.collectLines();
|
|
170
188
|
const gutter = this.primaryGutter();
|
|
171
189
|
if (!gutter) return;
|
|
172
|
-
const commentedLines = new Set(this.threads.keys());
|
|
173
190
|
for (const line of lines) {
|
|
174
191
|
const threads = this.threads.get(line);
|
|
175
192
|
if (threads && threads.length > 0) {
|
|
176
193
|
const item = this.createBadgeItem(line, threads);
|
|
177
194
|
gutter.appendChild(item);
|
|
178
|
-
} else if (this.opts.interactive
|
|
195
|
+
} else if (this.opts.interactive) {
|
|
179
196
|
const item = this.createAddItem(line);
|
|
180
197
|
gutter.appendChild(item);
|
|
181
198
|
}
|
|
@@ -277,6 +294,7 @@ var MrsfController = class _MrsfController {
|
|
|
277
294
|
const gutters = [this.gutterLeft, this.gutterRight].filter(Boolean);
|
|
278
295
|
for (const gutter of gutters) {
|
|
279
296
|
const items = gutter.querySelectorAll(".mrsf-gutter-item");
|
|
297
|
+
const positioned = [];
|
|
280
298
|
for (const item of items) {
|
|
281
299
|
const line = parseInt(item.dataset.mrsfGutterLine, 10);
|
|
282
300
|
let target = this.findDirectElementForLine(line);
|
|
@@ -290,9 +308,59 @@ var MrsfController = class _MrsfController {
|
|
|
290
308
|
const top = this.calculateItemTop(target, line, containerRect);
|
|
291
309
|
item.style.top = `${top}px`;
|
|
292
310
|
item.style.display = "";
|
|
311
|
+
positioned.push({
|
|
312
|
+
item,
|
|
313
|
+
target,
|
|
314
|
+
kind: item.querySelector(".mrsf-badge") ? "badge" : "add"
|
|
315
|
+
});
|
|
293
316
|
}
|
|
317
|
+
this.suppressAddItemsThatShareBadgeTargets(positioned);
|
|
294
318
|
}
|
|
295
319
|
}
|
|
320
|
+
suppressAddItemsThatShareBadgeTargets(positioned) {
|
|
321
|
+
const badgeTargets = new Set(
|
|
322
|
+
positioned.filter((entry) => entry.kind === "badge").map((entry) => entry.target)
|
|
323
|
+
);
|
|
324
|
+
for (const entry of positioned) {
|
|
325
|
+
if (entry.kind === "add" && badgeTargets.has(entry.target)) {
|
|
326
|
+
entry.item.style.display = "none";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
handleMutations(records) {
|
|
331
|
+
if (!records.some((record) => this.isExternalContentMutation(record))) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
this.queueRefresh();
|
|
335
|
+
}
|
|
336
|
+
isExternalContentMutation(record) {
|
|
337
|
+
const target = record.target instanceof Node ? record.target : null;
|
|
338
|
+
if (target && this.isControllerOwnedNode(target)) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
for (const node of [...record.addedNodes, ...record.removedNodes]) {
|
|
342
|
+
if (!this.isControllerOwnedNode(node)) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return record.type === "characterData";
|
|
347
|
+
}
|
|
348
|
+
isControllerOwnedNode(node) {
|
|
349
|
+
if (!(node instanceof Element)) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
return Boolean(
|
|
353
|
+
node.closest(".mrsf-gutter") || node.closest(".mrsf-orphaned-section") || node.closest('script[type="application/mrsf+json"]')
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
queueRefresh() {
|
|
357
|
+
if (this.refreshQueued) return;
|
|
358
|
+
this.refreshQueued = true;
|
|
359
|
+
queueMicrotask(() => {
|
|
360
|
+
this.refreshQueued = false;
|
|
361
|
+
this.refresh();
|
|
362
|
+
});
|
|
363
|
+
}
|
|
296
364
|
findDirectElementForLine(line) {
|
|
297
365
|
const els = this.container.querySelectorAll(`[data-mrsf-line="${line}"]`);
|
|
298
366
|
for (const el of els) {
|
|
@@ -729,18 +797,22 @@ var MrsfController = class _MrsfController {
|
|
|
729
797
|
injectStyles() {
|
|
730
798
|
if (this.styleInjected) return;
|
|
731
799
|
const css = `
|
|
732
|
-
.mrsf-overlay { position: fixed; inset: 0; background: rgba(
|
|
733
|
-
.mrsf-dialog { background: var(--mrsf-dialog-bg, var(--
|
|
734
|
-
.mrsf-dialog header { padding: 12px
|
|
735
|
-
.mrsf-dialog form { padding:
|
|
736
|
-
.mrsf-
|
|
737
|
-
.mrsf-field
|
|
738
|
-
.mrsf-field
|
|
739
|
-
.mrsf-field textarea {
|
|
740
|
-
.mrsf-
|
|
741
|
-
.mrsf-
|
|
742
|
-
.mrsf-
|
|
743
|
-
.mrsf-
|
|
800
|
+
.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; }
|
|
801
|
+
.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; }
|
|
802
|
+
.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))); }
|
|
803
|
+
.mrsf-dialog form { padding: 12px; display: flex; flex-direction: column; gap: 10px; }
|
|
804
|
+
.mrsf-dialog-body { padding: 12px; line-height: 1.45; }
|
|
805
|
+
.mrsf-field { display: flex; flex-direction: column; gap: 4px; }
|
|
806
|
+
.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)); }
|
|
807
|
+
.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; }
|
|
808
|
+
.mrsf-field textarea { min-height: 76px; resize: vertical; }
|
|
809
|
+
.mrsf-field select { min-height: 34px; }
|
|
810
|
+
.mrsf-field pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
811
|
+
.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; }
|
|
812
|
+
.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))); }
|
|
813
|
+
.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; }
|
|
814
|
+
.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; }
|
|
815
|
+
.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)); }
|
|
744
816
|
`;
|
|
745
817
|
const style = document.createElement("style");
|
|
746
818
|
style.textContent = css;
|
|
@@ -764,6 +836,7 @@ var MrsfController = class _MrsfController {
|
|
|
764
836
|
const endCol = detail.end_column ?? null;
|
|
765
837
|
const overlay = document.createElement("div");
|
|
766
838
|
overlay.className = "mrsf-overlay";
|
|
839
|
+
this.applyThemeVariables(overlay);
|
|
767
840
|
const dialog = document.createElement("div");
|
|
768
841
|
dialog.className = "mrsf-dialog";
|
|
769
842
|
const header = document.createElement("header");
|
|
@@ -810,8 +883,6 @@ var MrsfController = class _MrsfController {
|
|
|
810
883
|
if (selText) {
|
|
811
884
|
const pre = document.createElement("pre");
|
|
812
885
|
pre.textContent = selText;
|
|
813
|
-
pre.style.margin = "0";
|
|
814
|
-
pre.style.whiteSpace = "pre-wrap";
|
|
815
886
|
field("Selected text", pre, "Captured automatically");
|
|
816
887
|
}
|
|
817
888
|
const actions = document.createElement("div");
|
|
@@ -860,13 +931,14 @@ var MrsfController = class _MrsfController {
|
|
|
860
931
|
this.closeOverlay();
|
|
861
932
|
const overlay = document.createElement("div");
|
|
862
933
|
overlay.className = "mrsf-overlay";
|
|
934
|
+
this.applyThemeVariables(overlay);
|
|
863
935
|
const dialog = document.createElement("div");
|
|
864
936
|
dialog.className = "mrsf-dialog";
|
|
865
937
|
const header = document.createElement("header");
|
|
866
938
|
header.textContent = action === "delete" ? "Delete comment" : "Change status";
|
|
867
939
|
dialog.appendChild(header);
|
|
868
940
|
const body = document.createElement("div");
|
|
869
|
-
body.
|
|
941
|
+
body.className = "mrsf-dialog-body";
|
|
870
942
|
body.textContent = action === "delete" ? "Delete this comment?" : action === "resolve" ? "Mark this comment as resolved?" : "Mark this comment as unresolved?";
|
|
871
943
|
dialog.appendChild(body);
|
|
872
944
|
const actions = document.createElement("div");
|
|
@@ -907,7 +979,13 @@ var MrsfController = class _MrsfController {
|
|
|
907
979
|
this.overlayEl = overlay;
|
|
908
980
|
}
|
|
909
981
|
};
|
|
982
|
+
var controllerRegistry = /* @__PURE__ */ new Set();
|
|
910
983
|
var autoInitDone = false;
|
|
984
|
+
function refreshAll() {
|
|
985
|
+
for (const controller of controllerRegistry) {
|
|
986
|
+
controller.refresh();
|
|
987
|
+
}
|
|
988
|
+
}
|
|
911
989
|
function autoInit() {
|
|
912
990
|
if (autoInitDone) return;
|
|
913
991
|
autoInitDone = true;
|
|
@@ -927,6 +1005,7 @@ if (typeof document !== "undefined") {
|
|
|
927
1005
|
}
|
|
928
1006
|
export {
|
|
929
1007
|
MrsfController,
|
|
930
|
-
autoInit
|
|
1008
|
+
autoInit,
|
|
1009
|
+
refreshAll
|
|
931
1010
|
};
|
|
932
1011
|
//# 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 const commentedLines = new Set(this.threads.keys());\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 (\n this.opts.interactive &&\n !commentedLines.has(line - 1) &&\n !commentedLines.has(line + 1)\n ) {\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 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 }\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 // \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 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;\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: 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 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 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 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 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/dist/types.d.ts
CHANGED
|
@@ -38,10 +38,9 @@ export interface MrsfPluginOptions {
|
|
|
38
38
|
/**
|
|
39
39
|
* Position of the gutter badge relative to the line content.
|
|
40
40
|
* - `'left'`: badge in a left margin gutter, outside the content area
|
|
41
|
-
* - `'tight'`: badge inline, immediately before the text
|
|
42
41
|
* - `'right'`: badge floated to the right of the content (default)
|
|
43
42
|
*/
|
|
44
|
-
gutterPosition?: "left" | "
|
|
43
|
+
gutterPosition?: "left" | "right";
|
|
45
44
|
/**
|
|
46
45
|
* Whether to show the gutter badge on lines that have inline-highlighted
|
|
47
46
|
* comments (i.e. comments with `selected_text`). When false, lines where
|
package/package.json
CHANGED