mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-10-31 19:10:30 +00:00
Pressing F11 froze the Tauri app on Linux under X11 or XWayland: GNOME reported "CodeNomad is not responding" and the window never went fullscreen. The released 1.0.0 AppImage froze the same way, so this predates the AppImage display-stack fix (#1138), which keeps GTK on X11 for this very shortcut. A backtrace of the frozen process showed a deadlock between two threads: - global-hotkey's X11 thread received F11 and ran the plugin handler on that thread. toggle_fullscreen_window then asked the main thread whether the window was focused (is_focused) and waited. - The key grab itself moves X11 focus. Its focus events reached the main thread, where update_fullscreen_shortcut re-registered F11 and waited for the X11 thread to confirm. The handler now queues the toggle on the main thread (run_on_main_thread) and returns, so the shortcut thread is free to answer the registration. Two leaks from #1138 are closed on the way: - prefer_x11_display exported GDK_BACKEND=x11,wayland, which every child inherited: the backend, the shared OpenCode service it may start, agent shells, editors and the browser opened for links. Their GUI programs then ran under XWayland. CodeNomad now sets its own GTK's preference with gdk_set_allowed_backends, and children keep the session's choice. A GDK_BACKEND set by the session still wins. - Children that leave an AppImage's mount started in AppRun's working directory inside it (PWD=/tmp/.mount_*/usr for the browser opened by a link). use_host_environment now starts them in / while ours is inside the mount; callers that pick a directory (backend, editors) still do. dev-docs/HOTKEYS.md records that a global-shortcut handler only queues work on the main thread; dev-docs/TAURI_UPDATER.md describes the GTK backend preference and the working directory. Validation on Ubuntu 24.04 GNOME Wayland, with the #1141 CI AppImage repacked around a binary built from this branch: - After focusing the window, about ten F11 presses toggle fullscreen in and out. The main thread stays in its event loop (poll), where the CI build and the 1.0.0 release deadlock on the first press (gdb backtraces). - The window is still an XWayland client. The backend and the OpenCode service carry neither GDK_BACKEND nor mount paths. - The help link opens Firefox with / as working directory, PWD=/ and no GDK_BACKEND. - The appimage_environment and appimage_exit tests pass, and the Linux release binary builds.
4.3 KiB
4.3 KiB
Custom keyboard shortcuts
Agent conventions
- Settings → Keyboard shortcuts (
components/settings/hotkeys-settings-section.tsx, square token-based styles instyles/components/hotkeys-settings.css) manages the static renderer-only catalog inlib/hotkey-definitions.tsin both app and native Preferences renderers, without mounting the app. Keep thehotkeyssection allowlist identical in renderer, Electron and Tauri. Initially unbound palette commands appear with “No shortcut” under their existing translated names; aliases (new-session→session-new) keep one row. Native workspace actions are configurable but register for execution only in local desktop windows. - Bindings live in the UI owner's
settings.hotkeyOverrides: a missing ID uses its default,[]disables it, merge-patchnullresets one ID or the map. The savedpromptSubmitOnEntersupplies Enter defaults until an explicit composer override replaces them. - Recording is renderer-only and target-scoped: it suppresses app shortcuts (not native accelerators), ignores IME composition and bare modifiers, lets unmodified Tab leave normally, and ends on blur, cancellation, inactive settings or disposal. Conflicts warn on every affected row and never silently disable a binding.
- Digit-row and bracket keys share one physical identity for recording, dispatch and conflicts (
Digit1→1,BracketLeft→[, including AZERTY and QWERTZ); other keys stay logical. Saved layout-symbol overrides (&,ü,!) must be re-recorded to bind physical slots; never guess or silently migrate them. - Initially unbound commands dispatch the palette's real action through
use-app-lifecycle.tsand the existing keyboard registry, respecting its disabled state, active project/session and modal/selector guards, without key repeat; global commands also work on the home view. - Shortcut hints live only in tooltips and the manager, never status strips or help footers: a bound control sets
title={withHotkey(label, id)}(lib/hotkeys.ts) and menu items setdescription: hotkeyLabel(id) || undefined, leaving the accessible name unchanged, only where the shortcut performs that action in the control's current context (Ctrl+W only on the active tab). - Composer shortcuts (Clear, agent/model/thinking selectors) reach the shown composer, including the no-session draft, through the instance's
session-sidebar-eventsrequest; Clear dispatchesPROMPT_CLEAR_EVENTso the composer runs its own Clear action, never a bare textarea write. - Jump to previous/next user message (
user-message-prev/-next,components/user-message-navigation.ts) defaults to Alt+↑/↓, and to Cmd+Option+↑/↓ on macOS, where Option+↑/↓ moves within a text field's paragraphs: neither default edits text, so the jump also works from the composer and other text fields. It runs in the activeMessageSection's capture-phase handler, before the transcript reads arrows as scrolling; palette commands reach it throughJUMP_USER_MESSAGE_EVENT. Code editors and terminals (which bind modified arrows), arrow-key widgets, selects and popup triggers (Alt+↓ opens them), fields that move a list's active option (aria-activedescendant,input[aria-controls], such as the mission picker search), non-text inputs, IME composition, modal dialogs and an open composer picker ([data-prompt-picker], which moves its selection with every arrow) keep the keystroke; key repeat does not repeat the jump. Targets are the timeline's markers, including indexed history beyond the resident window (requested even while the timeline is hidden; a jump that finds nothing resident runs once it arrives). The last target stays the reference while loading or shown, until a scroll gesture or other navigation. Covered bytests/browser/history-navigation.test.ts. - Native hotkeys keep their platform defaults outside the manager; native menu keys and OS-reserved combinations may intercept assignments before the renderer.
- Tauri's F11 fullscreen shortcut (
tauri-plugin-global-shortcut, Windows/Linux) is registered only while a local window has focus. On Linux its handler runs on the shortcut thread, and the main thread waits on that thread to (un)register F11 when focus changes, which the X11 key grab itself causes: a handler only queues work withrun_on_main_thread, never a synchronous window call.