Replace the inline SVG icon set with the vuesax linear pack, add skeleton primitives for loading states, and keep page scroll inside the app shell. Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
3.4 KiB
JavaScript
67 lines
3.4 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
const src = 'icons (1).json';
|
|
const destDir = 'apps/web/src/icons';
|
|
const dest = path.join(destDir, 'vuesax-linear.json');
|
|
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
const raw = JSON.parse(fs.readFileSync(src, 'utf8'));
|
|
|
|
/** Normalize hardcoded stroke/fill hex to currentColor so theme/CSS color works. */
|
|
function normalizeSvg(svg) {
|
|
return String(svg)
|
|
.replace(/\sstroke="#[0-9A-Fa-f]{3,8}"/g, ' stroke="currentColor"')
|
|
.replace(/\sfill="#[0-9A-Fa-f]{3,8}"/g, ' fill="currentColor"')
|
|
.replace(/\n+/g, '')
|
|
.trim();
|
|
}
|
|
|
|
const out = {};
|
|
for (const [key, svg] of Object.entries(raw)) {
|
|
const short = key.startsWith('vuesax-linear:')
|
|
? key.slice('vuesax-linear:'.length)
|
|
: key;
|
|
out[short] = normalizeSvg(svg);
|
|
}
|
|
|
|
fs.writeFileSync(dest, JSON.stringify(out));
|
|
console.log('wrote', dest, 'icons', Object.keys(out).length);
|
|
|
|
// Verify mapping coverage for planned aliases
|
|
const keys = new Set(Object.keys(out));
|
|
const needed = [
|
|
'add', 'home', 'user', 'profile-2user', 'search-normal', 'search-status',
|
|
'setting-2', 'setting', 'calendar', 'calendar-1', 'document', 'document-text',
|
|
'briefcase', 'tick-circle', 'tick-square', 'close-circle', 'close-square',
|
|
'trash', 'edit', 'edit-2', 'eye', 'eye-slash', 'lock', 'logout', 'notification',
|
|
'bell', 'message', 'messages-1', 'sms', 'call', 'chart', 'chart-2', 'chart-1',
|
|
'arrow-right', 'arrow-left', 'arrow-up', 'arrow-down', 'arrow-down-1',
|
|
'filter', 'funnel', 'star', 'gift', 'cup', 'medal', 'crown', 'warning-2',
|
|
'info-circle', 'more', 'more-circle', 'menu', 'grid-1', 'grid-2', 'element-3',
|
|
'layer', 'refresh', 'refresh-2', 'reload', 'send-2', 'send', 'paperclip',
|
|
'attach-circle', 'camera', 'gallery', 'microphone', 'microphone-2', 'stop',
|
|
'minus', 'dollar-circle', 'money', 'wallet', 'wallet-2', 'bank', 'card',
|
|
'building', 'buildings', 'flag', 'location', 'map', 'printer', 'export',
|
|
'import', 'document-download', 'document-upload', 'link', 'link-2', 'copy',
|
|
'save-2', 'archive', 'box', 'box-1', 'shopping-bag', 'shopping-cart',
|
|
'truck', 'car', 'security', 'shield', 'shield-tick', 'flash', 'lamp-on',
|
|
'lamp-charge', 'emoji-happy', 'emoji-normal', 'robot', 'cpu', 'task',
|
|
'task-square', 'clipboard', 'clipboard-text', 'note', 'note-1', 'book',
|
|
'bookmark', 'tag', 'tag-2', 'hashtag', 'percentage-square', 'discount-shape',
|
|
'receipt', 'receipt-2', 'receipt-item', 'profile-add', 'user-add', 'people',
|
|
'category', 'element-4', 'slider', 'slider-horizontal', 'slider-vertical',
|
|
'sound', 'volume-high', 'volume-low', 'music', 'play', 'pause', 'record',
|
|
'fullscreen', 'maximize', 'maximize-3', 'maximize-4', 'scan', 'scan-barcode',
|
|
'zoom', 'search-zoom-in', 'search-zoom-out', 'arrow-swap-horizontal',
|
|
'arrange-square', 'row-vertical', 'row-horizontal', 'text', 'textalign-left',
|
|
'text-bold', 'text-italic', 'text-underline', 'quote-down', 'quote-up',
|
|
'like', 'dislike', 'heart', 'lovely', 'danger', 'info-circle',
|
|
'arrow-circle-right', 'arrow-circle-left', 'arrow-circle-up', 'arrow-circle-down',
|
|
];
|
|
const missing = needed.filter((k) => !keys.has(k));
|
|
console.log('missing samples', missing.length, missing.slice(0, 40).join(', '));
|
|
console.log('search*', [...keys].filter((k) => k.includes('search')).slice(0, 20).join(', '));
|
|
console.log('setting*', [...keys].filter((k) => k.includes('setting')).slice(0, 20).join(', '));
|
|
console.log('user*', [...keys].filter((k) => k.startsWith('user') || k.includes('profile')).slice(0, 25).join(', '));
|