diff --git a/frontend/scripts/generate-icons.js b/frontend/scripts/generate-icons.js index ba3ce3408..870e530e9 100644 --- a/frontend/scripts/generate-icons.js +++ b/frontend/scripts/generate-icons.js @@ -30,12 +30,17 @@ function scanForUsedIcons() { // Recursively scan all .tsx and .ts files function scanDirectory(dir) { const files = fs.readdirSync(dir); - + files.forEach(file => { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); - + if (stat.isDirectory()) { + // Skip the assets directory to avoid scanning generated files + if (file === 'assets') { + debug(` Skipping assets directory: ${path.relative(srcDir, filePath)}`); + return; + } scanDirectory(filePath); } else if (file.endsWith('.tsx') || file.endsWith('.ts')) { const content = fs.readFileSync(filePath, 'utf8'); @@ -52,6 +57,18 @@ function scanForUsedIcons() { }); } + // Match React.createElement(LocalIcon, { icon: 'icon-name', ... }) + const createElementMatches = content.match(/React\.createElement\(LocalIcon,\s*\{[^}]*icon:\s*['"]([^'"]+)['"]/g); + if (createElementMatches) { + createElementMatches.forEach(match => { + const iconMatch = match.match(/icon:\s*['"]([^'"]+)['"]/); + if (iconMatch) { + usedIcons.add(iconMatch[1]); + debug(` Found (createElement): ${iconMatch[1]} in ${path.relative(srcDir, filePath)}`); + } + }); + } + // Match old material-symbols-rounded spans: icon-name const spanMatches = content.match(/]*className="[^"]*material-symbols-rounded[^"]*"[^>]*>([^<]+)<\/span>/g); if (spanMatches) { @@ -77,21 +94,23 @@ function scanForUsedIcons() { }); } - // Match icon strings in icon configuration files (iconMap.tsx, toolsTaxonomy.ts) - // Only scan these specific files to avoid false positives - if (filePath.includes('iconMap.tsx') || filePath.includes('toolsTaxonomy.ts')) { - // Pattern: : 'icon-name' or : "icon-name" (object value assignment) - const configIconMatches = content.match(/:\s*['"]([a-z][a-z0-9-]*(?:-rounded|-outline|-sharp)?)['"][,\s}]/g); - if (configIconMatches) { - configIconMatches.forEach(match => { - const iconMatch = match.match(/:\s*['"]([a-z][a-z0-9-]*(?:-rounded|-outline|-sharp)?)['"][,\s}]/); - if (iconMatch && iconMatch[1]) { - const iconName = iconMatch[1]; + // Match icon strings with common Material Symbols suffixes anywhere in the code + // Pattern: any string ending with -rounded, -outline, or -sharp that looks like an icon name + const iconStringMatches = content.match(/['"]([a-z][a-z0-9-]*(?:-rounded|-outline|-sharp))['"][,\s})]/g); + if (iconStringMatches) { + iconStringMatches.forEach(match => { + const iconMatch = match.match(/['"]([a-z][a-z0-9-]*(?:-rounded|-outline|-sharp))['"][,\s})]/); + if (iconMatch && iconMatch[1]) { + const iconName = iconMatch[1]; + // Skip common false positives (CSS classes, file paths, etc.) + if (!iconName.includes('/') && + !iconName.startsWith('--') && + iconName.length < 50) { usedIcons.add(iconName); - debug(` Found (config): ${iconName} in ${path.relative(srcDir, filePath)}`); + debug(` Found (string): ${iconName} in ${path.relative(srcDir, filePath)}`); } - }); - } + } + }); } } }); diff --git a/frontend/src/core/components/shared/LocalIcon.test.ts b/frontend/src/core/components/shared/LocalIcon.test.ts index 76a3808ae..b12095c18 100644 --- a/frontend/src/core/components/shared/LocalIcon.test.ts +++ b/frontend/src/core/components/shared/LocalIcon.test.ts @@ -23,7 +23,7 @@ describe('LocalIcon Validation', () => { let grepOutput: string; try { grepOutput = execSync( - `grep -r 'LocalIcon' --include="*.tsx" --include="*.ts" ${srcPath} | grep 'icon='`, + `grep -r 'LocalIcon' --include="*.tsx" --include="*.ts" --exclude="*.test.ts" --exclude="*.test.tsx" ${srcPath} | grep 'icon='`, { encoding: 'utf-8' } ); } catch (error: any) { diff --git a/frontend/src/desktop/components/shared/config/configNavSections.tsx b/frontend/src/desktop/components/shared/config/configNavSections.tsx index 9f8a63114..50c813e97 100644 --- a/frontend/src/desktop/components/shared/config/configNavSections.tsx +++ b/frontend/src/desktop/components/shared/config/configNavSections.tsx @@ -23,7 +23,7 @@ export const useConfigNavSections = ( { key: 'connectionMode', label: t('settings.connection.title', 'Connection Mode'), - icon: 'cloud-rounded', + icon: 'cloud', component: , }, ], @@ -53,7 +53,7 @@ export const createConfigNavSections = ( { key: 'connectionMode', label: 'Connection Mode', - icon: 'cloud-rounded', + icon: 'cloud', component: , }, ], diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 111099896..f29de86e6 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -11,7 +11,8 @@ export default defineConfig({ exclude: [ 'node_modules/', 'src/**/*.spec.ts', // Exclude Playwright E2E tests - 'src/tests/test-fixtures/**' + 'src/tests/test-fixtures/**', + 'src/assets/**' // Exclude generated icon files ], testTimeout: 10000, hookTimeout: 10000, @@ -22,7 +23,8 @@ export default defineConfig({ 'src/core/setupTests.ts', '**/*.d.ts', 'src/tests/test-fixtures/**', - 'src/**/*.spec.ts' + 'src/**/*.spec.ts', + 'src/assets/**' // Exclude generated icon files ] }, projects: [