@in-the-loop-labs/pair-review 3.1.3 → 3.2.0
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/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
- package/public/css/pr.css +980 -3
- package/public/js/components/AIPanel.js +7 -4
- package/public/js/components/ChatPanel.js +34 -4
- package/public/js/components/CouncilProgressModal.js +11 -0
- package/public/js/components/NotificationDropdown.js +257 -0
- package/public/js/components/StackAnalysisDialog.js +313 -0
- package/public/js/components/StackProgressModal.js +475 -0
- package/public/js/components/StatusIndicator.js +1 -0
- package/public/js/components/SuggestionNavigator.js +2 -0
- package/public/js/modules/comment-manager.js +7 -0
- package/public/js/modules/comment-minimizer.js +151 -4
- package/public/js/modules/file-comment-manager.js +66 -2
- package/public/js/modules/suggestion-manager.js +2 -1
- package/public/js/pr.js +433 -2
- package/public/js/utils/notification-sounds.js +62 -0
- package/public/local.html +10 -0
- package/public/pr.html +12 -0
- package/public/setup.html +4 -0
- package/src/ai/claude-provider.js +1 -11
- package/src/ai/codex-provider.js +18 -16
- package/src/ai/copilot-provider.js +21 -21
- package/src/ai/gemini-provider.js +10 -0
- package/src/ai/pi-provider.js +22 -25
- package/src/ai/provider.js +26 -3
- package/src/chat/pi-bridge.js +8 -0
- package/src/chat/session-manager.js +1 -0
- package/src/git/base-branch.js +1 -51
- package/src/git/worktree-lock.js +88 -0
- package/src/git/worktree.js +64 -0
- package/src/github/stack-walker.js +196 -0
- package/src/routes/local.js +12 -8
- package/src/routes/pr.js +139 -26
- package/src/routes/sound.js +49 -0
- package/src/routes/stack-analysis.js +886 -0
- package/src/server.js +4 -0
- package/src/setup/stack-setup.js +77 -0
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
* are injected on the right edge of each diff line that has comments, showing
|
|
8
8
|
* a person icon (user comments) or sparkles icon (AI suggestions).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* File-level comments (.file-comment-card inside .file-comments-zone) are also
|
|
11
|
+
* hidden, with an indicator button injected into the file header bar.
|
|
12
|
+
*
|
|
13
|
+
* Clicking an indicator toggles visibility of that line's or file's comments.
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
class CommentMinimizer {
|
|
@@ -24,6 +27,8 @@ class CommentMinimizer {
|
|
|
24
27
|
this._active = false;
|
|
25
28
|
// Track which diff lines have been expanded by the user (Set of diff row elements)
|
|
26
29
|
this._expandedLines = new Set();
|
|
30
|
+
// Track which file-comments-zones have been expanded (Set of zone elements)
|
|
31
|
+
this._expandedFiles = new Set();
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/** @returns {boolean} Whether minimize mode is active */
|
|
@@ -38,6 +43,7 @@ class CommentMinimizer {
|
|
|
38
43
|
setMinimized(minimized) {
|
|
39
44
|
this._active = minimized;
|
|
40
45
|
this._expandedLines.clear();
|
|
46
|
+
this._expandedFiles.clear();
|
|
41
47
|
|
|
42
48
|
const diffContainer = document.getElementById('diff-container');
|
|
43
49
|
if (!diffContainer) return;
|
|
@@ -50,6 +56,8 @@ class CommentMinimizer {
|
|
|
50
56
|
this._removeAllIndicators();
|
|
51
57
|
// Remove any per-line expansion overrides
|
|
52
58
|
document.querySelectorAll('.comment-expanded').forEach(el => el.classList.remove('comment-expanded'));
|
|
59
|
+
// Remove any per-file expansion overrides
|
|
60
|
+
document.querySelectorAll('.file-comments-expanded').forEach(el => el.classList.remove('file-comments-expanded'));
|
|
53
61
|
}
|
|
54
62
|
}
|
|
55
63
|
|
|
@@ -103,10 +111,13 @@ class CommentMinimizer {
|
|
|
103
111
|
lineMap.set(diffRow, entry);
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
// Inject indicators
|
|
114
|
+
// Inject line-level indicators
|
|
107
115
|
for (const [diffRow, info] of lineMap) {
|
|
108
116
|
this._injectIndicator(diffRow, info);
|
|
109
117
|
}
|
|
118
|
+
|
|
119
|
+
// Scan file-comments-zones and inject file-header indicators
|
|
120
|
+
this._refreshFileIndicators();
|
|
110
121
|
}
|
|
111
122
|
|
|
112
123
|
/**
|
|
@@ -266,7 +277,22 @@ class CommentMinimizer {
|
|
|
266
277
|
expandForElement(element) {
|
|
267
278
|
if (!this._active) return;
|
|
268
279
|
|
|
269
|
-
//
|
|
280
|
+
// Check if this element is inside a file-comments-zone (file-level comment)
|
|
281
|
+
const zone = element.closest('.file-comments-zone');
|
|
282
|
+
if (zone) {
|
|
283
|
+
if (this._expandedFiles.has(zone)) return; // already expanded
|
|
284
|
+
this._expandedFiles.add(zone);
|
|
285
|
+
zone.classList.add('file-comments-expanded');
|
|
286
|
+
// Update the file-header indicator button
|
|
287
|
+
const wrapper = zone.closest('.d2h-file-wrapper');
|
|
288
|
+
const btn = wrapper?.querySelector('.d2h-file-header .file-comment-indicator');
|
|
289
|
+
if (btn) {
|
|
290
|
+
btn.classList.add('expanded');
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Line-level: find the containing comment/suggestion row
|
|
270
296
|
const commentRow = element.closest('.user-comment-row, .ai-suggestion-row') || element;
|
|
271
297
|
if (!commentRow.classList.contains('user-comment-row') && !commentRow.classList.contains('ai-suggestion-row')) {
|
|
272
298
|
return;
|
|
@@ -290,9 +316,130 @@ class CommentMinimizer {
|
|
|
290
316
|
}
|
|
291
317
|
}
|
|
292
318
|
|
|
293
|
-
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
// File-level comment indicators
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Scan all file-comments-zones and inject indicator buttons into file headers.
|
|
325
|
+
*/
|
|
326
|
+
_refreshFileIndicators() {
|
|
327
|
+
const zones = document.querySelectorAll('.file-comments-zone');
|
|
328
|
+
for (const zone of zones) {
|
|
329
|
+
const cards = zone.querySelectorAll('.file-comment-card');
|
|
330
|
+
if (cards.length === 0) continue;
|
|
331
|
+
|
|
332
|
+
// Count comment types
|
|
333
|
+
const info = { hasUser: false, hasAI: false, hasAdopted: false, userCount: 0, aiCount: 0, adoptedCount: 0 };
|
|
334
|
+
for (const card of cards) {
|
|
335
|
+
// Skip collapsed cards (adopted/dismissed originals remain in DOM)
|
|
336
|
+
if (card.classList.contains('collapsed')) continue;
|
|
337
|
+
|
|
338
|
+
if (card.classList.contains('ai-suggestion')) {
|
|
339
|
+
info.hasAI = true;
|
|
340
|
+
info.aiCount++;
|
|
341
|
+
} else if (card.classList.contains('user-comment')) {
|
|
342
|
+
if (card.classList.contains('adopted-comment')) {
|
|
343
|
+
info.hasAdopted = true;
|
|
344
|
+
info.adoptedCount++;
|
|
345
|
+
} else {
|
|
346
|
+
info.hasUser = true;
|
|
347
|
+
info.userCount++;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (info.userCount + info.aiCount + info.adoptedCount === 0) continue;
|
|
353
|
+
|
|
354
|
+
// Find the file header — zone and header are siblings inside .d2h-file-wrapper
|
|
355
|
+
const wrapper = zone.closest('.d2h-file-wrapper');
|
|
356
|
+
const header = wrapper?.querySelector('.d2h-file-header');
|
|
357
|
+
if (!header) continue;
|
|
358
|
+
|
|
359
|
+
this._injectFileIndicator(header, zone, info);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Inject an indicator button into a file header, positioned before the comment button.
|
|
365
|
+
* @param {HTMLElement} header - The .d2h-file-header element
|
|
366
|
+
* @param {HTMLElement} zone - The .file-comments-zone element
|
|
367
|
+
* @param {Object} info - { hasUser, hasAI, hasAdopted, userCount, aiCount, adoptedCount }
|
|
368
|
+
*/
|
|
369
|
+
_injectFileIndicator(header, zone, info) {
|
|
370
|
+
// Don't double-inject
|
|
371
|
+
if (header.querySelector('.file-comment-indicator')) return;
|
|
372
|
+
|
|
373
|
+
const btn = document.createElement('button');
|
|
374
|
+
btn.className = 'file-comment-indicator';
|
|
375
|
+
btn.type = 'button';
|
|
376
|
+
|
|
377
|
+
// Build icon — pick the dominant type icon
|
|
378
|
+
const icons = [];
|
|
379
|
+
if (info.hasUser) {
|
|
380
|
+
icons.push(`<span class="indicator-icon indicator-user">${CommentMinimizer.PERSON_ICON}</span>`);
|
|
381
|
+
}
|
|
382
|
+
if (info.hasAdopted) {
|
|
383
|
+
icons.push(`<span class="indicator-icon indicator-adopted">${CommentMinimizer.AI_COMMENT_ICON}</span>`);
|
|
384
|
+
}
|
|
385
|
+
if (info.hasAI) {
|
|
386
|
+
icons.push(`<span class="indicator-icon indicator-ai">${CommentMinimizer.SPARKLES_ICON}</span>`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const total = info.userCount + info.adoptedCount + info.aiCount;
|
|
390
|
+
const countBadge = total > 1 ? `<span class="indicator-count">${total}</span>` : '';
|
|
391
|
+
|
|
392
|
+
btn.innerHTML = icons.join('') + countBadge;
|
|
393
|
+
|
|
394
|
+
const totalLabel = [];
|
|
395
|
+
if (info.userCount) totalLabel.push(`${info.userCount} file comment${info.userCount !== 1 ? 's' : ''}`);
|
|
396
|
+
if (info.adoptedCount) totalLabel.push(`${info.adoptedCount} adopted`);
|
|
397
|
+
if (info.aiCount) totalLabel.push(`${info.aiCount} suggestion${info.aiCount !== 1 ? 's' : ''}`);
|
|
398
|
+
btn.title = totalLabel.join(', ');
|
|
399
|
+
|
|
400
|
+
// Restore expanded state
|
|
401
|
+
if (this._expandedFiles.has(zone)) {
|
|
402
|
+
btn.classList.add('expanded');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
btn.addEventListener('click', (e) => {
|
|
406
|
+
e.stopPropagation();
|
|
407
|
+
e.preventDefault();
|
|
408
|
+
this._toggleFileComments(zone, btn);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Insert before the file-header-comment-btn if present, otherwise append
|
|
412
|
+
const commentBtn = header.querySelector('.file-header-comment-btn');
|
|
413
|
+
if (commentBtn) {
|
|
414
|
+
header.insertBefore(btn, commentBtn);
|
|
415
|
+
} else {
|
|
416
|
+
header.appendChild(btn);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Toggle visibility of file-level comments for a specific file.
|
|
422
|
+
* @param {HTMLElement} zone - The .file-comments-zone element
|
|
423
|
+
* @param {HTMLElement} btn - The indicator button
|
|
424
|
+
*/
|
|
425
|
+
_toggleFileComments(zone, btn) {
|
|
426
|
+
const isExpanded = this._expandedFiles.has(zone);
|
|
427
|
+
|
|
428
|
+
if (isExpanded) {
|
|
429
|
+
this._expandedFiles.delete(zone);
|
|
430
|
+
btn.classList.remove('expanded');
|
|
431
|
+
zone.classList.remove('file-comments-expanded');
|
|
432
|
+
} else {
|
|
433
|
+
this._expandedFiles.add(zone);
|
|
434
|
+
btn.classList.add('expanded');
|
|
435
|
+
zone.classList.add('file-comments-expanded');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Remove all indicator buttons (both line-level and file-level) from the DOM. */
|
|
294
440
|
_removeAllIndicators() {
|
|
295
441
|
document.querySelectorAll('.comment-indicator').forEach(btn => btn.remove());
|
|
442
|
+
document.querySelectorAll('.file-comment-indicator').forEach(btn => btn.remove());
|
|
296
443
|
}
|
|
297
444
|
}
|
|
298
445
|
|
|
@@ -17,12 +17,15 @@ class FileCommentManager {
|
|
|
17
17
|
if (chatBtn && window.chatPanel) {
|
|
18
18
|
e.stopPropagation();
|
|
19
19
|
const suggestionCard = chatBtn.closest('.ai-suggestion');
|
|
20
|
-
const bodyText = suggestionCard?.dataset?.
|
|
21
|
-
? JSON.parse(suggestionCard.dataset.
|
|
20
|
+
const bodyText = suggestionCard?.dataset?.formattedBody
|
|
21
|
+
? JSON.parse(suggestionCard.dataset.formattedBody)
|
|
22
|
+
: suggestionCard?.dataset?.originalBody
|
|
23
|
+
? JSON.parse(suggestionCard.dataset.originalBody) : '';
|
|
22
24
|
window.chatPanel.open({
|
|
23
25
|
reviewId: this.prManager?.currentPR?.id,
|
|
24
26
|
suggestionId: chatBtn.dataset.suggestionId,
|
|
25
27
|
suggestionContext: {
|
|
28
|
+
suggestionId: chatBtn.dataset.suggestionId || null,
|
|
26
29
|
title: chatBtn.dataset.title || '',
|
|
27
30
|
body: bodyText,
|
|
28
31
|
type: suggestionCard?.querySelector('.ai-suggestion-badge')?.dataset?.type || '',
|
|
@@ -312,6 +315,16 @@ class FileCommentManager {
|
|
|
312
315
|
// Update count badge
|
|
313
316
|
this.updateCommentCount(zone);
|
|
314
317
|
|
|
318
|
+
// Refresh minimize-mode indicators so file-header counts stay current
|
|
319
|
+
if (this.prManager?.commentMinimizer) {
|
|
320
|
+
this.prManager.commentMinimizer.refreshIndicators();
|
|
321
|
+
// Auto-expand so the new comment stays visible in minimize mode
|
|
322
|
+
const newCard = zone.querySelector(`.file-comment-card[data-comment-id="${commentData.id}"]`);
|
|
323
|
+
if (newCard) {
|
|
324
|
+
this.prManager.commentMinimizer.expandForElement(newCard);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
315
328
|
// Notify AI Panel if available
|
|
316
329
|
if (window.aiPanel?.addComment) {
|
|
317
330
|
window.aiPanel.addComment(commentData);
|
|
@@ -322,6 +335,8 @@ class FileCommentManager {
|
|
|
322
335
|
this.prManager.updateCommentCount();
|
|
323
336
|
}
|
|
324
337
|
|
|
338
|
+
window.chatPanel?.queueUserActionHint(`[User Action: created comment ${result.commentId}]`);
|
|
339
|
+
|
|
325
340
|
} catch (error) {
|
|
326
341
|
console.error('Error saving file-level comment:', error);
|
|
327
342
|
if (window.toast) {
|
|
@@ -432,6 +447,7 @@ class FileCommentManager {
|
|
|
432
447
|
// Store original markdown body for adopt functionality via extractSuggestionData
|
|
433
448
|
// Use JSON.stringify to preserve newlines and special characters (matches line-level suggestions)
|
|
434
449
|
card.dataset.originalBody = JSON.stringify(suggestion.body || '');
|
|
450
|
+
card.dataset.formattedBody = JSON.stringify(suggestion.formattedBody || '');
|
|
435
451
|
|
|
436
452
|
// Store target info on the card for reliable retrieval in getFileAndLineInfo
|
|
437
453
|
// File-level suggestions don't have line numbers, just the file name
|
|
@@ -604,6 +620,16 @@ class FileCommentManager {
|
|
|
604
620
|
this.displayUserComment(zone, commentData);
|
|
605
621
|
this.updateCommentCount(zone);
|
|
606
622
|
|
|
623
|
+
// Refresh minimize-mode indicators so file-header counts stay current
|
|
624
|
+
if (this.prManager?.commentMinimizer) {
|
|
625
|
+
this.prManager.commentMinimizer.refreshIndicators();
|
|
626
|
+
// Auto-expand so the new comment stays visible in minimize mode
|
|
627
|
+
const newCard = zone.querySelector(`.file-comment-card[data-comment-id="${commentData.id}"]`);
|
|
628
|
+
if (newCard) {
|
|
629
|
+
this.prManager.commentMinimizer.expandForElement(newCard);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
607
633
|
// Update parent comment count for Preview button
|
|
608
634
|
if (this.prManager?.updateCommentCount) {
|
|
609
635
|
this.prManager.updateCommentCount();
|
|
@@ -619,6 +645,8 @@ class FileCommentManager {
|
|
|
619
645
|
window.aiPanel.updateFindingStatus(suggestion.id, 'adopted');
|
|
620
646
|
}
|
|
621
647
|
|
|
648
|
+
window.chatPanel?.queueUserActionHint(`[User Action: adopted suggestion ${suggestion.id}]`);
|
|
649
|
+
|
|
622
650
|
} catch (error) {
|
|
623
651
|
console.error('Error adopting suggestion:', error);
|
|
624
652
|
if (window.toast) {
|
|
@@ -657,11 +685,18 @@ class FileCommentManager {
|
|
|
657
685
|
|
|
658
686
|
this.updateCommentCount(zone);
|
|
659
687
|
|
|
688
|
+
// Refresh minimize-mode indicators so file-header counts stay current
|
|
689
|
+
if (this.prManager?.commentMinimizer) {
|
|
690
|
+
this.prManager.commentMinimizer.refreshIndicators();
|
|
691
|
+
}
|
|
692
|
+
|
|
660
693
|
// Update finding status in AI Panel (mark suggestion as dismissed)
|
|
661
694
|
if (window.aiPanel?.updateFindingStatus) {
|
|
662
695
|
window.aiPanel.updateFindingStatus(suggestionId, 'dismissed');
|
|
663
696
|
}
|
|
664
697
|
|
|
698
|
+
window.chatPanel?.queueUserActionHint(`[User Action: dismissed suggestion ${suggestionId}]`);
|
|
699
|
+
|
|
665
700
|
} catch (error) {
|
|
666
701
|
console.error('Error dismissing suggestion:', error);
|
|
667
702
|
if (window.toast) {
|
|
@@ -703,6 +738,13 @@ class FileCommentManager {
|
|
|
703
738
|
// Update comment count (for consistency with dismissAISuggestion)
|
|
704
739
|
this.updateCommentCount(zone);
|
|
705
740
|
|
|
741
|
+
// Refresh minimize-mode indicators so file-header counts stay current
|
|
742
|
+
if (this.prManager?.commentMinimizer) {
|
|
743
|
+
this.prManager.commentMinimizer.refreshIndicators();
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
window.chatPanel?.queueUserActionHint(`[User Action: restored suggestion ${suggestionId}]`);
|
|
747
|
+
|
|
706
748
|
} catch (error) {
|
|
707
749
|
console.error('Error restoring suggestion:', error);
|
|
708
750
|
if (window.toast) {
|
|
@@ -848,6 +890,16 @@ class FileCommentManager {
|
|
|
848
890
|
this.displayUserComment(zone, commentData);
|
|
849
891
|
this.updateCommentCount(zone);
|
|
850
892
|
|
|
893
|
+
// Refresh minimize-mode indicators so file-header counts stay current
|
|
894
|
+
if (this.prManager?.commentMinimizer) {
|
|
895
|
+
this.prManager.commentMinimizer.refreshIndicators();
|
|
896
|
+
// Auto-expand so the new comment stays visible in minimize mode
|
|
897
|
+
const newCard = zone.querySelector(`.file-comment-card[data-comment-id="${commentData.id}"]`);
|
|
898
|
+
if (newCard) {
|
|
899
|
+
this.prManager.commentMinimizer.expandForElement(newCard);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
851
903
|
// Update parent comment count for Preview button
|
|
852
904
|
if (this.prManager?.updateCommentCount) {
|
|
853
905
|
this.prManager.updateCommentCount();
|
|
@@ -863,6 +915,8 @@ class FileCommentManager {
|
|
|
863
915
|
window.aiPanel.updateFindingStatus(suggestion.id, 'adopted');
|
|
864
916
|
}
|
|
865
917
|
|
|
918
|
+
window.chatPanel?.queueUserActionHint(`[User Action: adopted suggestion ${suggestion.id}]`);
|
|
919
|
+
|
|
866
920
|
} catch (error) {
|
|
867
921
|
console.error('Error adopting suggestion with edit:', error);
|
|
868
922
|
if (window.toast) {
|
|
@@ -1012,6 +1066,11 @@ class FileCommentManager {
|
|
|
1012
1066
|
|
|
1013
1067
|
this.updateCommentCount(zone);
|
|
1014
1068
|
|
|
1069
|
+
// Refresh minimize-mode indicators so file-header counts stay current
|
|
1070
|
+
if (this.prManager?.commentMinimizer) {
|
|
1071
|
+
this.prManager.commentMinimizer.refreshIndicators();
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1015
1074
|
// Update parent comment count
|
|
1016
1075
|
if (this.prManager?.updateCommentCount) {
|
|
1017
1076
|
this.prManager.updateCommentCount();
|
|
@@ -1029,6 +1088,11 @@ class FileCommentManager {
|
|
|
1029
1088
|
window.aiPanel.updateFindingStatus(apiResult.dismissedSuggestionId, 'dismissed');
|
|
1030
1089
|
}
|
|
1031
1090
|
|
|
1091
|
+
if (apiResult.dismissedSuggestionId) {
|
|
1092
|
+
window.chatPanel?.queueUserActionHint(`[User Action: dismissed suggestion ${apiResult.dismissedSuggestionId}]`);
|
|
1093
|
+
}
|
|
1094
|
+
window.chatPanel?.queueUserActionHint(`[User Action: dismissed comment ${commentId}]`);
|
|
1095
|
+
|
|
1032
1096
|
} catch (error) {
|
|
1033
1097
|
console.error('Error deleting comment:', error);
|
|
1034
1098
|
if (window.toast) {
|
|
@@ -24,8 +24,9 @@ class SuggestionManager {
|
|
|
24
24
|
reviewId: this.prManager?.currentPR?.id,
|
|
25
25
|
suggestionId: chatBtn.dataset.suggestionId,
|
|
26
26
|
suggestionContext: {
|
|
27
|
+
suggestionId: chatBtn.dataset.suggestionId || null,
|
|
27
28
|
title: chatBtn.dataset.title || suggestionData.suggestionTitle || '',
|
|
28
|
-
body: suggestionData.suggestionText || '',
|
|
29
|
+
body: suggestionData.formattedBody || suggestionData.suggestionText || '',
|
|
29
30
|
type: suggestionData.suggestionType || '',
|
|
30
31
|
file: chatBtn.dataset.file || '',
|
|
31
32
|
line_start: suggestionDiv?.dataset?.lineNumber ? parseInt(suggestionDiv.dataset.lineNumber) : null,
|