Skip to content

feat(desktop): run the workbench on NSH's AppKit backend behind MAKA_APPKIT - #6509

Merged
M4n5ter merged 4 commits into
rust-previewfrom
feat/desktop-appkit-backend
Oct 31, 2026
Merged

M4n5ter merged 4 commits into
rust-previewfrom
feat/desktop-appkit-backend

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Runs the real workbench on NSH's AppKit backend behind a non-empty MAKA_APPKIT, so the two backends can be compared on the same Host and TSX. GPUI stays the default on macOS, and Windows and Linux do not build this path. Host and shared TSX are unchanged.

The direction and the order of this work were agreed in #6502. The multi-session benchmark and the look into why streaming runs at about 120 fps come after this PR.

What the AppKit backend gains:

  • An embedding entry for a host's own EngineConfig, with asset:/ images read through that config's reader, the same way the GPUI backend does it.
  • within and fallback key bindings, so Ctrl+C clears a draft. A text view's key bindings report an unused key as noop:, and fallback runs there.
  • Input.replaceRange, which composer completion and clearing a draft depend on.
  • accessibilityPerformPress, for VoiceOver and automation.

products/maka/desktop-shell/src/appkit.rs carries out the workbench's quit, folder and text size requests, and reports the window's key state as Foreground.

Root cause: the window growing on rapid sidebar clicks

This was not a Maka bug. It reproduced from 1280×820 at (116,90) to 1396×877 at (0,33), with the bottom right corner fixed and the top left corner at the screen edge.

Background automation clicks send an activation click at a far-off point before the real click, and AppKit clamps it to just outside the window's top left corner. Rapid clicks make two of those a double click on the corner, and AppKit's corner gesture (_attemptToSnapWindowSizeFromPoint:, seen in lldb) resizes the window. On GPUI the same tool uses AXPress, so it sends no raw click. AppKit views had no AXPress until this PR, and with it rapid sidebar presses keep the window at 1280×820.

Rapid clicks with a physical mouse were not tested.

Gaps in this first version

Each of these keeps GPUI behavior out of scope for now:

  • Following the Host. If the Host stops, the window keeps the last workbench and does not reconnect. Generalizing follow and Detach touches the GPUI path. Before adding this, Image.getSize replies need to stay with the engine that asked: Surface::event posts to the current engine and request ids restart at 1, so a late reply can answer a second load's request. AppView::load already has this problem, and the path here loads once.
  • UI reload. The AppKit backend has no pause_input, so a plugin change that needs a reload is refused with an error rather than left hanging.
  • System notifications. Not posted; the shell logs that it dropped one.
  • Window position storage. The frame lives in NSUserDefaults per Root, not clients/desktop-window.json, and the full-screen state is not restored.
  • Input tokens. Not implemented on AppKit, so skill tokens in the composer are plain text.

Verification

Checks run on this branch, rebased on rust-preview:

  • cargo fmt --all -- --check, and cargo clippy -p maka-desktop-shell --no-deps -- -D warnings.
  • cargo nextest run -p maka-desktop-shell: 8 passed.
  • cargo clippy -p nsh-platform-macos -p nsh-macos-host -p nsh-macos-app --all-targets --features nsh-platform-macos/dev: no new warnings (3 existing ones in navigation.rs).
  • cargo test -p nsh-platform-macos --features dev --lib: 59 passed.
  • nsh test crates/nsh-platform-macos/tests/keymap/phases.test.tsx --target macos: 4 passed in a real window. It covers within, fallback in plain and masked fields, and a replaceRange clipped to maxLength. Headless apps refuse these bindings, so CI does not run it.
  • examples/gallery/tests/shortcuts.test.tsx and press.test.tsx pass with --target macos. In form.test.tsx, "a controlled input shows what the app makes of each key" fails with or without this change.

nsh-cli does not compile on rust-preview since #6507 (run::Target::Tui is not covered in test_host.rs). I ran these with a one-line local patch that is not in this PR.

Real window, Maka release build on a long test Root, before the rebase onto #6507 (screenshots and AX):

  • The sidebar asset:/ logo renders.
  • AXPress switches conversations.
  • Ctrl+C clears a draft.
  • Up, Down and Escape work in the command palette.
  • View menu text sizes apply.
  • / completion moves, accepts and clears the /.
  • Cancelling and accepting the folder sheet both work.
  • Quit detaches cleanly, within 2 seconds and with nothing logged.

