Skip to content

Commit 0e9fd12

Browse files
jsonifyclaude
andauthored
Refactor large template browser view file (#116)
* refactor: break up templateBrowserView into modular components The templateBrowserView.ts file was 4,542 lines, making it difficult to maintain, navigate, and test. This refactor splits it into focused, single-responsibility modules. ## New Structure Created `src/templates/browser/` directory with: - **TemplateBrowserView.ts** (~200 lines): Main webview orchestrator - **messageHandlers.ts** (~500 lines): All message handler functions - **templateOperations.ts** (~200 lines): Template CRUD operations - **templateBrowser.html**: HTML template structure - **templateBrowser.css** (1,356 lines): All styles - **templateBrowser.js** (2,019 lines): Client-side logic - **types.ts**: Shared TypeScript interfaces ## Benefits 1. **Maintainability**: Each file < 500 lines, single responsibility 2. **IDE Support**: Separate .html/.css/.js files with proper syntax highlighting 3. **Testability**: Can unit test handlers and operations separately 4. **Reusability**: Can share CSS patterns across webviews 5. **Performance**: Better code organization and loading ## Changes - Extracted message handlers to dedicated module - Extracted template operations (load, find, update usage) - Separated HTML/CSS/JS into proper files - Updated imports in extension.ts - Updated CLAUDE.md documentation - All tests pass, compilation successful * perf: use async I/O and optimize template loading Applied code review feedback to improve performance and responsiveness. ## Changes 1. **TemplateBrowserView.ts**: Replaced synchronous file operations - Changed `fs.readFileSync` to `await fsp.readFile` - Made `getTemplateBrowserHtml` async and return `Promise<string>` - Prevents blocking the extension host thread during file reads 2. **templateOperations.ts**: Optimized template loading loop - Pre-build Set of JSON basenames for O(1) lookup - Replaced `await pathExists(jsonPath)` in loop with Set.has() - Eliminates N file system calls for legacy template checking - Significantly faster when there are many .txt/.md templates ## Performance Impact - **Before**: Blocking sync I/O + O(N*M) file checks - **After**: Non-blocking async I/O + O(N+M) with in-memory Set These changes follow VS Code extension best practices for keeping the UI responsive during I/O operations. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9cef377 commit 0e9fd12

File tree

9 files changed

+2431
-2529
lines changed

9 files changed

+2431
-2529
lines changed

CLAUDE.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@ The extension now uses a fully modular architecture with clear separation of con
6868
- `TemplateTypes.ts`: TypeScript interfaces for templates and bundles (updated v1.43.3 with validation metadata)
6969
- `TemplateGenerator.ts`: AI-powered template generation and advanced validation (Phase 1, 3)
7070
- `BundleService.ts`: Multi-note workflow bundle creation and management (Phase 2)
71-
- `templateBrowserView.ts`: Visual template browser webview UI (Phase 4) with variable editor message handlers (Phase 3)
71+
- **`browser/`**: Modular Template Browser UI (Phase 4, refactored for maintainability)
72+
- `TemplateBrowserView.ts`: Main webview orchestrator (~200 lines)
73+
- `messageHandlers.ts`: Message handler functions (~500 lines)
74+
- `templateOperations.ts`: Template CRUD operations (~200 lines)
75+
- `templateBrowser.html`: HTML template structure
76+
- `templateBrowser.css`: Styles (1,356 lines)
77+
- `templateBrowser.js`: Client-side logic (2,019 lines)
78+
- `types.ts`: Shared TypeScript interfaces
7279
- **`src/providers/`**: VS Code tree view providers
7380
- `treeItems.ts`: Tree item classes (includes ConnectionSectionItem and ConnectionItem)
7481
- `templatesTreeProvider.ts`: Templates view
@@ -348,7 +355,7 @@ Templates support 10 powerful placeholders via `replacePlaceholders()` function
348355
- `video-tutorial.bundle.json` - Script + guide + resources for tutorial videos
349356
- `project-planning.bundle.json` - Overview + tasks + meetings + resources for projects
350357

351-
**Template Browser UI** (Phase 4 - v1.44.0):
358+
**Template Browser UI** (Phase 4 - v1.44.0, refactored for maintainability):
352359
- Visual webview interface for browsing and managing templates
353360
- Command: `noted.showTemplateBrowser` - Opens template browser panel
354361
- Grid/list view toggle for different display modes
@@ -359,11 +366,17 @@ Templates support 10 powerful placeholders via `replacePlaceholders()` function
359366
- Quick actions per template: Create, Edit, Duplicate, Export, Delete
360367
- Built-in templates are read-only (only "Create" action available)
361368
- Works with JSON templates, legacy .txt/.md templates, and built-in templates
362-
- Implementation in `src/templates/templateBrowserView.ts`
363-
- Similar architecture to existing webviews (graph, calendar, activity)
369+
- **Modular Architecture** (refactored from 4,542-line monolith):
370+
- Implementation in `src/templates/browser/` directory
371+
- Separated concerns: TypeScript, HTML, CSS, JavaScript in dedicated files
372+
- Main orchestrator: `TemplateBrowserView.ts` (~200 lines)
373+
- Message handlers: `messageHandlers.ts` (~500 lines)
374+
- Template operations: `templateOperations.ts` (~200 lines)
375+
- Client-side code: `templateBrowser.html`, `.css` (1,356 lines), `.js` (2,019 lines)
376+
- Benefits: Better IDE support, easier testing, improved maintainability
364377

365378
**Template Variable Editor Enhancements** (Phase 3 - v1.43.3 - Issue #110):
366-
- Advanced validation message handlers in `templateBrowserView.ts`:
379+
- Advanced validation message handlers in `src/templates/browser/messageHandlers.ts`:
367380
- `validateVariable` - Real-time variable validation with errors and warnings
368381
- `getVariableUsageInfo` - Returns usage count and positions for highlighting
369382
- `exportVariables` - Exports template variables to JSON file for sharing

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { promises as fsp } from 'fs';
55
import { showCalendarView } from './calendar/calendarView';
66
import { showGraphView } from './graph/graphView';
77
import { showActivityView } from './activity/activityView';
8-
import { showTemplateBrowser } from './templates/templateBrowserView';
8+
import { showTemplateBrowser } from './templates/browser/TemplateBrowserView';
99
import { MarkdownToolbarService } from './services/markdownToolbarService';
1010
import { TagService } from './services/tagService';
1111
import { TagRenameProvider } from './services/tagRenameProvider';
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import * as vscode from 'vscode';
2+
import * as path from 'path';
3+
import { promises as fsp } from 'fs';
4+
import { TemplatesTreeProvider } from '../../providers/templatesTreeProvider';
5+
import { loadAllTemplates } from './templateOperations';
6+
import * as messageHandlers from './messageHandlers';
7+
8+
// Module-level reference to templates tree provider for refreshing
9+
let templatesProvider: TemplatesTreeProvider | undefined;
10+
11+
/**
12+
* Show the template browser webview
13+
*/
14+
export async function showTemplateBrowser(context: vscode.ExtensionContext, provider?: TemplatesTreeProvider): Promise<void> {
15+
// Store reference to templates provider for refreshing after operations
16+
templatesProvider = provider;
17+
18+
const panel = vscode.window.createWebviewPanel(
19+
'notedTemplateBrowser',
20+
'Template Browser',
21+
vscode.ViewColumn.One,
22+
{
23+
enableScripts: true,
24+
retainContextWhenHidden: true,
25+
localResourceRoots: [
26+
vscode.Uri.file(path.join(context.extensionPath, 'src', 'templates', 'browser'))
27+
]
28+
}
29+
);
30+
31+
// Load all templates
32+
const templates = await loadAllTemplates();
33+
34+
// Set the initial HTML
35+
panel.webview.html = await getTemplateBrowserHtml(panel.webview, context.extensionPath, templates);
36+
37+
// Helper function to refresh webview templates
38+
const refreshWebviewTemplates = async () => {
39+
const updatedTemplates = await loadAllTemplates();
40+
panel.webview.postMessage({
41+
command: 'updateTemplates',
42+
templates: updatedTemplates
43+
});
44+
};
45+
46+
// Handle messages from the webview
47+
panel.webview.onDidReceiveMessage(
48+
async message => {
49+
switch (message.command) {
50+
case 'createFromTemplate':
51+
await messageHandlers.handleCreateFromTemplate(message.templateId);
52+
break;
53+
54+
case 'editTemplate':
55+
await messageHandlers.handleEditTemplate(message.templateId);
56+
break;
57+
58+
case 'deleteTemplate':
59+
await messageHandlers.handleDeleteTemplate(message.templateId);
60+
await refreshWebviewTemplates();
61+
templatesProvider?.refresh(); // Refresh sidebar tree view
62+
break;
63+
64+
case 'duplicateTemplate':
65+
await messageHandlers.handleDuplicateTemplate(message.templateId);
66+
await refreshWebviewTemplates();
67+
templatesProvider?.refresh(); // Refresh sidebar tree view
68+
break;
69+
70+
case 'exportTemplate':
71+
await messageHandlers.handleExportTemplate(message.templateId);
72+
break;
73+
74+
case 'refresh':
75+
await refreshWebviewTemplates();
76+
break;
77+
78+
case 'getPreview':
79+
await messageHandlers.handleGetPreview(panel, message.templateId, message.maxLines);
80+
break;
81+
82+
case 'getFullPreview':
83+
await messageHandlers.handleGetFullPreview(panel, message.templateId);
84+
break;
85+
86+
case 'getTemplateData':
87+
await messageHandlers.handleGetTemplateData(panel, message.templateId);
88+
break;
89+
90+
case 'saveTemplateVariables':
91+
await messageHandlers.handleSaveTemplateVariables(panel, message.templateId, message.variables);
92+
await refreshWebviewTemplates();
93+
templatesProvider?.refresh();
94+
break;
95+
96+
case 'validateVariable':
97+
await messageHandlers.handleValidateVariable(panel, message.templateId, message.variable, message.existingVariables, message.originalName);
98+
break;
99+
100+
case 'getVariableUsageInfo':
101+
await messageHandlers.handleGetVariableUsageInfo(panel, message.templateId, message.variableName);
102+
break;
103+
104+
case 'exportVariables':
105+
await messageHandlers.handleExportVariables(panel, message.templateId);
106+
break;
107+
108+
case 'importVariables':
109+
await messageHandlers.handleImportVariables(panel, message.templateId, message.variablesJson);
110+
break;
111+
}
112+
},
113+
undefined,
114+
context.subscriptions
115+
);
116+
}
117+
118+
/**
119+
* Generate the HTML content for the template browser webview
120+
*/
121+
async function getTemplateBrowserHtml(webview: vscode.Webview, extensionPath: string, templates: any[]): Promise<string> {
122+
// Get paths to resources
123+
const browserPath = path.join(extensionPath, 'src', 'templates', 'browser');
124+
const htmlPath = path.join(browserPath, 'templateBrowser.html');
125+
const cssPath = path.join(browserPath, 'templateBrowser.css');
126+
const jsPath = path.join(browserPath, 'templateBrowser.js');
127+
128+
// Read the HTML template
129+
let html = await fsp.readFile(htmlPath, 'utf8');
130+
131+
// Read CSS and JS content for inline inclusion (since webview URIs can be tricky)
132+
const cssContent = await fsp.readFile(cssPath, 'utf8');
133+
const jsContent = await fsp.readFile(jsPath, 'utf8');
134+
135+
// Replace placeholders
136+
html = html.replace('{{TEMPLATES_JSON}}', JSON.stringify(templates));
137+
html = html.replace('<link rel="stylesheet" href="{{CSS_URI}}">', `<style>${cssContent}</style>`);
138+
html = html.replace('<script src="{{JS_URI}}"></script>', `<script>${jsContent}</script>`);
139+
140+
return html;
141+
}

0 commit comments

Comments
 (0)