From 7e56fc50301c441540941491ab4657976099d7fa Mon Sep 17 00:00:00 2001
From: akane <107654710+genericness@users.noreply.github.com>
Date: Sat, 21 Mar 2026 11:56:34 -0700
Subject: [PATCH] fix(ui): command palette accessibility, theme handling, and
edge cases
---
index.html | 7 ++++++-
js/commandPalette.js | 31 ++++++++++++++++++++++++++-----
styles.css | 1 +
3 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/index.html b/index.html
index 4ef6d04..9af8e98 100644
--- a/index.html
+++ b/index.html
@@ -1505,10 +1505,15 @@
placeholder="Search commands, music, settings..."
autocomplete="off"
spellcheck="false"
+ aria-label="Command palette search"
+ role="combobox"
+ aria-expanded="true"
+ aria-controls="command-palette-results"
+ aria-autocomplete="list"
/>
ESC
-
+
↑↓ navigate
↵ select
diff --git a/js/commandPalette.js b/js/commandPalette.js
index 5ea9dd5..c99d204 100644
--- a/js/commandPalette.js
+++ b/js/commandPalette.js
@@ -642,6 +642,7 @@ class CommandPalette {
icon: 'search',
label: 'Search Settings...',
keywords: ['setting', 'find', 'search', 'preference', 'option', 'configure'],
+ keepOpen: true,
action: () => this.enterSettingsMode(),
},
@@ -923,6 +924,7 @@ class CommandPalette {
appendMusicGroups(musicGroups) {
this.removeMusicLoading();
+ this.resultsContainer.querySelector('.cmdk-empty')?.remove();
this.resultsContainer.querySelectorAll('[data-music-group]').forEach((el) => el.remove());
let index = this.flatItems.length;
@@ -1000,7 +1002,10 @@ class CommandPalette {
createItemElement(item, index) {
const el = document.createElement('div');
el.className = 'cmdk-item';
+ el.id = `cmdk-item-${index}`;
+ el.setAttribute('role', 'option');
el.setAttribute('data-index', index);
+ el.setAttribute('aria-selected', index === this.selectedIndex ? 'true' : 'false');
if (index === this.selectedIndex) el.setAttribute('data-selected', 'true');
let iconHtml = '';
@@ -1041,18 +1046,34 @@ class CommandPalette {
const idx = parseInt(item.getAttribute('data-index'));
if (idx === this.selectedIndex) {
item.setAttribute('data-selected', 'true');
+ item.setAttribute('aria-selected', 'true');
item.scrollIntoView({ block: 'nearest' });
} else {
item.removeAttribute('data-selected');
+ item.setAttribute('aria-selected', 'false');
}
});
+ this.input.setAttribute('aria-activedescendant', `cmdk-item-${this.selectedIndex}`);
}
- executeSelected() {
+ async executeSelected() {
const item = this.flatItems[this.selectedIndex];
if (!item || !item.action) return;
- item.action();
+ if (item.keepOpen) {
+ try {
+ await item.action();
+ } catch (e) {
+ console.error('Command palette action error:', e);
+ }
+ return;
+ }
+
+ try {
+ await item.action();
+ } catch (e) {
+ console.error('Command palette action error:', e);
+ }
this.close();
}
@@ -1132,9 +1153,9 @@ class CommandPalette {
}
}
- setTheme(theme) {
- document.documentElement.setAttribute('data-theme', theme);
- localStorage.setItem('theme', theme);
+ async setTheme(theme) {
+ const { themeManager } = await import('./storage.js');
+ themeManager.setTheme(theme);
const themeOptions = document.querySelectorAll('.theme-option');
themeOptions.forEach((opt) => {
if (opt.dataset.theme === theme) opt.classList.add('active');
diff --git a/styles.css b/styles.css
index 95d3844..9e96209 100644
--- a/styles.css
+++ b/styles.css
@@ -8527,6 +8527,7 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
color: var(--muted-foreground);
}
+/* stylelint-disable-next-line no-descending-specificity */
.cmdk-item-icon img {
width: 28px;
height: 28px;