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: [