@salmexio/ui 0.3.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -3
- package/dist/dialogs/ContextMenu/ContextMenu.svelte +96 -93
- package/dist/dialogs/ContextMenu/ContextMenu.svelte.d.ts +3 -2
- package/dist/dialogs/ContextMenu/ContextMenu.svelte.d.ts.map +1 -1
- package/dist/dialogs/Modal/Modal.svelte +112 -102
- package/dist/dialogs/Modal/Modal.svelte.d.ts +1 -1
- package/dist/feedback/Alert/Alert.svelte +115 -221
- package/dist/feedback/Alert/Alert.svelte.d.ts +1 -1
- package/dist/feedback/ProgressBar/ProgressBar.svelte +246 -0
- package/dist/feedback/ProgressBar/ProgressBar.svelte.d.ts +40 -0
- package/dist/feedback/ProgressBar/ProgressBar.svelte.d.ts.map +1 -0
- package/dist/feedback/ProgressBar/index.d.ts +2 -0
- package/dist/feedback/ProgressBar/index.d.ts.map +1 -0
- package/dist/feedback/ProgressBar/index.js +1 -0
- package/dist/feedback/Skeleton/Skeleton.svelte +153 -0
- package/dist/feedback/Skeleton/Skeleton.svelte.d.ts +37 -0
- package/dist/feedback/Skeleton/Skeleton.svelte.d.ts.map +1 -0
- package/dist/feedback/Skeleton/index.d.ts +2 -0
- package/dist/feedback/Skeleton/index.d.ts.map +1 -0
- package/dist/feedback/Skeleton/index.js +1 -0
- package/dist/feedback/Spinner/Spinner.svelte +88 -154
- package/dist/feedback/Spinner/Spinner.svelte.d.ts +5 -3
- package/dist/feedback/Spinner/Spinner.svelte.d.ts.map +1 -1
- package/dist/feedback/Toast/Toaster.svelte +431 -0
- package/dist/feedback/Toast/Toaster.svelte.d.ts +22 -0
- package/dist/feedback/Toast/Toaster.svelte.d.ts.map +1 -0
- package/dist/feedback/Toast/index.d.ts +4 -0
- package/dist/feedback/Toast/index.d.ts.map +1 -0
- package/dist/feedback/Toast/index.js +2 -0
- package/dist/feedback/Toast/toastStore.d.ts +34 -0
- package/dist/feedback/Toast/toastStore.d.ts.map +1 -0
- package/dist/feedback/Toast/toastStore.js +43 -0
- package/dist/feedback/index.d.ts +4 -0
- package/dist/feedback/index.d.ts.map +1 -1
- package/dist/feedback/index.js +3 -0
- package/dist/forms/Checkbox/Checkbox.svelte +82 -103
- package/dist/forms/Checkbox/Checkbox.svelte.d.ts +1 -1
- package/dist/forms/Select/Select.svelte +136 -177
- package/dist/forms/Select/Select.svelte.d.ts +1 -1
- package/dist/forms/Slider/Slider.svelte +356 -0
- package/dist/forms/Slider/Slider.svelte.d.ts +50 -0
- package/dist/forms/Slider/Slider.svelte.d.ts.map +1 -0
- package/dist/forms/Slider/index.d.ts +2 -0
- package/dist/forms/Slider/index.d.ts.map +1 -0
- package/dist/forms/Slider/index.js +1 -0
- package/dist/forms/TextInput/TextInput.svelte +148 -164
- package/dist/forms/TextInput/TextInput.svelte.d.ts +1 -1
- package/dist/forms/Textarea/Textarea.svelte +615 -0
- package/dist/forms/Textarea/Textarea.svelte.d.ts +47 -0
- package/dist/forms/Textarea/Textarea.svelte.d.ts.map +1 -0
- package/dist/forms/Textarea/index.d.ts +2 -0
- package/dist/forms/Textarea/index.d.ts.map +1 -0
- package/dist/forms/Textarea/index.js +1 -0
- package/dist/forms/Toggle/Toggle.svelte +239 -0
- package/dist/forms/Toggle/Toggle.svelte.d.ts +39 -0
- package/dist/forms/Toggle/Toggle.svelte.d.ts.map +1 -0
- package/dist/forms/Toggle/index.d.ts +2 -0
- package/dist/forms/Toggle/index.d.ts.map +1 -0
- package/dist/forms/Toggle/index.js +1 -0
- package/dist/forms/index.d.ts +3 -0
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +3 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/layout/Card/Card.svelte +64 -40
- package/dist/layout/Card/Card.svelte.d.ts +1 -1
- package/dist/layout/Card/Card.svelte.d.ts.map +1 -1
- package/dist/layout/Container/Container.svelte +71 -71
- package/dist/layout/Container/Container.svelte.d.ts +2 -2
- package/dist/navigation/CommandPalette/CommandPalette.svelte +410 -181
- package/dist/navigation/CommandPalette/CommandPalette.svelte.d.ts +8 -3
- package/dist/navigation/CommandPalette/CommandPalette.svelte.d.ts.map +1 -1
- package/dist/navigation/Tabs/Tabs.svelte +94 -178
- package/dist/navigation/Tabs/Tabs.svelte.d.ts +2 -2
- package/dist/primitives/Badge/Badge.svelte +85 -223
- package/dist/primitives/Badge/Badge.svelte.d.ts +2 -2
- package/dist/primitives/Badge/Badge.svelte.d.ts.map +1 -1
- package/dist/primitives/Button/Button.svelte +138 -208
- package/dist/primitives/Button/Button.svelte.d.ts +3 -3
- package/dist/primitives/Button/Button.svelte.d.ts.map +1 -1
- package/dist/primitives/Tooltip/Tooltip.svelte +260 -0
- package/dist/primitives/Tooltip/Tooltip.svelte.d.ts +36 -0
- package/dist/primitives/Tooltip/Tooltip.svelte.d.ts.map +1 -0
- package/dist/primitives/Tooltip/index.d.ts +2 -0
- package/dist/primitives/Tooltip/index.d.ts.map +1 -0
- package/dist/primitives/Tooltip/index.js +1 -0
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +1 -0
- package/dist/styles/tokens.css +200 -259
- package/package.json +5 -5
- package/dist/windowing/Window/Window.svelte +0 -602
- package/dist/windowing/Window/Window.svelte.d.ts +0 -65
- package/dist/windowing/Window/Window.svelte.d.ts.map +0 -1
- package/dist/windowing/Window/index.d.ts +0 -2
- package/dist/windowing/Window/index.d.ts.map +0 -1
- package/dist/windowing/Window/index.js +0 -1
- package/dist/windowing/WindowManager/WindowManager.svelte +0 -410
- package/dist/windowing/WindowManager/WindowManager.svelte.d.ts +0 -38
- package/dist/windowing/WindowManager/WindowManager.svelte.d.ts.map +0 -1
- package/dist/windowing/WindowManager/index.d.ts +0 -2
- package/dist/windowing/WindowManager/index.d.ts.map +0 -1
- package/dist/windowing/WindowManager/index.js +0 -1
- package/dist/windowing/index.d.ts +0 -5
- package/dist/windowing/index.d.ts.map +0 -1
- package/dist/windowing/index.js +0 -3
- package/dist/windowing/windowStore.svelte.d.ts +0 -49
- package/dist/windowing/windowStore.svelte.d.ts.map +0 -1
- package/dist/windowing/windowStore.svelte.js +0 -170
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component CommandPalette
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Neo-Brutalist Dark — Premium keyboard-first command launcher.
|
|
5
|
+
Glass backdrop, entrance animations, fuzzy search with match highlighting,
|
|
6
|
+
categorised results, shortcut display, live result count announcements.
|
|
7
|
+
|
|
8
|
+
ARIA: dialog containing combobox with listbox. Focus stays on input;
|
|
9
|
+
aria-activedescendant communicates visual focus to screen readers.
|
|
6
10
|
|
|
7
11
|
@example
|
|
8
12
|
<CommandPalette
|
|
@@ -14,12 +18,14 @@
|
|
|
14
18
|
/>
|
|
15
19
|
-->
|
|
16
20
|
<script lang="ts" module>
|
|
21
|
+
import type { Snippet } from 'svelte';
|
|
22
|
+
|
|
17
23
|
export interface CommandItem {
|
|
18
24
|
id: string;
|
|
19
25
|
label: string;
|
|
20
26
|
shortcut?: string;
|
|
21
27
|
group?: string;
|
|
22
|
-
icon?:
|
|
28
|
+
icon?: Snippet;
|
|
23
29
|
description?: string;
|
|
24
30
|
disabled?: boolean;
|
|
25
31
|
action: () => void;
|
|
@@ -33,7 +39,7 @@ export interface CommandGroup {
|
|
|
33
39
|
|
|
34
40
|
<script lang="ts">
|
|
35
41
|
import { cn } from '../../utils/cn.js';
|
|
36
|
-
import { Keys, createFocusTrap } from '../../utils/keyboard.js';
|
|
42
|
+
import { Keys, createFocusTrap, generateId } from '../../utils/keyboard.js';
|
|
37
43
|
import { onMount, tick } from 'svelte';
|
|
38
44
|
import type { FocusTrap } from '../../utils/keyboard.js';
|
|
39
45
|
|
|
@@ -61,63 +67,100 @@ let {
|
|
|
61
67
|
testId
|
|
62
68
|
}: Props = $props();
|
|
63
69
|
|
|
70
|
+
const instanceId = generateId('cmd');
|
|
71
|
+
const resultsId = `${instanceId}-results`;
|
|
72
|
+
|
|
64
73
|
let query = $state('');
|
|
65
74
|
let activeIndex = $state(0);
|
|
66
75
|
let inputEl = $state<HTMLInputElement | null>(null);
|
|
67
76
|
let panelEl = $state<HTMLDivElement | null>(null);
|
|
68
77
|
let listEl = $state<HTMLDivElement | null>(null);
|
|
69
|
-
let backdropEl = $state<HTMLDivElement | null>(null);
|
|
70
78
|
let wrapperEl = $state<HTMLDivElement | null>(null);
|
|
71
79
|
let focusTrap: FocusTrap | null = null;
|
|
80
|
+
let previouslyFocused: HTMLElement | null = null;
|
|
81
|
+
let isAnimatingOut = $state(false);
|
|
72
82
|
|
|
73
|
-
// Fuzzy match:
|
|
74
|
-
function
|
|
75
|
-
if (!search) return
|
|
83
|
+
// Fuzzy match: return matched character indices for highlighting
|
|
84
|
+
function fuzzyMatchIndices(text: string, search: string): number[] | null {
|
|
85
|
+
if (!search) return [];
|
|
76
86
|
const lower = text.toLowerCase();
|
|
77
87
|
const searchLower = search.toLowerCase();
|
|
88
|
+
const indices: number[] = [];
|
|
78
89
|
let si = 0;
|
|
79
90
|
for (let ti = 0; ti < lower.length && si < searchLower.length; ti++) {
|
|
80
|
-
if (lower[ti] === searchLower[si])
|
|
91
|
+
if (lower[ti] === searchLower[si]) {
|
|
92
|
+
indices.push(ti);
|
|
93
|
+
si++;
|
|
94
|
+
}
|
|
81
95
|
}
|
|
82
|
-
return si === searchLower.length;
|
|
96
|
+
return si === searchLower.length ? indices : null;
|
|
83
97
|
}
|
|
84
98
|
|
|
85
|
-
// Score: prefer starts-with
|
|
99
|
+
// Score: prefer starts-with, word-boundary matches, shorter labels
|
|
86
100
|
function fuzzyScore(text: string, search: string): number {
|
|
87
101
|
if (!search) return 0;
|
|
88
102
|
const lower = text.toLowerCase();
|
|
89
103
|
const searchLower = search.toLowerCase();
|
|
90
104
|
let score = 0;
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
|
|
106
|
+
// Exact prefix — highest
|
|
107
|
+
if (lower.startsWith(searchLower)) score += 200;
|
|
108
|
+
// Word boundary matches
|
|
109
|
+
else if (lower.includes(searchLower)) score += 100;
|
|
110
|
+
|
|
111
|
+
// Consecutive matches bonus
|
|
112
|
+
const indices = fuzzyMatchIndices(text, search);
|
|
113
|
+
if (indices && indices.length > 1) {
|
|
114
|
+
let consecutive = 0;
|
|
115
|
+
for (let i = 1; i < indices.length; i++) {
|
|
116
|
+
if (indices[i] === indices[i - 1] + 1) consecutive++;
|
|
117
|
+
}
|
|
118
|
+
score += consecutive * 15;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Word-boundary bonus: match at start of word
|
|
122
|
+
if (indices) {
|
|
123
|
+
for (const idx of indices) {
|
|
124
|
+
if (idx === 0 || text[idx - 1] === ' ' || text[idx - 1] === '-' || text[idx - 1] === '_') {
|
|
125
|
+
score += 10;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Shorter labels rank higher
|
|
93
131
|
score -= text.length;
|
|
94
132
|
return score;
|
|
95
133
|
}
|
|
96
134
|
|
|
97
|
-
const filteredCommands = $derived<CommandItem[]>(
|
|
135
|
+
const filteredCommands = $derived<(CommandItem & { matchIndices: number[] })[]>(
|
|
98
136
|
commands
|
|
99
|
-
.
|
|
137
|
+
.map((c) => {
|
|
138
|
+
const labelIndices = fuzzyMatchIndices(c.label, query);
|
|
139
|
+
const descIndices = c.description ? fuzzyMatchIndices(c.description, query) : null;
|
|
140
|
+
if (labelIndices === null && descIndices === null) return null;
|
|
141
|
+
return { ...c, matchIndices: labelIndices ?? [] };
|
|
142
|
+
})
|
|
143
|
+
.filter((c): c is CommandItem & { matchIndices: number[] } => c !== null)
|
|
100
144
|
.sort((a, b) => fuzzyScore(b.label, query) - fuzzyScore(a.label, query))
|
|
101
145
|
);
|
|
102
146
|
|
|
103
147
|
// Group filtered results
|
|
104
148
|
const groupedResults = $derived.by(() => {
|
|
105
|
-
const groups = new Map<string, CommandItem[]>();
|
|
149
|
+
const groups = new Map<string, (CommandItem & { matchIndices: number[] })[]>();
|
|
106
150
|
for (const item of filteredCommands) {
|
|
107
151
|
const key = item.group ?? '';
|
|
108
152
|
if (!groups.has(key)) groups.set(key, []);
|
|
109
153
|
groups.get(key)!.push(item);
|
|
110
154
|
}
|
|
111
|
-
return Array.from(groups.entries()).map(([label, items]) => ({ label, items }))
|
|
155
|
+
return Array.from(groups.entries()).map(([label, items]) => ({ label, items }));
|
|
112
156
|
});
|
|
113
157
|
|
|
114
|
-
// Flat list for keyboard navigation
|
|
115
|
-
const flatFiltered = $derived(filteredCommands);
|
|
116
|
-
|
|
117
158
|
function openPalette() {
|
|
159
|
+
previouslyFocused = document.activeElement as HTMLElement;
|
|
118
160
|
open = true;
|
|
119
161
|
query = '';
|
|
120
162
|
activeIndex = 0;
|
|
163
|
+
isAnimatingOut = false;
|
|
121
164
|
requestAnimationFrame(() => {
|
|
122
165
|
inputEl?.focus();
|
|
123
166
|
if (panelEl) {
|
|
@@ -128,18 +171,27 @@ function openPalette() {
|
|
|
128
171
|
}
|
|
129
172
|
|
|
130
173
|
function closePalette() {
|
|
131
|
-
|
|
132
|
-
query = '';
|
|
133
|
-
activeIndex = 0;
|
|
174
|
+
isAnimatingOut = true;
|
|
134
175
|
focusTrap?.deactivate();
|
|
135
176
|
focusTrap = null;
|
|
136
|
-
|
|
177
|
+
|
|
178
|
+
// Wait for exit animation
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
open = false;
|
|
181
|
+
isAnimatingOut = false;
|
|
182
|
+
query = '';
|
|
183
|
+
activeIndex = 0;
|
|
184
|
+
onclose?.();
|
|
185
|
+
previouslyFocused?.focus();
|
|
186
|
+
previouslyFocused = null;
|
|
187
|
+
}, 120);
|
|
137
188
|
}
|
|
138
189
|
|
|
139
|
-
function executeItem(item: CommandItem) {
|
|
190
|
+
function executeItem(item: CommandItem & { matchIndices: number[] }) {
|
|
140
191
|
if (item.disabled) return;
|
|
141
192
|
closePalette();
|
|
142
|
-
|
|
193
|
+
// Execute after close animation
|
|
194
|
+
setTimeout(() => item.action(), 130);
|
|
143
195
|
}
|
|
144
196
|
|
|
145
197
|
function scrollActiveIntoView() {
|
|
@@ -147,15 +199,7 @@ function scrollActiveIntoView() {
|
|
|
147
199
|
if (!listEl) return;
|
|
148
200
|
const active = listEl.querySelector('[data-active="true"]') as HTMLElement;
|
|
149
201
|
if (!active) return;
|
|
150
|
-
|
|
151
|
-
const listHeight = listEl.clientHeight;
|
|
152
|
-
const elTop = active.offsetTop;
|
|
153
|
-
const elHeight = active.offsetHeight;
|
|
154
|
-
if (elTop < listTop) {
|
|
155
|
-
listEl.scrollTop = elTop;
|
|
156
|
-
} else if (elTop + elHeight > listTop + listHeight) {
|
|
157
|
-
listEl.scrollTop = elTop + elHeight - listHeight;
|
|
158
|
-
}
|
|
202
|
+
active.scrollIntoView({ block: 'nearest' });
|
|
159
203
|
});
|
|
160
204
|
}
|
|
161
205
|
|
|
@@ -163,7 +207,7 @@ function handleKeydown(e: KeyboardEvent) {
|
|
|
163
207
|
switch (e.key) {
|
|
164
208
|
case Keys.ArrowDown:
|
|
165
209
|
e.preventDefault();
|
|
166
|
-
activeIndex = Math.min(activeIndex + 1,
|
|
210
|
+
activeIndex = Math.min(activeIndex + 1, filteredCommands.length - 1);
|
|
167
211
|
scrollActiveIntoView();
|
|
168
212
|
break;
|
|
169
213
|
case Keys.ArrowUp:
|
|
@@ -173,7 +217,7 @@ function handleKeydown(e: KeyboardEvent) {
|
|
|
173
217
|
break;
|
|
174
218
|
case Keys.Enter:
|
|
175
219
|
e.preventDefault();
|
|
176
|
-
if (
|
|
220
|
+
if (filteredCommands[activeIndex]) executeItem(filteredCommands[activeIndex]);
|
|
177
221
|
break;
|
|
178
222
|
case Keys.Escape:
|
|
179
223
|
e.preventDefault();
|
|
@@ -186,7 +230,7 @@ function handleKeydown(e: KeyboardEvent) {
|
|
|
186
230
|
break;
|
|
187
231
|
case Keys.End:
|
|
188
232
|
e.preventDefault();
|
|
189
|
-
activeIndex = Math.max(
|
|
233
|
+
activeIndex = Math.max(filteredCommands.length - 1, 0);
|
|
190
234
|
scrollActiveIntoView();
|
|
191
235
|
break;
|
|
192
236
|
}
|
|
@@ -196,16 +240,12 @@ function handleInput() {
|
|
|
196
240
|
activeIndex = 0;
|
|
197
241
|
}
|
|
198
242
|
|
|
199
|
-
function handleBackdropClick() {
|
|
200
|
-
closePalette();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
243
|
// Watch for external open changes
|
|
204
244
|
$effect(() => {
|
|
205
|
-
if (open) openPalette();
|
|
245
|
+
if (open && !isAnimatingOut) openPalette();
|
|
206
246
|
});
|
|
207
247
|
|
|
208
|
-
// Portal
|
|
248
|
+
// Portal to document.body
|
|
209
249
|
$effect(() => {
|
|
210
250
|
if (wrapperEl && open) {
|
|
211
251
|
document.body.appendChild(wrapperEl);
|
|
@@ -233,7 +273,6 @@ onMount(() => {
|
|
|
233
273
|
};
|
|
234
274
|
});
|
|
235
275
|
|
|
236
|
-
// Track cumulative index for items within grouped display
|
|
237
276
|
function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
238
277
|
let offset = 0;
|
|
239
278
|
for (let g = 0; g < groupIdx; g++) {
|
|
@@ -241,18 +280,27 @@ function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
|
241
280
|
}
|
|
242
281
|
return offset + itemIdx;
|
|
243
282
|
}
|
|
283
|
+
|
|
284
|
+
function getItemId(itemId: string): string {
|
|
285
|
+
return `${instanceId}-item-${itemId}`;
|
|
286
|
+
}
|
|
244
287
|
</script>
|
|
245
288
|
|
|
246
289
|
{#if open}
|
|
247
290
|
<div bind:this={wrapperEl}>
|
|
248
291
|
<!-- Backdrop -->
|
|
249
292
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
250
|
-
<div
|
|
293
|
+
<div
|
|
294
|
+
class={cn('sx-command-backdrop', isAnimatingOut && 'sx-command-backdrop-exit')}
|
|
295
|
+
onclick={() => closePalette()}
|
|
296
|
+
onkeydown={() => {}}
|
|
297
|
+
role="presentation"
|
|
298
|
+
></div>
|
|
251
299
|
|
|
252
300
|
<!-- Panel -->
|
|
253
301
|
<div
|
|
254
302
|
bind:this={panelEl}
|
|
255
|
-
class={cn('
|
|
303
|
+
class={cn('sx-command', isAnimatingOut && 'sx-command-exit', className)}
|
|
256
304
|
role="dialog"
|
|
257
305
|
tabindex="-1"
|
|
258
306
|
aria-label="Command palette"
|
|
@@ -260,9 +308,9 @@ function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
|
260
308
|
onkeydown={handleKeydown}
|
|
261
309
|
>
|
|
262
310
|
<!-- Search field -->
|
|
263
|
-
<div class="
|
|
264
|
-
<span class="
|
|
265
|
-
<svg width="
|
|
311
|
+
<div class="sx-command-search">
|
|
312
|
+
<span class="sx-command-search-icon" aria-hidden="true">
|
|
313
|
+
<svg width="18" height="18" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
266
314
|
<circle cx="6.5" cy="6.5" r="5" />
|
|
267
315
|
<path d="M10 10L14.5 14.5" />
|
|
268
316
|
</svg>
|
|
@@ -270,38 +318,51 @@ function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
|
270
318
|
<input
|
|
271
319
|
bind:this={inputEl}
|
|
272
320
|
type="text"
|
|
273
|
-
class="
|
|
321
|
+
class="sx-command-input"
|
|
322
|
+
role="combobox"
|
|
274
323
|
{placeholder}
|
|
275
324
|
bind:value={query}
|
|
276
325
|
oninput={handleInput}
|
|
277
326
|
aria-label="Search commands"
|
|
278
|
-
aria-
|
|
279
|
-
aria-
|
|
327
|
+
aria-expanded={filteredCommands.length > 0}
|
|
328
|
+
aria-haspopup="listbox"
|
|
329
|
+
aria-controls={resultsId}
|
|
330
|
+
aria-activedescendant={filteredCommands[activeIndex] ? getItemId(filteredCommands[activeIndex].id) : undefined}
|
|
331
|
+
aria-autocomplete="list"
|
|
280
332
|
autocomplete="off"
|
|
281
333
|
spellcheck="false"
|
|
282
334
|
/>
|
|
283
|
-
<
|
|
335
|
+
<kbd class="sx-command-esc" aria-hidden="true">ESC</kbd>
|
|
284
336
|
</div>
|
|
285
337
|
|
|
286
338
|
<!-- Results -->
|
|
287
|
-
<div bind:this={listEl} id=
|
|
288
|
-
{#if
|
|
289
|
-
<div class="
|
|
339
|
+
<div bind:this={listEl} id={resultsId} class="sx-command-results" role="listbox" aria-label="Commands">
|
|
340
|
+
{#if filteredCommands.length === 0}
|
|
341
|
+
<div class="sx-command-empty">
|
|
342
|
+
<span class="sx-command-empty-icon" aria-hidden="true">
|
|
343
|
+
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
|
|
344
|
+
<circle cx="11" cy="11" r="8" />
|
|
345
|
+
<path d="M21 21l-4.35-4.35" />
|
|
346
|
+
<path d="M8 11h6" />
|
|
347
|
+
</svg>
|
|
348
|
+
</span>
|
|
349
|
+
<span class="sx-command-empty-text">No commands match "<strong>{query}</strong>"</span>
|
|
350
|
+
</div>
|
|
290
351
|
{:else}
|
|
291
352
|
{#each groupedResults as group, gi}
|
|
292
353
|
{#if group.label}
|
|
293
|
-
<div class="
|
|
354
|
+
<div class="sx-command-group-label" role="presentation">{group.label}</div>
|
|
294
355
|
{/if}
|
|
295
356
|
{#each group.items as item, ii}
|
|
296
357
|
{@const globalIdx = getCumulativeIndex(gi, ii)}
|
|
297
358
|
{@const isActive = globalIdx === activeIndex}
|
|
298
359
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions a11y_click_events_have_key_events -->
|
|
299
360
|
<div
|
|
300
|
-
id=
|
|
361
|
+
id={getItemId(item.id)}
|
|
301
362
|
class={cn(
|
|
302
|
-
'
|
|
303
|
-
isActive && '
|
|
304
|
-
item.disabled && '
|
|
363
|
+
'sx-command-item',
|
|
364
|
+
isActive && 'sx-command-item-active',
|
|
365
|
+
item.disabled && 'sx-command-item-disabled'
|
|
305
366
|
)}
|
|
306
367
|
role="option"
|
|
307
368
|
tabindex="-1"
|
|
@@ -310,19 +371,30 @@ function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
|
310
371
|
data-active={isActive}
|
|
311
372
|
onmouseenter={() => { activeIndex = globalIdx; }}
|
|
312
373
|
onclick={() => executeItem(item)}
|
|
313
|
-
onkeydown={handleKeydown}
|
|
314
374
|
>
|
|
315
375
|
{#if item.icon}
|
|
316
|
-
<span class="
|
|
376
|
+
<span class="sx-command-item-icon" aria-hidden="true">{@render item.icon()}</span>
|
|
317
377
|
{/if}
|
|
318
|
-
<div class="
|
|
319
|
-
<span class="
|
|
378
|
+
<div class="sx-command-item-content">
|
|
379
|
+
<span class="sx-command-item-label">
|
|
380
|
+
{#if query && item.matchIndices.length > 0}
|
|
381
|
+
{#each item.label.split('') as char, ci}
|
|
382
|
+
{#if item.matchIndices.includes(ci)}
|
|
383
|
+
<mark class="sx-command-match">{char}</mark>
|
|
384
|
+
{:else}
|
|
385
|
+
{char}
|
|
386
|
+
{/if}
|
|
387
|
+
{/each}
|
|
388
|
+
{:else}
|
|
389
|
+
{item.label}
|
|
390
|
+
{/if}
|
|
391
|
+
</span>
|
|
320
392
|
{#if item.description}
|
|
321
|
-
<span class="
|
|
393
|
+
<span class="sx-command-item-desc">{item.description}</span>
|
|
322
394
|
{/if}
|
|
323
395
|
</div>
|
|
324
396
|
{#if item.shortcut}
|
|
325
|
-
<
|
|
397
|
+
<kbd class="sx-command-item-shortcut">{item.shortcut}</kbd>
|
|
326
398
|
{/if}
|
|
327
399
|
</div>
|
|
328
400
|
{/each}
|
|
@@ -330,11 +402,21 @@ function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
|
330
402
|
{/if}
|
|
331
403
|
</div>
|
|
332
404
|
|
|
333
|
-
<!--
|
|
334
|
-
<div class="
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
405
|
+
<!-- Live region for screen readers -->
|
|
406
|
+
<div class="sx-sr-only" aria-live="polite" aria-atomic="true">
|
|
407
|
+
{#if query}
|
|
408
|
+
{filteredCommands.length} {filteredCommands.length === 1 ? 'result' : 'results'} available
|
|
409
|
+
{/if}
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<!-- Footer hints -->
|
|
413
|
+
<div class="sx-command-footer">
|
|
414
|
+
<span class="sx-command-footer-hint"><kbd>↑↓</kbd> navigate</span>
|
|
415
|
+
<span class="sx-command-footer-hint"><kbd>↵</kbd> select</span>
|
|
416
|
+
<span class="sx-command-footer-hint"><kbd>esc</kbd> close</span>
|
|
417
|
+
{#if filteredCommands.length > 0}
|
|
418
|
+
<span class="sx-command-footer-count">{filteredCommands.length} {filteredCommands.length === 1 ? 'command' : 'commands'}</span>
|
|
419
|
+
{/if}
|
|
338
420
|
</div>
|
|
339
421
|
</div>
|
|
340
422
|
</div>
|
|
@@ -342,232 +424,379 @@ function getCumulativeIndex(groupIdx: number, itemIdx: number): number {
|
|
|
342
424
|
|
|
343
425
|
<style>
|
|
344
426
|
/* ========================================
|
|
345
|
-
BACKDROP
|
|
427
|
+
BACKDROP — Glass blur with entrance/exit
|
|
346
428
|
======================================== */
|
|
347
|
-
.
|
|
429
|
+
.sx-command-backdrop {
|
|
348
430
|
position: fixed;
|
|
349
431
|
inset: 0;
|
|
350
|
-
z-index: var(--
|
|
351
|
-
background:
|
|
432
|
+
z-index: var(--sx-z-modal-backdrop);
|
|
433
|
+
background: var(--sx-color-backdrop);
|
|
434
|
+
backdrop-filter: blur(8px);
|
|
435
|
+
-webkit-backdrop-filter: blur(8px);
|
|
436
|
+
animation: sx-cmd-backdrop-in 150ms ease-out both;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.sx-command-backdrop-exit {
|
|
440
|
+
animation: sx-cmd-backdrop-out 120ms ease-in both;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
@keyframes sx-cmd-backdrop-in {
|
|
444
|
+
from { opacity: 0; }
|
|
445
|
+
to { opacity: 1; }
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
@keyframes sx-cmd-backdrop-out {
|
|
449
|
+
from { opacity: 1; }
|
|
450
|
+
to { opacity: 0; }
|
|
352
451
|
}
|
|
353
452
|
|
|
354
453
|
/* ========================================
|
|
355
|
-
PANEL
|
|
454
|
+
PANEL — Elevated surface with entrance/exit
|
|
356
455
|
======================================== */
|
|
357
|
-
.
|
|
456
|
+
.sx-command {
|
|
358
457
|
position: fixed;
|
|
359
|
-
top: 20
|
|
458
|
+
top: min(20%, 160px);
|
|
360
459
|
left: 50%;
|
|
361
460
|
transform: translateX(-50%);
|
|
362
|
-
z-index: var(--
|
|
363
|
-
width: min(
|
|
364
|
-
max-height:
|
|
461
|
+
z-index: var(--sx-z-modal);
|
|
462
|
+
width: min(600px, calc(100vw - 32px));
|
|
463
|
+
max-height: min(460px, calc(100vh - 120px));
|
|
365
464
|
display: flex;
|
|
366
465
|
flex-direction: column;
|
|
367
|
-
background:
|
|
368
|
-
border:
|
|
466
|
+
background: var(--sx-color-surface);
|
|
467
|
+
border: 1px solid var(--sx-color-border-strong);
|
|
468
|
+
border-radius: var(--sx-radius-lg);
|
|
369
469
|
box-shadow:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
font-family: var(--
|
|
470
|
+
var(--sx-shadow-xl),
|
|
471
|
+
0 0 0 1px rgba(255, 255, 255, 0.04),
|
|
472
|
+
0 0 60px rgba(0, 0, 0, 0.5);
|
|
473
|
+
font-family: var(--sx-font-body);
|
|
374
474
|
overflow: hidden;
|
|
475
|
+
animation: sx-cmd-panel-in 200ms var(--sx-ease-out) both;
|
|
375
476
|
}
|
|
376
477
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
478
|
+
.sx-command-exit {
|
|
479
|
+
animation: sx-cmd-panel-out 120ms ease-in both;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
@keyframes sx-cmd-panel-in {
|
|
483
|
+
from {
|
|
484
|
+
opacity: 0;
|
|
485
|
+
transform: translateX(-50%) translateY(-8px) scale(0.98);
|
|
486
|
+
}
|
|
487
|
+
to {
|
|
488
|
+
opacity: 1;
|
|
489
|
+
transform: translateX(-50%) translateY(0) scale(1);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
@keyframes sx-cmd-panel-out {
|
|
494
|
+
from {
|
|
495
|
+
opacity: 1;
|
|
496
|
+
transform: translateX(-50%) translateY(0) scale(1);
|
|
497
|
+
}
|
|
498
|
+
to {
|
|
499
|
+
opacity: 0;
|
|
500
|
+
transform: translateX(-50%) translateY(4px) scale(0.98);
|
|
501
|
+
}
|
|
382
502
|
}
|
|
383
503
|
|
|
384
504
|
/* ========================================
|
|
385
|
-
SEARCH FIELD
|
|
505
|
+
SEARCH FIELD
|
|
386
506
|
======================================== */
|
|
387
|
-
.
|
|
507
|
+
.sx-command-search {
|
|
388
508
|
display: flex;
|
|
389
509
|
align-items: center;
|
|
390
|
-
gap: var(--
|
|
391
|
-
padding: var(--
|
|
392
|
-
border-bottom: 1px solid
|
|
510
|
+
gap: var(--sx-space-3);
|
|
511
|
+
padding: var(--sx-space-4) var(--sx-space-5);
|
|
512
|
+
border-bottom: 1px solid var(--sx-color-border);
|
|
393
513
|
}
|
|
394
514
|
|
|
395
|
-
.
|
|
515
|
+
.sx-command-search-icon {
|
|
396
516
|
flex-shrink: 0;
|
|
397
517
|
display: flex;
|
|
398
|
-
color:
|
|
518
|
+
color: var(--sx-color-cyan);
|
|
519
|
+
opacity: 0.7;
|
|
399
520
|
}
|
|
400
521
|
|
|
401
|
-
.
|
|
522
|
+
.sx-command-input {
|
|
402
523
|
flex: 1;
|
|
524
|
+
min-width: 0;
|
|
403
525
|
border: none;
|
|
404
526
|
background: transparent;
|
|
405
527
|
outline: none;
|
|
406
|
-
font-family: var(--
|
|
407
|
-
font-size: var(--
|
|
408
|
-
font-weight:
|
|
409
|
-
color:
|
|
528
|
+
font-family: var(--sx-font-body);
|
|
529
|
+
font-size: var(--sx-text-base);
|
|
530
|
+
font-weight: 400;
|
|
531
|
+
color: var(--sx-color-text);
|
|
532
|
+
padding: 0;
|
|
533
|
+
line-height: 1.5;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.sx-command-input:focus-visible {
|
|
537
|
+
box-shadow: none;
|
|
538
|
+
outline: none;
|
|
410
539
|
}
|
|
411
540
|
|
|
412
|
-
.
|
|
413
|
-
color:
|
|
541
|
+
.sx-command-input::placeholder {
|
|
542
|
+
color: var(--sx-color-text-disabled);
|
|
414
543
|
}
|
|
415
544
|
|
|
416
|
-
.
|
|
545
|
+
.sx-command-esc {
|
|
417
546
|
flex-shrink: 0;
|
|
418
|
-
font-family: var(--
|
|
419
|
-
font-size: var(--
|
|
420
|
-
font-weight:
|
|
421
|
-
padding:
|
|
422
|
-
border:
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
inset -1px -1px 0 rgb(var(--salmex-button-shadow));
|
|
547
|
+
font-family: var(--sx-font-mono);
|
|
548
|
+
font-size: var(--sx-text-xs);
|
|
549
|
+
font-weight: 600;
|
|
550
|
+
padding: var(--sx-space-0-5) var(--sx-space-2);
|
|
551
|
+
border: 1px solid var(--sx-color-border-strong);
|
|
552
|
+
border-radius: var(--sx-radius-sm);
|
|
553
|
+
background: var(--sx-color-surface-2);
|
|
554
|
+
color: var(--sx-color-text-disabled);
|
|
555
|
+
line-height: 1.4;
|
|
428
556
|
}
|
|
429
557
|
|
|
430
558
|
/* ========================================
|
|
431
559
|
RESULTS LIST
|
|
432
560
|
======================================== */
|
|
433
|
-
.
|
|
561
|
+
.sx-command-results {
|
|
434
562
|
flex: 1;
|
|
435
563
|
overflow-y: auto;
|
|
436
|
-
|
|
437
|
-
|
|
564
|
+
overscroll-behavior: contain;
|
|
565
|
+
padding: var(--sx-space-2) 0;
|
|
566
|
+
max-height: 320px;
|
|
567
|
+
scroll-behavior: smooth;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.sx-command-results::-webkit-scrollbar {
|
|
571
|
+
width: 4px;
|
|
438
572
|
}
|
|
439
573
|
|
|
440
|
-
.
|
|
441
|
-
|
|
574
|
+
.sx-command-results::-webkit-scrollbar-track {
|
|
575
|
+
background: transparent;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.sx-command-results::-webkit-scrollbar-thumb {
|
|
579
|
+
background: var(--sx-color-border-strong);
|
|
580
|
+
border-radius: 2px;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.sx-command-empty {
|
|
584
|
+
display: flex;
|
|
585
|
+
flex-direction: column;
|
|
586
|
+
align-items: center;
|
|
587
|
+
gap: var(--sx-space-3);
|
|
588
|
+
padding: var(--sx-space-8) var(--sx-space-5);
|
|
442
589
|
text-align: center;
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.sx-command-empty-icon {
|
|
593
|
+
color: var(--sx-color-text-disabled);
|
|
594
|
+
opacity: 0.5;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.sx-command-empty-text {
|
|
598
|
+
font-size: var(--sx-text-sm);
|
|
599
|
+
color: var(--sx-color-text-disabled);
|
|
600
|
+
font-weight: 400;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.sx-command-empty-text strong {
|
|
604
|
+
color: var(--sx-color-text-secondary);
|
|
605
|
+
font-weight: 500;
|
|
446
606
|
}
|
|
447
607
|
|
|
448
608
|
/* ========================================
|
|
449
609
|
GROUP LABEL
|
|
450
610
|
======================================== */
|
|
451
|
-
.
|
|
452
|
-
padding: var(--
|
|
453
|
-
margin-top: var(--
|
|
454
|
-
font-
|
|
455
|
-
font-size: 10px;
|
|
611
|
+
.sx-command-group-label {
|
|
612
|
+
padding: var(--sx-space-3) var(--sx-space-5) var(--sx-space-1);
|
|
613
|
+
margin-top: var(--sx-space-1);
|
|
614
|
+
font-size: var(--sx-text-xs);
|
|
456
615
|
font-weight: 600;
|
|
616
|
+
letter-spacing: var(--sx-tracking-wider);
|
|
457
617
|
text-transform: uppercase;
|
|
458
|
-
|
|
459
|
-
color: rgb(var(--salmex-text-disabled));
|
|
460
|
-
border-top: 1px solid rgb(var(--salmex-border-light));
|
|
618
|
+
color: var(--sx-color-text-disabled);
|
|
461
619
|
user-select: none;
|
|
462
620
|
}
|
|
463
621
|
|
|
464
|
-
.
|
|
465
|
-
border-top: none;
|
|
622
|
+
.sx-command-group-label:first-child {
|
|
466
623
|
margin-top: 0;
|
|
467
624
|
}
|
|
468
625
|
|
|
469
626
|
/* ========================================
|
|
470
627
|
COMMAND ITEM
|
|
471
628
|
======================================== */
|
|
472
|
-
.
|
|
629
|
+
.sx-command-item {
|
|
473
630
|
display: flex;
|
|
474
631
|
align-items: center;
|
|
475
|
-
gap: var(--
|
|
476
|
-
padding: var(--
|
|
632
|
+
gap: var(--sx-space-3);
|
|
633
|
+
padding: var(--sx-space-2-5) var(--sx-space-5);
|
|
477
634
|
cursor: pointer;
|
|
478
635
|
user-select: none;
|
|
479
|
-
|
|
636
|
+
color: var(--sx-color-text-secondary);
|
|
637
|
+
border-left: 2px solid transparent;
|
|
638
|
+
transition: background 100ms ease, color 100ms ease, border-color 100ms ease;
|
|
480
639
|
}
|
|
481
640
|
|
|
482
|
-
.
|
|
483
|
-
background:
|
|
484
|
-
color:
|
|
641
|
+
.sx-command-item-active {
|
|
642
|
+
background: var(--sx-color-cyan-hover);
|
|
643
|
+
color: var(--sx-color-text);
|
|
644
|
+
border-left-color: var(--sx-color-cyan);
|
|
485
645
|
}
|
|
486
646
|
|
|
487
|
-
:
|
|
488
|
-
background:
|
|
489
|
-
color: rgb(var(--salmex-midnight-black));
|
|
647
|
+
.sx-command-item:active:not(.sx-command-item-disabled) {
|
|
648
|
+
background: var(--sx-color-cyan-active);
|
|
490
649
|
}
|
|
491
650
|
|
|
492
|
-
.
|
|
651
|
+
.sx-command-item-disabled {
|
|
493
652
|
opacity: 0.4;
|
|
494
653
|
cursor: not-allowed;
|
|
495
654
|
}
|
|
496
655
|
|
|
497
|
-
.
|
|
656
|
+
.sx-command-item-icon {
|
|
498
657
|
flex-shrink: 0;
|
|
499
658
|
width: 20px;
|
|
500
|
-
|
|
501
|
-
|
|
659
|
+
height: 20px;
|
|
660
|
+
display: flex;
|
|
661
|
+
align-items: center;
|
|
662
|
+
justify-content: center;
|
|
663
|
+
color: var(--sx-color-text-secondary);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.sx-command-item-active .sx-command-item-icon {
|
|
667
|
+
color: var(--sx-color-cyan);
|
|
502
668
|
}
|
|
503
669
|
|
|
504
|
-
.
|
|
670
|
+
.sx-command-item-content {
|
|
505
671
|
flex: 1;
|
|
506
672
|
min-width: 0;
|
|
507
673
|
display: flex;
|
|
508
674
|
flex-direction: column;
|
|
675
|
+
gap: 1px;
|
|
509
676
|
}
|
|
510
677
|
|
|
511
|
-
.
|
|
512
|
-
font-size: var(--
|
|
513
|
-
font-weight:
|
|
678
|
+
.sx-command-item-label {
|
|
679
|
+
font-size: var(--sx-text-sm);
|
|
680
|
+
font-weight: 500;
|
|
514
681
|
overflow: hidden;
|
|
515
682
|
text-overflow: ellipsis;
|
|
516
683
|
white-space: nowrap;
|
|
684
|
+
line-height: 1.4;
|
|
517
685
|
}
|
|
518
686
|
|
|
519
|
-
.
|
|
520
|
-
font-
|
|
521
|
-
|
|
687
|
+
.sx-command-item-active .sx-command-item-label {
|
|
688
|
+
font-weight: 600;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* Match highlighting */
|
|
692
|
+
.sx-command-match {
|
|
693
|
+
background: none;
|
|
694
|
+
color: var(--sx-color-cyan);
|
|
695
|
+
font-weight: 700;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.sx-command-item-active .sx-command-match {
|
|
699
|
+
color: var(--sx-color-cyan);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.sx-command-item-desc {
|
|
703
|
+
font-size: var(--sx-text-xs);
|
|
704
|
+
color: var(--sx-color-text-disabled);
|
|
522
705
|
overflow: hidden;
|
|
523
706
|
text-overflow: ellipsis;
|
|
524
707
|
white-space: nowrap;
|
|
708
|
+
line-height: 1.3;
|
|
525
709
|
}
|
|
526
710
|
|
|
527
|
-
.
|
|
528
|
-
|
|
711
|
+
.sx-command-item-active .sx-command-item-desc {
|
|
712
|
+
color: var(--sx-color-text-secondary);
|
|
529
713
|
}
|
|
530
714
|
|
|
531
|
-
.
|
|
715
|
+
.sx-command-item-shortcut {
|
|
532
716
|
flex-shrink: 0;
|
|
533
|
-
font-family: var(--
|
|
534
|
-
font-size: var(--
|
|
535
|
-
font-weight:
|
|
536
|
-
|
|
717
|
+
font-family: var(--sx-font-mono);
|
|
718
|
+
font-size: var(--sx-text-xs);
|
|
719
|
+
font-weight: 500;
|
|
720
|
+
padding: var(--sx-space-0-5) var(--sx-space-2);
|
|
721
|
+
background: var(--sx-color-surface-2);
|
|
722
|
+
border: 1px solid var(--sx-color-border);
|
|
723
|
+
border-radius: var(--sx-radius-sm);
|
|
724
|
+
color: var(--sx-color-text-disabled);
|
|
725
|
+
line-height: 1.3;
|
|
537
726
|
}
|
|
538
727
|
|
|
539
|
-
.
|
|
540
|
-
|
|
728
|
+
.sx-command-item-active .sx-command-item-shortcut {
|
|
729
|
+
background: var(--sx-color-surface-3);
|
|
730
|
+
border-color: var(--sx-color-border-strong);
|
|
731
|
+
color: var(--sx-color-text-secondary);
|
|
541
732
|
}
|
|
542
733
|
|
|
543
734
|
/* ========================================
|
|
544
735
|
FOOTER
|
|
545
736
|
======================================== */
|
|
546
|
-
.
|
|
737
|
+
.sx-command-footer {
|
|
547
738
|
display: flex;
|
|
548
739
|
align-items: center;
|
|
549
|
-
gap: var(--
|
|
550
|
-
padding: var(--
|
|
551
|
-
border-top: 1px solid
|
|
552
|
-
font-size: var(--
|
|
553
|
-
color:
|
|
554
|
-
background:
|
|
740
|
+
gap: var(--sx-space-5);
|
|
741
|
+
padding: var(--sx-space-2-5) var(--sx-space-5);
|
|
742
|
+
border-top: 1px solid var(--sx-color-border);
|
|
743
|
+
font-size: var(--sx-text-xs);
|
|
744
|
+
color: var(--sx-color-text-disabled);
|
|
745
|
+
background: var(--sx-color-surface-2);
|
|
746
|
+
border-radius: 0 0 var(--sx-radius-lg) var(--sx-radius-lg);
|
|
555
747
|
}
|
|
556
748
|
|
|
557
|
-
.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
749
|
+
.sx-command-footer-hint {
|
|
750
|
+
display: flex;
|
|
751
|
+
align-items: center;
|
|
752
|
+
gap: var(--sx-space-1);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.sx-command-footer kbd {
|
|
756
|
+
font-family: var(--sx-font-mono);
|
|
757
|
+
font-weight: 600;
|
|
758
|
+
padding: 1px var(--sx-space-1);
|
|
759
|
+
border-radius: var(--sx-radius-sm);
|
|
760
|
+
background: var(--sx-color-surface-3);
|
|
761
|
+
border: 1px solid var(--sx-color-border);
|
|
563
762
|
font-size: 0.9em;
|
|
763
|
+
color: var(--sx-color-text-disabled);
|
|
764
|
+
line-height: 1.3;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.sx-command-footer-count {
|
|
768
|
+
margin-left: auto;
|
|
769
|
+
font-family: var(--sx-font-mono);
|
|
770
|
+
font-weight: 500;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* ========================================
|
|
774
|
+
SCREEN READER ONLY
|
|
775
|
+
======================================== */
|
|
776
|
+
.sx-sr-only {
|
|
777
|
+
position: absolute;
|
|
778
|
+
width: 1px;
|
|
779
|
+
height: 1px;
|
|
780
|
+
padding: 0;
|
|
781
|
+
margin: -1px;
|
|
782
|
+
overflow: hidden;
|
|
783
|
+
clip: rect(0, 0, 0, 0);
|
|
784
|
+
white-space: nowrap;
|
|
785
|
+
border: 0;
|
|
564
786
|
}
|
|
565
787
|
|
|
566
788
|
/* ========================================
|
|
567
789
|
REDUCED MOTION
|
|
568
790
|
======================================== */
|
|
569
791
|
@media (prefers-reduced-motion: reduce) {
|
|
570
|
-
.
|
|
792
|
+
.sx-command,
|
|
793
|
+
.sx-command-exit,
|
|
794
|
+
.sx-command-backdrop,
|
|
795
|
+
.sx-command-backdrop-exit {
|
|
796
|
+
animation: none;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.sx-command-item {
|
|
571
800
|
transition: none;
|
|
572
801
|
}
|
|
573
802
|
}
|