feat(desktop): first-class Linux window chrome, menu, quit and packaging
Why
On Linux the desktop app was unusable as a window: frame: false + titleBarStyle: "hidden" with macOS traffic-light positions on every platform meant no minimize/maximize/close anywhere (the sign-in screen had no way out), the hidden title bar also hid the menu bar, closing the last window left an invisible process, and the packaged binary was named @supersetdesktop with a desktop entry whose window class never matched and a window with no icon.
Verified on a cloud sandbox desktop (Debian 12, Xfce on Xvnc) running the AppImage built there with the full prebuild, driven over VNC screenshots, xdotool and CDP. Checklist with before/after evidence per row: docs/linux-desktop-acceptance.md — every row ticked, including the second-pass rows.
What changes
- Window controls: Windows and Linux use Electron's window-controls overlay — native minimize/maximize/close on every screen, coloured from the app theme; every header row that sits under it (top bar, workspace tab bar, workspaces board, tasks, pull requests, task detail, new workspace) ends with an inset sized from the titlebar-area CSS variables. The React
WindowControlsand its procedures are gone. - App menu: an ≡ button at the start of the top strip on Windows/Linux pops the application menu (File/Edit/View/Window/Resources/Help); File carries Settings…, Check for Updates…, Quit and Quit Superset Completely where there is no application menu.
- Quit semantics: the last window's close quits through the confirmation and stays when cancelled; with no window left the app quits without a second prompt; one confirmation at a time. Terminal host and pty-daemon deliberately outlive a plain quit, as on macOS.
- Packaging:
executableName: "superset",StartupWMClass=superset, Linux windows carry the build's icon. - host-service on Linux — inotify deadlock: when the user's inotify instance limit is reached (
fs.inotify.max_user_instances, 128 by default, shared by every process — a few dev servers and an editor get there),@parcel/watcher's backend thread throws before signalling it started and the caller blocks forever. That caller is host-service's event loop: health stops answering and every workspace shows "Connecting… restart the host service from the tray menu". A throwawayfs.watchprobe before the native subscribe makes the sameinotify_initcall and fails cleanly, so git and file views keep working without live updates. Native stack, reproduction and after-state are in checklist row 2.3. git credential fillnow runs withGIT_TERMINAL_PROMPT=0like the git commands themselves: launched from a terminal with no credential helper it otherwise prompts on the tty until its timeout.
Verified on the packaged build
- 1.1 window controls on every route; 1.2 strip drag and the overlay inset on hidden-TopBar views; 1.3 maximize/restore from the overlay and by double-click, bounds restored after quit and relaunch; 1.4 app menu; 1.5 close/quit.
- 2.1 host-service start/stop; 2.2/2.3/2.5 via the cloud sandbox acceptance plus the inotify case above; 2.4 the app's
Notificationdraws the desktop bubble. - 3.1 Help → Documentation opens the system browser; 3.2 downloads land in
~/Downloads; 3.3 clipboard; 3.4xdg-open superset://tasksreaches the running app through the desktop entry's scheme handler and switches it to Tasks; 3.5 diffs render in the Changes pane with no worker errors (the dev-serverWorker errorlines are dev-only); 3.6 the packaged renderer never hits the dev-only request budget. - 4.1 AppImage build; 4.2 desktop entry, window class and dock icon; 4.3 bundled skill extras — root cause is
fs.cpon asar directories (not Linux-specific), fixed in #7429 and proven on a rebuild with it applied. - 5.1 every
darwinbranch in the desktop main process and host-service is in the audit table with its Linux counterpart or deliberate no-op.
Wider sweep (second pass)
- Every screen sampled for elements under the overlay (board, tasks, pull requests, pages, automations, search, settings, command palette, new workspace, workspace view with terminal / files / chat / browser panes, create-organization): two hits, the side panel's Set Run cluster and the create-organization screen's only exit (its Sign Out / Cancel button, which made that screen a dead end on Linux). Both fixed by moving the inset to whichever strip is rightmost; verified on a rebuilt package (checklist 1.6).
- Browser pane under load: Wikipedia, GitHub, YouTube, Hacker News and MDN typed into the address bar all rendered;
window.openfrom a page opens as a second pane; a 59 MB download from a page lands in~/Downloads; ten rapid navigations plus a heavy page with no renderer crash and steady memory (checklist 3.7). - Multiple windows: a third window crashed a renderer every time on the bench. The minidump (Electron symbols) shows
partition_alloc::TerminateBecauseOutOfMemoryinCreateSharedImageForSoftwareCompositor: with no GPU, Chromium backs each window's tiles with shared memory, and the sandbox VM mounts a 64 MB/dev/shm(about 17 MB per window). Not an app bug; the sandbox boot script now sizes/dev/shmlike a desktop (#7408), after which four windows open with every renderer alive. The menu emitter's listener ceiling is raised here since five subscriptions per window are expected (checklist 1.7). - Normal user on a current distro: the AppImage runtime needs libfuse2, absent on Debian 12 and Ubuntu 22.04+, so it stops at "AppImages require FUSE to run";
APPIMAGE_EXTRACT_AND_RUN=1works. This branch adds a deb target (gzip; fpm's xz took 16 minutes) that the workflow verifies and uploads: installed on the bench and launched by a non-root user through its own desktop entry, it starts with the real Chromium sandbox and reaches sign-in. Its "New Window" desktop action pointed atAppRun, which only exists inside an AppImage; it now callssuperset --new-window(checklist 4.4).
Noticed, not changed here
- Electron's
setAsDefaultProtocolClientlogsxdg-settings: default-url-scheme-handler not implemented for xfce; registration comes from the desktop entry'sMimeType, which AppImage integration installs. - The terminal host / pty-daemon children inherit the parent's
--remote-debugging-portlistener, so a relaunch after a plain quit cannot bind it until they exit (dev-only flag). - On one launch the first host-service instance exited with code 0 at startup and the coordinator respawned it ten seconds later; not reproduced.
- Electron does not publish a window icon on X11 even when
setIconis called with a loaded image (checked in the packaged main process), so docks rely on the icon theme entry that the deb and the desktop entry install. - The "restart the host service from the Superset tray menu" copy points at a tray Linux does not have.
#7429 is independent and should merge first.
https://claude.ai/code/session_01WcGbULcxE9Nw1Y8YVqe3KZ
Summary by CodeRabbit
-
New Features
- Added Linux desktop packaging in
.debformat alongside AppImage. - Added an application menu button for Windows and Linux.
- Added platform-aware title bars, window-control spacing, and theme-matched title-bar colors.
- Added Settings, update-check, and quit options to the non-macOS File menu.
- Improved close and quit confirmation behavior, including multi-window handling.
- Added Linux desktop packaging in
-
Bug Fixes
- Prevented filesystem watching from hanging when Linux inotify resources are unavailable.
- Prevented interactive Git prompts from blocking operations.
-
Documentation
- Added Linux desktop acceptance and support documentation.