Real window on the final code:

  • The asset:/ logo renders in the sidebar and on the welcome screen, on the loader chore(nsh): sync upstream main and migrate Maka consumers #6507 rewrote (screenshot below).
  • The window restores a saved frame (1400×800 at (100,100), then 1280×820). Before the fix in chrome.rs it lost the title bar's height on every launch. Three restarts in a row kept the frame; those ran just before the rebase, on the same chrome.rs code.
  • Read through lldb while the screen was locked: AXPress returned true and switched conversations, and returned true on Focus message composer, which is focusable={false}.

The workbench in the AppKit window

An adversarial review by GPT 6.1 Sol High found no P0 or P1. Of its four P2s, three are fixed here (AXPress and focusable={false}, the masked field, the clipped replaceRange caret), and the Image.getSize one is listed above. It reviewed the commit before those fixes.

Not verified:

  • IME composition.
  • The feel of real gesture scrolling.
  • Physical rapid mouse clicks.
  • Foreground switching (it needs to take focus from a machine in use).
  • The UI reload refusal end to end. On the test Root the Host rejects plugin changes first (binding selects a disabled provider), so beginUiReload was never reached.
  • Windows and Linux builds, left to CI.

Review focus

Cargo.lock: adding the path dependency made Cargo re-resolve some multi-version ranges, moving existing crates from windows-sys 0.59 to 0.61 and windows-core 0.61 to 0.62. This only affects Windows. Restoring the old entries by hand was not accepted by --locked, so this is Cargo's own result. Please check Windows CI with that in mind.

Public API

All in nsh-platform-macos:

  • AppView::load_with_config(&self, config: EngineConfig): runs an engine the host configured, replacing what ran.
  • AppView::choose_directory(&self, done: impl FnOnce(Option<PathBuf>) + 'static): asks for one folder in a sheet of the view's window.
  • run_app_with_config(config: EngineConfig, options: AppOptions, on_active: impl Fn(bool) + 'static) -> ExitCode: run_app for such an engine; on_active hears whether the window has the keyboard.
  • with_running_view(f: impl FnOnce(&AppView) + Send + 'static): runs f with the standalone view on the main thread, from any thread.
  • AppOptions::min_size: Option<(i32, i32)>: the content's minimum size.
  • AppOptions::frame_autosave_name: Option<String>: saves and restores the window's frame under this name.

AI use

Select exactly one:

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

Tool(s) and scope: Claude Code wrote the code, tests and docs, and ran the verification. GPT 6.1 Sol High (Codex) reviewed the branch before this PR opened.

Checklist

  • Tests cover the change and fail without it. The key binding and replaceRange test runs in a window by hand, not in CI. The embedding entries, window frame and AXPress were checked in the real window, with no automated test.
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above. Only with MAKA_APPKIT set; the default path is unchanged.
  • No

Astro-Han and others added 4 commits November 1, 2026 00:03
The macOS backend did not compile against the shared hover signature and
could only run bundles it loaded itself, so a host could not hand it an
EngineConfig. Maka's composer also needs what only the GPUI and terminal
backends implemented.

Add load_with_config, run_app_with_config and with_running_view, with
asset:/ images read through the config's reader. Support `within` and
`fallback` key bindings; a text view's key bindings report an unused key
as noop:, which is where fallback runs. Implement Input.replaceRange.

Add accessibilityPerformPress. Automation clicks without it sent AppKit
an activation click outside the window's corner, which it read as a
double click on the corner and resized the window to the screen edges.

Keep a saved frame when the hidden title bar applies: AppKit keeps the
content size there, and the window lost the title bar's height on every
launch.

Generated-by: Claude Code
Comparing the backends needs the real workbench on AppKit without
changing what anyone runs today. A non-empty MAKA_APPKIT opens the same
admitted Connection in an AppKit window; GPUI stays the default, and
Windows and Linux do not build this path.

The path carries out the workbench's quit, folder and text size
requests and reports the window's key state as Foreground. It has one
Host and refuses a UI reload rather than pausing input it cannot pause.
Detaching moves to host.rs so both windows share it.

Adding the path dependency made Cargo re-resolve some multi-version
ranges in Cargo.lock: windows-sys 0.59 to 0.61 and windows-core 0.61 to
0.62 for existing crates, on Windows only.

Generated-by: Claude Code
The native test host starter already rejects Tui before host_target is called. Make that existing boundary explicit so nsh-cli compiles after the NSH synchronization. Refresh the source fingerprint; no dependency or notice text changes.

Generated-by: OpenAI Codex
@M4n5ter
M4n5ter marked this pull request as ready for review October 31, 2026 16:47
@M4n5ter
M4n5ter merged commit 2ba23ef into rust-preview Oct 31, 2026
5 checks passed
@M4n5ter
M4n5ter deleted the feat/desktop-appkit-backend branch October 31, 2026 16:47
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.

2 participants