Skip to content

Commit 81a006d

Browse files
ozgesolidkeyclaude
andcommitted
Fix MD preview bleeding into log view after file switch
Root cause: showMarkdownPreview() set logViewerWrapper.style.display='none' as an inline style. In edge cases (rapid tab switching, async timing) this inline style leaked onto newly-created wrapper elements. Fix: - Replace inline style with CSS class .markdown-active on editorContainer (.editor-container.markdown-active .log-viewer-wrapper { display:none }) — CSS class cannot leak across DOM element recreation - Add hideMarkdownPreview() as the single authoritative reset function: hides div, clears innerHTML, removes markdown-active class, resets button - All code paths (loadFile, switchToTab) now call hideMarkdownPreview() first, then showMarkdownPreview() if the new file is markdown - showMarkdownPreview() guard (isMarkdownFile check) prevents accidental re-show Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 60662b1 commit 81a006d

2 files changed

Lines changed: 92 additions & 37 deletions

File tree

src/renderer/renderer.ts

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9327,16 +9327,10 @@ async function loadFile(filePath: string, createNewTab: boolean = true): Promise
93279327

93289328
updateFileStatsUI();
93299329

9330-
// Reset markdown state before creating the log viewer so the preview
9331-
// doesn't remain visible while the new file's lines are loading.
9330+
// Always hide markdown preview before creating the log viewer
9331+
// if the new file is markdown, showMarkdownPreview() will re-enable it below.
93329332
isMarkdownFile = isMarkdownExtension(filePath);
9333-
if (!isMarkdownFile) {
9334-
markdownPreviewMode = false;
9335-
elements.markdownPreview.classList.add('hidden');
9336-
elements.markdownPreview.innerHTML = '';
9337-
elements.btnWordWrap.textContent = 'Wrap';
9338-
elements.btnWordWrap.title = 'Toggle word wrap (⌥Z)';
9339-
}
9333+
hideMarkdownPreview();
93409334

93419335
createLogViewer();
93429336
renderTabBar();
@@ -9395,9 +9389,6 @@ async function loadFile(filePath: string, createNewTab: boolean = true): Promise
93959389

93969390
// isMarkdownFile was already set above; show preview if needed
93979391
if (isMarkdownFile) {
9398-
markdownPreviewMode = true;
9399-
elements.btnWordWrap.textContent = 'Raw';
9400-
elements.btnWordWrap.title = 'Show raw markdown';
94019392
await renderMarkdownPreview();
94029393
showMarkdownPreview();
94039394
}
@@ -12112,27 +12103,27 @@ function showMarkdownPreview(): void {
1211212103

1211312104
markdownPreviewMode = true;
1211412105
elements.markdownPreview.classList.remove('hidden');
12115-
12116-
// Hide log viewer wrapper if it exists
12117-
const wrapper = document.querySelector('.log-viewer-wrapper') as HTMLElement;
12118-
if (wrapper) {
12119-
wrapper.style.display = 'none';
12120-
}
12106+
// Use a class on the container — no inline styles that can leak across file loads
12107+
elements.editorContainer.classList.add('markdown-active');
1212112108

1212212109
// Update button state
1212312110
elements.btnWordWrap.textContent = 'Raw';
1212412111
elements.btnWordWrap.title = 'Show raw markdown';
1212512112
}
1212612113

