Skip to content

Commit 24c12d4

Browse files
authored
Merge pull request #6 from jamesainslie/fix-filescanner-error
fix(core): resolve file scanner errors and rendering issues
2 parents 7eb93da + 325a868 commit 24c12d4

File tree

11 files changed

+180
-41
lines changed

11 files changed

+180
-41
lines changed

src/core/render-pipeline.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,14 @@ export class RenderPipeline {
6464
this.workersEnabled = true;
6565
debug.info('RenderPipeline', '✅ Worker pool initialized');
6666
} catch (error) {
67-
// Workers unavailable (file:// URLs) - use optimized sync rendering
68-
if (window.location.protocol === 'file:') {
69-
debug.warn(
70-
'RenderPipeline',
71-
'Using optimized synchronous rendering (workers unavailable on file:// URLs)'
72-
);
73-
} else {
67+
// Workers unavailable - WorkerPool already logged the reason
68+
// Silently fall back to synchronous rendering
69+
this.workersEnabled = false;
70+
71+
// Only log errors for unexpected failures (non-file:// protocols)
72+
if (window.location.protocol !== 'file:') {
7473
debug.error('RenderPipeline', 'Worker initialization failed, using sync fallback:', error);
7574
}
76-
this.workersEnabled = false;
7775
}
7876
}
7977

src/options/options.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ <h2>Advanced</h2>
203203
<button id="btn-save" class="primary-button">Save Changes</button>
204204
<span id="save-status" class="save-status"></span>
205205
</div>
206-
<p class="version">MDView v1.0.0</p>
206+
<p class="version" id="app-version">MDView v1.0.0</p>
207207
</footer>
208208
</div>
209209

src/options/options.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,24 @@ class OptionsManager {
3131
// Setup storage listener
3232
this.setupStorageListener();
3333

34+
// Set version
35+
this.setAppVersion();
36+
3437
debug.log('Options', 'Initialized');
3538
}
3639

