mirror of
https://github.com/kennethreitz/kennethreitz.org.git
synced 2026-06-05 22:50:17 +00:00
Add keyboard shortcuts and command palette
This commit is contained in:
@@ -1637,6 +1637,28 @@ pre[class*="language-"] {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.explorer-fab .shortcut-hint {
|
||||
position: absolute;
|
||||
bottom: -25px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgb(var(--color-background-code));
|
||||
color: rgb(var(--color-text-secondary));
|
||||
padding: 3px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
border: 1px solid rgb(var(--color-border));
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.explorer-fab:hover .shortcut-hint {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.explorer-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -1755,6 +1777,12 @@ pre[class*="language-"] {
|
||||
background-color: rgb(var(--color-primary));
|
||||
}
|
||||
|
||||
.explorer-fab .shortcut-hint {
|
||||
background-color: rgb(var(--color-background-code-dark));
|
||||
color: rgb(var(--color-text-light) / 0.7);
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
|
||||
.explorer-panel {
|
||||
background-color: rgb(var(--color-background-code-dark));
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
@@ -1823,6 +1851,307 @@ pre[class*="language-"] {
|
||||
}
|
||||
}
|
||||
|
||||
/* Command Palette */
|
||||
.command-palette {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0.95);
|
||||
width: 600px;
|
||||
max-width: 90vw;
|
||||
max-height: 400px;
|
||||
background-color: rgb(var(--color-background-code));
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgb(var(--color-border));
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.command-palette.show {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
|
||||
.command-palette-header {
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid rgb(var(--color-border));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.command-palette-search {
|
||||
flex: 1;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: rgb(var(--color-text));
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.command-palette-search::placeholder {
|
||||
color: rgb(var(--color-text-secondary) / 0.6);
|
||||
}
|
||||
|
||||
.command-palette-results {
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.command-palette-item {
|
||||
padding: 8px 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.command-palette-item:hover,
|
||||
.command-palette-item.selected {
|
||||
background-color: rgba(var(--color-primary), 0.1);
|
||||
}
|
||||
|
||||
.command-palette-icon {
|
||||
color: rgb(var(--color-text-secondary));
|
||||
width: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.command-palette-label {
|
||||
flex: 1;
|
||||
font-size: 0.9rem;
|
||||
color: rgb(var(--color-text));
|
||||
}
|
||||
|
||||
.command-palette-shortcut {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.command-palette-key {
|
||||
font-size: 0.75rem;
|
||||
color: rgb(var(--color-text-secondary));
|
||||
background-color: rgba(var(--color-background), 0.8);
|
||||
border: 1px solid rgb(var(--color-border));
|
||||
border-radius: 3px;
|
||||
padding: 1px 5px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
}
|
||||
|
||||
/* Keyboard Shortcuts Panel */
|
||||
.shortcuts-panel {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0.95);
|
||||
width: 700px;
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
background-color: rgb(var(--color-background));
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgb(var(--color-border));
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.shortcuts-panel.show {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
|
||||
.shortcuts-header {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid rgb(var(--color-border));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.shortcuts-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
color: rgb(var(--color-text));
|
||||
}
|
||||
|
||||
.shortcuts-close {
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
color: rgb(var(--color-text-secondary));
|
||||
}
|
||||
|
||||
.shortcuts-content {
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
max-height: calc(80vh - 60px);
|
||||
}
|
||||
|
||||
.shortcuts-section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.shortcuts-section-title {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: rgb(var(--color-primary));
|
||||
border-bottom: 1px solid rgba(var(--color-border), 0.3);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.shortcuts-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.shortcut-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.shortcut-item:hover {
|
||||
background-color: rgba(var(--color-primary), 0.05);
|
||||
}
|
||||
|
||||
.shortcut-label {
|
||||
font-size: 0.85rem;
|
||||
color: rgb(var(--color-text));
|
||||
}
|
||||
|
||||
.shortcut-combo {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.shortcut-key {
|
||||
font-size: 0.75rem;
|
||||
color: rgb(var(--color-text-secondary));
|
||||
background-color: rgb(var(--color-background-code));
|
||||
border: 1px solid rgb(var(--color-border));
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.shortcuts-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.shortcuts-overlay.show {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.command-palette {
|
||||
background-color: rgb(var(--color-background-code-dark));
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
|
||||
.command-palette-header {
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
|
||||
.command-palette-search {
|
||||
color: rgb(var(--color-text-light));
|
||||
}
|
||||
|
||||
.command-palette-search::placeholder {
|
||||
color: rgb(var(--color-text-light) / 0.5);
|
||||
}
|
||||
|
||||
.command-palette-item:hover,
|
||||
.command-palette-item.selected {
|
||||
background-color: rgba(var(--color-primary-dark), 0.3);
|
||||
}
|
||||
|
||||
.command-palette-icon {
|
||||
color: rgb(var(--color-text-light) / 0.7);
|
||||
}
|
||||
|
||||
.command-palette-label {
|
||||
color: rgb(var(--color-text-light));
|
||||
}
|
||||
|
||||
.command-palette-key {
|
||||
color: rgb(var(--color-text-light) / 0.7);
|
||||
background-color: rgb(40, 44, 52);
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
|
||||
.shortcuts-panel {
|
||||
background-color: rgb(var(--color-background-dark));
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
|
||||
.shortcuts-header {
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
|
||||
.shortcuts-title {
|
||||
color: rgb(var(--color-text-light));
|
||||
}
|
||||
|
||||
.shortcuts-close {
|
||||
color: rgb(var(--color-text-light) / 0.7);
|
||||
}
|
||||
|
||||
.shortcuts-section-title {
|
||||
color: rgb(var(--color-accent-light));
|
||||
border-color: rgba(var(--color-border-dark), 0.3);
|
||||
}
|
||||
|
||||
.shortcut-item:hover {
|
||||
background-color: rgba(var(--color-primary-dark), 0.2);
|
||||
}
|
||||
|
||||
.shortcut-label {
|
||||
color: rgb(var(--color-text-light));
|
||||
}
|
||||
|
||||
.shortcut-key {
|
||||
color: rgb(var(--color-text-light) / 0.8);
|
||||
background-color: rgb(var(--color-background-code-dark));
|
||||
border-color: rgb(var(--color-border-dark));
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
html {
|
||||
|
||||
+365
-8
@@ -65,6 +65,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 3h18v18H3zM9 3v18M3 9h6M3 15h6"></path>
|
||||
</svg>
|
||||
<span class="shortcut-hint">Ctrl+E</span>
|
||||
</div>
|
||||
|
||||
<!-- Explorer Panel -->
|
||||
@@ -119,6 +120,106 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Command Palette -->
|
||||
<div id="command-palette" class="command-palette">
|
||||
<div class="command-palette-header">
|
||||
<input type="text" class="command-palette-search" id="command-search" placeholder="Type a command or search..." autocomplete="off">
|
||||
</div>
|
||||
<div class="command-palette-results" id="command-results">
|
||||
<!-- Results will be populated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keyboard Shortcuts Panel -->
|
||||
<div id="shortcuts-panel" class="shortcuts-panel">
|
||||
<div class="shortcuts-header">
|
||||
<h3 class="shortcuts-title">Keyboard Shortcuts</h3>
|
||||
<button id="shortcuts-close" class="shortcuts-close">×</button>
|
||||
</div>
|
||||
<div class="shortcuts-content">
|
||||
<div class="shortcuts-section">
|
||||
<h4 class="shortcuts-section-title">Navigation</h4>
|
||||
<div class="shortcuts-list">
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Open Explorer</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">Ctrl</span>
|
||||
<span class="shortcut-key">E</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Command Palette</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">Ctrl</span>
|
||||
<span class="shortcut-key">P</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Go Home</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">Alt</span>
|
||||
<span class="shortcut-key">Home</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Keyboard Shortcuts</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcuts-section">
|
||||
<h4 class="shortcuts-section-title">Content</h4>
|
||||
<div class="shortcuts-list">
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Copy Code</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">Alt</span>
|
||||
<span class="shortcut-key">C</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Toggle Dark Mode</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">Alt</span>
|
||||
<span class="shortcut-key">T</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcuts-section">
|
||||
<h4 class="shortcuts-section-title">Quick Access</h4>
|
||||
<div class="shortcuts-list">
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Software</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">G</span>
|
||||
<span class="shortcut-key">S</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Essays</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">G</span>
|
||||
<span class="shortcut-key">E</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<span class="shortcut-label">Contact</span>
|
||||
<div class="shortcut-combo">
|
||||
<span class="shortcut-key">G</span>
|
||||
<span class="shortcut-key">C</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Overlay for modals -->
|
||||
<div id="shortcuts-overlay" class="shortcuts-overlay"></div>
|
||||
|
||||
<footer class="mt-auto py-10 bg-primary-light/50 dark:bg-primary-dark/10 border-t border-border dark:border-border-dark">
|
||||
<div class="container flex flex-col md:flex-row justify-between items-center gap-6">
|
||||
@@ -132,8 +233,17 @@
|
||||
<div class="text-center p-4 bg-white/10 dark:bg-white/5 rounded-lg max-w-[600px] mx-auto">
|
||||
<p id="kenneth-quote" class="italic relative cursor-pointer inline-block px-2 transition-all">"Attention is the only currency we have in life. Thanks for gifting me yours."</p>
|
||||
</div>
|
||||
<div id="elevenlabs-audionative-widget" data-height="90" data-width="100%" data-frameborder="no" data-scrolling="no" data-publicuserid="09af4c720273252ac5bcddb53500ed22b7c59ef815a710a03ea48b18e295c24f" data-playerurl="https://elevenlabs.io/player/index.html" class="mt-6 w-full rounded overflow-hidden">Loading the <a href="https://elevenlabs.io/text-to-speech" target="_blank" rel="noopener">Elevenlabs Text to Speech</a> AudioNative Player...</div>
|
||||
<script src="https://elevenlabs.io/player/audioNativeHelper.js" type="text/javascript"></script>
|
||||
<div class="flex items-center gap-4">
|
||||
<div id="shortcuts-indicator" class="cursor-pointer text-sm flex items-center gap-1 py-1 px-2 rounded bg-white/10 dark:bg-white/5 text-text-secondary dark:text-text-light/60 font-mono hover:bg-white/20 dark:hover:bg-white/10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
<span>Press ? for shortcuts</span>
|
||||
</div>
|
||||
<div id="elevenlabs-audionative-widget" data-height="90" data-width="100%" data-frameborder="no" data-scrolling="no" data-publicuserid="09af4c720273252ac5bcddb53500ed22b7c59ef815a710a03ea48b18e295c24f" data-playerurl="https://elevenlabs.io/player/index.html" class="mt-6 w-full rounded overflow-hidden">Loading the <a href="https://elevenlabs.io/text-to-speech" target="_blank" rel="noopener">Elevenlabs Text to Speech</a> AudioNative Player...</div>
|
||||
<script src="https://elevenlabs.io/player/audioNativeHelper.js" type="text/javascript"></script>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -407,15 +517,262 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Add keyboard shortcut (Ctrl+E or Cmd+E) to toggle explorer
|
||||
// Command palette setup
|
||||
const commandPalette = document.getElementById('command-palette');
|
||||
const commandSearch = document.getElementById('command-search');
|
||||
const commandResults = document.getElementById('command-results');
|
||||
const shortcutsPanel = document.getElementById('shortcuts-panel');
|
||||
const shortcutsClose = document.getElementById('shortcuts-close');
|
||||
const shortcutsOverlay = document.getElementById('shortcuts-overlay');
|
||||
|
||||
// Define available commands
|
||||
const commands = [
|
||||
{
|
||||
id: 'explorer',
|
||||
label: 'Toggle Explorer',
|
||||
shortcut: 'Ctrl+E',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3h18v18H3zM9 3v18M3 9h6M3 15h6"></path></svg>',
|
||||
action: () => toggleExplorer()
|
||||
},
|
||||
{
|
||||
id: 'home',
|
||||
label: 'Go to Home',
|
||||
shortcut: 'Alt+Home',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>',
|
||||
action: () => window.location.href = '/'
|
||||
},
|
||||
{
|
||||
id: 'software',
|
||||
label: 'Software Projects',
|
||||
shortcut: 'G S',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2V8L16 3H8z"></path><path d="M17 21v-8H7v8M7 3v5h8"></path></svg>',
|
||||
action: () => window.location.href = '/software'
|
||||
},
|
||||
{
|
||||
id: 'essays',
|
||||
label: 'Essays',
|
||||
shortcut: 'G E',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg>',
|
||||
action: () => window.location.href = '/essays'
|
||||
},
|
||||
{
|
||||
id: 'contact',
|
||||
label: 'Contact',
|
||||
shortcut: 'G C',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path></svg>',
|
||||
action: () => window.location.href = '/contact'
|
||||
},
|
||||
{
|
||||
id: 'shortcuts',
|
||||
label: 'Keyboard Shortcuts',
|
||||
shortcut: '?',
|
||||
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>',
|
||||
action: () => toggleShortcutsPanel()
|
||||
}
|
||||
];
|
||||
|
||||
// Function to render commands in palette
|
||||
function renderCommands(filterText = '') {
|
||||
commandResults.innerHTML = '';
|
||||
|
||||
const filteredCommands = filterText
|
||||
? commands.filter(cmd =>
|
||||
cmd.label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
cmd.id.toLowerCase().includes(filterText.toLowerCase()))
|
||||
: commands;
|
||||
|
||||
if (filteredCommands.length === 0) {
|
||||
const noResults = document.createElement('div');
|
||||
noResults.className = 'command-palette-item';
|
||||
noResults.innerHTML = `
|
||||
<div class="command-palette-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" y1="8" x2="12" y2="12"></line>
|
||||
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="command-palette-label">No matching commands found</span>
|
||||
`;
|
||||
commandResults.appendChild(noResults);
|
||||
return;
|
||||
}
|
||||
|
||||
filteredCommands.forEach((cmd, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'command-palette-item';
|
||||
if (index === 0) item.classList.add('selected');
|
||||
|
||||
item.innerHTML = `
|
||||
<div class="command-palette-icon">${cmd.icon}</div>
|
||||
<span class="command-palette-label">${cmd.label}</span>
|
||||
<div class="command-palette-shortcut">
|
||||
${cmd.shortcut.split(' ').map(key =>
|
||||
`<span class="command-palette-key">${key}</span>`
|
||||
).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
item.addEventListener('click', () => {
|
||||
cmd.action();
|
||||
toggleCommandPalette(false);
|
||||
});
|
||||
|
||||
commandResults.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle command palette
|
||||
function toggleCommandPalette(show = true) {
|
||||
if (show) {
|
||||
commandPalette.classList.add('show');
|
||||
shortcutsOverlay.classList.add('show');
|
||||
commandSearch.value = '';
|
||||
renderCommands();
|
||||
setTimeout(() => commandSearch.focus(), 100);
|
||||
} else {
|
||||
commandPalette.classList.remove('show');
|
||||
shortcutsOverlay.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle shortcuts panel
|
||||
function toggleShortcutsPanel(show = true) {
|
||||
if (show) {
|
||||
shortcutsPanel.classList.add('show');
|
||||
shortcutsOverlay.classList.add('show');
|
||||
} else {
|
||||
shortcutsPanel.classList.remove('show');
|
||||
shortcutsOverlay.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle explorer
|
||||
function toggleExplorer(show) {
|
||||
if (show === undefined) {
|
||||
explorerPanel.classList.toggle('show');
|
||||
} else if (show) {
|
||||
explorerPanel.classList.add('show');
|
||||
} else {
|
||||
explorerPanel.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// Command palette search functionality
|
||||
if (commandSearch) {
|
||||
commandSearch.addEventListener('input', (e) => {
|
||||
renderCommands(e.target.value);
|
||||
});
|
||||
|
||||
commandSearch.addEventListener('keydown', (e) => {
|
||||
const items = commandResults.querySelectorAll('.command-palette-item');
|
||||
const selected = commandResults.querySelector('.selected');
|
||||
let index = Array.from(items).indexOf(selected);
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
if (index < items.length - 1) {
|
||||
if (selected) selected.classList.remove('selected');
|
||||
items[index + 1].classList.add('selected');
|
||||
items[index + 1].scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
if (index > 0) {
|
||||
if (selected) selected.classList.remove('selected');
|
||||
items[index - 1].classList.add('selected');
|
||||
items[index - 1].scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
if (selected) {
|
||||
selected.click();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
toggleCommandPalette(false);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Close shortcuts panel
|
||||
if (shortcutsClose) {
|
||||
shortcutsClose.addEventListener('click', () => {
|
||||
toggleShortcutsPanel(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Close modals when clicking on overlay
|
||||
if (shortcutsOverlay) {
|
||||
shortcutsOverlay.addEventListener('click', () => {
|
||||
toggleCommandPalette(false);
|
||||
toggleShortcutsPanel(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle shortcuts indicator click
|
||||
const shortcutsIndicator = document.getElementById('shortcuts-indicator');
|
||||
if (shortcutsIndicator) {
|
||||
shortcutsIndicator.addEventListener('click', () => {
|
||||
toggleShortcutsPanel(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Global keyboard shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Toggle explorer (Ctrl+E or Cmd+E)
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'e') {
|
||||
e.preventDefault();
|
||||
if (explorerPanel.classList.contains('show')) {
|
||||
explorerPanel.classList.remove('show');
|
||||
} else {
|
||||
explorerPanel.classList.add('show');
|
||||
}
|
||||
toggleExplorer();
|
||||
}
|
||||
|
||||
// Command palette (Ctrl+P or Cmd+P)
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
|
||||
e.preventDefault();
|
||||
toggleCommandPalette(true);
|
||||
}
|
||||
|
||||
// Shortcuts panel (?)
|
||||
if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey &&
|
||||
!(document.activeElement instanceof HTMLInputElement) &&
|
||||
!(document.activeElement instanceof HTMLTextAreaElement)) {
|
||||
e.preventDefault();
|
||||
toggleShortcutsPanel(true);
|
||||
}
|
||||
|
||||
// Quick navigation with g prefix
|
||||
if (e.key === 'g' && !shortcutsPanel.classList.contains('show') &&
|
||||
!commandPalette.classList.contains('show') &&
|
||||
!(document.activeElement instanceof HTMLInputElement) &&
|
||||
!(document.activeElement instanceof HTMLTextAreaElement)) {
|
||||
|
||||
const handleSecondKey = (e2) => {
|
||||
document.removeEventListener('keydown', handleSecondKey);
|
||||
|
||||
if (e2.key === 's') {
|
||||
window.location.href = '/software';
|
||||
} else if (e2.key === 'e') {
|
||||
window.location.href = '/essays';
|
||||
} else if (e2.key === 'c') {
|
||||
window.location.href = '/contact';
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleSecondKey);
|
||||
}
|
||||
|
||||
// Home shortcut (Alt+Home)
|
||||
if (e.altKey && e.key === 'Home') {
|
||||
e.preventDefault();
|
||||
window.location.href = '/';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user