12127-
function showMarkdownRaw(): void {
12114+
function hideMarkdownPreview(): void {
1212812115
markdownPreviewMode = false;
1212912116
elements.markdownPreview.classList.add('hidden');
12117+
elements.markdownPreview.innerHTML = '';
12118+
elements.editorContainer.classList.remove('markdown-active');
12119+
elements.btnWordWrap.textContent = 'Wrap';
12120+
elements.btnWordWrap.title = 'Toggle word wrap (⌥Z)';
12121+
}
1213012122

12131-
// Show log viewer wrapper
12132-
const wrapper = document.querySelector('.log-viewer-wrapper') as HTMLElement;
12133-
if (wrapper) {
12134-
wrapper.style.display = '';
12135-
}
12123+
function showMarkdownRaw(): void {
12124+
markdownPreviewMode = false;
12125+
elements.markdownPreview.classList.add('hidden');
12126+
elements.editorContainer.classList.remove('markdown-active');
1213612127

1213712128
// Update button state
1213812129
elements.btnWordWrap.textContent = 'Preview';
@@ -14272,22 +14263,10 @@ async function switchToTab(tabId: string): Promise<void> {
1427214263

1427314264
// Handle markdown preview state for the new tab
1427414265
isMarkdownFile = isMarkdownExtension(tab.filePath);
14266+
hideMarkdownPreview(); // always reset first
1427514267
if (isMarkdownFile) {
14276-
markdownPreviewMode = true;
14277-
elements.btnWordWrap.textContent = 'Raw';
14278-
elements.btnWordWrap.title = 'Show raw markdown';
1427914268
await renderMarkdownPreview();
1428014269
showMarkdownPreview();
14281-
} else {
14282-
markdownPreviewMode = false;
14283-
elements.markdownPreview.classList.add('hidden');
14284-
elements.markdownPreview.innerHTML = '';
14285-
elements.btnWordWrap.textContent = 'Wrap';
14286-
elements.btnWordWrap.title = 'Toggle word wrap (⌥Z)';
14287-
const wrapper = document.querySelector('.log-viewer-wrapper') as HTMLElement;
14288-
if (wrapper) {
14289-
wrapper.style.display = '';
14290-
}
1429114270
}
1429214271

1429314272
// Update with fresh info from backend

src/renderer/styles.css

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4366,6 +4366,78 @@ kbd {
43664366
cursor: default;
43674367
}
43684368

4369+
/* Typing indicator */
4370+
.chat-typing-indicator {
4371+
align-self: flex-start;
4372+
display: flex;
4373+
align-items: center;
4374+
gap: 4px;
4375+
padding: 8px 12px;
4376+
background: #2d2d2d;
4377+
border-radius: 8px;
4378+
border-bottom-left-radius: 2px;
4379+
}
4380+
4381+
.chat-typing-dot {
4382+
width: 6px;
4383+
height: 6px;
4384+
border-radius: 50%;
4385+
background: #888;
4386+
animation: chat-dot-bounce 1.2s ease-in-out infinite;
4387+
}
4388+
4389+
.chat-typing-dot:nth-child(2) { animation-delay: 0.2s; }
4390+
.chat-typing-dot:nth-child(3) { animation-delay: 0.4s; }
4391+
4392+
@keyframes chat-dot-bounce {
4393+
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
4394+
30% { transform: translateY(-5px); opacity: 1; }
4395+
}
4396+
4397+
/* Reconnect banner */
4398+
.chat-reconnect-banner {
4399+
display: flex;
4400+
align-items: center;
4401+
gap: 6px;
4402+
padding: 6px 12px;
4403+
background: rgba(192, 57, 43, 0.18);
4404+
border-bottom: 1px solid rgba(192, 57, 43, 0.35);
4405+
font-size: 12px;
4406+
color: #e88;
4407+
flex-shrink: 0;
4408+
}
4409+
4410+
.chat-reconnect-banner.hidden { display: none; }
4411+
4412+
.chat-reconnect-icon { font-size: 13px; }
4413+
4414+
.chat-reconnect-text { flex: 1; }
4415+
4416+
.chat-reconnect-btn {
4417+
background: #c0392b;
4418+
color: #fff;
4419+
border: none;
4420+
border-radius: 4px;
4421+
padding: 3px 10px;
4422+
font-size: 11px;
4423+
cursor: pointer;
4424+
white-space: nowrap;
4425+
}
4426+
4427+
.chat-reconnect-btn:hover { opacity: 0.85; }
4428+
4429+
.chat-reconnect-dismiss {
4430+
background: none;
4431+
border: none;
4432+
color: #e88;
4433+
cursor: pointer;
4434+
font-size: 12px;
4435+
padding: 0 2px;
4436+
opacity: 0.7;
4437+
}
4438+
4439+
.chat-reconnect-dismiss:hover { opacity: 1; }
4440+
43694441
/* Search Results content (inside bottom panel tab) */
43704442
.bottom-tab-sub-header {
43714443
display: flex;
@@ -4819,6 +4891,10 @@ kbd {
48194891
}
48204892

48214893
/* Markdown Preview */
4894+
/* When markdown preview is active, the log viewer wrapper is hidden via CSS class
4895+
(not inline style) so there is no risk of it leaking across file loads */
4896+
.editor-container.markdown-active .log-viewer-wrapper { display: none !important; }
4897+
48224898
.markdown-preview {
48234899
position: absolute;
48244900
top: 0;

0 commit comments

Comments
 (0)