/**
* Keyboard Command Palette (Spotlight Search)
* Instantiates the command palette UI, binds listeners for hotkeys,
* and handles arrow-key navigation.
*/
(function() {
// Generate Command Palette HTML dynamically
var html = `
`;
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html.trim();
document.body.appendChild(tempDiv.firstChild);
var palette = document.getElementById('cmd-palette');
var input = document.getElementById('cmd-palette-input');
var resultsContainer = document.getElementById('cmd-palette-results');
var activeIndex = 0;
var filteredItems = [];
// Ensure we have a default list of items if window.cmdPaletteItems is missing
var items = window.cmdPaletteItems || [
{ title: "Home", url: "/", category: "Navigation" },
{ title: "All Projects", url: "/projects/", category: "Navigation" },
{ title: "About", url: "/about.html", category: "Navigation" },
{ title: "Contact", url: "/contact.html", category: "Navigation" }
];
function showPalette() {
palette.classList.add('visible');
input.value = '';
input.focus();
activeIndex = 0;
renderResults("");
document.body.style.overflow = 'hidden';
}
function hidePalette() {
palette.classList.remove('visible');
document.body.style.overflow = '';
}
function renderResults(query) {
resultsContainer.innerHTML = '';
query = query.toLowerCase().trim();
// Filter items
filteredItems = items.filter(function(item) {
return item.title.toLowerCase().includes(query) ||
item.category.toLowerCase().includes(query);
});
if (filteredItems.length === 0) {
resultsContainer.innerHTML = 'No results found
';
return;
}
// Render filtered items
filteredItems.forEach(function(item, idx) {
var div = document.createElement('div');
div.className = 'cmd-palette-item' + (idx === activeIndex ? ' active' : '');
div.innerHTML = `
${item.title}
${item.category}
`;
div.addEventListener('click', function() {
window.location.href = item.url;
});
resultsContainer.appendChild(div);
});
// Ensure active item is scrolled into view
var activeNode = resultsContainer.children[activeIndex];
if (activeNode) {
activeNode.scrollIntoView({ block: 'nearest' });
}
}
input.addEventListener('input', function() {
activeIndex = 0;
renderResults(input.value);
});
// Handle Keyboard Navigation inside Modal
document.addEventListener('keydown', function(e) {
var isOpen = palette.classList.contains('visible');
// Toggle triggers
if ((e.key === 'k' && (e.metaKey || e.ctrlKey)) || (e.key === '/' && !isOpen && !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName))) {
e.preventDefault();
showPalette();
return;
}
if (!isOpen) return;
switch (e.key) {
case 'Escape':
e.preventDefault();
hidePalette();
break;
case 'ArrowDown':
e.preventDefault();
activeIndex = (activeIndex + 1) % filteredItems.length;
renderResults(input.value);
break;
case 'ArrowUp':
e.preventDefault();
activeIndex = (activeIndex - 1 + filteredItems.length) % filteredItems.length;
renderResults(input.value);
break;
case 'Enter':
e.preventDefault();
if (filteredItems[activeIndex]) {
window.location.href = filteredItems[activeIndex].url;
}
break;
}
});
// Close on backdrop click
palette.querySelector('.cmd-palette-backdrop').addEventListener('click', hidePalette);
// Bind trigger button if it exists
var triggerBtn = document.getElementById('cmd-palette-btn');
if (triggerBtn) {
triggerBtn.addEventListener('click', function(e) {
e.preventDefault();
showPalette();
});
}
})();