40+
private setAppVersion(): void {
41+
const versionElement = document.getElementById('app-version');
42+
if (versionElement) {
43+
// __APP_VERSION__ is injected by Vite at build time
44+
try {
45+
versionElement.textContent = `MDView v${__APP_VERSION__}`;
46+
} catch (e) {
47+
debug.warn('Options', 'Failed to set app version:', e);
48+
}
49+
}
50+
}
51+
3752
private setupStorageListener(): void {
3853
chrome.storage.onChanged.addListener((changes, areaName) => {
3954
if (areaName === 'sync' && changes.preferences) {

src/popup/popup.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ <h1>MDView</h1>
2929
</select>
3030
</section>
3131

32+
<!-- Log Level Selection -->
33+
<section class="setting-section">
34+
<label for="log-level-select" class="setting-label">Log Level</label>
35+
<select id="log-level-select" class="theme-select">
36+
<option value="none">None</option>
37+
<option value="error">Error</option>
38+
<option value="warn">Warning</option>
39+
<option value="info">Info</option>
40+
<option value="debug">Debug</option>
41+
</select>
42+
</section>
43+
3244
<!-- Quick Toggles -->
3345
<section class="setting-section">
3446
<div class="toggle-group">
@@ -78,7 +90,7 @@ <h1>MDView</h1>
7890
</main>
7991

8092
<footer class="popup-footer">
81-
<p class="version">Version 1.0.0</p>
93+
<p class="version" id="app-version">Version 1.0.0</p>
8294
</footer>
8395
</div>
8496

src/popup/popup.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Manages popup UI and interactions
44
*/
55

6-
import type { AppState, ThemeName } from '../types';
6+
import type { AppState, ThemeName, LogLevel } from '../types';
77
import { debug } from '../utils/debug-logger';
88

99
class PopupManager {
@@ -24,9 +24,25 @@ class PopupManager {
2424
// Setup storage listener
2525
this.setupStorageListener();
2626

27+
// Set version
28+
this.setAppVersion();
29+
2730
debug.log('Popup', 'Initialized');
2831
}
2932

33+
private setAppVersion(): void {
34+
const versionElement = document.getElementById('app-version');
35+
if (versionElement) {
36+
// __APP_VERSION__ is injected by Vite at build time
37+
try {
38+
versionElement.textContent = `Version ${__APP_VERSION__}`;
39+
} catch (e) {
40+
// Fallback if define replacement fails (e.g. in some dev environments)
41+
debug.warn('Popup', 'Failed to set app version:', e);
42+
}
43+
}
44+
}
45+
3046
private setupStorageListener(): void {
3147
chrome.storage.onChanged.addListener((changes, areaName) => {
3248
if (areaName === 'sync' && changes.preferences) {
@@ -87,6 +103,12 @@ class PopupManager {
87103
if (syncTabs) {
88104
syncTabs.checked = preferences.syncTabs;
89105
}
106+
107+
// Update log level select
108+
const logLevelSelect = document.getElementById('log-level-select') as HTMLSelectElement;
109+
if (logLevelSelect) {
110+
logLevelSelect.value = preferences.logLevel || 'error';
111+
}
90112
}
91113

92114
private setupEventListeners(): void {
@@ -156,6 +178,15 @@ class PopupManager {
156178
});
157179
}
158180

181+
// Log level select
182+
const logLevelSelect = document.getElementById('log-level-select');
183+
if (logLevelSelect) {
184+
logLevelSelect.addEventListener('change', (e) => {
185+
const target = e.target as HTMLSelectElement;
186+
void this.handlePreferenceChange({ logLevel: target.value as LogLevel });
187+
});
188+
}
189+
159190
// Settings button
160191
const btnSettings = document.getElementById('btn-settings');
161192
if (btnSettings) {

src/types/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* Global window extensions
55
*/
66
declare global {
7+
const __APP_VERSION__: string;
8+
79
interface Window {
810
__MDVIEW_MERMAID_CODE__?: Map<string, string>;
911
}
@@ -281,4 +283,3 @@ export interface MermaidTaskResult {
281283
svg: string;
282284
id: string;
283285
}
284-

src/utils/file-scanner.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,48 @@ export class FileScanner {
140140
debug.log('FileScanner', 'No change detected');
141141
}
142142
} catch (error) {
143+
// Debug: Log error details for troubleshooting
144+
debug.debug('FileScanner', '=== Error Analysis ===');
145+
debug.debug('FileScanner', 'Error type:', typeof error);
146+
debug.debug('FileScanner', 'Error instanceof Error:', error instanceof Error);
147+
debug.debug('FileScanner', 'Error toString():', String(error));
148+
debug.debug(
149+
'FileScanner',
150+
'Error message:',
151+
error instanceof Error ? error.message : 'N/A'
152+
);
153+
debug.debug('FileScanner', 'Error name:', error instanceof Error ? error.name : 'N/A');
154+
155+
// Improved error handling with case-insensitive check
156+
const errorStr = String(error).toLowerCase();
157+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : '';
158+
159+
debug.debug('FileScanner', 'errorStr (lowercase):', errorStr);
160+
debug.debug('FileScanner', 'errorMessage (lowercase):', errorMessage);
161+
162+
const check1 = errorStr.includes('context invalidated');
163+
const check2 = errorStr.includes('extension context');
164+
const check3 = errorMessage.includes('context invalidated');
165+
166+
debug.debug('FileScanner', 'Check results:');
167+
debug.debug('FileScanner', ' - errorStr.includes("context invalidated"):', check1);
168+
debug.debug('FileScanner', ' - errorStr.includes("extension context"):', check2);
169+
debug.debug('FileScanner', ' - errorMessage.includes("context invalidated"):', check3);
170+
171+
const isContextInvalid = check1 || check2 || check3;
172+
debug.debug('FileScanner', 'isContextInvalid:', isContextInvalid);
173+
debug.debug('FileScanner', '=== End Error Analysis ===');
174+
175+
// Handle extension context invalidation (e.g. after extension update/reload)
176+
if (isContextInvalid) {
177+
debug.warn('FileScanner', 'Extension context invalidated. Stopping file watcher.');
178+
if (intervalId !== null) {
179+
clearInterval(intervalId);
180+
intervalId = null;
181+
}
182+
return;
183+
}
184+
143185
debug.error('FileScanner', 'Error communicating with background:', error);
144186
}
145187
})();

src/utils/section-splitter.ts

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,44 @@ export interface MarkdownSection {
1919
export function splitIntoSections(markdown: string): MarkdownSection[] {
2020
const lines = markdown.split('\n');
2121
const sections: MarkdownSection[] = [];
22-
22+
2323
let currentSection: string[] = [];
2424
let currentHeading: string | undefined;
2525
let currentLevel: number | undefined;
2626
let sectionStartLine = 0;
2727
let sectionId = 0;
28-
28+
29+
// Track fenced code block state
30+
let inCodeFence = false;
31+
let codeFenceChar = ''; // '`' or '~'
32+
let codeFenceLength = 0; // minimum 3
33+
2934
lines.forEach((line, index) => {
30-
// Check if line is a heading (ATX style: # Heading)
31-
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
32-
35+
// Check for code fence start/end (``` or ~~~)
36+
const fenceMatch = line.match(/^(`{3,}|~{3,})/);
37+
38+
if (fenceMatch) {
39+
const fenceStr = fenceMatch[1];
40+
const fenceChar = fenceStr[0];
41+
const fenceLen = fenceStr.length;
42+
43+
if (!inCodeFence) {
44+
// Starting a code fence
45+
inCodeFence = true;
46+
codeFenceChar = fenceChar;
47+
codeFenceLength = fenceLen;
48+
} else if (fenceChar === codeFenceChar && fenceLen >= codeFenceLength) {
49+
// Closing the code fence (must use same char and >= length)
50+
inCodeFence = false;
51+
codeFenceChar = '';
52+
codeFenceLength = 0;
53+
}
54+
// If different fence char or shorter length, it's content inside the fence
55+
}
56+
57+
// Only check for headings when NOT inside a code fence
58+
const headingMatch = !inCodeFence && line.match(/^(#{1,6})\s+(.+)$/);
59+
3360
if (headingMatch) {
3461
// Save previous section if it exists
3562
if (currentSection.length > 0) {
@@ -42,7 +69,7 @@ export function splitIntoSections(markdown: string): MarkdownSection[] {
4269
id: `section-${sectionId++}`,
4370
});
4471
}
45-
72+
4673
// Start new section
4774
currentSection = [line];
4875
currentHeading = headingMatch[2];
@@ -52,7 +79,7 @@ export function splitIntoSections(markdown: string): MarkdownSection[] {
5279
currentSection.push(line);
5380
}
5481
});
55-
82+
5683
// Add final section
5784
if (currentSection.length > 0) {
5885
sections.push({
@@ -64,7 +91,7 @@ export function splitIntoSections(markdown: string): MarkdownSection[] {
6491
id: `section-${sectionId++}`,
6592
});
6693
}
67-
94+
6895
return sections;
6996
}
7097

@@ -74,16 +101,16 @@ export function splitIntoSections(markdown: string): MarkdownSection[] {
74101
export function splitIntoChunks(markdown: string, chunkSize = 50000): MarkdownSection[] {
75102
const sections: MarkdownSection[] = [];
76103
const lines = markdown.split('\n');
77-
104+
78105
let currentChunk: string[] = [];
79106
let currentSize = 0;
80107
let chunkStartLine = 0;
81108
let chunkId = 0;
82-
109+
83110
lines.forEach((line, index) => {
84111
currentChunk.push(line);
85112
currentSize += line.length + 1; // +1 for newline
86-
113+
87114
// If chunk is large enough, save it
88115
if (currentSize >= chunkSize) {
89116
sections.push({
@@ -92,13 +119,13 @@ export function splitIntoChunks(markdown: string, chunkSize = 50000): MarkdownSe
92119
endLine: index,
93120
id: `chunk-${chunkId++}`,
94121
});
95-
122+
96123
currentChunk = [];
97124
currentSize = 0;
98125
chunkStartLine = index + 1;
99126
}
100127
});
101-
128+
102129
// Add remaining lines
103130
if (currentChunk.length > 0) {
104131
sections.push({
@@ -108,7 +135,7 @@ export function splitIntoChunks(markdown: string, chunkSize = 50000): MarkdownSe
108135
id: `chunk-${chunkId++}`,
109136
});
110137
}
111-
138+
112139
return sections;
113140
}
114141

@@ -120,33 +147,32 @@ export function getInitialSections(
120147
options: { maxSections?: number; maxSize?: number; upToSectionId?: string } = {}
121148
): MarkdownSection[] {
122149
const { maxSections = 3, maxSize = 30000, upToSectionId } = options;
123-
150+
124151
// If we need to render up to a specific section (for scroll restoration),
125152
// render all sections up to and including that one
126153
if (upToSectionId) {
127-
const targetIndex = sections.findIndex(s => s.id === upToSectionId);
154+
const targetIndex = sections.findIndex((s) => s.id === upToSectionId);
128155
if (targetIndex !== -1) {
129156
// Render all sections up to the target, plus one more for context
130157
return sections.slice(0, Math.min(targetIndex + 2, sections.length));
131158
}
132159
}
133-
160+
134161
const initial: MarkdownSection[] = [];
135162
let totalSize = 0;
136-
163+
137164
for (const section of sections) {
138165
if (initial.length >= maxSections) break;
139166
if (totalSize + section.markdown.length > maxSize) break;
140-
167+
141168
initial.push(section);
142169
totalSize += section.markdown.length;
143170
}
144-
171+
145172
// Always include at least one section
146173
if (initial.length === 0 && sections.length > 0) {
147174
initial.push(sections[0]);
148175
}
149-
176+
150177
return initial;
151178
}
152-

0 commit comments

Comments
 (0)