Skip to content

fix(web): confirm dialogs respond to Enter and await async actions - #1744

Merged
wbxl2000 merged 2 commits into
mainfrom
fix/web-confirm-dialog-async
Jul 15, 2026
Merged

fix(web): confirm dialogs respond to Enter and await async actions#1744
wbxl2000 merged 2 commits into
mainfrom
fix/web-confirm-dialog-async

Conversation

@wbxl2000

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is described below.

Problem

In the web UI, modal confirmation dialogs (e.g. archive session) had two issues:

  1. Pressing Enter did not confirm the dialog. The initial focus was resolved from the Button component's $el, but Button has a template-root comment, so in dev builds it renders as a fragment whose $el is a text node. Focus fell back to the header close button, so Enter cancelled the dialog instead of confirming it.
  2. The confirmed action (archive session, remove workspace, delete provider) ran only after the dialog closed, with no in-dialog feedback — the dialog should stay open with a loading state until the async work settles.

What changed

  • The confirm dialog resolves its initial focus with a CSS selector on the confirm button instead of the component $el, so Enter confirms in both dev and production builds.
  • useConfirmDialog now accepts an async action: while it runs, the dialog stays open with the confirm button loading, and cancel / Esc / overlay-click are suppressed. A rejection closes the dialog and rethrows to the caller.
  • The archive-session / remove-workspace / delete-provider confirmations moved from the menu components (SessionRow, ChatHeader, Sidebar, MobileSwitcherSheet, ProviderManager) into App.vue, so the dialog can await the actual client call; those components now only emit the intent. Undo / goal / swarm confirmations keep the simpler boolean flow.
  • Added logic tests for the confirm flow (busy state, supersede, rejection, re-entry).

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

The confirm dialog's initial focus was resolved from the Button
component's $el, which is a text node in dev builds (the component has
a template-root comment, so it renders as a fragment). Focus fell back
to the header close button, so Enter cancelled instead of confirming.
Resolve the initial focus with a CSS selector on the confirm button
instead.

ConfirmOptions now accepts an async action: the dialog stays open with
a loading state (cancel/Esc/overlay suppressed) until the work settles.
The archive-session, remove-workspace, and delete-provider confirms
move from the menu components into App.vue so the dialog can await the
actual client call.
@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@fc9eb1e
npx https://pkg.pr.new/@moonshot-ai/kimi-code@fc9eb1e

commit: fc9eb1e

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b12d14b78

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

function settle(ok: boolean): void {
const req = current.value;
if (!req) return;
if (!req || busy.value) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve supersede behavior while busy

Because confirm() still uses settle(false) to cancel an existing request before replacing it, this new busy guard makes superseding a dialog during an in-flight action a no-op: the old promise is not cancelled and can later resolve/reject from the old action, while the newly opened dialog inherits the global busy state and is temporarily inert. This affects any programmatic confirm opened while a destructive action is awaiting; either block replacement while busy or explicitly settle the previous request on the supersede path.

Useful? React with 👍 / 👎.

A second confirm() during an in-flight action would replace the busy
dialog and inherit the global busy state, opening inert until the first
action settled. Resolve the new request unconfirmed instead.
@wbxl2000

Copy link
Copy Markdown
Collaborator Author

@codex review

@changeset-bot

changeset-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fc9eb1e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc9eb1ea05

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

</Button>
<Button
ref="confirmButtonRef"
class="confirm-dialog__confirm"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid relying on Button fallthrough for focus

In dev builds this class is applied to the Button component rather than guaranteed to the native <button>. Button.vue has a template-root comment, so Vue renders it as a fragment in the same dev scenario this change is trying to fix; fallthrough attrs such as class are not inherited onto the inner button. That leaves no .confirm-dialog__confirm in the DOM, so Dialog falls back to focusing the close button and Enter still cancels the dialog.

Useful? React with 👍 / 👎.

@wbxl2000
wbxl2000 merged commit b89d385 into main Jul 15, 2026
14 checks passed
@wbxl2000
wbxl2000 deleted the fix/web-confirm-dialog-async branch July 15, 2026 12:04
@github-actions github-actions Bot mentioned this pull request Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant