Collapse the terminal dock from Escape anywhere in the panel.
Drop unused suggestion state and copy, and give the panel a reduced-motion-aware open transition. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,11 +11,9 @@ export interface SignatureCopy {
|
||||
readonly inputLabel: string;
|
||||
readonly inputPlaceholder: string;
|
||||
readonly collapseLabel: string;
|
||||
readonly expandLabel: string;
|
||||
readonly restoreLabel: string;
|
||||
readonly maximizeLabel: string;
|
||||
readonly outputLabel: string;
|
||||
readonly emptySuggestions: string;
|
||||
readonly unknownCommand: string;
|
||||
readonly unknownTarget: string;
|
||||
readonly validTargetsLabel: string;
|
||||
@@ -61,11 +59,9 @@ export const SIGNATURE_COPY: Record<AppLocale, SignatureCopy> = {
|
||||
inputLabel: 'Befehl',
|
||||
inputPlaceholder: 'Befehl eingeben',
|
||||
collapseLabel: 'Terminal einklappen',
|
||||
expandLabel: 'Terminal öffnen',
|
||||
restoreLabel: 'Terminal verkleinern',
|
||||
maximizeLabel: 'Terminal vergrößern',
|
||||
outputLabel: 'Ausgabe',
|
||||
emptySuggestions: 'Keine passenden Befehle.',
|
||||
unknownCommand: 'Unbekannter Befehl: {command}',
|
||||
unknownTarget: 'Unbekanntes Ziel: {target}',
|
||||
validTargetsLabel: 'Gültige Ziele:',
|
||||
@@ -117,11 +113,9 @@ export const SIGNATURE_COPY: Record<AppLocale, SignatureCopy> = {
|
||||
inputLabel: 'Command',
|
||||
inputPlaceholder: 'Type a command',
|
||||
collapseLabel: 'Collapse the terminal',
|
||||
expandLabel: 'Open the terminal',
|
||||
restoreLabel: 'Restore the terminal',
|
||||
maximizeLabel: 'Maximise the terminal',
|
||||
outputLabel: 'Output',
|
||||
emptySuggestions: 'No matching commands.',
|
||||
unknownCommand: 'Unknown command: {command}',
|
||||
unknownTarget: 'Unknown target: {target}',
|
||||
validTargetsLabel: 'Valid targets:',
|
||||
|
||||
@@ -13,7 +13,13 @@
|
||||
</button>
|
||||
|
||||
@if (open()) {
|
||||
<section class="terminal-dock-panel" [id]="panelId" [attr.aria-label]="copy().panelLabel">
|
||||
<section
|
||||
class="terminal-dock-panel"
|
||||
[id]="panelId"
|
||||
tabindex="-1"
|
||||
[attr.aria-label]="copy().panelLabel"
|
||||
(keydown)="onPanelKeydown($event)"
|
||||
>
|
||||
<div class="terminal-dock-toolbar cluster">
|
||||
<p class="terminal-dock-title">{{ copy().panelLabel }}</p>
|
||||
<button
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-raised);
|
||||
color: var(--color-text);
|
||||
animation: terminal-dock-in var(--duration-base) var(--ease-standard);
|
||||
transition:
|
||||
width var(--duration-base) var(--ease-standard),
|
||||
max-height var(--duration-base) var(--ease-standard);
|
||||
}
|
||||
|
||||
.terminal-dock.is-open {
|
||||
@@ -130,9 +134,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes terminal-dock-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(0.4rem);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.terminal-dock,
|
||||
.terminal-dock-panel {
|
||||
animation: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,24 @@ describe('TerminalDock', () => {
|
||||
expect(document.activeElement).toBe(trigger(fixture));
|
||||
});
|
||||
|
||||
it.each(APP_LOCALES)(
|
||||
'closes on Escape from a panel control and returns focus to the trigger (%s)',
|
||||
async (locale) => {
|
||||
const fixture = await createFixture(locale);
|
||||
await openViaTrigger(fixture);
|
||||
|
||||
const maximize = fixture.nativeElement.querySelector('[aria-pressed]') as HTMLButtonElement;
|
||||
maximize.focus();
|
||||
expect(document.activeElement).toBe(maximize);
|
||||
|
||||
maximize.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
||||
await flush(fixture);
|
||||
|
||||
expect(panel(fixture)).toBeNull();
|
||||
expect(document.activeElement).toBe(trigger(fixture));
|
||||
},
|
||||
);
|
||||
|
||||
it.each(APP_LOCALES)('does not lock scroll or mark the page inert (%s)', async (locale) => {
|
||||
document.body.style.overflow = 'auto';
|
||||
const fixture = await createFixture(locale);
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
COMMAND_IDS,
|
||||
navigateTargetKeys,
|
||||
parseCommand,
|
||||
suggestCompletions,
|
||||
} from './terminal-commands';
|
||||
import { TerminalDockService } from './terminal-dock.service';
|
||||
|
||||
@@ -69,7 +68,6 @@ export class TerminalDock {
|
||||
);
|
||||
protected readonly open = computed(() => this.state() !== 'collapsed');
|
||||
protected readonly maximized = computed(() => this.state() === 'maximized');
|
||||
protected readonly suggestions = computed(() => suggestCompletions(this.query()));
|
||||
protected readonly maximizeLabel = computed(() =>
|
||||
this.maximized() ? this.copy().restoreLabel : this.copy().maximizeLabel,
|
||||
);
|
||||
@@ -121,13 +119,16 @@ export class TerminalDock {
|
||||
this.runRaw(this.query());
|
||||
}
|
||||
|
||||
protected onInputKeydown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
this.dock.collapse();
|
||||
protected onPanelKeydown(event: KeyboardEvent): void {
|
||||
if (event.key !== 'Escape') {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
this.dock.collapse();
|
||||
}
|
||||
|
||||
protected onInputKeydown(event: KeyboardEvent): void {
|
||||
if (event.key === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
this.stepHistory(-1);
|
||||
|
||||
Reference in New Issue
Block a user