@diegopetrucci/pi-extensions 0.1.72 → 0.1.73
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 +2 -2
- package/extensions/agent-workflow-audit/index.ts +41 -8
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/README.md +7 -3
- package/extensions/annotate-git-diff/git.ts +3 -2
- package/extensions/annotate-git-diff/index.ts +366 -81
- package/extensions/annotate-git-diff/package.json +2 -2
- package/extensions/annotate-git-diff/quiet-glimpse.ts +4 -2
- package/extensions/annotate-git-diff/types.ts +6 -0
- package/extensions/annotate-git-diff/web/app.js +54 -4
- package/extensions/annotate-git-diff/web/index.html +2 -2
- package/extensions/annotate-last-message/README.md +9 -1
- package/extensions/annotate-last-message/index.ts +53 -13
- package/extensions/annotate-last-message/package.json +2 -1
- package/extensions/annotate-last-message/quiet-glimpse.ts +4 -2
- package/extensions/annotate-last-message/theme.ts +247 -0
- package/extensions/annotate-last-message/ui.ts +28 -2
- package/extensions/annotate-last-message/web/app.js +351 -162
- package/extensions/annotate-last-message/web/index.html +153 -24
- package/extensions/annotate-last-message/web/md-renderer.js +458 -0
- package/extensions/code-reviewer/index.ts +12 -1
- package/extensions/code-reviewer/package.json +2 -1
- package/extensions/context-cap/README.md +33 -4
- package/extensions/context-cap/index.ts +271 -68
- package/extensions/context-cap/package.json +1 -1
- package/extensions/context-inspector/README.md +2 -0
- package/extensions/context-inspector/index.ts +90 -9
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/index.ts +21 -15
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dynamic-context-pruning/README.md +17 -5
- package/extensions/dynamic-context-pruning/index.ts +17 -4
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/README.md +24 -3
- package/extensions/fast/index.ts +2 -2
- package/extensions/fast/package.json +1 -1
- package/extensions/git-footer/README.md +1 -0
- package/extensions/git-footer/package.json +1 -1
- package/extensions/librarian/index.ts +7 -0
- package/extensions/librarian/package.json +2 -1
- package/extensions/minimal-footer/README.md +16 -4
- package/extensions/minimal-footer/index.ts +263 -14
- package/extensions/minimal-footer/openai-usage.ts +62 -8
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/oracle/index.ts +19 -18
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/README.md +6 -0
- package/extensions/permission-gate/index.ts +37 -16
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/quiet-tools/README.md +1 -1
- package/extensions/quiet-tools/package.json +1 -1
- package/extensions/triage-comments/package.json +2 -1
- package/package.json +4 -4
|
@@ -1,211 +1,400 @@
|
|
|
1
1
|
/* global window, document */
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Markdown parsing helpers are provided by the md-renderer.js script that
|
|
4
|
+
// ui.ts inlines before this file. Access them via globalThis.__tlhMdRenderer.
|
|
4
5
|
|
|
6
|
+
const messageData = JSON.parse(
|
|
7
|
+
document.getElementById("annotate-last-message-data").textContent || "{}",
|
|
8
|
+
);
|
|
5
9
|
if (!Array.isArray(messageData.lines)) messageData.lines = [];
|
|
6
10
|
if (!Array.isArray(messageData.sections)) messageData.sections = [];
|
|
7
11
|
|
|
8
12
|
const state = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
overallComment: "",
|
|
14
|
+
inlineComments: new Map(),
|
|
15
|
+
sectionComments: new Map(),
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
const elements = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
messageLines: document.getElementById("message-lines"),
|
|
20
|
+
overallComment: document.getElementById("overall-comment"),
|
|
21
|
+
sectionComments: document.getElementById("section-comments"),
|
|
22
|
+
status: document.getElementById("status"),
|
|
23
|
+
submitButton: document.getElementById("submit-button"),
|
|
24
|
+
cancelButton: document.getElementById("cancel-button"),
|
|
21
25
|
};
|
|
22
26
|
|
|
27
|
+
// Compute fence context once over the complete source message. Section cards
|
|
28
|
+
// reuse this source-line metadata so a section that starts after a blank line
|
|
29
|
+
// inside a fence remains a code preview instead of reopening markdown parsing.
|
|
30
|
+
const fullMessageFenceState = globalThis.__tlhMdRenderer.applyFenceState(
|
|
31
|
+
messageData.lines.map((line) => line.text),
|
|
32
|
+
);
|
|
33
|
+
|
|
23
34
|
function feedbackCount() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
let count = state.overallComment.trim().length > 0 ? 1 : 0;
|
|
36
|
+
for (const value of state.inlineComments.values()) {
|
|
37
|
+
if (value.trim().length > 0) count += 1;
|
|
38
|
+
}
|
|
39
|
+
for (const value of state.sectionComments.values()) {
|
|
40
|
+
if (value.trim().length > 0) count += 1;
|
|
41
|
+
}
|
|
42
|
+
return count;
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
function setStatus(message, status = "idle") {
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
elements.status.textContent = message;
|
|
47
|
+
elements.status.dataset.state = status;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
function updateSubmitState() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
const count = feedbackCount();
|
|
52
|
+
elements.submitButton.disabled = count === 0;
|
|
53
|
+
if (count === 0) {
|
|
54
|
+
setStatus("Add any feedback you want to send to the agent.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const noun = count === 1 ? "item" : "items";
|
|
58
|
+
setStatus(`Ready to submit ${count} feedback ${noun}.`, "ready");
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
function setInlineComment(lineNumber, value) {
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
state.inlineComments.set(lineNumber, value);
|
|
63
|
+
updateSubmitState();
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
function setSectionComment(sectionId, value) {
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
state.sectionComments.set(sectionId, value);
|
|
68
|
+
updateSubmitState();
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
function createInlineEditor(line) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function createLineRow(line) {
|
|
79
|
-
const wrapper = document.createElement("div");
|
|
80
|
-
wrapper.className = "message-line";
|
|
81
|
-
|
|
82
|
-
const row = document.createElement("div");
|
|
83
|
-
row.className = "message-line-row";
|
|
84
|
-
|
|
85
|
-
const lineNumber = document.createElement("div");
|
|
86
|
-
lineNumber.className = "line-number";
|
|
87
|
-
lineNumber.textContent = String(line.number);
|
|
88
|
-
row.append(lineNumber);
|
|
89
|
-
|
|
90
|
-
const lineText = document.createElement("pre");
|
|
91
|
-
lineText.className = "line-text";
|
|
92
|
-
lineText.textContent = line.text.length > 0 ? line.text : " ";
|
|
93
|
-
row.append(lineText);
|
|
94
|
-
|
|
95
|
-
const toggle = document.createElement("button");
|
|
96
|
-
toggle.className = "inline-toggle";
|
|
97
|
-
toggle.type = "button";
|
|
98
|
-
toggle.textContent = "Add inline note";
|
|
99
|
-
row.append(toggle);
|
|
100
|
-
|
|
101
|
-
const editor = createInlineEditor(line);
|
|
102
|
-
const syncToggle = () => {
|
|
103
|
-
const hasValue = (state.inlineComments.get(line.number) || "").trim().length > 0;
|
|
104
|
-
toggle.dataset.active = hasValue || !editor.container.hidden ? "true" : "false";
|
|
105
|
-
toggle.textContent = hasValue ? "Edit inline note" : "Add inline note";
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
editor.textarea.addEventListener("input", () => {
|
|
109
|
-
setInlineComment(line.number, editor.textarea.value);
|
|
110
|
-
syncToggle();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
toggle.addEventListener("click", () => {
|
|
114
|
-
editor.container.hidden = !editor.container.hidden;
|
|
115
|
-
syncToggle();
|
|
116
|
-
if (!editor.container.hidden) {
|
|
117
|
-
editor.textarea.focus();
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
wrapper.append(row);
|
|
122
|
-
wrapper.append(editor.container);
|
|
123
|
-
syncToggle();
|
|
124
|
-
return wrapper;
|
|
72
|
+
const container = document.createElement("div");
|
|
73
|
+
container.className = "inline-editor";
|
|
74
|
+
container.hidden = true;
|
|
75
|
+
|
|
76
|
+
const meta = document.createElement("p");
|
|
77
|
+
meta.className = "line-meta";
|
|
78
|
+
meta.textContent = `Inline note for line ${line.number}`;
|
|
79
|
+
container.append(meta);
|
|
80
|
+
|
|
81
|
+
const textarea = document.createElement("textarea");
|
|
82
|
+
textarea.placeholder =
|
|
83
|
+
"Explain what should change here, what is unclear, or what planning detail is missing.";
|
|
84
|
+
textarea.value = state.inlineComments.get(line.number) || "";
|
|
85
|
+
container.append(textarea);
|
|
86
|
+
|
|
87
|
+
return { container, textarea };
|
|
125
88
|
}
|
|
126
89
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Markdown DOM rendering helpers
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Render an array of inline tokens into a parent element using
|
|
96
|
+
* createElement/textContent only (no innerHTML).
|
|
97
|
+
*/
|
|
98
|
+
function renderInlineTokens(parent, tokens) {
|
|
99
|
+
for (const token of tokens) {
|
|
100
|
+
switch (token.type) {
|
|
101
|
+
case "text":
|
|
102
|
+
parent.append(document.createTextNode(token.text));
|
|
103
|
+
break;
|
|
104
|
+
case "code": {
|
|
105
|
+
const code = document.createElement("code");
|
|
106
|
+
code.textContent = token.text;
|
|
107
|
+
parent.append(code);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case "bold": {
|
|
111
|
+
const strong = document.createElement("strong");
|
|
112
|
+
renderInlineTokens(strong, token.children);
|
|
113
|
+
parent.append(strong);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "italic": {
|
|
117
|
+
const em = document.createElement("em");
|
|
118
|
+
renderInlineTokens(em, token.children);
|
|
119
|
+
parent.append(em);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case "boldItalic": {
|
|
123
|
+
const strong = document.createElement("strong");
|
|
124
|
+
const em = document.createElement("em");
|
|
125
|
+
renderInlineTokens(em, token.children);
|
|
126
|
+
strong.append(em);
|
|
127
|
+
parent.append(strong);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case "strikethrough": {
|
|
131
|
+
const s = document.createElement("s");
|
|
132
|
+
renderInlineTokens(s, token.children);
|
|
133
|
+
parent.append(s);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case "link": {
|
|
137
|
+
// Render the link label text inside an <a> element.
|
|
138
|
+
// href is intentionally not set to avoid javascript: URL XSS;
|
|
139
|
+
// the URL is shown as muted text beside the link.
|
|
140
|
+
const a = document.createElement("a");
|
|
141
|
+
renderInlineTokens(a, token.labelTokens);
|
|
142
|
+
const urlSpan = document.createElement("span");
|
|
143
|
+
urlSpan.className = "md-link-url";
|
|
144
|
+
urlSpan.textContent = ` (${token.url})`;
|
|
145
|
+
parent.append(a);
|
|
146
|
+
parent.append(urlSpan);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
default:
|
|
150
|
+
if (typeof token.text === "string") {
|
|
151
|
+
parent.append(document.createTextNode(token.text));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
137
156
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Populate a container element with DOM nodes for a classified (non-fence) line.
|
|
159
|
+
* Leading indentation is preserved via text nodes where relevant.
|
|
160
|
+
*/
|
|
161
|
+
function renderClassifiedLine(container, classification) {
|
|
162
|
+
switch (classification.type) {
|
|
163
|
+
case "blank":
|
|
164
|
+
container.textContent = " ";
|
|
165
|
+
break;
|
|
166
|
+
case "heading": {
|
|
167
|
+
const h = document.createElement(`h${classification.level}`);
|
|
168
|
+
renderInlineTokens(h, globalThis.__tlhMdRenderer.tokenizeLine(classification.text));
|
|
169
|
+
container.append(h);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "hr":
|
|
173
|
+
container.append(document.createElement("hr"));
|
|
174
|
+
break;
|
|
175
|
+
case "blockquote": {
|
|
176
|
+
const bq = document.createElement("blockquote");
|
|
177
|
+
renderInlineTokens(bq, globalThis.__tlhMdRenderer.tokenizeLine(classification.text));
|
|
178
|
+
container.append(bq);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
case "ul":
|
|
182
|
+
case "ol": {
|
|
183
|
+
// Preserve leading indentation as a text node.
|
|
184
|
+
if (classification.indent) {
|
|
185
|
+
container.append(document.createTextNode(classification.indent));
|
|
186
|
+
}
|
|
187
|
+
const bullet = document.createElement("span");
|
|
188
|
+
bullet.style.cssText = "color: var(--mdListBullet)";
|
|
189
|
+
bullet.textContent = classification.bullet;
|
|
190
|
+
container.append(bullet);
|
|
191
|
+
container.append(document.createTextNode(" "));
|
|
192
|
+
renderInlineTokens(container, globalThis.__tlhMdRenderer.tokenizeLine(classification.text));
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
default: {
|
|
196
|
+
// 'plain' and any unrecognised types
|
|
197
|
+
const lineText = typeof classification.text === "string" ? classification.text : "";
|
|
198
|
+
renderInlineTokens(container, globalThis.__tlhMdRenderer.tokenizeLine(lineText));
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
142
203
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
204
|
+
/**
|
|
205
|
+
* Build the content of a markdown line-text container element.
|
|
206
|
+
* lineType is one of: 'plain' | 'fence-open' | 'fence-body' | 'fence-close'
|
|
207
|
+
*/
|
|
208
|
+
function renderLineContent(container, rawText, lineType) {
|
|
209
|
+
if (lineType === "fence-body") {
|
|
210
|
+
// Code-block body: raw text, no inline markdown, code-block styling.
|
|
211
|
+
container.style.cssText = "border-left: 3px solid var(--mdCodeBlockBorder); padding-left: 8px;";
|
|
212
|
+
const code = document.createElement("code");
|
|
213
|
+
code.style.cssText = "display: block; color: var(--mdCodeBlock, var(--text))";
|
|
214
|
+
code.textContent = rawText.length > 0 ? rawText : " ";
|
|
215
|
+
container.append(code);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (lineType === "fence-open" || lineType === "fence-close") {
|
|
220
|
+
// Fence delimiter: visible, styled as block-edge marker.
|
|
221
|
+
const span = document.createElement("span");
|
|
222
|
+
span.style.cssText = "color: var(--mdCodeBlockBorder)";
|
|
223
|
+
span.textContent = rawText;
|
|
224
|
+
container.append(span);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Plain line: classify and render inline markdown.
|
|
229
|
+
if (rawText.trim().length === 0) {
|
|
230
|
+
container.textContent = " ";
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
renderClassifiedLine(container, globalThis.__tlhMdRenderer.classifyLine(rawText));
|
|
235
|
+
}
|
|
147
236
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Line row and section card builders
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
function createLineRow(line, lineType) {
|
|
242
|
+
const wrapper = document.createElement("div");
|
|
243
|
+
wrapper.className = "message-line";
|
|
244
|
+
const row = document.createElement("div");
|
|
245
|
+
row.className = "message-line-row";
|
|
246
|
+
|
|
247
|
+
const lineNumber = document.createElement("div");
|
|
248
|
+
lineNumber.className = "line-number";
|
|
249
|
+
lineNumber.textContent = String(line.number);
|
|
250
|
+
row.append(lineNumber);
|
|
251
|
+
|
|
252
|
+
// Use a <div> with markdown-content class so heading/blockquote/hr/etc.
|
|
253
|
+
// CSS rules inside .markdown-content apply correctly.
|
|
254
|
+
const lineText = document.createElement("div");
|
|
255
|
+
lineText.className = "line-text markdown-content";
|
|
256
|
+
renderLineContent(lineText, line.text, lineType);
|
|
257
|
+
row.append(lineText);
|
|
258
|
+
wrapper.append(row);
|
|
259
|
+
|
|
260
|
+
const toggle = document.createElement("button");
|
|
261
|
+
toggle.className = "inline-toggle";
|
|
262
|
+
toggle.type = "button";
|
|
263
|
+
toggle.textContent = "Add inline note";
|
|
264
|
+
row.append(toggle);
|
|
265
|
+
|
|
266
|
+
const editor = createInlineEditor(line);
|
|
267
|
+
const syncToggle = () => {
|
|
268
|
+
const hasValue = (state.inlineComments.get(line.number) || "").trim().length > 0;
|
|
269
|
+
toggle.dataset.active = hasValue || !editor.container.hidden ? "true" : "false";
|
|
270
|
+
toggle.textContent = hasValue ? "Edit inline note" : "Add inline note";
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
editor.textarea.addEventListener("input", () => {
|
|
274
|
+
setInlineComment(line.number, editor.textarea.value);
|
|
275
|
+
syncToggle();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
toggle.addEventListener("click", () => {
|
|
279
|
+
editor.container.hidden = !editor.container.hidden;
|
|
280
|
+
syncToggle();
|
|
281
|
+
if (!editor.container.hidden) {
|
|
282
|
+
editor.textarea.focus();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
wrapper.append(editor.container);
|
|
287
|
+
syncToggle();
|
|
288
|
+
return wrapper;
|
|
289
|
+
}
|
|
155
290
|
|
|
156
|
-
|
|
291
|
+
function createSectionCard(section) {
|
|
292
|
+
const card = document.createElement("div");
|
|
293
|
+
card.className = "section-card";
|
|
294
|
+
|
|
295
|
+
const title = document.createElement("h3");
|
|
296
|
+
const lineRange =
|
|
297
|
+
section.startLine === section.endLine
|
|
298
|
+
? `line ${section.startLine}`
|
|
299
|
+
: `lines ${section.startLine}-${section.endLine}`;
|
|
300
|
+
title.textContent = `Section ${section.index}`;
|
|
301
|
+
card.append(title);
|
|
302
|
+
|
|
303
|
+
const meta = document.createElement("p");
|
|
304
|
+
meta.className = "section-meta";
|
|
305
|
+
meta.textContent = lineRange;
|
|
306
|
+
card.append(meta);
|
|
307
|
+
|
|
308
|
+
// Render the exact source lines in this section. Do not parse section.text
|
|
309
|
+
// independently: its opening fence may be in an earlier section separated
|
|
310
|
+
// by a blank line.
|
|
311
|
+
const preview = document.createElement("div");
|
|
312
|
+
preview.className = "section-preview markdown-content";
|
|
313
|
+
|
|
314
|
+
const startIndex = Number.isInteger(section.startLine) ? section.startLine - 1 : -1;
|
|
315
|
+
const endIndex = Number.isInteger(section.endLine) ? section.endLine : -1;
|
|
316
|
+
const sourceLines =
|
|
317
|
+
startIndex >= 0 && endIndex >= startIndex && endIndex <= messageData.lines.length
|
|
318
|
+
? messageData.lines.slice(startIndex, endIndex).map((line) => line.text)
|
|
319
|
+
: typeof section.text === "string"
|
|
320
|
+
? section.text.split("\n")
|
|
321
|
+
: [];
|
|
322
|
+
for (let idx = 0; idx < sourceLines.length; idx++) {
|
|
323
|
+
const lineDiv = document.createElement("div");
|
|
324
|
+
const sourceIndex = startIndex >= 0 ? startIndex + idx : -1;
|
|
325
|
+
const lineType = fullMessageFenceState[sourceIndex]?.lineType ?? "plain";
|
|
326
|
+
renderLineContent(lineDiv, sourceLines[idx], lineType);
|
|
327
|
+
preview.append(lineDiv);
|
|
328
|
+
}
|
|
329
|
+
card.append(preview);
|
|
330
|
+
const textarea = document.createElement("textarea");
|
|
331
|
+
textarea.placeholder =
|
|
332
|
+
"Describe what should change across this section or what larger concern should be addressed.";
|
|
333
|
+
textarea.value = state.sectionComments.get(section.id) || "";
|
|
334
|
+
textarea.addEventListener("input", () => {
|
|
335
|
+
setSectionComment(section.id, textarea.value);
|
|
336
|
+
});
|
|
337
|
+
card.append(textarea);
|
|
338
|
+
|
|
339
|
+
return card;
|
|
157
340
|
}
|
|
158
341
|
|
|
159
342
|
function renderMessageLines() {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
343
|
+
elements.messageLines.replaceChildren();
|
|
344
|
+
for (let i = 0; i < messageData.lines.length; i++) {
|
|
345
|
+
elements.messageLines.append(createLineRow(messageData.lines[i], fullMessageFenceState[i].lineType));
|
|
346
|
+
}
|
|
164
347
|
}
|
|
165
348
|
|
|
166
349
|
function renderSectionComments() {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
350
|
+
elements.sectionComments.replaceChildren();
|
|
351
|
+
if (messageData.sections.length === 0) {
|
|
352
|
+
const empty = document.createElement("p");
|
|
353
|
+
empty.className = "empty-hint";
|
|
354
|
+
empty.textContent = "This message does not have any non-empty sections to annotate.";
|
|
355
|
+
elements.sectionComments.append(empty);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
for (const section of messageData.sections) {
|
|
359
|
+
elements.sectionComments.append(createSectionCard(section));
|
|
360
|
+
}
|
|
178
361
|
}
|
|
179
362
|
|
|
180
363
|
function collectPayload() {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
364
|
+
return {
|
|
365
|
+
type: "submit",
|
|
366
|
+
overallComment: state.overallComment,
|
|
367
|
+
inlineComments: Array.from(state.inlineComments.entries()).map(([line, body]) => ({
|
|
368
|
+
line,
|
|
369
|
+
body,
|
|
370
|
+
})),
|
|
371
|
+
sectionComments: Array.from(state.sectionComments.entries()).map(([sectionId, body]) => ({
|
|
372
|
+
sectionId,
|
|
373
|
+
body,
|
|
374
|
+
})),
|
|
375
|
+
};
|
|
187
376
|
}
|
|
188
377
|
|
|
189
378
|
function sendPayload(payload) {
|
|
190
|
-
|
|
191
|
-
|
|
379
|
+
window.glimpse?.send?.(payload);
|
|
380
|
+
window.glimpse?.close?.();
|
|
192
381
|
}
|
|
193
382
|
|
|
194
383
|
function submit() {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
384
|
+
if (feedbackCount() === 0) {
|
|
385
|
+
setStatus("Add at least one comment before submitting.", "error");
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
sendPayload(collectPayload());
|
|
200
389
|
}
|
|
201
390
|
|
|
202
391
|
function cancel() {
|
|
203
|
-
|
|
392
|
+
sendPayload({ type: "cancel" });
|
|
204
393
|
}
|
|
205
394
|
|
|
206
395
|
elements.overallComment.addEventListener("input", () => {
|
|
207
|
-
|
|
208
|
-
|
|
396
|
+
state.overallComment = elements.overallComment.value;
|
|
397
|
+
updateSubmitState();
|
|
209
398
|
});
|
|
210
399
|
|
|
211
400
|
elements.submitButton.addEventListener("click", submit);
|
|
@@ -213,15 +402,15 @@ elements.submitButton.addEventListener("click", submit);
|
|
|
213
402
|
elements.cancelButton.addEventListener("click", cancel);
|
|
214
403
|
|
|
215
404
|
document.addEventListener("keydown", (event) => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
405
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
406
|
+
event.preventDefault();
|
|
407
|
+
submit();
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (event.key === "Escape") {
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
cancel();
|
|
413
|
+
}
|
|
225
414
|
});
|
|
226
415
|
|
|
227
416
|
renderMessageLines();
|