feat: Cmd+K search — rich keywords, real logos, category icons

- Projects searchable by technologies, "open source", category (cli/app/web)
- Experience searchable by technologies, shows company logo + position
- Courses show institution logos
- Project logos used as icons instead of generic mdi:web
- Category-specific fallback icons (console, apple, puzzle, etc.)
This commit is contained in:
juanatsap
2026-05-04 15:23:15 +01:00
parent b9db689981
commit aae1a28627
2 changed files with 67 additions and 28 deletions
+25 -21
View File
@@ -111,50 +111,54 @@
}
}
/**
* Convert API experience entries to ninja-keys actions
* @param {Array} experiences - Experience entries from API
* @returns {Array} ninja-keys actions
*/
/** Category icon mapping */
const categoryIcons = {
cli: 'mdi:console',
app: 'mdi:apple',
web: 'mdi:web',
webapp: 'mdi:web',
plugin: 'mdi:puzzle',
sdk: 'mdi:package-variant',
contrib: 'mdi:source-pull'
};
/** Build icon HTML — use project logo if available, fallback to iconify */
function makeIcon(logoFile, folder, fallbackIcon) {
if (logoFile) {
return `<img src="/static/images/${folder}/${logoFile}" style="width:20px;height:20px;object-fit:contain;border-radius:3px" alt="">`;
}
return `<iconify-icon icon="${fallbackIcon}" width="20"></iconify-icon>`;
}
function mapExperienceActions(experiences) {
return experiences.map(exp => ({
id: exp.id,
title: exp.title,
section: exp.section,
keywords: `${exp.keywords} work job career`.toLowerCase(),
icon: '<iconify-icon icon="mdi:office-building" width="20"></iconify-icon>',
keywords: exp.keywords.toLowerCase(),
icon: makeIcon(exp.icon, 'companies', 'mdi:office-building'),
handler: () => scrollToSection(exp.id)
}));
}
/**
* Convert API project entries to ninja-keys actions
* @param {Array} projects - Project entries from API
* @returns {Array} ninja-keys actions
*/
function mapProjectActions(projects) {
return projects.map(proj => ({
id: proj.id,
title: proj.title,
section: proj.section,
keywords: `${proj.keywords} project website app`.toLowerCase(),
icon: '<iconify-icon icon="mdi:web" width="20"></iconify-icon>',
keywords: proj.keywords.toLowerCase(),
icon: makeIcon(proj.icon, 'projects', categoryIcons[proj.category] || 'mdi:web'),
handler: () => scrollToSection(proj.id)
}));
}
/**
* Convert API course entries to ninja-keys actions
* @param {Array} courses - Course entries from API
* @returns {Array} ninja-keys actions
*/
function mapCourseActions(courses) {
return courses.map(course => ({
id: course.id,
title: course.title,
section: course.section,
keywords: `${course.keywords} course training certification`.toLowerCase(),
icon: '<iconify-icon icon="mdi:school" width="20"></iconify-icon>',
keywords: course.keywords.toLowerCase(),
icon: makeIcon(course.icon, 'courses', 'mdi:school'),
handler: () => scrollToSection(course.id)
}));
}