@salmexio/ui 0.4.0 → 1.1.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 +97 -94
- 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 -116
- package/dist/dialogs/Modal/Modal.svelte.d.ts +1 -1
- package/dist/feedback/Alert/Alert.svelte +119 -220
- package/dist/feedback/Alert/Alert.svelte.d.ts +1 -1
- package/dist/feedback/ProgressBar/ProgressBar.svelte +265 -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 +157 -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 +86 -151
- 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 +87 -104
- package/dist/forms/Checkbox/Checkbox.svelte.d.ts +1 -1
- package/dist/forms/Select/Select.svelte +137 -179
- 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 +161 -167
- 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 +66 -39
- 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/layout/ThermalBackground/ThermalBackground.svelte +313 -0
- package/dist/layout/ThermalBackground/ThermalBackground.svelte.d.ts +16 -0
- package/dist/layout/ThermalBackground/ThermalBackground.svelte.d.ts.map +1 -0
- package/dist/layout/ThermalBackground/index.d.ts +2 -0
- package/dist/layout/ThermalBackground/index.d.ts.map +1 -0
- package/dist/layout/ThermalBackground/index.js +1 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +1 -0
- package/dist/navigation/CommandPalette/CommandPalette.svelte +407 -189
- 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 +139 -192
- package/dist/navigation/Tabs/Tabs.svelte.d.ts +2 -2
- package/dist/primitives/Badge/Badge.svelte +85 -220
- 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 +214 -194
- 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 +329 -260
- package/package.json +5 -5
- package/dist/windowing/Window/Window.svelte +0 -637
- 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 -425
- 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
|
+
INFRARED — 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,243 +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:
|
|
369
|
-
border-radius: var(--
|
|
466
|
+
background: var(--sx-color-surface);
|
|
467
|
+
border: 1px solid var(--sx-color-border-strong);
|
|
468
|
+
border-radius: var(--sx-radius-lg);
|
|
370
469
|
box-shadow:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
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);
|
|
375
474
|
overflow: hidden;
|
|
475
|
+
animation: sx-cmd-panel-in 200ms var(--sx-ease-out) both;
|
|
376
476
|
}
|
|
377
477
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
+
}
|
|
383
502
|
}
|
|
384
503
|
|
|
385
504
|
/* ========================================
|
|
386
|
-
SEARCH FIELD
|
|
505
|
+
SEARCH FIELD
|
|
387
506
|
======================================== */
|
|
388
|
-
.
|
|
507
|
+
.sx-command-search {
|
|
389
508
|
display: flex;
|
|
390
509
|
align-items: center;
|
|
391
|
-
gap: var(--
|
|
392
|
-
padding: var(--
|
|
393
|
-
border-bottom: 1px solid
|
|
394
|
-
background: rgb(var(--salmex-button-face));
|
|
395
|
-
box-shadow:
|
|
396
|
-
inset 1px 1px 0 rgb(var(--salmex-button-shadow)),
|
|
397
|
-
inset -1px -1px 0 rgb(var(--salmex-button-highlight));
|
|
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);
|
|
398
513
|
}
|
|
399
514
|
|
|
400
|
-
.
|
|
515
|
+
.sx-command-search-icon {
|
|
401
516
|
flex-shrink: 0;
|
|
402
517
|
display: flex;
|
|
403
|
-
color:
|
|
518
|
+
color: var(--sx-color-primary);
|
|
519
|
+
opacity: 0.7;
|
|
404
520
|
}
|
|
405
521
|
|
|
406
|
-
.
|
|
522
|
+
.sx-command-input {
|
|
407
523
|
flex: 1;
|
|
408
524
|
min-width: 0;
|
|
409
525
|
border: none;
|
|
410
526
|
background: transparent;
|
|
411
527
|
outline: none;
|
|
412
|
-
font-family: var(--
|
|
413
|
-
font-size: var(--
|
|
414
|
-
|
|
415
|
-
|
|
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;
|
|
416
534
|
}
|
|
417
535
|
|
|
418
|
-
.
|
|
536
|
+
.sx-command-input:focus-visible {
|
|
419
537
|
box-shadow: none;
|
|
538
|
+
outline: none;
|
|
420
539
|
}
|
|
421
540
|
|
|
422
|
-
.
|
|
423
|
-
color:
|
|
541
|
+
.sx-command-input::placeholder {
|
|
542
|
+
color: var(--sx-color-text-disabled);
|
|
424
543
|
}
|
|
425
544
|
|
|
426
|
-
.
|
|
545
|
+
.sx-command-esc {
|
|
427
546
|
flex-shrink: 0;
|
|
428
|
-
font-family: var(--
|
|
429
|
-
font-size: var(--
|
|
430
|
-
font-weight:
|
|
431
|
-
padding:
|
|
432
|
-
border: 1px solid
|
|
433
|
-
border-radius: var(--
|
|
434
|
-
background:
|
|
435
|
-
color:
|
|
436
|
-
|
|
437
|
-
inset 1px 1px 0 rgb(var(--salmex-button-highlight)),
|
|
438
|
-
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;
|
|
439
556
|
}
|
|
440
557
|
|
|
441
558
|
/* ========================================
|
|
442
559
|
RESULTS LIST
|
|
443
560
|
======================================== */
|
|
444
|
-
.
|
|
561
|
+
.sx-command-results {
|
|
445
562
|
flex: 1;
|
|
446
563
|
overflow-y: auto;
|
|
447
|
-
|
|
448
|
-
|
|
564
|
+
overscroll-behavior: contain;
|
|
565
|
+
padding: var(--sx-space-2) 0;
|
|
566
|
+
max-height: 320px;
|
|
567
|
+
scroll-behavior: smooth;
|
|
449
568
|
}
|
|
450
569
|
|
|
451
|
-
.
|
|
452
|
-
|
|
570
|
+
.sx-command-results::-webkit-scrollbar {
|
|
571
|
+
width: 4px;
|
|
572
|
+
}
|
|
573
|
+
|
|
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);
|
|
453
589
|
text-align: center;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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;
|
|
457
606
|
}
|
|
458
607
|
|
|
459
608
|
/* ========================================
|
|
460
609
|
GROUP LABEL
|
|
461
610
|
======================================== */
|
|
462
|
-
.
|
|
463
|
-
padding: var(--
|
|
464
|
-
margin-top: var(--
|
|
465
|
-
font-
|
|
466
|
-
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);
|
|
467
615
|
font-weight: 600;
|
|
468
|
-
letter-spacing:
|
|
469
|
-
|
|
470
|
-
|
|
616
|
+
letter-spacing: var(--sx-tracking-wider);
|
|
617
|
+
text-transform: uppercase;
|
|
618
|
+
color: var(--sx-color-text-disabled);
|
|
471
619
|
user-select: none;
|
|
472
620
|
}
|
|
473
621
|
|
|
474
|
-
.
|
|
475
|
-
border-top: none;
|
|
622
|
+
.sx-command-group-label:first-child {
|
|
476
623
|
margin-top: 0;
|
|
477
624
|
}
|
|
478
625
|
|
|
479
626
|
/* ========================================
|
|
480
627
|
COMMAND ITEM
|
|
481
628
|
======================================== */
|
|
482
|
-
.
|
|
629
|
+
.sx-command-item {
|
|
483
630
|
display: flex;
|
|
484
631
|
align-items: center;
|
|
485
|
-
gap: var(--
|
|
486
|
-
padding: var(--
|
|
632
|
+
gap: var(--sx-space-3);
|
|
633
|
+
padding: var(--sx-space-2-5) var(--sx-space-5);
|
|
487
634
|
cursor: pointer;
|
|
488
635
|
user-select: none;
|
|
489
|
-
|
|
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;
|
|
490
639
|
}
|
|
491
640
|
|
|
492
|
-
.
|
|
493
|
-
background:
|
|
494
|
-
color:
|
|
641
|
+
.sx-command-item-active {
|
|
642
|
+
background: var(--sx-color-primary-hover);
|
|
643
|
+
color: var(--sx-color-text);
|
|
644
|
+
border-left-color: var(--sx-color-primary);
|
|
495
645
|
}
|
|
496
646
|
|
|
497
|
-
:
|
|
498
|
-
background:
|
|
499
|
-
color: rgb(var(--salmex-midnight-black));
|
|
647
|
+
.sx-command-item:active:not(.sx-command-item-disabled) {
|
|
648
|
+
background: var(--sx-color-primary-active);
|
|
500
649
|
}
|
|
501
650
|
|
|
502
|
-
.
|
|
651
|
+
.sx-command-item-disabled {
|
|
503
652
|
opacity: 0.4;
|
|
504
653
|
cursor: not-allowed;
|
|
505
654
|
}
|
|
506
655
|
|
|
507
|
-
.
|
|
656
|
+
.sx-command-item-icon {
|
|
508
657
|
flex-shrink: 0;
|
|
509
658
|
width: 20px;
|
|
510
|
-
|
|
511
|
-
|
|
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-primary);
|
|
512
668
|
}
|
|
513
669
|
|
|
514
|
-
.
|
|
670
|
+
.sx-command-item-content {
|
|
515
671
|
flex: 1;
|
|
516
672
|
min-width: 0;
|
|
517
673
|
display: flex;
|
|
518
674
|
flex-direction: column;
|
|
675
|
+
gap: 1px;
|
|
519
676
|
}
|
|
520
677
|
|
|
521
|
-
.
|
|
522
|
-
font-size: var(--
|
|
523
|
-
font-weight:
|
|
678
|
+
.sx-command-item-label {
|
|
679
|
+
font-size: var(--sx-text-sm);
|
|
680
|
+
font-weight: 500;
|
|
524
681
|
overflow: hidden;
|
|
525
682
|
text-overflow: ellipsis;
|
|
526
683
|
white-space: nowrap;
|
|
684
|
+
line-height: 1.4;
|
|
527
685
|
}
|
|
528
686
|
|
|
529
|
-
.
|
|
530
|
-
font-
|
|
531
|
-
|
|
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-primary);
|
|
695
|
+
font-weight: 700;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.sx-command-item-active .sx-command-match {
|
|
699
|
+
color: var(--sx-color-primary);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.sx-command-item-desc {
|
|
703
|
+
font-size: var(--sx-text-xs);
|
|
704
|
+
color: var(--sx-color-text-disabled);
|
|
532
705
|
overflow: hidden;
|
|
533
706
|
text-overflow: ellipsis;
|
|
534
707
|
white-space: nowrap;
|
|
708
|
+
line-height: 1.3;
|
|
535
709
|
}
|
|
536
710
|
|
|
537
|
-
.
|
|
538
|
-
|
|
711
|
+
.sx-command-item-active .sx-command-item-desc {
|
|
712
|
+
color: var(--sx-color-text-secondary);
|
|
539
713
|
}
|
|
540
714
|
|
|
541
|
-
.
|
|
715
|
+
.sx-command-item-shortcut {
|
|
542
716
|
flex-shrink: 0;
|
|
543
|
-
font-family: var(--
|
|
544
|
-
font-size: var(--
|
|
545
|
-
font-weight:
|
|
546
|
-
|
|
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;
|
|
547
726
|
}
|
|
548
727
|
|
|
549
|
-
.
|
|
550
|
-
|
|
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);
|
|
551
732
|
}
|
|
552
733
|
|
|
553
734
|
/* ========================================
|
|
554
735
|
FOOTER
|
|
555
736
|
======================================== */
|
|
556
|
-
.
|
|
737
|
+
.sx-command-footer {
|
|
557
738
|
display: flex;
|
|
558
739
|
align-items: center;
|
|
559
|
-
gap: var(--
|
|
560
|
-
padding: var(--
|
|
561
|
-
border-top: 1px solid
|
|
562
|
-
font-size: var(--
|
|
563
|
-
color:
|
|
564
|
-
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);
|
|
565
747
|
}
|
|
566
748
|
|
|
567
|
-
.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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);
|
|
574
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;
|
|
575
786
|
}
|
|
576
787
|
|
|
577
788
|
/* ========================================
|
|
578
789
|
REDUCED MOTION
|
|
579
790
|
======================================== */
|
|
580
791
|
@media (prefers-reduced-motion: reduce) {
|
|
581
|
-
.
|
|
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 {
|
|
582
800
|
transition: none;
|
|
583
801
|
}
|
|
584
802
|
}
|