Add unicorn and auto fix#52
Conversation
|
Caution Review failedAn error occurred during the review process. Please try again later. ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| viewType: viewType, | ||
| timestamp: Date.now(), | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, |
Check failure
Code scanning / CodeQL
Insecure randomness High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 8 months ago
To fix the problem, replace the insecure use of Math.random() in the creation of the sessionId with a cryptographically secure source of randomness. In Node.js (and in VS Code extensions), require('crypto').randomBytes or the global crypto module's randomBytes function can be used to securely generate random values. The fix should introduce an import of the crypto module, generate random bytes, and encode them (for example, using base64 or hexadecimal) to use as the random component of the session ID string. Only lines related to the construction of the session ID require changes; add any necessary import at the top of the file.
| @@ -1,5 +1,6 @@ | ||
| import type * as vscode from 'vscode'; | ||
| import { getLogger } from '../services/logger'; | ||
| import * as crypto from 'crypto'; | ||
| import type { EventTrigger } from '../chat/ChatController'; | ||
| import type { RequestContext, ViewApiEvent, ViewEvents } from '../api/viewApi'; | ||
|
|
||
| @@ -92,7 +93,7 @@ | ||
| viewId: id, | ||
| viewType: viewType, | ||
| timestamp: Date.now(), | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, | ||
| sessionId: `session-${Date.now()}-${crypto.randomBytes(9).toString('base64').replace(/[^a-zA-Z0-9]/g, '')}`, | ||
| }; | ||
|
|
||
| this.connectedViews.set(id, { view, context }); |
| viewType: 'chat-interface', | ||
| timestamp: Date.now(), | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, |
Check failure
Code scanning / CodeQL
Insecure randomness High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 8 months ago
To fix the issue, we must replace any usage of Math.random() for security or uniqueness-relevant values (like session IDs) with a cryptographically secure random generator. In browser environments, that is window.crypto.getRandomValues. Since this is a React context in a webview (browser JS), we should replace Math.random().toString(36).slice(2, 11) with a random value sourced from window.crypto.getRandomValues, formatted to output a random string.
We'll need a function that generates a random string from secure random bytes, for example using a Uint8Array, then base64-url encoding or hex encoding or using a character selection for short random strings. All places in the file that generate an ID using Math.random() should be updated. Specifically:
- The session ID assignment on line 108.
- The viewId assignment on line 105.
- The
generateId()function on line 60 (used for request IDs).
Each of these should use a secure random string generator. We will define a helper function usingwindow.crypto.getRandomValuesthat generates random strings, then use that in all the relevant locations.
No new external CRUD dependencies are required, but we may need to polyfill for environments lacking window.crypto if that ever occurs.
| @@ -55,10 +55,20 @@ | ||
| } | ||
|
|
||
| /** | ||
| * Generate a cryptographically secure random string (length is numBytes*2 for hex) | ||
| */ | ||
| function secureRandomString(numBytes: number = 9): string { | ||
| const array = new Uint8Array(numBytes); | ||
| window.crypto.getRandomValues(array); | ||
| // Convert Uint8Array to hex string | ||
| return Array.from(array, b => b.toString(16).padStart(2, '0')).join(''); | ||
| } | ||
|
|
||
| /** | ||
| * Generate a unique ID for requests | ||
| */ | ||
| function generateId(): string { | ||
| return `req_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`; | ||
| return `req_${Date.now()}_${secureRandomString()}`; | ||
| } | ||
|
|
||
| /** | ||
| @@ -102,10 +109,10 @@ | ||
|
|
||
| // Generate context for this webview instance | ||
| const contextReference = useRef<RequestContext>({ | ||
| viewId: `webview-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, | ||
| viewId: `webview-${Date.now()}-${secureRandomString()}`, | ||
| viewType: 'chat-interface', | ||
| timestamp: Date.now(), | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, | ||
| sessionId: `session-${Date.now()}-${secureRandomString()}`, | ||
| }); | ||
|
|
||
| /** |
| const patchFn = postReducer[data.key]; | ||
| setState(prev => patchFn(prev, data.patch)); | ||
| const patchFunction = postReducer[data.key]; | ||
| setState(previous => patchFunction(previous, data.patch)); |
Check failure
Code scanning / CodeQL
Unvalidated dynamic method call High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 8 months ago
The best way to fix the problem is to further restrict the keys that can be used for dynamic method invocation, avoiding prototype chain pollution and completely eliminating the risk from inherited properties. The recommended approach is to refactor postReducer to use a Map instead of a plain object so that only explicitly registered function properties are looked up, and validation can use map.has(key) and typeof map.get(key) === 'function' checks. However, if refactoring to a Map is impractical for the codebase shown (since postReducer may come in as a plain object), then the next strongest approach is to ensure the code checks for both hasOwnProperty and typeof before looking up and calling the method, and also to avoid leaking prototype properties by using Object.create(null) for postReducer. Within the provided code, the existing validation is already strong; we can enhance it by verifying that postReducer is a plain object (no prototype) and by optionally refactoring to support both Map and plain objects robustly.
For this code region in src/webview/hooks/useVscodeState.ts, you should:
- Further document/enforce that
postReducerdoes not have a prototype chain, or strongly validate withObject.create(null)if possible. - Additionally, consider refactoring to support a
Mapinterface forpostReducer(with a robust conditional). - For extra safety, you may assert that
patchFunctionis indeed a function right before calling.
| @@ -47,14 +47,28 @@ | ||
| const handler = (event: MessageEvent) => { | ||
| const { data } = event; | ||
| if (isMyPatchMessage<A>(data, providerId)) { | ||
| if ( | ||
| validKeys.has(String(data.key)) && | ||
| Object.prototype.hasOwnProperty.call(postReducer, data.key) && | ||
| typeof postReducer[data.key] === 'function' | ||
| ) { | ||
| const patchFunction = postReducer[data.key]; | ||
| setState(previous => patchFunction(previous, data.patch)); | ||
| // Defensive: Support either Map or plain object with null prototype | ||
| let patchFunction: unknown; | ||
| if (postReducer instanceof Map) { | ||
| if ( | ||
| validKeys.has(String(data.key)) && | ||
| postReducer.has(data.key) && | ||
| typeof postReducer.get(data.key) === 'function' | ||
| ) { | ||
| patchFunction = postReducer.get(data.key); | ||
| } | ||
| } else { | ||
| if ( | ||
| validKeys.has(String(data.key)) && | ||
| Object.prototype.hasOwnProperty.call(postReducer, data.key) && | ||
| typeof postReducer[data.key] === 'function' | ||
| ) { | ||
| patchFunction = postReducer[data.key]; | ||
| } | ||
| } | ||
| if (typeof patchFunction === 'function') { | ||
| setState(previous => (patchFunction as Function)(previous, data.patch)); | ||
| } else { | ||
| throw new Error( | ||
| `Could not find a function for ${String(data.key)} in postReducer` | ||
| ); |
There was a problem hiding this comment.
Summary of Changes
Hello @hbmartin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request focuses on enhancing code quality, maintainability, and developer experience by integrating a new ESLint plugin for stricter code standards and enabling automated linting fixes. It also includes a significant refactoring effort to modernize the codebase with contemporary JavaScript/TypeScript patterns and improve overall readability and consistency. These changes aim to reduce technical debt and facilitate future development.
Highlights
- New ESLint Plugin: The
eslint-plugin-unicornhas been added to the project's ESLint configuration. This plugin enforces best practices and provides helpful rules for writing cleaner and more consistent JavaScript/TypeScript code. - Automated Code Formatting: The
lintscript inpackage.jsonhas been updated to include the--fixflag. This change enables automatic fixing of linting issues, streamlining the code formatting and maintenance process. - Code Modernization and Refactoring: Numerous files across the codebase have been refactored to use more modern JavaScript/TypeScript syntax and improve readability. This includes changes like using
for...ofloops instead offorEach, array spread syntax ([...]) instead ofArray.from(), simplifying conditional assignments, and renaming variables for clarity (e.g.,argstoarguments_,msgtomessage,prevtoprevious). Numeric separators have also been added to large numbers for better readability. - Node.js Module Imports: Standard Node.js built-in modules (like
path,fs,os,child_process) are now imported using thenode:prefix (e.g.,import * as path from 'node:path';). This is a modern best practice for Node.js development, explicitly indicating built-in modules and improving module resolution clarity. - Webview API and UI Enhancements: Improvements have been made to the webview API interactions, including more robust error handling and clearer message passing. UI components in the webview have also received minor updates, such as wrapping switch cases in blocks and refining event listeners.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed everything up to 00a4ac2 in 2 minutes and 41 seconds. Click for details.
- Reviewed
5453lines of code in63files - Skipped
1files when reviewing. - Skipped posting
10draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. src/webview/components/Chat/ThemePreview.tsx:130
- Draft comment:
Using dangerouslySetInnerHTML for sampleHTML can pose security risks if content isn’t fully trusted. Ensure that the provided HTML is sanitized. - Reason this comment was not posted:
Comment was on unchanged code.
2. src/webview/components/Chat/ModelSelector.tsx:24
- Draft comment:
Consider debouncing the search input to avoid filtering on every keystroke if the model list grows large. - Reason this comment was not posted:
Comment was on unchanged code.
3. src/webview/hooks/useDebouncedSave.ts:50
- Draft comment:
Repeatedly calling JSON.stringify(data) in the effect may be expensive for large data arrays. Consider memoizing the serialized string. - Reason this comment was not posted:
Comment was on unchanged code.
4. src/webview/contexts/WebviewContext.tsx:218
- Draft comment:
Avoid using console.log for production debugging; consider using the injected logger for consistency. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. src/webview/hooks/useVscodeState.ts:86
- Draft comment:
The Proxy-based dynamic creation of action methods is clever; ensure all action keys are properly typed to prevent runtime errors. - Reason this comment was not posted:
Comment did not seem useful. Confidence is useful =0%<= threshold50%The comment is informative and suggests ensuring that action keys are properly typed to prevent runtime errors. However, it does not provide a specific suggestion or ask for a specific test to be written. It falls into the category of asking the author to ensure something, which is against the rules.
6. src/webview/utils/themeParser.ts:90
- Draft comment:
The regex used in parseCSSVariables may not handle property values containing semicolons. Consider a more robust parser for edge cases. - Reason this comment was not posted:
Comment was not on a location in the diff, so it can't be submitted as a review comment.
7. src/webview/components/Chat/ThemePreview.tsx:36
- Draft comment:
Typographical error: Unexpected closing parenthesis appears before the closing bracket in the SYSTEM_FONTS array declaration. Likely, this should simply be "]" instead of "]);". - Reason this comment was not posted:
Comment looked like it was already resolved.
8. src/webview/components/Chat/ThemePreviewHeader.tsx:11
- Draft comment:
Typographical Issue: The component's typing has been changed from using 'ThemePreviewHeaderProps' to 'ThemePreviewHeaderProperties', which does not match the interface defined at the top of the file. Please ensure consistent naming. - Reason this comment was not posted:
Comment looked like it was already resolved.
9. src/webview/components/DesignFrame.tsx:31
- Draft comment:
Typo: The interface is defined as 'DesignFrameProps' earlier, but here it's referenced as 'DesignFrameProperties'. Please confirm if this was an intentional change or a mistake. - Reason this comment was not posted:
Comment looked like it was already resolved.
10. src/webview/components/DesignFrame.tsx:142
- Draft comment:
Typo: The prompt message for the 'bolt' case uses "using this as reference". For consistency and clarity, consider changing it to "using this as a reference". - Reason this comment was not posted:
Comment was on unchanged code.
Workflow ID: wflow_cJ05RJgWtfORwbFl
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||||||
There was a problem hiding this comment.
Code Review
This pull request introduces the ESLint unicorn plugin and applies its recommended rules across the codebase, resulting in a large-scale, beneficial refactoring. The changes consistently modernize the code with features like node: imports, numeric separators, for...of loops, and modern DOM/string manipulation methods. The codebase is now more readable, maintainable, and aligned with modern JavaScript/TypeScript best practices. I have reviewed the changes and found them to be well-executed and correct. This is an excellent enhancement to the project's code quality.
There was a problem hiding this comment.
Actionable comments posted: 30
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (27)
src/helpers/htmlTemplate.ts (1)
46-53: CSP nonce must be cryptographically strong; replace Math.random() with crypto.randomBytes().Math.random() is predictable and weak for CSP nonces. Use Node’s crypto to generate an unpredictable nonce.
Apply:
+import { randomBytes } from 'node:crypto'; @@ export function getNonce(): string { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let index = 0; index < 32; index++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; + // 16 bytes ~ 128 bits of entropy; Base64 for compactness + return randomBytes(16).toString('base64'); }src/chat/getCssFileContent.ts (2)
9-21: Constrain paths to workspace roots; prevent traversal/out-of-root access.Absolute or crafted relative paths can escape the workspace. Enforce that resolvedPath stays within a workspace folder.
@@ - if (!path.isAbsolute(filePath)) { + if (!path.isAbsolute(filePath)) { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; if (!workspaceFolder) { throw new Error('No workspace folder found'); } @@ - resolvedPath = path.join(workspaceFolder.uri.fsPath, resolvedPath); + resolvedPath = path.resolve(workspaceFolder.uri.fsPath, resolvedPath); + // Ensure resolved path is inside the workspace + const rel = path.relative(workspaceFolder.uri.fsPath, resolvedPath); + if (rel.startsWith('..') || path.isAbsolute(rel)) { + throw new Error('CSS path escapes the workspace root'); + } }Additionally, when filePath is absolute, verify it’s inside one of vscode.workspace.workspaceFolders before proceeding.
25-49: Multi-root: search all workspace folders; remove single-folder assumption.Current fallback only checks workspaceFolders[0]. Iterate all folders for both primary and alt paths.
- const { workspaceFolders } = vscode.workspace; + const { workspaceFolders } = vscode.workspace; if ( workspaceFolders !== undefined && !filePath.startsWith('.superdesign/') && !filePath.startsWith('/') ) { - // TODO: detect correct workspace index - const altPath = path.join(workspaceFolders[0].uri.fsPath, '.superdesign', filePath); - try { - const altUri = vscode.Uri.file(altPath); - await vscode.workspace.fs.stat(altUri); - resolvedPath = altPath; - fileUri = altUri; - } catch { - throw new Error(`CSS file not found at: ${resolvedPath} or ${altPath}`); - } + for (const folder of workspaceFolders) { + const candidate = path.join(folder.uri.fsPath, '.superdesign', filePath); + try { + const candidateUri = vscode.Uri.file(candidate); + await vscode.workspace.fs.stat(candidateUri); + resolvedPath = candidate; + fileUri = candidateUri; + break; + } catch { /* try next */ } + } + if (!fileUri) { + throw new Error(`CSS file not found for: ${filePath}`); + } } else { throw new Error(`CSS file not found: ${resolvedPath}`); }Happy to wire this with workspace change handling if you want.
src/webview/components/Chat/ModeToggle.tsx (1)
45-48: Make the control accessible and keyboard-navigable.Use a button with aria-pressed; div+onClick isn’t keyboard accessible.
- <div className='mode-toggle' onClick={handleToggle}> + <button + type="button" + className="mode-toggle" + onClick={handleToggle} + aria-pressed={isDarkMode} + aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'} + > <span className='mode-toggle-icon'>{isDarkMode ? '🌙' : '☀️'}</span> <span className='mode-toggle-text'>{isDarkMode ? 'Dark' : 'Light'}</span> - </div> + </button>src/webview/components/Chat/ColorPalette.tsx (1)
23-35: Use button semantics for a11y.Clickable divs aren’t keyboard accessible. Use a button with an aria-label.
- <div + <button key={name} - className='color-swatch' - onClick={() => handleColorCopy(color)} - title={`${name}: ${color}`} + type="button" + className="color-swatch" + onClick={() => handleColorCopy(color)} + title={`${name}: ${color}`} + aria-label={`Copy ${name} ${color} to clipboard`} > <div className='color-preview' style={{ backgroundColor: color }} /> <div className='color-info'> <span className='color-name'>{name}</span> <span className='color-value'>{color}</span> </div> - </div> + </button>src/webview/components/ConnectionLines.tsx (1)
47-56: pointerEvents: 'none' on the SVG disables hover/tooltip on children.Your hover-target path won’t receive events. Allow events at the SVG level.
- pointerEvents: 'none', + pointerEvents: 'auto',src/webview/components/Chat/ThemePreviewHeader.tsx (1)
24-25: Add a11y semantics (role, keyboard, aria) to header and buttons.Clickable div needs keyboard and aria; icon-only buttons need labels. This aligns with the webview accessibility guideline.
@@ - <div className='tool-message__header' onClick={onToggleExpanded}> + <div + className='tool-message__header' + role='button' + tabIndex={0} + aria-expanded={isExpanded} + onClick={onToggleExpanded} + onKeyDown={e => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onToggleExpanded(); + } + }} + > @@ - <button + <button className='theme-copy-btn' - onClick={e => handleActionClick(e, onCopyCSS)} + onClick={e => handleActionClick(e, onCopyCSS)} title='Copy CSS to clipboard' + aria-label='Copy CSS to clipboard' @@ - <button className={`tool-expand-btn ${isExpanded ? 'expanded' : ''}`}> + <button + type='button' + className={`tool-expand-btn ${isExpanded ? 'expanded' : ''}`} + aria-expanded={isExpanded} + aria-label={isExpanded ? 'Collapse' : 'Expand'} + onClick={e => { + e.stopPropagation(); + onToggleExpanded(); + }} + >Also applies to: 52-72, 73-77
src/tools/read-tool.ts (1)
40-79: Use VS Code’s workspace.fs (async) instead of node:fs (sync) for workspace files.Required by the guidelines; enables remote workspaces and avoids extension-host blocking.
+import * as vscode from 'vscode'; @@ -function isBinaryFile(filePath: string): boolean { - try { - const fd = fs.openSync(filePath, 'r'); - const fileSize = fs.fstatSync(fd).size; +async function isBinaryFile(filePath: string): Promise<boolean> { + try { + const uri = vscode.Uri.file(filePath); + const fileData = await vscode.workspace.fs.readFile(uri); + const fileSize = fileData.byteLength; @@ - const bufferSize = Math.min(4096, fileSize); - const buffer = Buffer.alloc(bufferSize); - const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0); - fs.closeSync(fd); + const bufferSize = Math.min(4096, fileSize); + const buffer = Buffer.from(fileData.subarray(0, bufferSize)); - if (bytesRead === 0) { + if (buffer.length === 0) { return false; } @@ - for (let index = 0; index < bytesRead; index++) { + for (let index = 0; index < buffer.length; index++) { if (buffer[index] === 0) { return true; } } @@ - for (let index = 0; index < bytesRead; index++) { + for (let index = 0; index < buffer.length; index++) { if (buffer[index] < 9 || (buffer[index] > 13 && buffer[index] < 32)) { nonPrintableCount++; } } @@ - return nonPrintableCount / bytesRead > 0.3; + return nonPrintableCount / buffer.length > 0.3; } catch { return false; } } @@ - const content = await fs.promises.readFile(filePath, encoding as BufferEncoding); + const content = Buffer.from( + await vscode.workspace.fs.readFile(vscode.Uri.file(filePath)) + ).toString(encoding as BufferEncoding); @@ - const buffer = await fs.promises.readFile(filePath); + const buffer = await vscode.workspace.fs.readFile(vscode.Uri.file(filePath)); @@ - // Check if it's a directory - const stats = fs.statSync(absolutePath); - if (stats.isDirectory()) { + // Check if it's a directory + const uri = vscode.Uri.file(absolutePath); + const stats = await vscode.workspace.fs.stat(uri); + if ((stats.type & vscode.FileType.Directory) === vscode.FileType.Directory) { return handleToolError( `Path is a directory, not a file: ${filePath}`, 'Path validation', 'validation' ); } @@ - if (stats.size > MAX_FILE_SIZE_BYTES) { - const sizeMB = (stats.size / 1024 / 1024).toFixed(1); + if (stats.size > MAX_FILE_SIZE_BYTES) { + const sizeMB = (stats.size / 1024 / 1024).toFixed(1); return handleToolError( `File too large (${sizeMB}MB). Maximum size: ${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB`, 'File size check', 'validation' ); } @@ - const fileType = detectFileType(absolutePath); + const fileType = await (async () => { + const dt = detectFileType(absolutePath); + return dt === 'text' && (await isBinaryFile(absolutePath)) ? 'binary' : dt; + })(); context.logger.info( - `[read] Reading ${fileType} file: ${filePath} (${(stats.size / 1024).toFixed(1)} KB)` + `[read] Reading ${fileType} file: ${filePath} (${(stats.size / 1024).toFixed(1)} KB)` ); @@ - const fileSize = (stats.size / 1024).toFixed(1); + const fileSize = (stats.size / 1024).toFixed(1); content = `[BINARY FILE: ${fileName}]\nFile size: ${fileSize} KB\nCannot display binary content as text.`; break; }Note: adjust detectFileType to await isBinaryFile (now async), or inline as shown.
Also applies to: 146-153, 198-215, 258-281, 263-271, 274-281, 283-289, 314-319
src/providers/BaseWebviewViewProvider.ts (1)
92-100: Wrap delegate invocation in try/catch and log errors.Prevents the listener from failing open on delegate exceptions.
- const patch = await delegateFunction(...message.params); + let patch: Patch<A>[typeof message.key]; + try { + patch = await delegateFunction(...message.params); + } catch (err) { + this.logger.error('Delegate error', { key: message.key, err }); + return; + }src/SuperdesignCanvasPanel.ts (1)
251-256: Don’t overwrite workspaceUri; causes panel-map corruption in multi-root.Using workspaceFolders[0] here can mismatch the panel’s key and break cleanup.
- const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - if (workspaceFolder && this._state) { - this._state.workspaceUri = workspaceFolder.uri.toString(); - } + // Preserve the workspaceUri assigned at construction/deserialization. + if (!this._state.workspaceUri) { + const wf = vscode.workspace.workspaceFolders?.[0]; + if (wf) this._state.workspaceUri = wf.uri.toString(); + }src/providers/chatSidebarProvider.ts (1)
105-112: Harden handler: type the message and guard custom handler errors.Prevents an extension crash if a custom handler throws and removes any.
- protected async handleMessage(message: any, webview: vscode.Webview): Promise<void> { + protected async handleMessage(message: unknown, webview: vscode.Webview): Promise<void> { // First try custom message handler for auto-canvas functionality if (this.customMessageHandler) { - if (message.key !== 'log') { - this.logger.debug('Calling custom message handler'); - } - this.customMessageHandler(message); + try { + if ((message as any)?.key !== 'log') { + this.logger.debug('Calling custom message handler'); + } + this.customMessageHandler(message); + } catch (err) { + this.logger.error(`Custom message handler failed: ${String(err)}`, { err }); + } }src/tools/theme-tool.ts (1)
75-81: Use VS Code FS API for workspace files (remote/virtual FS safe).Direct fs.* breaks in remote workspaces; guidelines require vscode.workspace.fs.
+import * as vscode from 'vscode'; @@ - if (create_directories) { - const dirPath = path.dirname(resolvedPath); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - context.logger.info(`[theme] Created directory: ${dirPath}`); - } - } + if (create_directories) { + const dirUri = vscode.Uri.file(path.dirname(resolvedPath)); + await vscode.workspace.fs.createDirectory(dirUri); + context.logger.info(`[theme] Ensured directory exists: ${dirUri.fsPath}`); + } @@ - // Write the CSS content to file - fs.writeFileSync(resolvedPath, cssSheet, 'utf8'); + // Write the CSS content to file + await vscode.workspace.fs.writeFile( + vscode.Uri.file(resolvedPath), + Buffer.from(cssSheet, 'utf8') + );Also applies to: 84-85
src/tools/tool-utils.ts (1)
87-101: Workspace boundary check is bypassable; switch to path.relative.startsWith() can misclassify siblings (e.g., /work vs /workspace) and is case-sensitive on Windows.
- const normalizedWorkspace = path.normalize(context.workingDirectory); - - // Handle both absolute and relative paths - let resolvedPath: string; - resolvedPath = path.isAbsolute(filePath) ? path.normalize(filePath) : path.resolve(context.workingDirectory, filePath); - - // Check if path is within workspace boundary - if (!resolvedPath.startsWith(normalizedWorkspace)) { + const workspaceAbs = path.resolve(context.workingDirectory); + const targetAbs = path.isAbsolute(filePath) + ? path.resolve(filePath) + : path.resolve(workspaceAbs, filePath); + // On Windows, compare case-insensitively + const base = process.platform === 'win32' ? workspaceAbs.toLowerCase() : workspaceAbs; + const target = process.platform === 'win32' ? targetAbs.toLowerCase() : targetAbs; + const rel = path.relative(base, target); + if (rel.startsWith('..') || path.isAbsolute(rel)) { return handleToolError( `Path must be within workspace directory: ${filePath}`, 'Security check', 'security' ); }src/tools/ls-tool.ts (1)
145-146: Avoid blocking the extension host; use VS Code FS API instead of sync Node fs.Synchronous
fs.readdirSync/fs.statSyncwill block the event loop and violate our guideline to usevscode.workspace.fsfor workspace files. Switch to the async VS Code FS APIs and batch stats withPromise.all.Apply:
- // Read directory contents - const files = fs.readdirSync(absolutePath); + // Read directory contents (VS Code FS API) + const dirUri = vscode.Uri.file(absolutePath); + const dirEntries = await vscode.workspace.fs.readDirectory(dirUri); ... - for (const file of files) { + for (const [file, fileType] of dirEntries) { ... - const fullPath = path.join(absolutePath, file); - - try { - const fileStats = fs.statSync(fullPath); - const isDir = fileStats.isDirectory(); + const fullPath = path.join(absolutePath, file); + const fileUri = vscode.Uri.file(fullPath); + try { + const stat = await vscode.workspace.fs.stat(fileUri); + const isDir = (stat.type & vscode.FileType.Directory) === vscode.FileType.Directory; ... - size: isDir ? 0 : fileStats.size, - modifiedTime: fileStats.mtime, + size: isDir ? 0 : stat.size ?? 0, + modifiedTime: new Date(stat.mtime ?? 0),And add this import at top:
import * as vscode from 'vscode';Also applies to: 178-186
src/webview/utils/gridLayout.ts (1)
417-422: Return type mismatch:parts.at(-1)can be undefined.The function signature returns
string, butat(-1)isstring | undefined. Either coalesce or widen the return type.-export function getCurrentLevelVersion(filename: string): string { +export function getCurrentLevelVersion(filename: string): string { ... - return parts.at(-1); + return parts.length ? parts[parts.length - 1] : '';src/tools/glob-tool.ts (1)
148-151: Windows path separator mismatch breaks matchingrelativePath uses platform separators; globToRegex assumes forward slashes. Normalize before matching to keep behavior consistent on Windows.
- const relativePath = path.relative(searchDir, fullPath); + const relativePath = path + .relative(searchDir, fullPath) + .split(path.sep) + .join('/');src/tools/grep-tool.ts (1)
61-76: Glob include pattern conversion is incomplete (brace sets) and brittle across OS path separators.
- Your examples allow "*.{ts,tsx}", but the converter doesn’t support brace expansion, so that pattern won’t match anything.
- On Windows, filePath uses backslashes; using [^/] and anchoring against "/" will cause mismatches.
Minimal hotfix to normalize paths and avoid Windows mismatches:
function matchesIncludePattern(filePath: string, includePattern?: string): boolean { if (!includePattern) { return true; } - // Convert glob pattern to regex (simplified) - const regexPattern = includePattern + // Normalize path to POSIX-style for matching + const normalizedPath = filePath.replace(/\\/g, '/'); + // Convert glob pattern to regex (still simplified; no brace sets) + const regexPattern = includePattern .replaceAll(/[.+^${}()|[\]\\]/g, String.raw`\$&`) // Escape special regex chars .replaceAll('**', '###DOUBLESTAR###') // Temporarily replace ** .replaceAll('*', '[^/]*') // * becomes [^/]* (no directory separators) .replaceAll('###DOUBLESTAR###', '.*') // ** becomes .* (any characters) .replaceAll('?', '[^/]'); // ? becomes [^/] (single char, no dir sep) - const regex = new RegExp(`^${regexPattern}$`); - return regex.test(filePath); + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(normalizedPath); }Recommended robust fix (preferred): switch to a battle‑tested glob matcher that supports brace sets and cross‑platform paths, e.g. picomatch or minimatch.
Example:
import picomatch from 'picomatch'; function matchesIncludePattern(filePath: string, includePattern?: string): boolean { if (!includePattern) return true; const isMatch = picomatch(includePattern, { dot: true }); return isMatch(filePath.replace(/\\/g, '/')); }src/services/logger.ts (1)
16-37: Data scrubbing bug: removePromptsFromData returns the original object, not the scrubbed clone.This defeats prompt/content redaction and risks leaking sensitive message content in logs.
Apply this diff:
- const clone = structuredClone(dictionary); + const clone = structuredClone(dictionary); @@ - return dictionary; + return dictionary; } - return dictionary; + return clone;Optional: consider deep traversal to scrub nested objects, not just arrays at the top level.
src/webview/components/CanvasView.tsx (1)
795-805: Use correct wheel prop and retain scale-based doubleClick.step
- Replace the incorrect
wheelDisabledwithdisabledin thewheelconfig.- Leave
doubleClick.step={50}as-is—this uses the library’s scale-step units (defaults are ~40–70).wheel={{ - wheelDisabled: true, + disabled: true, touchPadDisabled: false, step: 0.05, }}src/webview/hooks/useVscodeState.ts (1)
41-45: Symbol-key bug: validKeys misses symbols; patch application rejects valid symbol actionsObject.keys excludes symbols; later checks use String(data.key). Fix by tracking PropertyKey and checking membership directly.
- const validKeys = useMemo( - () => new Set(Object.keys(postReducer).filter(k => !dangerousKeys.has(k))), - [postReducer] - ); + const validKeys = useMemo(() => { + const stringKeys = Object.keys(postReducer); + const symbolKeys = Object.getOwnPropertySymbols(postReducer); + const filteredStringKeys = stringKeys.filter(k => !dangerousKeys.has(k)); + return new Set<PropertyKey>([...filteredStringKeys, ...symbolKeys]); + }, [postReducer]); @@ - if ( - validKeys.has(String(data.key)) && + if ( + validKeys.has(data.key as PropertyKey) && Object.prototype.hasOwnProperty.call(postReducer, data.key) && typeof postReducer[data.key] === 'function' ) { const patchFunction = postReducer[data.key]; setState(previous => patchFunction(previous, data.patch)); } else {Also applies to: 50-57
src/webview/contexts/WebviewContext.tsx (2)
60-62: Harden request ID generationAlign generateId with secure randomness; keep a minimal, reliable fallback only if crypto is unavailable.
-function generateId(): string { - return `req_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`; -} +function generateId(): string { + const idPart = + typeof crypto !== 'undefined' && 'randomUUID' in crypto + ? crypto.randomUUID() + : (typeof crypto !== 'undefined' + ? Array.from(crypto.getRandomValues(new Uint8Array(16)), b => b.toString(16).padStart(2, '0')).join('') + : Math.random().toString(36).slice(2, 11)); + return `req_${Date.now()}_${idPart}`; +}
216-223: Validate message origin before processingFilter postMessage events to the VS Code webview origin to avoid handling unsolicited messages from nested iframes or injected scripts.
- const handleMessage = (event: MessageEvent<any>) => { + const handleMessage = (event: MessageEvent<any>) => { const message = event.data; + if (typeof event.origin === 'string' && !event.origin.startsWith('vscode-webview://')) { + return; + }src/webview/components/DesignFrame.tsx (5)
428-446: Injected script references logger (undefined in iframe) → ReferenceErrorlogger is a React hook variable; it doesn’t exist inside the iframe’s srcDoc. These calls will throw and may prevent fallbacks from running.
- navigator.serviceWorker.register(swUrl).then(registration => { - logger.info('Service Worker registered successfully'); + navigator.serviceWorker.register(swUrl).then(registration => { + console.log('[DesignFrame] Service Worker registered'); ... - }).catch(error => { - logger.info('Service Worker registration failed, falling back to direct loading'); + }).catch(error => { + console.log('[DesignFrame] SW registration failed; using direct loading');Alternatively, remove SW entirely and keep processImages().
385-386: CSP is overly permissive; tighten and use nonceCurrent CSP allows 'unsafe-inline' and 'unsafe-eval' across the board. Enforce nonce-based scripting and minimal sources.
- const iframeCSP = `<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: https: http:; img-src 'self' data: blob: https: http: *; style-src 'self' 'unsafe-inline' data: https: http: *; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: https: http: *; connect-src 'self' https: http: *; frame-src 'self' data: blob: https: http: *;">`; + const iframeCSP = `<meta http-equiv="Content-Security-Policy" content=" + default-src 'none'; + img-src data: https:; + style-src 'unsafe-inline'; + script-src ${nonce ? `'nonce-${nonce}'` : "'self'"} 'strict-dynamic'; + connect-src https:; + font-src data:; + frame-src 'self'; + object-src 'none'; + base-uri 'none'; + ">`;Note: If nonce is absent, fall back to self and avoid eval entirely.
542-566: Sandbox iframes to reduce riskAdd a sandbox attribute allowing only what you need (scripts and same-origin if required). Apply to both iframe render paths.
<iframe srcDoc={modifiedContent} title={`${file.name} - ${getViewportLabel(viewport)}`} + sandbox="allow-scripts allow-same-origin" style={{Also add the same to the SVG iframe.
374-380: Nonce injection via replaceAll is brittleString replacement can miss edge cases or double-inject. Use DOMParser to set nonce on scripts safely.
- const injectNonce = (html: string, nonce: string | null) => { - if (!nonce) { - return html; - } - return html.replaceAll('<script', `<script nonce="${nonce}"`); - }; + const injectNonce = (html: string, nonce: string | null) => { + if (!nonce) return html; + const doc = new DOMParser().parseFromString(html, 'text/html'); + doc.querySelectorAll('script:not([nonce])').forEach(s => s.setAttribute('nonce', nonce)); + return '<!DOCTYPE html>' + doc.documentElement.outerHTML; + };
571-608: Direct HTML/SVG rendering without sanitizationdangerouslySetInnerHTML must be sanitized. Adopt DOMPurify (or equivalent) and limit allowed tags/attrs. If adding a dep isn’t viable, disable 'html' mode for untrusted content.
- case 'html': { + case 'html': { // Direct HTML/SVG rendering - USE WITH CAUTION (security risk) // Only use for trusted content or when iframe fails if (file.fileType === 'svg') { return ( <div ... - dangerouslySetInnerHTML={{ __html: file.content }} + dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(file.content, { USE_PROFILES: { svg: true, svgFilters: true } }) }} /> ); } return ( <div - dangerouslySetInnerHTML={{ __html: file.content }} + dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(file.content, { USE_PROFILES: { html: true } }) }}Add at top of file:
import DOMPurify from 'dompurify';And ensure the bundle includes it.
| showInformationMessage: (message: string) => void; | ||
| showErrorMessage: (message: string) => void; | ||
| executeCommand: (command: string, args?: any) => Promise<void>; | ||
| executeCommand: (command: string, arguments_?: any) => Promise<void>; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Preserve varargs and avoid any in executeCommand signature
Expose a varargs API and drop any to keep type safety and compatibility with vscode.commands (which is variadic).
- executeCommand: (command: string, arguments_?: any) => Promise<void>;
+ executeCommand: (command: string, ...args: unknown[]) => Promise<void>;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| executeCommand: (command: string, arguments_?: any) => Promise<void>; | |
| // Before: | |
| - executeCommand: (command: string, arguments_?: any) => Promise<void>; | |
| // After: | |
| executeCommand: (command: string, ...args: unknown[]) => Promise<void>; |
🤖 Prompt for AI Agents
In src/api/viewApi.ts around line 14, the executeCommand signature currently
uses a single arguments_?: any parameter; change it to a variadic, typed
signature to match vscode.commands and remove any for type safety — e.g. make it
generic and accept rest args like executeCommand<T = unknown>(command: string,
...args: unknown[]): Promise<T>; update all implementations and callers to
forward/rest-spread arguments accordingly and adjust return types to the generic
T.
| export function isViewApiRequest(message: any): message is ViewApiRequest { | ||
| return ( | ||
| msg && | ||
| typeof msg === 'object' && | ||
| msg.type === 'request' && | ||
| typeof msg.id === 'string' && | ||
| typeof msg.key === 'string' && | ||
| Array.isArray(msg.params) && | ||
| (msg.context === undefined || | ||
| (typeof msg.context === 'object' && | ||
| typeof msg.context.viewId === 'string' && | ||
| typeof msg.context.viewType === 'string' && | ||
| typeof msg.context.timestamp === 'number')) | ||
| message && | ||
| typeof message === 'object' && | ||
| message.type === 'request' && | ||
| typeof message.id === 'string' && | ||
| typeof message.key === 'string' && | ||
| Array.isArray(message.params) && | ||
| (message.context === undefined || | ||
| (typeof message.context === 'object' && | ||
| typeof message.context.viewId === 'string' && | ||
| typeof message.context.viewType === 'string' && | ||
| typeof message.context.timestamp === 'number')) | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Type guards: accept unknown and harden null/object checks
Use unknown and explicit null checks.
-export function isViewApiRequest(message: any): message is ViewApiRequest {
+export function isViewApiRequest(message: unknown): message is ViewApiRequest {
- return (
- message &&
- typeof message === 'object' &&
+ return (
+ message != null &&
+ typeof message === 'object' &&
(message as any).type === 'request' &&
typeof (message as any).id === 'string' &&
typeof (message as any).key === 'string' &&
Array.isArray((message as any).params) &&
- (message.context === undefined ||
- (typeof message.context === 'object' &&
- typeof message.context.viewId === 'string' &&
- typeof message.context.viewType === 'string' &&
- typeof message.context.timestamp === 'number'))
+ ((message as any).context === undefined ||
+ ((message as any).context != null &&
+ typeof (message as any).context === 'object' &&
+ typeof (message as any).context.viewId === 'string' &&
+ typeof (message as any).context.viewType === 'string' &&
+ typeof (message as any).context.timestamp === 'number'))
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function isViewApiRequest(message: any): message is ViewApiRequest { | |
| return ( | |
| msg && | |
| typeof msg === 'object' && | |
| msg.type === 'request' && | |
| typeof msg.id === 'string' && | |
| typeof msg.key === 'string' && | |
| Array.isArray(msg.params) && | |
| (msg.context === undefined || | |
| (typeof msg.context === 'object' && | |
| typeof msg.context.viewId === 'string' && | |
| typeof msg.context.viewType === 'string' && | |
| typeof msg.context.timestamp === 'number')) | |
| message && | |
| typeof message === 'object' && | |
| message.type === 'request' && | |
| typeof message.id === 'string' && | |
| typeof message.key === 'string' && | |
| Array.isArray(message.params) && | |
| (message.context === undefined || | |
| (typeof message.context === 'object' && | |
| typeof message.context.viewId === 'string' && | |
| typeof message.context.viewType === 'string' && | |
| typeof message.context.timestamp === 'number')) | |
| ); | |
| export function isViewApiRequest(message: unknown): message is ViewApiRequest { | |
| return ( | |
| message != null && | |
| typeof message === 'object' && | |
| (message as any).type === 'request' && | |
| typeof (message as any).id === 'string' && | |
| typeof (message as any).key === 'string' && | |
| Array.isArray((message as any).params) && | |
| ((message as any).context === undefined || | |
| ((message as any).context != null && | |
| typeof (message as any).context === 'object' && | |
| typeof (message as any).context.viewId === 'string' && | |
| typeof (message as any).context.viewType === 'string' && | |
| typeof (message as any).context.timestamp === 'number')) | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/api/viewApi.ts around lines 108 to 121, the type guard should accept an
unknown input and harden null/object checks: change the parameter type to
unknown, first verify message !== null && typeof message === 'object', use a
typed helper (or inline) to ensure message has the required string/array
properties (check typeof (message as any).id === 'string', etc.), and for
context ensure (message as any).context === undefined || ((message as
any).context !== null && typeof (message as any).context === 'object' && typeof
(message as any).context.viewId === 'string' && typeof (message as
any).context.viewType === 'string' && typeof (message as any).context.timestamp
=== 'number')); update the function signature and internal checks accordingly so
the guard is safe for unknown and null inputs.
| export function isViewApiResponse(message: any): message is ViewApiResponse { | ||
| return ( | ||
| msg && | ||
| typeof msg === 'object' && | ||
| msg.type === 'response' && | ||
| typeof msg.id === 'string' && | ||
| msg.hasOwnProperty('value') | ||
| message && | ||
| typeof message === 'object' && | ||
| message.type === 'response' && | ||
| typeof message.id === 'string' && | ||
| message.hasOwnProperty('value') | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use safe hasOwnProperty for responses
Avoid direct method access on possibly null-prototype objects.
-export function isViewApiResponse(message: any): message is ViewApiResponse {
+export function isViewApiResponse(message: unknown): message is ViewApiResponse {
return (
- message &&
- typeof message === 'object' &&
- message.type === 'response' &&
- typeof message.id === 'string' &&
- message.hasOwnProperty('value')
+ message != null &&
+ typeof message === 'object' &&
+ (message as any).type === 'response' &&
+ typeof (message as any).id === 'string' &&
+ Object.prototype.hasOwnProperty.call(message, 'value')
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function isViewApiResponse(message: any): message is ViewApiResponse { | |
| return ( | |
| msg && | |
| typeof msg === 'object' && | |
| msg.type === 'response' && | |
| typeof msg.id === 'string' && | |
| msg.hasOwnProperty('value') | |
| message && | |
| typeof message === 'object' && | |
| message.type === 'response' && | |
| typeof message.id === 'string' && | |
| message.hasOwnProperty('value') | |
| ); | |
| export function isViewApiResponse(message: unknown): message is ViewApiResponse { | |
| return ( | |
| message != null && | |
| typeof message === 'object' && | |
| (message as any).type === 'response' && | |
| typeof (message as any).id === 'string' && | |
| Object.prototype.hasOwnProperty.call(message, 'value') | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/api/viewApi.ts around lines 127 to 134, the type guard uses
message.hasOwnProperty('value') which can throw or be incorrect for objects with
null prototypes; replace that check with a safe call such as
Object.prototype.hasOwnProperty.call(message, 'value') (or Reflect.has(message,
'value')) so the presence check works for all plain and null-prototype objects
while preserving the rest of the type guard.
| export function isViewApiError(message: any): message is ViewApiError { | ||
| return ( | ||
| msg && | ||
| typeof msg === 'object' && | ||
| msg.type === 'error' && | ||
| typeof msg.id === 'string' && | ||
| typeof msg.value === 'string' | ||
| message && | ||
| typeof message === 'object' && | ||
| message.type === 'error' && | ||
| typeof message.id === 'string' && | ||
| typeof message.value === 'string' | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Harden error type guard (unknown + null check)
-export function isViewApiError(message: any): message is ViewApiError {
+export function isViewApiError(message: unknown): message is ViewApiError {
return (
- message &&
- typeof message === 'object' &&
- message.type === 'error' &&
- typeof message.id === 'string' &&
- typeof message.value === 'string'
+ message != null &&
+ typeof message === 'object' &&
+ (message as any).type === 'error' &&
+ typeof (message as any).id === 'string' &&
+ typeof (message as any).value === 'string'
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function isViewApiError(message: any): message is ViewApiError { | |
| return ( | |
| msg && | |
| typeof msg === 'object' && | |
| msg.type === 'error' && | |
| typeof msg.id === 'string' && | |
| typeof msg.value === 'string' | |
| message && | |
| typeof message === 'object' && | |
| message.type === 'error' && | |
| typeof message.id === 'string' && | |
| typeof message.value === 'string' | |
| ); | |
| export function isViewApiError(message: unknown): message is ViewApiError { | |
| return ( | |
| message != null && | |
| typeof message === 'object' && | |
| (message as any).type === 'error' && | |
| typeof (message as any).id === 'string' && | |
| typeof (message as any).value === 'string' | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/api/viewApi.ts around lines 140 to 147, the type guard currently accepts
any and doesn't guard against null; change the parameter type to unknown and
explicitly check message !== null before typeof message === 'object', then
narrow to a record/any to validate message.type === 'error' and that id and
value are strings (use typeof checks on (message as any).id and (message as
any).value). Ensure the function signature is updated to message: unknown and
the runtime checks prevent null dereference while preserving the ViewApiError
type predicate.
| export function isViewApiEvent(message: any): message is ViewApiEvent { | ||
| return ( | ||
| msg && | ||
| typeof msg === 'object' && | ||
| msg.type === 'event' && | ||
| typeof msg.key === 'string' && | ||
| Array.isArray(msg.value) | ||
| message && | ||
| typeof message === 'object' && | ||
| message.type === 'event' && | ||
| typeof message.key === 'string' && | ||
| Array.isArray(message.value) | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Harden event type guard (unknown + null check)
-export function isViewApiEvent(message: any): message is ViewApiEvent {
+export function isViewApiEvent(message: unknown): message is ViewApiEvent {
return (
- message &&
- typeof message === 'object' &&
- message.type === 'event' &&
- typeof message.key === 'string' &&
- Array.isArray(message.value)
+ message != null &&
+ typeof message === 'object' &&
+ (message as any).type === 'event' &&
+ typeof (message as any).key === 'string' &&
+ Array.isArray((message as any).value)
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function isViewApiEvent(message: any): message is ViewApiEvent { | |
| return ( | |
| msg && | |
| typeof msg === 'object' && | |
| msg.type === 'event' && | |
| typeof msg.key === 'string' && | |
| Array.isArray(msg.value) | |
| message && | |
| typeof message === 'object' && | |
| message.type === 'event' && | |
| typeof message.key === 'string' && | |
| Array.isArray(message.value) | |
| ); | |
| export function isViewApiEvent(message: unknown): message is ViewApiEvent { | |
| return ( | |
| message != null && | |
| typeof message === 'object' && | |
| (message as any).type === 'event' && | |
| typeof (message as any).key === 'string' && | |
| Array.isArray((message as any).value) | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/api/viewApi.ts around lines 153 to 160, the type guard currently accepts
any and doesn't protect against null or unknown input; change the function
signature to accept message: unknown, add an explicit null check (message !==
null) before typeof message === 'object', and perform property checks using safe
narrowing (e.g., ('type' in message) && (message as any).type === 'event' and
typeof (message as any).key === 'string' and Array.isArray((message as
any).value)) so the guard doesn't throw on null/primitive inputs and correctly
narrows to ViewApiEvent.
| a: ({ children, href, ...properties }) => ( | ||
| <a href={href} target='_blank' rel='noopener noreferrer' {...properties}> | ||
| {children} | ||
| </a> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Sanitize link protocols.
Restrict to http(s)/mailto to harden the webview.
- a: ({ children, href, ...properties }) => (
- <a href={href} target='_blank' rel='noopener noreferrer' {...properties}>
- {children}
- </a>
- ),
+ a: ({ children, href, ...properties }) => {
+ const safeHref =
+ typeof href === 'string' && /^(https?:|mailto:)/i.test(href)
+ ? href
+ : undefined;
+ return (
+ <a href={safeHref} target='_blank' rel='noopener noreferrer' {...properties}>
+ {children}
+ </a>
+ );
+ },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| a: ({ children, href, ...properties }) => ( | |
| <a href={href} target='_blank' rel='noopener noreferrer' {...properties}> | |
| {children} | |
| </a> | |
| a: ({ children, href, ...properties }) => { | |
| const safeHref = | |
| typeof href === 'string' && /^(https?:|mailto:)/i.test(href) | |
| ? href | |
| : undefined; | |
| return ( | |
| <a href={safeHref} target="_blank" rel="noopener noreferrer" {...properties}> | |
| {children} | |
| </a> | |
| ); | |
| }, |
| viewId: `webview-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, | ||
| viewType: 'chat-interface', | ||
| timestamp: Date.now(), | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, | ||
| sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`, | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use crypto.randomUUID for IDs (CodeQL: insecure randomness)
Math.random() is flagged as insecure. Prefer crypto.randomUUID() with a safe fallback.
- const contextReference = useRef<RequestContext>({
- viewId: `webview-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
+ const contextReference = useRef<RequestContext>({
+ viewId: `webview-${Date.now()}-${(typeof crypto !== 'undefined' && 'randomUUID' in crypto ? crypto.randomUUID() : generateId())}`,
viewType: 'chat-interface',
timestamp: Date.now(),
- sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
+ sessionId: `session-${Date.now()}-${(typeof crypto !== 'undefined' && 'randomUUID' in crypto ? crypto.randomUUID() : generateId())}`,
});Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 GitHub Check: CodeQL
[failure] 108-108: Insecure randomness
This uses a cryptographically insecure random number generated at Math.random() in a security context.
🤖 Prompt for AI Agents
In src/webview/contexts/WebviewContext.tsx around lines 105-109, the current ID
generation uses Math.random() which is insecure; replace those
Math.random()-based IDs with crypto.randomUUID() and provide a safe fallback for
environments without crypto.randomUUID (e.g., generate a UUIDv4 using
crypto.getRandomValues or import a small UUID v4 helper). Ensure viewId and
sessionId keep the existing prefixes (`webview-` and `session-`) and include the
UUID from crypto.randomUUID() (or the fallback) instead of the Math.random()
string.
| for (const deferred of currentRequests) { | ||
| deferred.clearTimeout(); // Clear timeout to prevent late firing | ||
| deferred.reject(new Error('WebviewProvider unmounted')); // Reject first while not settled | ||
| deferred.markSettled(); // Then mark as settled to prevent subsequent resolve/reject calls | ||
| }); | ||
| } | ||
| currentRequests.clear(); |
There was a problem hiding this comment.
Bug: pendingRequests cleanup iterates Map entries, not values
currentRequests is a Map, so iterating it yields [id, deferred]. The loop calls methods on the tuple, causing runtime errors and leaking pending promises on unmount.
Apply:
- for (const deferred of currentRequests) {
- deferred.clearTimeout(); // Clear timeout to prevent late firing
- deferred.reject(new Error('WebviewProvider unmounted')); // Reject first while not settled
- deferred.markSettled(); // Then mark as settled to prevent subsequent resolve/reject calls
- }
+ for (const [, deferred] of currentRequests) {
+ deferred.clearTimeout();
+ deferred.reject(new Error('WebviewProvider unmounted'));
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const deferred of currentRequests) { | |
| deferred.clearTimeout(); // Clear timeout to prevent late firing | |
| deferred.reject(new Error('WebviewProvider unmounted')); // Reject first while not settled | |
| deferred.markSettled(); // Then mark as settled to prevent subsequent resolve/reject calls | |
| }); | |
| } | |
| currentRequests.clear(); | |
| for (const [, deferred] of currentRequests) { | |
| deferred.clearTimeout(); // prevent late firing | |
| deferred.reject(new Error('WebviewProvider unmounted')); // reject pending promise | |
| deferred.markSettled(); // prevent any further settles | |
| } | |
| currentRequests.clear(); |
🤖 Prompt for AI Agents
In src/webview/contexts/WebviewContext.tsx around lines 285 to 290, the cleanup
loop treats currentRequests (a Map) as if it yields deferred objects directly —
iterating a Map returns [id, deferred] pairs, so calling methods on the tuple
causes runtime errors and leaks promises; change the loop to iterate the Map
values (or destructure the pair) so you call deferred.clearTimeout(),
deferred.reject(...), and deferred.markSettled() on the actual deferred object,
then call currentRequests.clear().
| for (const node of tree.nodes) { | ||
| maxX = Math.max(maxX, node.position.x + frameWidth + 100); | ||
| maxY = Math.max(maxY, node.position.y + frameHeight + 100); | ||
| }); | ||
| } | ||
|
|
There was a problem hiding this comment.
Bug: Iterating Map incorrectly; node is a [key, value] tuple.
for (const node of tree.nodes) yields [string, HierarchyNode]. Accessing node.position will throw. Iterate values or destructure.
- for (const node of tree.nodes) {
- maxX = Math.max(maxX, node.position.x + frameWidth + 100);
- maxY = Math.max(maxY, node.position.y + frameHeight + 100);
- }
+ for (const [, node] of tree.nodes) {
+ maxX = Math.max(maxX, node.position.x + frameWidth + 100);
+ maxY = Math.max(maxY, node.position.y + frameHeight + 100);
+ }
+ // or: for (const node of tree.nodes.values()) { ... }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const node of tree.nodes) { | |
| maxX = Math.max(maxX, node.position.x + frameWidth + 100); | |
| maxY = Math.max(maxY, node.position.y + frameHeight + 100); | |
| }); | |
| } | |
| // Before: iterating entries yields [key, node] tuples | |
| - for (const node of tree.nodes) { | |
| - maxX = Math.max(maxX, node.position.x + frameWidth + 100); | |
| - maxY = Math.max(maxY, node.position.y + frameHeight + 100); | |
| - } | |
| // After: destructure to get the node, or iterate over values() | |
| for (const [, node] of tree.nodes) { | |
| maxX = Math.max(maxX, node.position.x + frameWidth + 100); | |
| maxY = Math.max(maxY, node.position.y + frameHeight + 100); | |
| } | |
| // alternatively: | |
| // for (const node of tree.nodes.values()) { | |
| // maxX = Math.max(maxX, node.position.x + frameWidth + 100); | |
| // maxY = Math.max(maxY, node.position.y + frameHeight + 100); | |
| // } |
🤖 Prompt for AI Agents
In src/webview/utils/gridLayout.ts around lines 279 to 283, the loop `for (const
node of tree.nodes)` is iterating Map entries ([key, value]) so `node.position`
will throw; change the iteration to either iterate the Map values or destructure
the entry to obtain the HierarchyNode (e.g., use `for (const [, node] of
tree.nodes)` or `for (const node of tree.nodes.values())`) and then use
`node.position.x`/`node.position.y` to compute maxX/maxY as before.
| design.branchIndex = Number.parseInt(design.version) - 1; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
NaN risk for branchIndex on non-numeric versions.
Number.parseInt(design.version) - 1 can yield NaN. Guard and use radix 10.
- design.branchIndex = Number.parseInt(design.version) - 1;
+ {
+ const v = Number.parseInt(String(design.version), 10);
+ design.branchIndex = Number.isFinite(v) ? Math.max(0, v - 1) : 0;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| design.branchIndex = Number.parseInt(design.version) - 1; | |
| } | |
| { | |
| const v = Number.parseInt(String(design.version), 10); | |
| design.branchIndex = Number.isFinite(v) ? Math.max(0, v - 1) : 0; | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/webview/utils/gridLayout.ts around lines 469-470, replace the unguarded
Number.parseInt(design.version) - 1 with a radix-10 parse and an NaN check: call
Number.parseInt(design.version, 10), test Number.isNaN(result), and only assign
design.branchIndex = parsed - 1 when parsed is a valid number; otherwise set
design.branchIndex to a safe fallback (e.g., undefined or -1) or leave it
unchanged so it never becomes NaN.


PR Type
Enhancement, Other
Description
• Added ESLint unicorn plugin with recommended rules for modern JavaScript patterns
• Modernized Node.js imports to use built-in module syntax (
node:fs,node:path, etc.)• Improved code readability with numeric separators for large values (200000 → 200_000)
• Standardized parameter and variable naming throughout codebase (
params→parameters,args→arguments_)• Converted legacy iteration patterns (
forEach→for...of,Array.from→ spread operator)• Enhanced string manipulation methods (
replace→replaceAll,substr→slice)• Added proper block scoping to switch cases with curly braces
• Modernized array access patterns (array indexing →
at(-1)method)• Updated DOM manipulation methods (
appendChild→append,removeChild→remove)• Renamed React component interfaces to use
Propertiessuffix for consistency• Improved conditional logic flow with inverted conditions and ternary operators
• Enhanced error handling with better variable naming and appropriate error types
Diagram Walkthrough
File Walkthrough
40 files
BedrockProvider.ts
Improve readability with numeric separators and parameter renamingsrc/providers/implementations/BedrockProvider.ts
• Renamed type alias from
ProviderInstanceParamstoProviderInstanceParameters• Added numeric separators to large token
values (e.g., 200000 → 200_000)
• Renamed parameter from
paramstoparametersincreateInstancemethod• Simplified ternary operator
logic in
validateCredentialsmethodOpenRouterProvider.ts
Apply consistent parameter naming and numeric formattingsrc/providers/implementations/OpenRouterProvider.ts
• Renamed type alias from
ProviderInstanceParamstoProviderInstanceParameters• Added numeric separators to large token
values for better readability
• Renamed parameter from
paramstoparametersincreateInstancemethodgridLayout.ts
Improve code clarity with modern loop syntax and namingsrc/webview/utils/gridLayout.ts
• Renamed loop variables from
itoindexfor clarity• Converted
forEachloops tofor...ofsyntax throughout• Replaced array access
with
at(-1)method for getting last element• Renamed variables for
better readability (
nameWithoutExt→nameWithoutExtension)• Changed
parseInttoNumber.parseIntfor consistencyextension.ts
Add proper switch case scoping and modernize importssrc/extension.ts
• Added block scoping to switch cases with curly braces
• Changed
import from
child_processtonode:child_process• Renamed variable
from
doctodocumentfor claritychunkToolOutputToMessageToolOutput.ts
Enhance readability with better variable naming and scopingsrc/services/chunkToolOutputToMessageToolOutput.ts
• Renamed function from
getProptogetProperty• Renamed variables for
clarity (
obj→object,msg→message)• Added block scoping to switch
cases with curly braces
• Improved variable naming throughout the file
webviewMessageGuard.ts
Improve parameter naming and numeric formattingsrc/services/webviewMessageGuard.ts
• Renamed parameters and variables for clarity (
err→error_,func→function_,args→arguments_)• Renamed loop variable from
itoindex• Changed timeout value to use numeric separator (60000 → 60_000)
ChatController.ts
Enhance parameter naming and conditional logic claritysrc/chat/ChatController.ts
• Renamed parameter from
paramstoparametersandargstoarguments_•
Added block scoping to switch cases
• Simplified conditional logic by
inverting condition
• Improved parameter naming consistency
OpenAIProvider.ts
Apply consistent naming and numeric formattingsrc/providers/implementations/OpenAIProvider.ts
• Renamed type alias and parameter for consistency
• Added numeric
separators to token values
• Applied consistent parameter naming
pattern
useVscodeState.ts
Improve function and variable naming consistencysrc/webview/hooks/useVscodeState.ts
• Renamed function from
isFnKeytoisFunctionKey• Renamed variables
for clarity (
msg→message,arg→argument)• Renamed parameters
throughout for better readability
GoogleProvider.ts
Apply consistent naming and numeric formattingsrc/providers/implementations/GoogleProvider.ts
• Renamed type alias and parameter for consistency
• Added numeric
separators to large token values
• Applied consistent parameter naming
pattern
AnthropicProvider.ts
Apply consistent parameter naming and numeric formattingsrc/providers/implementations/AnthropicProvider.ts
• Renamed type alias from
ProviderInstanceParamstoProviderInstanceParameters• Added numeric separators to token values
• Renamed parameter for consistency
viewApi.ts
Enhance parameter and variable naming consistencysrc/api/viewApi.ts
• Renamed parameter from
argstoarguments_• Renamed variables from
msgtomessagein type guard functions• Improved parameter naming
consistency
MoonshotProvider.ts
Apply consistent naming and numeric formattingsrc/providers/implementations/MoonshotProvider.ts
• Renamed type alias and parameter for consistency
• Added numeric
separators to token values
• Applied consistent parameter naming
pattern
ipcReducer.ts
Improve type definitions with better naming conventionssrc/types/ipcReducer.ts
• Renamed parameters and variables for clarity (
args→arguments_,prop→property,obj→object)• Renamed type alias from
FnKeystoFunctionKeys• Improved naming consistency throughout type definitions
useDebouncedSave.ts
Enhance variable naming with clearer reference namingsrc/webview/hooks/useDebouncedSave.ts
• Renamed ref variables with
Referencesuffix for clarity• Renamed
variables for better readability (
currentDataStr→currentDataString)• Improved variable naming consistency throughout
SuperdesignCanvasPanel.ts
Improve control flow and array handlingsrc/SuperdesignCanvasPanel.ts
• Added block scoping to switch cases
• Changed while loop condition
for clarity
• Used spread operator instead of
Array.fromfor arrayconversion
chatSidebarProvider.ts
Enhance error handling and switch case structuresrc/providers/chatSidebarProvider.ts
• Renamed variable from
etoerrorin catch block• Added block
scoping to switch cases
• Improved error handling variable naming
logger.ts
Improve parameter handling and conditional logicsrc/services/logger.ts
• Made data parameter optional with proper typing
• Renamed variable
from
levelStrtolevelString• Inverted conditional logic for cleaner
flow
themeParser.ts
Modernize iteration patterns and improve variable namingsrc/webview/utils/themeParser.ts
• Converted
forEachloops tofor...ofsyntax• Renamed variable from
varKeytovariableKey• Consolidated array push operations into single
calls
ProviderRegistry.ts
Modernize array creation with spread operatorsrc/providers/ProviderRegistry.ts
• Replaced
Array.fromwith spread operator for array conversions•
Modernized array creation from iterables
types.ts
Standardize parameter interface namingsrc/providers/types.ts
• Renamed interface from
ProviderInstanceParamstoProviderInstanceParameters• Updated abstract method signature to use
new parameter name
workspaceStateService.ts
Improve loop variable naming consistencysrc/services/workspaceStateService.ts
• Renamed loop variable from
itoindexfor clarityBaseRepository.ts
Modernize iteration patternsrc/types/BaseRepository.ts
• Converted
forEachloop tofor...ofsyntaxProviderService.ts
Simplify conditional logic flowsrc/providers/ProviderService.ts
• Inverted conditional logic for cleaner ternary expression
VsCodeConfiguration.ts
Simplify conditional logic flowsrc/providers/VsCodeConfiguration.ts
• Inverted conditional logic for cleaner ternary expression
htmlTemplate.ts
Improve loop variable naming consistencysrc/helpers/htmlTemplate.ts
• Renamed loop variable from
itoindexuseLogger.ts
Simplify conditional logic flowsrc/webview/hooks/useLogger.ts
• Inverted conditional logic for cleaner ternary expression
agent.ts
Improve parameter naming consistencysrc/types/agent.ts
• Renamed parameter from
prevtopreviousfor clarityChatInterface.tsx
Apply ESLint unicorn rules and modernize JavaScript patternssrc/webview/components/Chat/ChatInterface.tsx
• Renamed interface
ChatInterfacePropstoChatInterfacePropertiesandparameter
prevStatetopreviousStatethroughout• Replaced
forEachloops with
for...ofloops andArray.from()with spread operator•
Converted if-else chains to switch statements for better readability
•
Updated DOM manipulation methods (
appendChildtoappend,removeChildto
remove)• Applied various ESLint unicorn rule fixes for modern
JavaScript patterns
CanvasView.tsx
Apply ESLint unicorn rules and modernize canvas componentsrc/webview/components/CanvasView.tsx
• Renamed interface
CanvasViewPropstoCanvasViewPropertiesandtransformReftotransformReference• Replaced
forEachloops withfor...ofloops for better performance• Converted if-else chains to
switch statements with proper block scoping
• Updated event listener
methods and global object references (
windowtoglobalThis)WebviewContext.tsx
Apply ESLint unicorn rules to webview contextsrc/webview/contexts/WebviewContext.tsx
• Renamed interface
WebviewProviderPropstoWebviewProviderPropertiesand parameters
argstoarguments_• Updated string manipulation
methods and replaced
forEachwithfor...ofloops• Applied timeout
value formatting and switch statement improvements
ThemePreviewCard.tsx
Apply ESLint unicorn rules to theme preview componentsrc/webview/components/Chat/ThemePreviewCard.tsx
• Renamed interface
ThemePreviewCardPropstoThemePreviewCardProperties• Updated DOM manipulation methods
(
appendChildtoappend,removeChildtoremove)• Applied variable
naming improvements and reducer pattern updates
MarkdownRenderer.tsx
Apply ESLint unicorn rules to markdown renderersrc/webview/components/MarkdownRenderer.tsx
• Renamed interface
MarkdownRendererPropstoLcJwR1WvVRsnxoe1A66pCzeXicuroDP6L6• Updated prop parameter names from
propsto
propertiesfor consistencyindex.tsx
Apply ESLint unicorn rules to webview indexsrc/webview/index.tsx
• Replaced
getElementByIdwithquerySelectorand updated global objectreferences
• Removed debug console.log statements and simplified
context checking logic
DesignPanel.tsx
Apply ESLint unicorn rules to design panelsrc/webview/components/DesignPanel.tsx
• Renamed interface
DesignPanelPropstoDesignPanelPropertiesModeToggle.tsx
Apply ESLint unicorn rules to mode togglesrc/webview/components/Chat/ModeToggle.tsx
• Renamed interface
ModeTogglePropstoModeTogglePropertiesThemePreviewHeader.tsx
Apply ESLint unicorn rules to theme preview headersrc/webview/components/Chat/ThemePreviewHeader.tsx
• Renamed interface
ThemePreviewHeaderPropstoThemePreviewHeaderPropertiesConnectionLines.tsx
Apply ESLint unicorn rules to connection linessrc/webview/components/ConnectionLines.tsx
• Renamed interface
ConnectionLinesPropstoConnectionLinesPropertiesThemeDetails.tsx
Apply ESLint unicorn rules to theme detailssrc/webview/components/Chat/ThemeDetails.tsx
• Renamed interface
ThemeDetailsPropstoThemeDetailsPropertiesColorPalette.tsx
Apply ESLint unicorn rules to color palettesrc/webview/components/Chat/ColorPalette.tsx
• Renamed interface
ColorPalettePropstoColorPaletteProperties15 files
customAgentService.ts
Modernize code with Node.js imports and improved syntaxsrc/services/customAgentService.ts
• Changed import from
ostonode:osfor Node.js built-in module•
Renamed variables for clarity (
tempSuperdesignUri→temporarySuperdesignUri,params→parameters)• Replaced array access
with
at(-1)method for getting last element• Simplified conditional
logic using ternary operators
• Converted for loops to use
for...ofsyntax and renamed loop variables
• Removed empty case block in switch
statement
grep-tool.ts
Modernize with Node.js imports and improved string handlingsrc/tools/grep-tool.ts
• Changed imports from
fsandpathtonode:fsandnode:path• Used
template literal with
String.rawfor regex pattern description•
Replaced
replacewithreplaceAllfor string replacements• Renamed
variables for clarity (
ext→extension)• Simplified file processing
logic with combined conditions
• Converted
forEachloops tofor...ofsyntax
glob-tool.ts
Modernize imports and improve string manipulation methodssrc/tools/glob-tool.ts
• Changed imports to use Node.js built-in module syntax (
node:fs,node:path)• Replaced
replacewithreplaceAllfor string replacements• Used
String.rawtemplate literal for regex escaping• Renamed
parameter from
paramstoparameters• Renamed variable
include_dirstoinclude_directoriesfor clarity• Replaced
new Date().getTime()withDate.now()WebviewApiProvider.ts
Modernize iteration patterns and string methodssrc/providers/WebviewApiProvider.ts
• Renamed parameter from
paramstoparameters• Converted
forEachloops to
for...ofsyntax• Replaced
substrwithslicemethod forstring manipulation
• Simplified loop syntax for disposables cleanup
ls-tool.ts
Modernize imports and improve string/number handlingsrc/tools/ls-tool.ts
• Changed imports to Node.js built-in module syntax
• Replaced
replacewith
replaceAllfor string operations• Used
String.rawfor regexescaping
• Renamed variables and parameters for clarity
• Changed
parseFloattoNumber.parseFloatmultiedit-tool.ts
Modernize imports and string manipulation methodssrc/tools/multiedit-tool.ts
• Changed import to Node.js built-in module syntax
• Used
String.rawfor regex escaping
• Renamed loop variable from
itoindex• Replaced
replacewithreplaceAllfor string operationsbash-tool.ts
Modernize imports and improve variable namingsrc/tools/bash-tool.ts
• Changed imports to Node.js built-in module syntax
• Renamed
variables for clarity (
shellArgs→shellArguments,processEnv→processEnvironment)• Added numeric separator to timeout value
•
Removed redundant logging code
read-tool.ts
Modernize imports and improve variable namingsrc/tools/read-tool.ts
• Changed imports to Node.js built-in module syntax
• Renamed loop
variable from
itoindex• Renamed variable from
exttoextension•
Added block scoping to switch case
tool-utils.ts
Modernize imports and simplify conditional logicsrc/tools/tool-utils.ts
• Changed imports to Node.js built-in module syntax
• Simplified
conditional expressions using ternary operators
• Renamed parameter
from
paramNametoparameterNameBaseWebviewViewProvider.ts
Improve type handling and error classificationsrc/providers/BaseWebviewViewProvider.ts
• Renamed type alias from
FnKeystoFunctionKeys• Renamed variables
for clarity (
delegateFn→delegateFunction)• Changed error type from
ErrortoTypeErrorfor type-related errorswrite-tool.ts
Modernize imports and improve parameter namingsrc/tools/write-tool.ts
• Changed imports to Node.js built-in module syntax
• Renamed
parameter from
create_dirstocreate_directoriestheme-tool.ts
Modernize imports and improve parameter namingsrc/tools/theme-tool.ts
• Changed imports to Node.js built-in module syntax
• Renamed
parameter from
create_dirstocreate_directoriesedit-tool.ts
Modernize imports and string manipulation methodssrc/tools/edit-tool.ts
• Changed imports to Node.js built-in module syntax
• Used
String.rawfor regex escaping
• Replaced
replacewithreplaceAllfor stringoperations
fileWatcherService.ts
Modernize imports and improve conditional checkssrc/services/fileWatcherService.ts
• Changed import to Node.js built-in module syntax
• Changed array
index check from
> -1to!== -1• Modified while loop condition for
clarity
DesignFrame.tsx
Apply ESLint unicorn rules and add logging to design framesrc/webview/components/DesignFrame.tsx
• Renamed interface
DesignFramePropstoDesignFrameProperties• Added
proper import for
useLoggerhook and implemented logging throughout•
Converted switch statements to use block scoping and updated DOM
manipulation methods
• Replaced
replaceAllusage and updated globalobject references
1 files
package.json
Add unicorn ESLint plugin and enable auto-fixpackage.json
• Added
eslint-plugin-unicorndependency for enhanced linting rules•
Updated lint script to include
--fixflag for automatic fixes1 files
eslint.config.mjs
Configure ESLint unicorn plugin with project-specific overrideseslint.config.mjs
• Added
eslint-plugin-unicornconfiguration with recommended rules•
Disabled specific unicorn rules that conflict with project conventions
(
filename-case,no-null,prevent-abbreviations)5 files
Summary by CodeRabbit
New Features
Bug Fixes
Chores
Refactor