Skip to content

fix(desktop): present desktop dialogs natively and in front - #6018

Draft
Astro-Han wants to merge 1 commit into
mainfrom
fix/desktop-native-dialogs
Draft

Astro-Han wants to merge 1 commit into
mainfrom
fix/desktop-native-dialogs

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Desktop decisions raised by the main process — quit while Runtime Host work is active, Runtime Host recovery, storage-root repair, Renderer recovery, settings confirmation and the fatal startup error — were drawn in a frameless BrowserWindow (#4474). That window has two defects that cannot be fixed inside it without re-implementing the platform:

  • On macOS it is not reachable through the accessibility tree: AppleScript / System Events cannot see or answer it.
  • It never activates the app. A quit requested while Maka is in the background (Dock or osascript -e 'tell application "Maka" to quit') shows "Maka is working in the background" behind the active app, and the quit coordinator silently swallows every further quit request while it waits, so the quit looks ignored.

This PR presents these decisions with Electron's native message box instead (an NSAlert sheet on macOS, a Task Dialog on Windows, a GTK dialog on Linux), attached to the main window when it is visible and app-modal otherwise. Before a decision is shown, the app is activated; a quit request that arrives while the quit decision is pending activates it again. E2E runs that may not reveal windows still settle the dialog as cancelled without showing it.

Process-level decisions use the platform's own dialog on purpose: they must work when the Renderer is missing or broken, which is exactly what forced the BrowserWindow version into its own lifecycle, presentation timeout and native fallback. Visually these dialogs follow the OS instead of Maka's palette; the macOS alert carries the app icon. Copy and button semantics are unchanged.

Removed with the BrowserWindow renderer: its presentation timeout and native fallback, the generated browser-dialog-design-tokens.css, its navigation allowance in the architecture check, the dialog-window guard in window-all-closed, and the @maka/core/maka-wordmark export (the path moves back into MakaWordmark, its only consumer).

Verification

  • npm run build, npm --workspace @maka/desktop run typecheck, npm run format, npm run lint, npx knip (desktop, ui, core), renderer architecture check and its tests
  • @maka/desktop test:dist suite: 3276 passed, 0 failed. New: desktop-message-box.test.ts (hidden runs cancel without showing; active runs activate before showing, as a sheet when a parent is given), and a quit-coordinator case for re-revealing a pending decision.
  • macOS 27, development build, with Maka in the background and another app frontmost:
    • presenting a decision brought Maka to the front; the sheet's buttons and text were readable through System Events, and clicking a button from AppleScript returned the matching response;
    • with active work simulated, an AppleScript quit brought Maka forward with the confirmation sheet; a menu Quit sent while it was pending reached before-quit and brought Maka forward again; choosing "Stop tasks and quit" exited the app.
    • A second AppleScript quit while the sheet is attached is refused by AppKit with -128 (user canceled) before before-quit; the sender gets an error instead of silence.
  • Windows and Linux, via a throwaway workflow_dispatch probe that bundles this branch's presentMessageBox, shows the quit decision attached to a window and the startup error standalone, and answers both from outside the process (Windows run, Linux run):
    • Windows Server 2025: both render as Task Dialogs. UI Automation exposes the title, message, detail and every button by name; the buttons carry no Invoke pattern (a Task Dialog property), so the probe answered them with UIA focus + Space, and both returned the expected response.
    • Ubuntu under Xvfb, with the a11y status switched on the way Orca does: both GTK dialogs render, but they are not in the AT-SPI tree. Electron sets NO_AT_BRIDGE=1, so GTK's bridge stays off, and Chromium's own bridge only covers its frames. This matches the open/save dialogs Maka already shows on Linux; in the same run Chromium exposed the window frame but none of its web content either, so the probe gives no evidence the BrowserWindow dialog was more reachable there.

Windows: quit decision as a Task Dialog

Linux: quit decision as a GTK dialog under Xvfb (no window manager)

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code — diagnosis, implementation, tests, macOS verification and the Windows/Linux probe.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

Quit confirmation, Runtime Host recovery, settings confirmation and the
other main-process decisions were drawn in a frameless BrowserWindow. That
window is invisible to the accessibility tree, so AppleScript, System
Events and VoiceOver could not see or answer it, and it never activated
the app. A quit requested while Maka was in the background (Dock or
AppleScript quit) put the "background work is running" confirmation
behind the active app, and every later quit request was swallowed while
it waited.

Present these decisions with Electron's native message box instead: a
sheet on the visible main window, or an app-modal alert without one. The
app is activated before a decision is shown, and a quit request that
arrives while one is pending activates it again. E2E runs that may not
reveal windows still settle the dialog as cancelled without showing it.

This removes the BrowserWindow dialog renderer, its presentation timeout,
native fallback, design-token asset, navigation allowance and
dialog-window lifetime guard, and moves the wordmark path back into the
UI component that is now its only consumer.

Generated-by: Claude Code
@github-actions github-actions Bot added the effort/XL Under 2500 readable lines label Oct 9, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Under 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant