test icon building

This commit is contained in:
Anthony Stirling 2025-08-25 12:08:54 +01:00
parent 888bac9408
commit 7bfb0d66f2
15 changed files with 477 additions and 95 deletions

4
frontend/.gitignore vendored
View File

@ -25,3 +25,7 @@ yarn-error.log*
playwright-report playwright-report
test-results test-results
# auto-generated files
/src/assets/material-symbols-icons.json
/src/assets/material-symbols-icons.d.ts

View File

@ -12,6 +12,7 @@
"@atlaskit/pragmatic-drag-and-drop": "^1.7.4", "@atlaskit/pragmatic-drag-and-drop": "^1.7.4",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@iconify/react": "^6.0.0",
"@mantine/core": "^8.0.1", "@mantine/core": "^8.0.1",
"@mantine/dropzone": "^8.0.1", "@mantine/dropzone": "^8.0.1",
"@mantine/hooks": "^8.0.1", "@mantine/hooks": "^8.0.1",
@ -29,7 +30,6 @@
"i18next-browser-languagedetector": "^8.1.0", "i18next-browser-languagedetector": "^8.1.0",
"i18next-http-backend": "^3.0.2", "i18next-http-backend": "^3.0.2",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"material-symbols": "^0.33.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdfjs-dist": "^3.11.174", "pdfjs-dist": "^3.11.174",
"react": "^19.1.0", "react": "^19.1.0",
@ -40,6 +40,8 @@
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/material-symbols": "^1.2.33",
"@iconify/utils": "^3.0.1",
"@playwright/test": "^1.40.0", "@playwright/test": "^1.40.0",
"@types/node": "^24.2.1", "@types/node": "^24.2.1",
"@types/react": "^19.1.4", "@types/react": "^19.1.4",
@ -89,6 +91,28 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@antfu/install-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
"integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
"dev": true,
"dependencies": {
"package-manager-detector": "^1.3.0",
"tinyexec": "^1.0.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@antfu/utils": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.2.0.tgz",
"integrity": "sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@asamuzakjp/css-color": { "node_modules/@asamuzakjp/css-color": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
@ -1192,6 +1216,104 @@
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@iconify-json/material-symbols": {
"version": "1.2.33",
"resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.2.33.tgz",
"integrity": "sha512-Bs0X1+/vpJydW63olrGh60zkR8/Y70sI14AIWaP7Z6YQXukzWANH4q3I0sIPklbIn1oL6uwLvh0zQyd6Vh79LQ==",
"dev": true,
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify/react": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@iconify/react/-/react-6.0.0.tgz",
"integrity": "sha512-eqNscABVZS8eCpZLU/L5F5UokMS9mnCf56iS1nM9YYHdH8ZxqZL9zyjSwW60IOQFsXZkilbBiv+1paMXBhSQnw==",
"license": "MIT",
"dependencies": {
"@iconify/types": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/cyberalien"
},
"peerDependencies": {
"react": ">=16"
}
},
"node_modules/@iconify/types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
"license": "MIT"
},
"node_modules/@iconify/utils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.0.1.tgz",
"integrity": "sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==",
"dev": true,
"dependencies": {
"@antfu/install-pkg": "^1.1.0",
"@antfu/utils": "^9.2.0",
"@iconify/types": "^2.0.0",
"debug": "^4.4.1",
"globals": "^15.15.0",
"kolorist": "^1.8.0",
"local-pkg": "^1.1.1",
"mlly": "^1.7.4"
}
},
"node_modules/@iconify/utils/node_modules/confbox": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"dev": true
},
"node_modules/@iconify/utils/node_modules/globals": {
"version": "15.15.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
"dev": true,
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@iconify/utils/node_modules/local-pkg": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
"integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
"dev": true,
"dependencies": {
"mlly": "^1.7.4",
"pkg-types": "^2.3.0",
"quansync": "^0.2.11"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@iconify/utils/node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true
},
"node_modules/@iconify/utils/node_modules/pkg-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
"integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
"dev": true,
"dependencies": {
"confbox": "^0.2.2",
"exsolve": "^1.0.7",
"pathe": "^2.0.3"
}
},
"node_modules/@isaacs/fs-minipass": { "node_modules/@isaacs/fs-minipass": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@ -4446,6 +4568,12 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/exsolve": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz",
"integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
"dev": true
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@ -5553,6 +5681,12 @@
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
}, },
"node_modules/kolorist": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
"integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
"dev": true
},
"node_modules/license-checker": { "node_modules/license-checker": {
"version": "25.0.1", "version": "25.0.1",
"resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz",
@ -6097,12 +6231,6 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/material-symbols": {
"version": "0.33.0",
"resolved": "https://registry.npmjs.org/material-symbols/-/material-symbols-0.33.0.tgz",
"integrity": "sha512-t9/Gz+14fClRgN7oVOt5CBuwsjFLxSNP9BRDyMrI5el3IZNvoD94IDGJha0YYivyAow24rCS0WOkAv4Dp+YjNg==",
"license": "Apache-2.0"
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -6653,6 +6781,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/package-manager-detector": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz",
"integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==",
"dev": true
},
"node_modules/pako": { "node_modules/pako": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
@ -7403,6 +7537,22 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/quansync": {
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
"integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/antfu"
},
{
"type": "individual",
"url": "https://github.com/sponsors/sxzz"
}
]
},
"node_modules/querystringify": { "node_modules/querystringify": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@ -8615,6 +8765,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/tinyexec": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
"integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
"dev": true
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.13", "version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",

View File

@ -8,6 +8,7 @@
"@atlaskit/pragmatic-drag-and-drop": "^1.7.4", "@atlaskit/pragmatic-drag-and-drop": "^1.7.4",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@iconify/react": "^6.0.0",
"@mantine/core": "^8.0.1", "@mantine/core": "^8.0.1",
"@mantine/dropzone": "^8.0.1", "@mantine/dropzone": "^8.0.1",
"@mantine/hooks": "^8.0.1", "@mantine/hooks": "^8.0.1",
@ -25,7 +26,6 @@
"i18next-browser-languagedetector": "^8.1.0", "i18next-browser-languagedetector": "^8.1.0",
"i18next-http-backend": "^3.0.2", "i18next-http-backend": "^3.0.2",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"material-symbols": "^0.33.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdfjs-dist": "^3.11.174", "pdfjs-dist": "^3.11.174",
"react": "^19.1.0", "react": "^19.1.0",
@ -36,10 +36,14 @@
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {
"predev": "npm run generate-icons",
"dev": "npx tsc --noEmit && vite", "dev": "npx tsc --noEmit && vite",
"prebuild": "npm run generate-icons",
"build": "npx tsc --noEmit && vite build", "build": "npx tsc --noEmit && vite build",
"preview": "vite preview", "preview": "vite preview",
"generate-licenses": "node scripts/generate-licenses.js", "generate-licenses": "node scripts/generate-licenses.js",
"generate-icons": "node scripts/generate-icons.js",
"generate-icons:verbose": "node scripts/generate-icons.js --verbose",
"test": "vitest", "test": "vitest",
"test:watch": "vitest --watch", "test:watch": "vitest --watch",
"test:coverage": "vitest --coverage", "test:coverage": "vitest --coverage",
@ -66,6 +70,8 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/material-symbols": "^1.2.33",
"@iconify/utils": "^3.0.1",
"@playwright/test": "^1.40.0", "@playwright/test": "^1.40.0",
"@types/node": "^24.2.1", "@types/node": "^24.2.1",
"@types/react": "^19.1.4", "@types/react": "^19.1.4",

View File

@ -0,0 +1,175 @@
#!/usr/bin/env node
const { icons } = require('@iconify-json/material-symbols');
const { getIcons } = require('@iconify/utils');
const fs = require('fs');
const path = require('path');
// Check for verbose flag
const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v');
// Logging functions
const info = (message) => console.log(message);
const debug = (message) => {
if (isVerbose) {
console.log(message);
}
};
// Function to scan codebase for LocalIcon usage
function scanForUsedIcons() {
const usedIcons = new Set();
const srcDir = path.join(__dirname, '..', 'src');
info('🔍 Scanning codebase for LocalIcon usage...');
if (!fs.existsSync(srcDir)) {
console.error('❌ Source directory not found:', srcDir);
process.exit(1);
}
// 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()) {
scanDirectory(filePath);
} else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
const content = fs.readFileSync(filePath, 'utf8');
// Match LocalIcon usage: <LocalIcon icon="icon-name" ...>
const localIconMatches = content.match(/<LocalIcon\s+[^>]*icon="([^"]+)"/g);
if (localIconMatches) {
localIconMatches.forEach(match => {
const iconMatch = match.match(/icon="([^"]+)"/);
if (iconMatch) {
usedIcons.add(iconMatch[1]);
debug(` Found: ${iconMatch[1]} in ${path.relative(srcDir, filePath)}`);
}
});
}
// Match old material-symbols-rounded spans: <span className="material-symbols-rounded">icon-name</span>
const spanMatches = content.match(/<span[^>]*className="[^"]*material-symbols-rounded[^"]*"[^>]*>([^<]+)<\/span>/g);
if (spanMatches) {
spanMatches.forEach(match => {
const iconMatch = match.match(/>([^<]+)<\/span>/);
if (iconMatch && iconMatch[1].trim()) {
const iconName = iconMatch[1].trim();
usedIcons.add(iconName);
debug(` Found (legacy): ${iconName} in ${path.relative(srcDir, filePath)}`);
}
});
}
// Match Icon component usage: <Icon icon="material-symbols:icon-name" ...>
const iconMatches = content.match(/<Icon\s+[^>]*icon="material-symbols:([^"]+)"/g);
if (iconMatches) {
iconMatches.forEach(match => {
const iconMatch = match.match(/icon="material-symbols:([^"]+)"/);
if (iconMatch) {
usedIcons.add(iconMatch[1]);
debug(` Found (Icon): ${iconMatch[1]} in ${path.relative(srcDir, filePath)}`);
}
});
}
}
});
}
scanDirectory(srcDir);
const iconArray = Array.from(usedIcons).sort();
info(`📋 Found ${iconArray.length} unique icons across codebase`);
return iconArray;
}
// Auto-detect used icons
const usedIcons = scanForUsedIcons();
// Check if we need to regenerate (compare with existing)
const outputPath = path.join(__dirname, '..', 'src', 'assets', 'material-symbols-icons.json');
let needsRegeneration = true;
if (fs.existsSync(outputPath)) {
try {
const existingSet = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
const existingIcons = Object.keys(existingSet.icons || {}).sort();
const currentIcons = [...usedIcons].sort();
if (JSON.stringify(existingIcons) === JSON.stringify(currentIcons)) {
needsRegeneration = false;
info(`✅ Icon set already up-to-date (${usedIcons.length} icons, ${Math.round(fs.statSync(outputPath).size / 1024)}KB)`);
}
} catch (error) {
// If we can't parse existing file, regenerate
needsRegeneration = true;
}
}
if (!needsRegeneration) {
info('🎉 No regeneration needed!');
process.exit(0);
}
info(`🔍 Extracting ${usedIcons.length} icons from Material Symbols...`);
// Extract only our used icons from the full set
const extractedIcons = getIcons(icons, usedIcons);
if (!extractedIcons) {
console.error('❌ Failed to extract icons');
process.exit(1);
}
// Check for missing icons
const extractedIconNames = Object.keys(extractedIcons.icons || {});
const missingIcons = usedIcons.filter(icon => !extractedIconNames.includes(icon));
if (missingIcons.length > 0) {
info(`⚠️ Missing icons (${missingIcons.length}): ${missingIcons.join(', ')}`);
info('💡 These icons don\'t exist in Material Symbols. Please use available alternatives.');
}
// Create output directory
const outputDir = path.join(__dirname, '..', 'src', 'assets');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// Write the extracted icon set to a file (outputPath already defined above)
fs.writeFileSync(outputPath, JSON.stringify(extractedIcons, null, 2));
info(`✅ Successfully extracted ${Object.keys(extractedIcons.icons || {}).length} icons`);
info(`📦 Bundle size: ${Math.round(JSON.stringify(extractedIcons).length / 1024)}KB`);
info(`💾 Saved to: ${outputPath}`);
// Generate TypeScript types
const typesContent = `// Auto-generated icon types
// This file is automatically generated by scripts/generate-icons.js
// Do not edit manually - changes will be overwritten
export type MaterialSymbolIcon = ${usedIcons.map(icon => `'${icon}'`).join(' | ')};
export interface IconSet {
prefix: string;
icons: Record<string, any>;
width?: number;
height?: number;
}
// Re-export the icon set as the default export with proper typing
declare const iconSet: IconSet;
export default iconSet;
`;
const typesPath = path.join(outputDir, 'material-symbols-icons.d.ts');
fs.writeFileSync(typesPath, typesContent);
info(`📝 Generated types: ${typesPath}`);
info(`🎉 Icon extraction complete!`);

