@in-the-loop-labs/pair-review 2.3.1 → 2.3.3
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 +9 -1
- package/public/js/components/AIPanel.js +51 -0
- package/public/js/components/PanelGroup.js +38 -13
- package/public/js/local.js +3 -1
- package/public/js/modules/comment-manager.js +16 -12
- package/public/js/modules/file-comment-manager.js +8 -6
- package/public/js/pr.js +23 -2
- package/public/local.html +1 -1
- package/public/pr.html +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pair-review",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "pair-review app integration — Open PRs and local changes in the pair-review web UI, run server-side AI analysis, and address review feedback. Requires the pair-review MCP server.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-critic",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "AI-powered code review analysis — Run three-level AI analysis and implement-review-fix loops directly in your coding agent. Works standalone, no server required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
package/public/css/pr.css
CHANGED
|
@@ -2106,6 +2106,7 @@ tr.newly-expanded .d2h-code-line-ctn {
|
|
|
2106
2106
|
position: relative;
|
|
2107
2107
|
display: flex;
|
|
2108
2108
|
align-items: center;
|
|
2109
|
+
flex-shrink: 0;
|
|
2109
2110
|
}
|
|
2110
2111
|
|
|
2111
2112
|
.btn-reasoning-toggle {
|
|
@@ -2439,7 +2440,7 @@ tr.newly-expanded .d2h-code-line-ctn {
|
|
|
2439
2440
|
text-overflow: ellipsis;
|
|
2440
2441
|
white-space: nowrap;
|
|
2441
2442
|
color: var(--color-text-secondary);
|
|
2442
|
-
|
|
2443
|
+
min-width: 0;
|
|
2443
2444
|
font-size: 13px;
|
|
2444
2445
|
}
|
|
2445
2446
|
|
|
@@ -4006,12 +4007,15 @@ tr.line-range-start .d2h-code-line-ctn {
|
|
|
4006
4007
|
margin-bottom: 4px;
|
|
4007
4008
|
padding-bottom: 4px;
|
|
4008
4009
|
border-bottom: 1px solid rgba(139, 92, 246, 0.1);
|
|
4010
|
+
min-width: 0;
|
|
4009
4011
|
}
|
|
4010
4012
|
|
|
4011
4013
|
.user-comment-header-left {
|
|
4012
4014
|
display: flex;
|
|
4013
4015
|
align-items: center;
|
|
4014
4016
|
gap: 8px;
|
|
4017
|
+
min-width: 0;
|
|
4018
|
+
overflow: hidden;
|
|
4015
4019
|
}
|
|
4016
4020
|
|
|
4017
4021
|
.user-comment-line-info {
|
|
@@ -4088,6 +4092,9 @@ tr.line-range-start .d2h-code-line-ctn {
|
|
|
4088
4092
|
font-size: 12px;
|
|
4089
4093
|
font-style: italic;
|
|
4090
4094
|
margin-left: 6px;
|
|
4095
|
+
overflow: hidden;
|
|
4096
|
+
text-overflow: ellipsis;
|
|
4097
|
+
white-space: nowrap;
|
|
4091
4098
|
}
|
|
4092
4099
|
|
|
4093
4100
|
/* Eye icon toggle button */
|
|
@@ -4156,6 +4163,7 @@ tr.line-range-start .d2h-code-line-ctn {
|
|
|
4156
4163
|
align-items: center;
|
|
4157
4164
|
gap: 4px;
|
|
4158
4165
|
margin-left: auto;
|
|
4166
|
+
flex-shrink: 0;
|
|
4159
4167
|
}
|
|
4160
4168
|
|
|
4161
4169
|
/* Hide edit/delete buttons when in editing mode */
|
|
@@ -207,6 +207,45 @@ class AIPanel {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Get the localStorage key for the collapsed state (per-review)
|
|
212
|
+
* @returns {string|null} Storage key or null if no PR context
|
|
213
|
+
*/
|
|
214
|
+
_getCollapsedStorageKey() {
|
|
215
|
+
if (!this.currentPRKey) return null;
|
|
216
|
+
return `pair-review-panel-collapsed_${this.currentPRKey}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Save the collapsed state to localStorage (per-review)
|
|
221
|
+
*/
|
|
222
|
+
_saveCollapsedState() {
|
|
223
|
+
const key = this._getCollapsedStorageKey();
|
|
224
|
+
if (key) {
|
|
225
|
+
localStorage.setItem(key, this.isCollapsed ? 'true' : 'false');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Restore the collapsed state from localStorage, or collapse for new reviews.
|
|
231
|
+
* If the user had the panel expanded for this review, expand it.
|
|
232
|
+
* Otherwise (new review or previously collapsed), collapse it.
|
|
233
|
+
*/
|
|
234
|
+
_restoreOrCollapsePanel() {
|
|
235
|
+
const key = this._getCollapsedStorageKey();
|
|
236
|
+
if (key) {
|
|
237
|
+
const stored = localStorage.getItem(key);
|
|
238
|
+
if (stored === 'false') {
|
|
239
|
+
this.expand();
|
|
240
|
+
} else {
|
|
241
|
+
// 'true' or no saved state (new review) → collapse
|
|
242
|
+
this.collapse();
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
this.collapse();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
210
249
|
/**
|
|
211
250
|
* Get the localStorage key for the filter state (per-review)
|
|
212
251
|
* @returns {string|null} Storage key or null if no PR context
|
|
@@ -275,6 +314,7 @@ class AIPanel {
|
|
|
275
314
|
// Set CSS variable to 0 so width calculations don't reserve space
|
|
276
315
|
document.documentElement.style.setProperty('--ai-panel-width', '0px');
|
|
277
316
|
window.panelGroup?._onReviewVisibilityChanged(false);
|
|
317
|
+
this._saveCollapsedState();
|
|
278
318
|
}
|
|
279
319
|
|
|
280
320
|
expand() {
|
|
@@ -285,6 +325,7 @@ class AIPanel {
|
|
|
285
325
|
// Restore CSS variable from saved width or default
|
|
286
326
|
document.documentElement.style.setProperty('--ai-panel-width', `${this.getEffectivePanelWidth()}px`);
|
|
287
327
|
window.panelGroup?._onReviewVisibilityChanged(true);
|
|
328
|
+
this._saveCollapsedState();
|
|
288
329
|
}
|
|
289
330
|
|
|
290
331
|
/**
|
|
@@ -296,6 +337,7 @@ class AIPanel {
|
|
|
296
337
|
*/
|
|
297
338
|
setPR(owner, repo, number) {
|
|
298
339
|
this.currentPRKey = `${owner}/${repo}#${number}`;
|
|
340
|
+
this._restoreOrCollapsePanel();
|
|
299
341
|
this.restoreSegmentSelection();
|
|
300
342
|
this.restoreFilterState();
|
|
301
343
|
}
|
|
@@ -327,6 +369,10 @@ class AIPanel {
|
|
|
327
369
|
*/
|
|
328
370
|
setAnalysisState(state) {
|
|
329
371
|
this.analysisState = state;
|
|
372
|
+
// Auto-expand panel when analysis starts
|
|
373
|
+
if (state === 'loading' && this.isCollapsed) {
|
|
374
|
+
this.expand();
|
|
375
|
+
}
|
|
330
376
|
// Re-render if currently showing empty state
|
|
331
377
|
if (this.findings.length === 0 && this.selectedSegment === 'ai') {
|
|
332
378
|
this.renderFindings();
|
|
@@ -1737,3 +1783,8 @@ class AIPanel {
|
|
|
1737
1783
|
document.addEventListener('DOMContentLoaded', () => {
|
|
1738
1784
|
window.aiPanel = new AIPanel();
|
|
1739
1785
|
});
|
|
1786
|
+
|
|
1787
|
+
// Export for testing
|
|
1788
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
1789
|
+
module.exports = { AIPanel };
|
|
1790
|
+
}
|
|
@@ -41,6 +41,7 @@ class PanelGroup {
|
|
|
41
41
|
this._reviewVisible = !document.getElementById('ai-panel')?.classList.contains('collapsed');
|
|
42
42
|
this._chatVisible = false;
|
|
43
43
|
this._popoverVisible = false;
|
|
44
|
+
this._currentPRKey = null; // Set via setPR() for per-review chat persistence
|
|
44
45
|
|
|
45
46
|
// Read persisted layout
|
|
46
47
|
const savedLayout = localStorage.getItem(PanelGroup.STORAGE_KEY);
|
|
@@ -79,20 +80,17 @@ class PanelGroup {
|
|
|
79
80
|
// Apply initial layout
|
|
80
81
|
this._applyLayout(this._layout);
|
|
81
82
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
if (chatState === 'available') {
|
|
85
|
-
this._restoreChatFromStorage();
|
|
86
|
-
} else {
|
|
87
|
-
// Chat not available yet — zero out CSS variable so max-width calcs are correct.
|
|
88
|
-
document.documentElement.style.setProperty('--chat-panel-width', '0px');
|
|
89
|
-
}
|
|
83
|
+
// Chat starts hidden; per-review state restored when setPR() is called.
|
|
84
|
+
document.documentElement.style.setProperty('--chat-panel-width', '0px');
|
|
90
85
|
|
|
91
86
|
// Listen for late chat-state transitions (config fetch may complete after constructor)
|
|
92
87
|
window.addEventListener('chat-state-changed', (e) => {
|
|
93
88
|
const state = e.detail?.state;
|
|
94
89
|
if (state === 'available') {
|
|
95
|
-
|
|
90
|
+
// Only restore if PR context is already set (setPR was called before chat became available)
|
|
91
|
+
if (this._currentPRKey) {
|
|
92
|
+
this._restoreChatFromStorage();
|
|
93
|
+
}
|
|
96
94
|
} else if (state === 'unavailable' && this.chatToggleBtn) {
|
|
97
95
|
this.chatToggleBtn.title = 'Install and configure Pi to enable chat';
|
|
98
96
|
}
|
|
@@ -422,10 +420,34 @@ class PanelGroup {
|
|
|
422
420
|
}
|
|
423
421
|
|
|
424
422
|
/**
|
|
425
|
-
*
|
|
423
|
+
* Set the current PR for per-review chat visibility persistence.
|
|
424
|
+
* Call this when a PR/review loads (after aiPanel.setPR).
|
|
425
|
+
* @param {string} prKey - e.g. "owner/repo#123" or "local/local#2"
|
|
426
|
+
*/
|
|
427
|
+
setPR(prKey) {
|
|
428
|
+
this._currentPRKey = prKey;
|
|
429
|
+
// Now that we know the review context, restore chat state if chat is available
|
|
430
|
+
if (this._isChatAvailable()) {
|
|
431
|
+
this._restoreChatFromStorage();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Get the per-review localStorage key for chat visibility.
|
|
437
|
+
* @returns {string|null} Storage key or null if no PR context
|
|
438
|
+
*/
|
|
439
|
+
_getChatVisibleStorageKey() {
|
|
440
|
+
if (!this._currentPRKey) return null;
|
|
441
|
+
return `${PanelGroup.CHAT_VISIBLE_KEY}_${this._currentPRKey}`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Restore chat visibility from localStorage (per-review).
|
|
446
|
+
* For new reviews (no saved state), chat stays hidden.
|
|
426
447
|
*/
|
|
427
448
|
_restoreChatFromStorage() {
|
|
428
|
-
const
|
|
449
|
+
const key = this._getChatVisibleStorageKey();
|
|
450
|
+
const savedChatVisible = key ? localStorage.getItem(key) : null;
|
|
429
451
|
if (savedChatVisible === 'true') {
|
|
430
452
|
this._chatVisible = true;
|
|
431
453
|
this.chatPanel.open({ suppressFocus: true });
|
|
@@ -478,8 +500,11 @@ class PanelGroup {
|
|
|
478
500
|
this.chatToggleBtn.classList.toggle('active', visible);
|
|
479
501
|
}
|
|
480
502
|
|
|
481
|
-
// Persist chat visibility
|
|
482
|
-
|
|
503
|
+
// Persist chat visibility (per-review)
|
|
504
|
+
const chatKey = this._getChatVisibleStorageKey();
|
|
505
|
+
if (chatKey) {
|
|
506
|
+
localStorage.setItem(chatKey, visible ? 'true' : 'false');
|
|
507
|
+
}
|
|
483
508
|
|
|
484
509
|
// Clear inline flex heights so the remaining panel fills the space
|
|
485
510
|
if (this._layout.startsWith('v-')) {
|
package/public/js/local.js
CHANGED
|
@@ -374,6 +374,7 @@ class LocalManager {
|
|
|
374
374
|
if (data.running && data.analysisId) {
|
|
375
375
|
manager.currentAnalysisId = data.analysisId;
|
|
376
376
|
manager.isAnalyzing = true;
|
|
377
|
+
window.aiPanel?.setAnalysisState('loading');
|
|
377
378
|
manager.setButtonAnalyzing(data.analysisId);
|
|
378
379
|
|
|
379
380
|
// Show the appropriate progress modal
|
|
@@ -791,10 +792,11 @@ class LocalManager {
|
|
|
791
792
|
window.aiPanel = new window.AIPanel();
|
|
792
793
|
}
|
|
793
794
|
|
|
794
|
-
// Set local context for AI Panel (restores
|
|
795
|
+
// Set local context for AI Panel and Panel Group (restores per-review state from localStorage)
|
|
795
796
|
if (window.aiPanel?.setPR) {
|
|
796
797
|
window.aiPanel.setPR('local', reviewData.repository, this.reviewId);
|
|
797
798
|
}
|
|
799
|
+
window.panelGroup?.setPR(`local/${reviewData.repository}#${this.reviewId}`);
|
|
798
800
|
|
|
799
801
|
// Load saved comments using the restored filter state from AI Panel
|
|
800
802
|
const includeDismissed = window.aiPanel?.showDismissedComments || false;
|
|
@@ -594,12 +594,14 @@ class CommentManager {
|
|
|
594
594
|
const commentHTML = `
|
|
595
595
|
<div class="${commentClasses}">
|
|
596
596
|
<div class="user-comment-header">
|
|
597
|
-
<
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
597
|
+
<div class="user-comment-header-left">
|
|
598
|
+
<span class="comment-origin-icon">
|
|
599
|
+
${commentIcon}
|
|
600
|
+
</span>
|
|
601
|
+
<span class="user-comment-line-info">${lineInfo}</span>
|
|
602
|
+
${expandedContextIndicator}
|
|
603
|
+
${metadataHTML}
|
|
604
|
+
</div>
|
|
603
605
|
<div class="user-comment-actions">
|
|
604
606
|
<button class="btn-chat-comment" title="Chat about comment" data-chat-comment-id="${comment.id}" data-chat-file="${escapeHtml(comment.file || '')}" data-chat-line-start="${comment.line_start ?? ''}" data-chat-line-end="${comment.line_end || comment.line_start || ''}" data-chat-parent-id="${comment.parent_id || ''}">
|
|
605
607
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"/></svg>
|
|
@@ -666,12 +668,14 @@ class CommentManager {
|
|
|
666
668
|
const commentHTML = `
|
|
667
669
|
<div class="user-comment editing-mode ${comment.parent_id ? 'adopted-comment comment-ai-origin' : 'comment-user-origin'}">
|
|
668
670
|
<div class="user-comment-header">
|
|
669
|
-
<
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
671
|
+
<div class="user-comment-header-left">
|
|
672
|
+
<span class="comment-origin-icon">
|
|
673
|
+
${commentIcon}
|
|
674
|
+
</span>
|
|
675
|
+
<span class="user-comment-line-info">${lineInfo}</span>
|
|
676
|
+
${comment.type === 'praise' ? `<span class="adopted-praise-badge" title="Nice Work"><svg viewBox="0 0 16 16" width="12" height="12"><path d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25z"/></svg>Nice Work</span>` : ''}
|
|
677
|
+
${comment.title ? `<span class="adopted-title">${escapeHtml(comment.title)}</span>` : ''}
|
|
678
|
+
</div>
|
|
675
679
|
</div>
|
|
676
680
|
<!-- Hidden body div for saving - pre-populate with markdown rendered content and store original -->
|
|
677
681
|
<div class="user-comment-body" style="display: none;" data-original-markdown="${window.escapeHtmlAttribute(comment.body)}">${window.renderMarkdown ? window.renderMarkdown(comment.body) : escapeHtml(comment.body)}</div>
|
|
@@ -377,12 +377,14 @@ class FileCommentManager {
|
|
|
377
377
|
// Use same structure as line-level user comments
|
|
378
378
|
card.innerHTML = `
|
|
379
379
|
<div class="user-comment-header">
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
380
|
+
<div class="user-comment-header-left">
|
|
381
|
+
<span class="comment-origin-icon">
|
|
382
|
+
${commentIcon}
|
|
383
|
+
</span>
|
|
384
|
+
<span class="file-comment-badge" title="Comment applies to the entire file">File comment</span>
|
|
385
|
+
${praiseBadge}
|
|
386
|
+
${titleHtml}
|
|
387
|
+
</div>
|
|
386
388
|
<div class="user-comment-actions">
|
|
387
389
|
<button class="btn-chat-comment" title="Chat about comment" data-chat-comment-id="${comment.id}" data-chat-file="${this.escapeHtml(comment.file || '')}" data-chat-parent-id="${comment.parent_id || ''}">
|
|
388
390
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"/></svg>
|
package/public/js/pr.js
CHANGED
|
@@ -368,6 +368,9 @@ class PRManager {
|
|
|
368
368
|
|
|
369
369
|
/**
|
|
370
370
|
* Auto-trigger analysis if ?analyze=true is present in the URL.
|
|
371
|
+
* Skips refresh if data was just loaded fresh by loadPR (to avoid redundant fetches).
|
|
372
|
+
* Otherwise, refreshes PR data first to ensure we analyze the latest code.
|
|
373
|
+
* If refresh fails, proceeds with existing data rather than failing entirely.
|
|
371
374
|
* Cleans up the query parameter afterwards regardless of success or failure.
|
|
372
375
|
* @param {string} owner - Repository owner
|
|
373
376
|
* @param {string} repo - Repository name
|
|
@@ -378,6 +381,21 @@ class PRManager {
|
|
|
378
381
|
if (autoAnalyze === 'true' && !this.isAnalyzing) {
|
|
379
382
|
this._autoAnalyzeRequested = true;
|
|
380
383
|
try {
|
|
384
|
+
// Skip refresh if we just loaded fresh data (loadPR sets _justLoaded = true).
|
|
385
|
+
// Otherwise, refresh to ensure we have the latest PR data in case the worktree
|
|
386
|
+
// already existed but the PR has new commits since last load.
|
|
387
|
+
if (this._justLoaded) {
|
|
388
|
+
this._justLoaded = false;
|
|
389
|
+
} else {
|
|
390
|
+
try {
|
|
391
|
+
await this.refreshPR();
|
|
392
|
+
} catch (e) {
|
|
393
|
+
// If refresh fails, proceed with existing data - this is intentional.
|
|
394
|
+
// We'd rather analyze stale data than fail entirely.
|
|
395
|
+
console.warn('Pre-analysis refresh failed, proceeding with existing data', e);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
381
399
|
await this.startAnalysis(owner, repo, prNumber, null, {});
|
|
382
400
|
} finally {
|
|
383
401
|
this._autoAnalyzeRequested = false;
|
|
@@ -426,11 +444,12 @@ class PRManager {
|
|
|
426
444
|
window.aiPanel = new window.AIPanel();
|
|
427
445
|
}
|
|
428
446
|
|
|
429
|
-
// Set PR context for AI Panel (for
|
|
430
|
-
// This restores the filter state from localStorage
|
|
447
|
+
// Set PR context for AI Panel and Panel Group (for per-review localStorage keys)
|
|
448
|
+
// This restores the filter state and chat visibility from localStorage
|
|
431
449
|
if (window.aiPanel?.setPR) {
|
|
432
450
|
window.aiPanel.setPR(owner, repo, number);
|
|
433
451
|
}
|
|
452
|
+
window.panelGroup?.setPR(`${owner}/${repo}#${number}`);
|
|
434
453
|
|
|
435
454
|
// Load saved comments using the restored filter state from AI Panel
|
|
436
455
|
// If AI Panel has showDismissedComments=true (restored from localStorage), use that
|
|
@@ -470,6 +489,8 @@ class PRManager {
|
|
|
470
489
|
this.showError(error.message);
|
|
471
490
|
} finally {
|
|
472
491
|
this.setLoading(false);
|
|
492
|
+
// Mark that we just loaded fresh data - used by _maybeAutoAnalyze to skip redundant refresh
|
|
493
|
+
this._justLoaded = true;
|
|
473
494
|
}
|
|
474
495
|
}
|
|
475
496
|
|
package/public/local.html
CHANGED
|
@@ -404,7 +404,7 @@
|
|
|
404
404
|
<!-- Right Panel Group (AI Panel + Chat) -->
|
|
405
405
|
<div class="right-panel-group" id="right-panel-group">
|
|
406
406
|
<!-- AI Analysis Panel (Right) -->
|
|
407
|
-
<aside class="ai-panel" id="ai-panel">
|
|
407
|
+
<aside class="ai-panel collapsed" id="ai-panel">
|
|
408
408
|
<div class="resize-handle resize-handle-left" data-panel="ai-panel"></div>
|
|
409
409
|
<div class="ai-panel-header">
|
|
410
410
|
<div class="ai-panel-title">
|
package/public/pr.html
CHANGED
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
<!-- Right Panel Group (AI Panel + Chat) -->
|
|
228
228
|
<div class="right-panel-group" id="right-panel-group">
|
|
229
229
|
<!-- AI Analysis Panel (Right) -->
|
|
230
|
-
<aside class="ai-panel" id="ai-panel">
|
|
230
|
+
<aside class="ai-panel collapsed" id="ai-panel">
|
|
231
231
|
<div class="resize-handle resize-handle-left" data-panel="ai-panel"></div>
|
|
232
232
|
<div class="ai-panel-header">
|
|
233
233
|
<div class="ai-panel-title">
|