diff --git a/.changeset/web-confirm-dialog-enter-key.md b/.changeset/web-confirm-dialog-enter-key.md new file mode 100644 index 000000000..c74cc4e05 --- /dev/null +++ b/.changeset/web-confirm-dialog-enter-key.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +web: Press Enter to confirm in archive and other confirmation dialogs. diff --git a/apps/kimi-web/src/components/dialogs/ConfirmDialog.vue b/apps/kimi-web/src/components/dialogs/ConfirmDialog.vue index 803031631..a4ac057d1 100644 --- a/apps/kimi-web/src/components/dialogs/ConfirmDialog.vue +++ b/apps/kimi-web/src/components/dialogs/ConfirmDialog.vue @@ -3,11 +3,19 @@ Dialog (height auto, right-aligned footer). The single confirmation surface for user actions — driven app-wide by useConfirmDialog(). --> diff --git a/apps/kimi-web/src/components/ui/Dialog.vue b/apps/kimi-web/src/components/ui/Dialog.vue index 6624a4f92..a11153bce 100644 --- a/apps/kimi-web/src/components/ui/Dialog.vue +++ b/apps/kimi-web/src/components/ui/Dialog.vue @@ -22,6 +22,9 @@ const props = withDefaults(defineProps<{ /** When false, the body has no padding so the consumer controls layout * (e.g. a full-bleed side-nav). */ padded?: boolean; + /** Element (or selector / resolver) to receive focus when the dialog opens. + * Falls back to the first focusable element, then the dialog panel. */ + initialFocus?: HTMLElement | string | (() => HTMLElement | null | undefined); }>(), { closeOnOverlay: true, closeOnEsc: true, @@ -50,6 +53,18 @@ function focusables(): HTMLElement[] { return panel.value ? Array.from(panel.value.querySelectorAll(FOCUSABLE)) : []; } +function resolveInitialFocus(): HTMLElement | null { + const { initialFocus } = props; + if (!initialFocus) return null; + if (typeof initialFocus === 'function') { + return initialFocus() ?? null; + } + if (typeof initialFocus === 'string') { + return panel.value?.querySelector(initialFocus) ?? null; + } + return panel.value?.contains(initialFocus) ? initialFocus : null; +} + function onKeydown(event: KeyboardEvent) { if (!props.open) return; if (event.key === 'Escape' && props.closeOnEsc) { @@ -87,8 +102,9 @@ watch( openDialogCount.value += 1; previouslyFocused = document.activeElement; await nextTick(); + const initial = resolveInitialFocus(); const list = focusables(); - (list[0] ?? panel.value)?.focus(); + (initial ?? list[0] ?? panel.value)?.focus(); } else { openDialogCount.value = Math.max(0, openDialogCount.value - 1); if (previouslyFocused instanceof HTMLElement) {