View File

@ -0,0 +1,44 @@
import React from 'react';
import { addCollection, Icon } from '@iconify/react';
// Try to import the icon set - it will be auto-generated
let iconSet: any = null;
let iconsLoaded = false;
try {
iconSet = require('../../assets/material-symbols-icons.json');
if (!iconsLoaded && iconSet) {
addCollection(iconSet);
iconsLoaded = true;
}
} catch (error) {
console.warn('Local icon set not found. Run `npm run generate-icons` to create it.');
}
interface LocalIconProps {
icon: string;
width?: string | number;
height?: string | number;
style?: React.CSSProperties;
className?: string;
}
/**
* LocalIcon component that uses our locally bundled Material Symbols icons
* instead of loading from CDN
*/
export const LocalIcon: React.FC<LocalIconProps> = ({ icon, ...props }) => {
// Convert our icon naming convention to the local collection format
const iconName = icon.startsWith('material-symbols:')
? icon
: `material-symbols:${icon}`;
// Fallback if icons haven't been loaded yet
if (!iconsLoaded || !iconSet) {
return <span style={{ display: 'inline-block', width: props.width || 24, height: props.height || 24 }} />;
}
return <Icon icon={iconName} {...props} />;
};
export default LocalIcon;

View File

@ -1,6 +1,7 @@
import React, { useState, useRef, forwardRef, useEffect } from "react"; import React, { useState, useRef, forwardRef, useEffect } from "react";
import { ActionIcon, Stack, Divider } from "@mantine/core"; import { ActionIcon, Stack, Divider } from "@mantine/core";
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import LocalIcon from './LocalIcon';
import MenuBookIcon from "@mui/icons-material/MenuBookRounded"; import MenuBookIcon from "@mui/icons-material/MenuBookRounded";
import SettingsIcon from "@mui/icons-material/SettingsRounded"; import SettingsIcon from "@mui/icons-material/SettingsRounded";
import FolderIcon from "@mui/icons-material/FolderRounded"; import FolderIcon from "@mui/icons-material/FolderRounded";
@ -57,10 +58,7 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
{ {
id: 'sign', id: 'sign',
name: t("quickAccess.sign", "Sign"), name: t("quickAccess.sign", "Sign"),
icon: icon: <LocalIcon icon="signature-rounded" width="20" height="20" />,
<span className="material-symbols-rounded font-size-20">
signature
</span>,
size: 'lg', size: 'lg',
isRound: false, isRound: false,
type: 'navigation', type: 'navigation',
@ -72,10 +70,7 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
{ {
id: 'automate', id: 'automate',
name: t("quickAccess.automate", "Automate"), name: t("quickAccess.automate", "Automate"),
icon: icon: <LocalIcon icon="automation" width="20" height="20" />,
<span className="material-symbols-rounded font-size-20">
automation
</span>,
size: 'lg', size: 'lg',
isRound: false, isRound: false,
type: 'navigation', type: 'navigation',
@ -96,10 +91,7 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
{ {
id: 'activity', id: 'activity',
name: t("quickAccess.activity", "Activity"), name: t("quickAccess.activity", "Activity"),
icon: icon: <LocalIcon icon="vital-signs-rounded" width="20" height="20" />,
<span className="material-symbols-rounded font-size-20">
vital_signs
</span>,
isRound: true, isRound: true,
size: 'lg', size: 'lg',
type: 'navigation', type: 'navigation',

View File

@ -1,5 +1,6 @@
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import { useMantineColorScheme } from '@mantine/core'; import { useMantineColorScheme } from '@mantine/core';
import LocalIcon from './LocalIcon';
import styles from './textInput/TextInput.module.css'; import styles from './textInput/TextInput.module.css';
/** /**
@ -96,7 +97,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(({
style={{ color: colorScheme === 'dark' ? '#FFFFFF' : '#6B7382' }} style={{ color: colorScheme === 'dark' ? '#FFFFFF' : '#6B7382' }}
aria-label="Clear input" aria-label="Clear input"
> >
<span className="material-symbols-rounded">close</span> <LocalIcon icon="close-rounded" width="20" height="20" />
</button> </button>
)} )}
</div> </div>

View File

@ -1,5 +1,6 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import LocalIcon from './LocalIcon';
import { isClickOutside, addEventListenerWithCleanup } from '../../utils/genericUtils'; import { isClickOutside, addEventListenerWithCleanup } from '../../utils/genericUtils';
import { useTooltipPosition } from '../../hooks/useTooltipPosition'; import { useTooltipPosition } from '../../hooks/useTooltipPosition';
import { TooltipTip } from '../../types/tips'; import { TooltipTip } from '../../types/tips';
@ -171,9 +172,7 @@ export const Tooltip: React.FC<TooltipProps> = ({
}} }}
title="Close tooltip" title="Close tooltip"
> >
<span className="material-symbols-rounded"> <LocalIcon icon="close-rounded" width="20" height="20" />
close
</span>
</button> </button>
)} )}
{arrow && getArrowClass() && ( {arrow && getArrowClass() && (

View File

@ -1,5 +1,6 @@
import React, { createContext, useContext, useMemo, useRef } from 'react'; import React, { createContext, useContext, useMemo, useRef } from 'react';
import { Text, Stack, Box, Flex, Divider } from '@mantine/core'; import { Text, Stack, Box, Flex, Divider } from '@mantine/core';
import LocalIcon from '../../shared/LocalIcon';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import { Tooltip } from '../../shared/Tooltip'; import { Tooltip } from '../../shared/Tooltip';
@ -54,9 +55,7 @@ const renderTooltipTitle = (
<Text fw={500} size="lg"> <Text fw={500} size="lg">
{title} {title}
</Text> </Text>
<span className="material-symbols-rounded" style={{ fontSize: '1.2rem', color: 'var(--icon-files-color)' }}> <LocalIcon icon="gpp-maybe-rounded" width="20" height="20" style={{ color: 'var(--icon-files-color)' }} />
gpp_maybe
</span>
</Flex> </Flex>
</Tooltip> </Tooltip>
); );

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Flex, Text, Divider } from '@mantine/core'; import { Flex, Text, Divider } from '@mantine/core';
import LocalIcon from '../../shared/LocalIcon';
import { Tooltip } from '../../shared/Tooltip'; import { Tooltip } from '../../shared/Tooltip';
export interface ToolWorkflowTitleProps { export interface ToolWorkflowTitleProps {
@ -29,9 +30,7 @@ export function ToolWorkflowTitle({ title, tooltip }: ToolWorkflowTitleProps) {
<Text fw={500} size="xl" p="md"> <Text fw={500} size="xl" p="md">
{title} {title}
</Text> </Text>
<span className="material-symbols-rounded" style={{ fontSize: '1.2rem', color: 'var(--icon-files-color)' }}> <LocalIcon icon="gpp-maybe-rounded" width="20" height="20" style={{ color: 'var(--icon-files-color)' }} />
gpp_maybe
</span>
</Flex> </Flex>
</Tooltip> </Tooltip>
</Flex> </Flex>

View File

@ -1,6 +1,7 @@
import React, { useState, useRef, useEffect, useMemo } from "react"; import React, { useState, useRef, useEffect, useMemo } from "react";
import { Stack, Button, Text } from "@mantine/core"; import { Stack, Button, Text } from "@mantine/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import LocalIcon from '../../shared/LocalIcon';
import { ToolRegistryEntry } from "../../../data/toolsTaxonomy"; import { ToolRegistryEntry } from "../../../data/toolsTaxonomy";
import { TextInput } from "../../shared/TextInput"; import { TextInput } from "../../shared/TextInput";
import './ToolPicker.css'; import './ToolPicker.css';
@ -74,7 +75,7 @@ const ToolSearch = ({
value={value} value={value}
onChange={handleSearchChange} onChange={handleSearchChange}
placeholder={placeholder || t("toolPicker.searchPlaceholder", "Search tools...")} placeholder={placeholder || t("toolPicker.searchPlaceholder", "Search tools...")}
icon={hideIcon ? undefined : <span className="material-symbols-rounded">search</span>} icon={hideIcon ? undefined : <LocalIcon icon="search-rounded" width="24" height="24" />}
autoComplete="off" autoComplete="off"
/> />

View File

@ -1,4 +1,5 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import LocalIcon from '../components/shared/LocalIcon';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import SplitPdfPanel from "../tools/Split"; import SplitPdfPanel from "../tools/Split";
import CompressPdfPanel from "../tools/Compress"; import CompressPdfPanel from "../tools/Compress";
@ -50,7 +51,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Signing // Signing
"certSign": { "certSign": {
icon: <span className="material-symbols-rounded">workspace_premium</span>, icon: <LocalIcon icon="workspace-premium-rounded" width="24" height="24" />,
name: t("home.certSign.title", "Sign with Certificate"), name: t("home.certSign.title", "Sign with Certificate"),
component: null, component: null,
view: "sign", view: "sign",
@ -59,7 +60,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.SIGNING subcategoryId: SubcategoryId.SIGNING
}, },
"sign": { "sign": {
icon: <span className="material-symbols-rounded">signature</span>, icon: <LocalIcon icon="signature-rounded" width="24" height="24" />,
name: t("home.sign.title", "Sign"), name: t("home.sign.title", "Sign"),
component: null, component: null,
view: "sign", view: "sign",
@ -72,7 +73,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Document Security // Document Security
"addPassword": { "addPassword": {
icon: <span className="material-symbols-rounded">password</span>, icon: <LocalIcon icon="password-rounded" width="24" height="24" />,
name: t("home.addPassword.title", "Add Password"), name: t("home.addPassword.title", "Add Password"),
component: AddPassword, component: AddPassword,
view: "security", view: "security",
@ -85,7 +86,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: AddPasswordSettings settingsComponent: AddPasswordSettings
}, },
"watermark": { "watermark": {
icon: <span className="material-symbols-rounded">branding_watermark</span>, icon: <LocalIcon icon="branding-watermark-outline-rounded" width="24" height="24" />,
name: t("home.watermark.title", "Add Watermark"), name: t("home.watermark.title", "Add Watermark"),
component: AddWatermark, component: AddWatermark,
view: "format", view: "format",
@ -98,7 +99,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: AddWatermarkSingleStepSettings settingsComponent: AddWatermarkSingleStepSettings
}, },
"add-stamp": { "add-stamp": {
icon: <span className="material-symbols-rounded">approval</span>, icon: <LocalIcon icon="approval-rounded" width="24" height="24" />,
name: t("home.AddStampRequest.title", "Add Stamp to PDF"), name: t("home.AddStampRequest.title", "Add Stamp to PDF"),
component: null, component: null,
view: "format", view: "format",
@ -107,7 +108,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.DOCUMENT_SECURITY subcategoryId: SubcategoryId.DOCUMENT_SECURITY
}, },
"sanitize": { "sanitize": {
icon: <span className="material-symbols-rounded">cleaning_services</span>, icon: <LocalIcon icon="cleaning-services-outline-rounded" width="24" height="24" />,
name: t("home.sanitize.title", "Sanitize"), name: t("home.sanitize.title", "Sanitize"),
component: Sanitize, component: Sanitize,
view: "security", view: "security",
@ -120,7 +121,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: SanitizeSettings settingsComponent: SanitizeSettings
}, },
"flatten": { "flatten": {
icon: <span className="material-symbols-rounded">layers_clear</span>, icon: <LocalIcon icon="layers-clear-rounded" width="24" height="24" />,
name: t("home.flatten.title", "Flatten"), name: t("home.flatten.title", "Flatten"),
component: null, component: null,
view: "format", view: "format",
@ -129,7 +130,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.DOCUMENT_SECURITY subcategoryId: SubcategoryId.DOCUMENT_SECURITY
}, },
"unlock-pdf-forms": { "unlock-pdf-forms": {
icon: <span className="material-symbols-rounded">preview_off</span>, icon: <LocalIcon icon="preview-off-rounded" width="24" height="24" />,
name: t("home.unlockPDFForms.title", "Unlock PDF Forms"), name: t("home.unlockPDFForms.title", "Unlock PDF Forms"),
component: UnlockPdfForms, component: UnlockPdfForms,
view: "security", view: "security",
@ -142,7 +143,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: UnlockPdfFormsSettings settingsComponent: UnlockPdfFormsSettings
}, },
"manage-certificates": { "manage-certificates": {
icon: <span className="material-symbols-rounded">license</span>, icon: <LocalIcon icon="license-rounded" width="24" height="24" />,
name: t("home.manageCertificates.title", "Manage Certificates"), name: t("home.manageCertificates.title", "Manage Certificates"),
component: null, component: null,
view: "security", view: "security",
@ -151,7 +152,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.DOCUMENT_SECURITY subcategoryId: SubcategoryId.DOCUMENT_SECURITY
}, },
"change-permissions": { "change-permissions": {
icon: <span className="material-symbols-rounded">lock</span>, icon: <LocalIcon icon="lock-outline" width="24" height="24" />,
name: t("home.changePermissions.title", "Change Permissions"), name: t("home.changePermissions.title", "Change Permissions"),
component: ChangePermissions, component: ChangePermissions,
view: "security", view: "security",
@ -166,7 +167,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Verification // Verification
"get-all-info-on-pdf": { "get-all-info-on-pdf": {
icon: <span className="material-symbols-rounded">fact_check</span>, icon: <LocalIcon icon="fact-check-rounded" width="24" height="24" />,
name: t("home.getPdfInfo.title", "Get ALL Info on PDF"), name: t("home.getPdfInfo.title", "Get ALL Info on PDF"),
component: null, component: null,
view: "extract", view: "extract",
@ -175,7 +176,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.VERIFICATION subcategoryId: SubcategoryId.VERIFICATION
}, },
"validate-pdf-signature": { "validate-pdf-signature": {
icon: <span className="material-symbols-rounded">verified</span>, icon: <LocalIcon icon="verified-rounded" width="24" height="24" />,
name: t("home.validateSignature.title", "Validate PDF Signature"), name: t("home.validateSignature.title", "Validate PDF Signature"),
component: null, component: null,
view: "security", view: "security",
@ -188,7 +189,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Document Review // Document Review
"read": { "read": {
icon: <span className="material-symbols-rounded">article</span>, icon: <LocalIcon icon="article-rounded" width="24" height="24" />,
name: t("home.read.title", "Read"), name: t("home.read.title", "Read"),
component: null, component: null,
view: "view", view: "view",
@ -197,7 +198,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.DOCUMENT_REVIEW subcategoryId: SubcategoryId.DOCUMENT_REVIEW
}, },
"change-metadata": { "change-metadata": {
icon: <span className="material-symbols-rounded">assignment</span>, icon: <LocalIcon icon="assignment-rounded" width="24" height="24" />,
name: t("home.changeMetadata.title", "Change Metadata"), name: t("home.changeMetadata.title", "Change Metadata"),
component: null, component: null,
view: "format", view: "format",
@ -208,7 +209,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Page Formatting // Page Formatting
"cropPdf": { "cropPdf": {
icon: <span className="material-symbols-rounded">crop</span>, icon: <LocalIcon icon="crop-rounded" width="24" height="24" />,
name: t("home.crop.title", "Crop PDF"), name: t("home.crop.title", "Crop PDF"),
component: null, component: null,
view: "format", view: "format",
@ -217,7 +218,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.PAGE_FORMATTING subcategoryId: SubcategoryId.PAGE_FORMATTING
}, },
"rotate": { "rotate": {
icon: <span className="material-symbols-rounded">rotate_right</span>, icon: <LocalIcon icon="rotate-right-rounded" width="24" height="24" />,
name: t("home.rotate.title", "Rotate"), name: t("home.rotate.title", "Rotate"),
component: null, component: null,
view: "format", view: "format",
@ -226,7 +227,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.PAGE_FORMATTING subcategoryId: SubcategoryId.PAGE_FORMATTING
}, },
"splitPdf": { "splitPdf": {
icon: <span className="material-symbols-rounded">content_cut</span>, icon: <LocalIcon icon="content-cut-rounded" width="24" height="24" />,
name: t("home.split.title", "Split"), name: t("home.split.title", "Split"),
component: SplitPdfPanel, component: SplitPdfPanel,
view: "split", view: "split",
@ -237,7 +238,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: SplitSettings settingsComponent: SplitSettings
}, },
"reorganize-pages": { "reorganize-pages": {
icon: <span className="material-symbols-rounded">move_down</span>, icon: <LocalIcon icon="move-down-rounded" width="24" height="24" />,
name: t("home.reorganizePages.title", "Reorganize Pages"), name: t("home.reorganizePages.title", "Reorganize Pages"),
component: null, component: null,
view: "pageEditor", view: "pageEditor",
@ -246,7 +247,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.PAGE_FORMATTING subcategoryId: SubcategoryId.PAGE_FORMATTING
}, },
"adjust-page-size-scale": { "adjust-page-size-scale": {
icon: <span className="material-symbols-rounded">crop_free</span>, icon: <LocalIcon icon="crop-free-rounded" width="24" height="24" />,
name: t("home.scalePages.title", "Adjust page size/scale"), name: t("home.scalePages.title", "Adjust page size/scale"),
component: null, component: null,
view: "format", view: "format",
@ -255,7 +256,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.PAGE_FORMATTING subcategoryId: SubcategoryId.PAGE_FORMATTING
}, },
"addPageNumbers": { "addPageNumbers": {
icon: <span className="material-symbols-rounded">123</span>, icon: <LocalIcon icon="123-rounded" width="24" height="24" />,
name: t("home.addPageNumbers.title", "Add Page Numbers"), name: t("home.addPageNumbers.title", "Add Page Numbers"),
component: null, component: null,
view: "format", view: "format",
@ -264,7 +265,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.PAGE_FORMATTING subcategoryId: SubcategoryId.PAGE_FORMATTING
}, },
"multi-page-layout": { "multi-page-layout": {
icon: <span className="material-symbols-rounded">dashboard</span>, icon: <LocalIcon icon="dashboard-rounded" width="24" height="24" />,
name: t("home.pageLayout.title", "Multi-Page Layout"), name: t("home.pageLayout.title", "Multi-Page Layout"),
component: null, component: null,
view: "format", view: "format",
@ -273,7 +274,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.PAGE_FORMATTING subcategoryId: SubcategoryId.PAGE_FORMATTING
}, },
"single-large-page": { "single-large-page": {
icon: <span className="material-symbols-rounded">looks_one</span>, icon: <LocalIcon icon="looks-one-outline-rounded" width="24" height="24" />,
name: t("home.pdfToSinglePage.title", "PDF to Single Large Page"), name: t("home.pdfToSinglePage.title", "PDF to Single Large Page"),
component: SingleLargePage, component: SingleLargePage,
view: "format", view: "format",
@ -285,7 +286,7 @@ export function useFlatToolRegistry(): ToolRegistry {
operationConfig: singleLargePageOperationConfig operationConfig: singleLargePageOperationConfig
}, },
"add-attachments": { "add-attachments": {
icon: <span className="material-symbols-rounded">attachment</span>, icon: <LocalIcon icon="attachment-rounded" width="24" height="24" />,
name: t("home.attachments.title", "Add Attachments"), name: t("home.attachments.title", "Add Attachments"),
component: null, component: null,
view: "format", view: "format",
@ -298,7 +299,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Extraction // Extraction
"extractPages": { "extractPages": {
icon: <span className="material-symbols-rounded">upload</span>, icon: <LocalIcon icon="upload-rounded" width="24" height="24" />,
name: t("home.extractPages.title", "Extract Pages"), name: t("home.extractPages.title", "Extract Pages"),
component: null, component: null,
view: "extract", view: "extract",
@ -307,7 +308,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.EXTRACTION subcategoryId: SubcategoryId.EXTRACTION
}, },
"extract-images": { "extract-images": {
icon: <span className="material-symbols-rounded">filter</span>, icon: <LocalIcon icon="filter-alt" width="24" height="24" />,
name: t("home.extractImages.title", "Extract Images"), name: t("home.extractImages.title", "Extract Images"),
component: null, component: null,
view: "extract", view: "extract",
@ -320,7 +321,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Removal // Removal
"removePages": { "removePages": {
icon: <span className="material-symbols-rounded">delete</span>, icon: <LocalIcon icon="delete-outline-rounded" width="24" height="24" />,
name: t("home.removePages.title", "Remove Pages"), name: t("home.removePages.title", "Remove Pages"),
component: null, component: null,
view: "remove", view: "remove",
@ -329,7 +330,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.REMOVAL subcategoryId: SubcategoryId.REMOVAL
}, },
"remove-blank-pages": { "remove-blank-pages": {
icon: <span className="material-symbols-rounded">scan_delete</span>, icon: <LocalIcon icon="scan-delete-rounded" width="24" height="24" />,
name: t("home.removeBlanks.title", "Remove Blank Pages"), name: t("home.removeBlanks.title", "Remove Blank Pages"),
component: null, component: null,
view: "remove", view: "remove",
@ -338,7 +339,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.REMOVAL subcategoryId: SubcategoryId.REMOVAL
}, },
"remove-annotations": { "remove-annotations": {
icon: <span className="material-symbols-rounded">thread_unread</span>, icon: <LocalIcon icon="thread-unread-rounded" width="24" height="24" />,
name: t("home.removeAnnotations.title", "Remove Annotations"), name: t("home.removeAnnotations.title", "Remove Annotations"),
component: null, component: null,
view: "remove", view: "remove",
@ -347,7 +348,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.REMOVAL subcategoryId: SubcategoryId.REMOVAL
}, },
"remove-image": { "remove-image": {
icon: <span className="material-symbols-rounded">remove_selection</span>, icon: <LocalIcon icon="remove-selection-rounded" width="24" height="24" />,
name: t("home.removeImagePdf.title", "Remove Image"), name: t("home.removeImagePdf.title", "Remove Image"),
component: null, component: null,
view: "format", view: "format",
@ -356,7 +357,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.REMOVAL subcategoryId: SubcategoryId.REMOVAL
}, },
"remove-password": { "remove-password": {
icon: <span className="material-symbols-rounded">lock_open_right</span>, icon: <LocalIcon icon="lock-open-right-outline-rounded" width="24" height="24" />,
name: t("home.removePassword.title", "Remove Password"), name: t("home.removePassword.title", "Remove Password"),
component: RemovePassword, component: RemovePassword,
view: "security", view: "security",
@ -369,7 +370,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: RemovePasswordSettings settingsComponent: RemovePasswordSettings
}, },
"remove-certificate-sign": { "remove-certificate-sign": {
icon: <span className="material-symbols-rounded">remove_moderator</span>, icon: <LocalIcon icon="remove-moderator-outline-rounded" width="24" height="24" />,
name: t("home.removeCertSign.title", "Remove Certificate Sign"), name: t("home.removeCertSign.title", "Remove Certificate Sign"),
component: RemoveCertificateSign, component: RemoveCertificateSign,
view: "security", view: "security",
@ -385,7 +386,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Automation // Automation
"automate": { "automate": {
icon: <span className="material-symbols-rounded">automation</span>, icon: <LocalIcon icon="automation" width="24" height="24" />,
name: t("home.automate.title", "Automate"), name: t("home.automate.title", "Automate"),
component: React.lazy(() => import('../tools/Automate')), component: React.lazy(() => import('../tools/Automate')),
view: "format", view: "format",
@ -396,7 +397,7 @@ export function useFlatToolRegistry(): ToolRegistry {
endpoints: ["handleData"] endpoints: ["handleData"]
}, },
"auto-rename-pdf-file": { "auto-rename-pdf-file": {
icon: <span className="material-symbols-rounded">match_word</span>, icon: <LocalIcon icon="match-word-rounded" width="24" height="24" />,
name: t("home.auto-rename.title", "Auto Rename PDF File"), name: t("home.auto-rename.title", "Auto Rename PDF File"),
component: null, component: null,
view: "format", view: "format",
@ -405,7 +406,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.AUTOMATION subcategoryId: SubcategoryId.AUTOMATION
}, },
"auto-split-pages": { "auto-split-pages": {
icon: <span className="material-symbols-rounded">split_scene_right</span>, icon: <LocalIcon icon="split-scene-right-rounded" width="24" height="24" />,
name: t("home.autoSplitPDF.title", "Auto Split Pages"), name: t("home.autoSplitPDF.title", "Auto Split Pages"),
component: null, component: null,
view: "format", view: "format",
@ -414,7 +415,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.AUTOMATION subcategoryId: SubcategoryId.AUTOMATION
}, },
"auto-split-by-size-count": { "auto-split-by-size-count": {
icon: <span className="material-symbols-rounded">content_cut</span>, icon: <LocalIcon icon="content-cut-rounded" width="24" height="24" />,
name: t("home.autoSizeSplitPDF.title", "Auto Split by Size/Count"), name: t("home.autoSizeSplitPDF.title", "Auto Split by Size/Count"),
component: null, component: null,
view: "format", view: "format",
@ -427,7 +428,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Advanced Formatting // Advanced Formatting
"adjustContrast": { "adjustContrast": {
icon: <span className="material-symbols-rounded">palette</span>, icon: <LocalIcon icon="palette" width="24" height="24" />,
name: t("home.adjustContrast.title", "Adjust Colors/Contrast"), name: t("home.adjustContrast.title", "Adjust Colors/Contrast"),
component: null, component: null,
view: "format", view: "format",
@ -436,7 +437,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.ADVANCED_FORMATTING subcategoryId: SubcategoryId.ADVANCED_FORMATTING
}, },
"repair": { "repair": {
icon: <span className="material-symbols-rounded">build</span>, icon: <LocalIcon icon="build-outline-rounded" width="24" height="24" />,
name: t("home.repair.title", "Repair"), name: t("home.repair.title", "Repair"),
component: Repair, component: Repair,
view: "format", view: "format",
@ -449,7 +450,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: RepairSettings settingsComponent: RepairSettings
}, },
"detect-split-scanned-photos": { "detect-split-scanned-photos": {
icon: <span className="material-symbols-rounded">scanner</span>, icon: <LocalIcon icon="scanner-rounded" width="24" height="24" />,
name: t("home.ScannerImageSplit.title", "Detect & Split Scanned Photos"), name: t("home.ScannerImageSplit.title", "Detect & Split Scanned Photos"),
component: null, component: null,
view: "format", view: "format",
@ -458,7 +459,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.ADVANCED_FORMATTING subcategoryId: SubcategoryId.ADVANCED_FORMATTING
}, },
"overlay-pdfs": { "overlay-pdfs": {
icon: <span className="material-symbols-rounded">layers</span>, icon: <LocalIcon icon="layers-rounded" width="24" height="24" />,
name: t("home.overlay-pdfs.title", "Overlay PDFs"), name: t("home.overlay-pdfs.title", "Overlay PDFs"),
component: null, component: null,
view: "format", view: "format",
@ -467,7 +468,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.ADVANCED_FORMATTING subcategoryId: SubcategoryId.ADVANCED_FORMATTING
}, },
"replace-and-invert-color": { "replace-and-invert-color": {
icon: <span className="material-symbols-rounded">format_color_fill</span>, icon: <LocalIcon icon="format-color-fill-rounded" width="24" height="24" />,
name: t("home.replaceColorPdf.title", "Replace & Invert Color"), name: t("home.replaceColorPdf.title", "Replace & Invert Color"),
component: null, component: null,
view: "format", view: "format",
@ -476,7 +477,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.ADVANCED_FORMATTING subcategoryId: SubcategoryId.ADVANCED_FORMATTING
}, },
"add-image": { "add-image": {
icon: <span className="material-symbols-rounded">image</span>, icon: <LocalIcon icon="image-rounded" width="24" height="24" />,
name: t("home.addImage.title", "Add Image"), name: t("home.addImage.title", "Add Image"),
component: null, component: null,
view: "format", view: "format",
@ -485,7 +486,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.ADVANCED_FORMATTING subcategoryId: SubcategoryId.ADVANCED_FORMATTING
}, },
"edit-table-of-contents": { "edit-table-of-contents": {
icon: <span className="material-symbols-rounded">bookmark_add</span>, icon: <LocalIcon icon="bookmark-add-rounded" width="24" height="24" />,
name: t("home.editTableOfContents.title", "Edit Table of Contents"), name: t("home.editTableOfContents.title", "Edit Table of Contents"),
component: null, component: null,
view: "format", view: "format",
@ -494,7 +495,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.ADVANCED_FORMATTING subcategoryId: SubcategoryId.ADVANCED_FORMATTING
}, },
"scanner-effect": { "scanner-effect": {
icon: <span className="material-symbols-rounded">scanner</span>, icon: <LocalIcon icon="scanner-rounded" width="24" height="24" />,
name: t("home.fakeScan.title", "Scanner Effect"), name: t("home.fakeScan.title", "Scanner Effect"),
component: null, component: null,
view: "format", view: "format",
@ -507,7 +508,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Developer Tools // Developer Tools
"show-javascript": { "show-javascript": {
icon: <span className="material-symbols-rounded">javascript</span>, icon: <LocalIcon icon="javascript-rounded" width="24" height="24" />,
name: t("home.showJS.title", "Show JavaScript"), name: t("home.showJS.title", "Show JavaScript"),
component: null, component: null,
view: "extract", view: "extract",
@ -516,7 +517,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.DEVELOPER_TOOLS subcategoryId: SubcategoryId.DEVELOPER_TOOLS
}, },
"dev-api": { "dev-api": {
icon: <span className="material-symbols-rounded" style={{ color: '#2F7BF6' }}>open_in_new</span>, icon: <LocalIcon icon="open-in-new-rounded" width="24" height="24" style={{ color: '#2F7BF6' }} />,
name: t("home.devApi.title", "API"), name: t("home.devApi.title", "API"),
component: null, component: null,
view: "external", view: "external",
@ -526,7 +527,7 @@ export function useFlatToolRegistry(): ToolRegistry {
link: "https://stirlingpdf.io/swagger-ui/5.21.0/index.html" link: "https://stirlingpdf.io/swagger-ui/5.21.0/index.html"
}, },
"dev-folder-scanning": { "dev-folder-scanning": {
icon: <span className="material-symbols-rounded" style={{ color: '#2F7BF6' }}>open_in_new</span>, icon: <LocalIcon icon="open-in-new-rounded" width="24" height="24" style={{ color: '#2F7BF6' }} />,
name: t("home.devFolderScanning.title", "Automated Folder Scanning"), name: t("home.devFolderScanning.title", "Automated Folder Scanning"),
component: null, component: null,
view: "external", view: "external",
@ -536,7 +537,7 @@ export function useFlatToolRegistry(): ToolRegistry {
link: "https://docs.stirlingpdf.com/Advanced%20Configuration/Folder%20Scanning/" link: "https://docs.stirlingpdf.com/Advanced%20Configuration/Folder%20Scanning/"
}, },
"dev-sso-guide": { "dev-sso-guide": {
icon: <span className="material-symbols-rounded" style={{ color: '#2F7BF6' }}>open_in_new</span>, icon: <LocalIcon icon="open-in-new-rounded" width="24" height="24" style={{ color: '#2F7BF6' }} />,
name: t("home.devSsoGuide.title", "SSO Guide"), name: t("home.devSsoGuide.title", "SSO Guide"),
component: null, component: null,
view: "external", view: "external",
@ -546,7 +547,7 @@ export function useFlatToolRegistry(): ToolRegistry {
link: "https://docs.stirlingpdf.com/Advanced%20Configuration/Single%20Sign-On%20Configuration", link: "https://docs.stirlingpdf.com/Advanced%20Configuration/Single%20Sign-On%20Configuration",
}, },
"dev-airgapped": { "dev-airgapped": {
icon: <span className="material-symbols-rounded" style={{ color: '#2F7BF6' }}>open_in_new</span>, icon: <LocalIcon icon="open-in-new-rounded" width="24" height="24" style={{ color: '#2F7BF6' }} />,
name: t("home.devAirgapped.title", "Air-gapped Setup"), name: t("home.devAirgapped.title", "Air-gapped Setup"),
component: null, component: null,
view: "external", view: "external",
@ -559,7 +560,7 @@ export function useFlatToolRegistry(): ToolRegistry {
// Recommended Tools // Recommended Tools
"compare": { "compare": {
icon: <span className="material-symbols-rounded">compare</span>, icon: <LocalIcon icon="compare-rounded" width="24" height="24" />,
name: t("home.compare.title", "Compare"), name: t("home.compare.title", "Compare"),
component: null, component: null,
view: "format", view: "format",
@ -568,7 +569,7 @@ export function useFlatToolRegistry(): ToolRegistry {
subcategoryId: SubcategoryId.GENERAL subcategoryId: SubcategoryId.GENERAL
}, },
"compress": { "compress": {
icon: <span className="material-symbols-rounded">zoom_in_map</span>, icon: <LocalIcon icon="zoom-in-map-rounded" width="24" height="24" />,
name: t("home.compress.title", "Compress"), name: t("home.compress.title", "Compress"),
component: CompressPdfPanel, component: CompressPdfPanel,
view: "compress", view: "compress",
@ -580,7 +581,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: CompressSettings settingsComponent: CompressSettings
}, },
"convert": { "convert": {
icon: <span className="material-symbols-rounded">sync_alt</span>, icon: <LocalIcon icon="sync-alt-rounded" width="24" height="24" />,
name: t("home.convert.title", "Convert"), name: t("home.convert.title", "Convert"),
component: ConvertPanel, component: ConvertPanel,
view: "convert", view: "convert",
@ -626,7 +627,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: ConvertSettings settingsComponent: ConvertSettings
}, },
"mergePdfs": { "mergePdfs": {
icon: <span className="material-symbols-rounded">library_add</span>, icon: <LocalIcon icon="library-add-rounded" width="24" height="24" />,
name: t("home.merge.title", "Merge"), name: t("home.merge.title", "Merge"),
component: null, component: null,
view: "merge", view: "merge",
@ -636,7 +637,7 @@ export function useFlatToolRegistry(): ToolRegistry {
maxFiles: -1 maxFiles: -1
}, },
"multi-tool": { "multi-tool": {
icon: <span className="material-symbols-rounded">dashboard_customize</span>, icon: <LocalIcon icon="dashboard-customize-rounded" width="24" height="24" />,
name: t("home.multiTool.title", "Multi-Tool"), name: t("home.multiTool.title", "Multi-Tool"),
component: null, component: null,
view: "pageEditor", view: "pageEditor",
@ -646,7 +647,7 @@ export function useFlatToolRegistry(): ToolRegistry {
maxFiles: -1 maxFiles: -1
}, },
"ocr": { "ocr": {
icon: <span className="material-symbols-rounded">quick_reference_all</span>, icon: <LocalIcon icon="quick-reference-all-outline-rounded" width="24" height="24" />,
name: t("home.ocr.title", "OCR"), name: t("home.ocr.title", "OCR"),
component: OCRPanel, component: OCRPanel,
view: "convert", view: "convert",
@ -658,7 +659,7 @@ export function useFlatToolRegistry(): ToolRegistry {
settingsComponent: OCRSettings settingsComponent: OCRSettings
}, },
"redact": { "redact": {
icon: <span className="material-symbols-rounded">visibility_off</span>, icon: <LocalIcon icon="visibility-off-rounded" width="24" height="24" />,
name: t("home.redact.title", "Redact"), name: t("home.redact.title", "Redact"),
component: null, component: null,
view: "redact", view: "redact",

View File

@ -5,3 +5,14 @@ declare module "../components/Viewer";
declare module "*.js"; declare module "*.js";
declare module '*.module.css'; declare module '*.module.css';
declare module 'pdfjs-dist'; declare module 'pdfjs-dist';
// Auto-generated icon set JSON import
declare module '../assets/material-symbols-icons.json' {
const value: {
prefix: string;
icons: Record<string, any>;
width?: number;
height?: number;
};
export default value;
}

View File

@ -1,9 +1,3 @@
@import 'material-symbols/rounded.css';
.material-symbols-rounded {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',

View File

@ -42,7 +42,7 @@
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */ "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */