# Platform notes — macOS / Windows / Linux behavior matrix macOS (Apple Silicon) is the primary target; Windows 11+ x64 and Linux x64 are **beta** targets (win-arm64 and linux-arm64 deferred). **Intel Macs were dropped** with the move to Electrobun 2.x, which publishes no macOS x64 core. Every platform seam carries a `PORTABILITY:` comment at the branch site. ## Seam inventory (where platform branches live) | Concern | File | mac | win | linux | |---|---|---|---|---| | Window chrome | `src/bun/windows.ts chromeOptions` | hiddenInset + traffic-light offset, UI drag header | native titlebar, compact header | same as win | | Scrollbars | `ui/src/styles.css` (`::-webkit-scrollbar`) | custom square, always visible (styling them turns off overlay scrollbars) | custom square | custom square | | External editor launch | `src/bun/edits.ts launchEditor` | `open [-a app]` | `cmd /c start "" [app] file` | app directly / `xdg-open` | | Editor app picker | `rpc-handlers.ts pickEditorApp` | /Applications, `.app` | %ProgramFiles%, `.exe` | home, any | | Owner/group names | `fs/local.ts resolveId` | `id -nu` + `dscacheutil` | undefined → "—" | `id -nu` + `getent` | | Drive roots | `fs/local.ts listLocalDrives` | empty | `fsutil fsinfo drives` | empty | | chmod/Permissions | context-menu.service.ts, file-panel, file-actions | full | hidden for LOCAL panels (only flips read-only); SFTP unaffected | full | | Shell wording | `ui context-menu.service.ts` | Reveal in Finder / Move to Trash | Show in Explorer / Move to Recycle Bin | Show in File Manager | | Shortcut labels | `ui platform.ts MOD_LABEL/ALT_LABEL` | ⌘ / ⌥ | Ctrl+ / Alt+ | Ctrl+ / Alt+ | | Tray | `src/bun/tray.ts` | 22 pt template PNG | 16 px colored PNG | colored (needs AppIndicator, see below) | | App menu | `src/bun/menu.ts` | full menu incl. About | none (early return) — Settings opens from the tab-bar gear (Ctrl+,), About from its About section | none (unsupported upstream) | | connections.json protection | `store/connections.ts` | chmod 0600 | `icacls` current-user ACL (0600 is meaningless) | 0600 | | App data dir | `store/paths.ts` | ~/Library/Application Support | %APPDATA% | $XDG_CONFIG_HOME/.config | | ssh-agent | `fs/sftp.ts` | $SSH_AUTH_SOCK | `\\.\pipe\openssh-ssh-agent` fallback | $SSH_AUTH_SOCK | | Paths in UI | `ui format.ts joinPath/isRootPath` | "/" | separator detected from the dir string; drive-root regex | "/" | The **context menu is HTML on every platform** (`ui/src/app/skins/shadlike/context-menu.ts` + `context-menu.service.ts`), not a native electrobun menu — see the Linux section below for why. Only the three shell actions it can't do itself (reveal, open with the default app, copy path) cross to bun, via the `shellItemAction` RPC. Remote (SFTP) paths are ALWAYS POSIX regardless of host or server OS (`path.posix` throughout `fs/sftp.ts`). ## Memory footprint (expected, not a bug) Investigated 2026-07 — packaged build. Roughly **~200 MB macOS / ~300 MB Windows** (Linux sits near macOS: WebKitGTK, like WKWebView, is lighter than Chromium), which is normal for a Bun + system-WebView app (30 MB is native / no-web-engine territory; Tauri-class apps run ~80–200 MB, Electron 150–400 MB). Not reducible to tens of MB without dropping the web UI. - macOS splits across several "miniSCP" processes: the **Bun main** (~120 MB: backend + the bundled electrobun runtime + live SSH sessions — the largest and least reducible piece), the **`views://app`** WKWebView content process (~60 MB, the Angular UI), and the standard WebKit **Networking** / **Graphics and Media** helpers. All are one app; WebKit just names them per-role. - Windows is higher because **WebView2 is Chromium** (multi-process: browser + GPU + renderer + utility + crashpad), vs macOS's lighter WKWebKit. - Measure against a **packaged** build — `bun run dev` inflates the WebContent heap (ng-serve, unminified, sourcemaps, live-reload) and is not representative. - Verified non-issues: transfers stream (no whole-file buffering), icons tree-shaken to ~33 glyphs, zoneless Angular, 364 KB prod bundle. - Only real growth levers if this ever matters: the file list is **not virtualized** (a directory with tens of thousands of entries retains a DOM row each), and each open tab keeps both panels' full `entries` arrays; terminal transfers are retained until cleared. None affect normal use. ## Linux runtime dependencies (REQUIRED — the app will not start without them) electrobun's `libNativeWrapper.so` links AppIndicator *hard* for the tray, so a missing library is a hard boot failure (`ERR_DLOPEN_FAILED` out of `bun:ffi` `dlopen`), not a degraded tray. The transitive deps (`libdbusmenu-glib`, `libdbusmenu-gtk3`, `libayatana-indicator3`, `libayatana-ido3`) come in as package dependencies: | Distro | Install | |---|---| | Arch / CachyOS | `sudo pacman -S libayatana-appindicator` | | Debian / Ubuntu | `sudo apt install libayatana-appindicator3-1` | | Fedora | `sudo dnf install libayatana-appindicator-gtk3` | GTK/WebKit itself comes from the distro too — electrobun uses the system webview (`bundleCEF: false`), so `webkit2gtk` must be present. Diagnosing a boot failure: ```sh ldd build/dev-linux-x64/*/bin/libNativeWrapper.so | grep 'not found' ``` `libasar.so => not found` in that output is **expected and harmless** — it ships in the same `bin/` folder and resolves at load time; only an unresolved `libayatana-appindicator3.so.1` is fatal. (Hard link and path verified on 1.18.1 — re-check both on the first 2.x Linux build.) **Tray visibility** is a separate matter from linking: GNOME 3.26+ hides the legacy tray, so the icon needs the [AppIndicator extension](https://extensions.gnome.org/extension/615/appindicator-support/). KDE Plasma, XFCE, MATE and Cinnamon show it out of the box. The app runs fine either way — the tray is a convenience, and every action it offers exists in-app. ## Linux known issues (upstream electrobun) - **No native menus of any kind.** Application menus and `showContextMenu` are both no-ops (electrobun's `.hutch/devkit/api/sdks/main/proc/linux.md`, still in 2.0.2; the app logs "Application menus are not supported on Linux" at boot). The app menubar was already macOS-only, and the panel context menus are now HTML on every platform, so nothing is missing at the UI level — but do not add a feature that depends on a native menu. - **NVIDIA renders black without the DMABUF workaround.** WebKitGTK can't allocate GBM buffers on the proprietary driver, so the window paints nothing while the log looks perfectly healthy. The dev launches (`scripts/linux-gpu.ts`) set `WEBKIT_DISABLE_DMABUF_RENDERER=1` when `/proc/driver/nvidia` exists; an installed build (the official Setup installer) does not — launch it with that variable set (e.g. in the desktop entry's `Exec`). The app can't do it itself (Bun's `process.env` writes don't reach child processes); see [gotchas.md](gotchas.md#linux). - **Benign startup noise**: a deprecation warning pointing at `libayatana-appindicator-glib`. Not fatal. - **Unsigned; Electrobun's own installer**: the release artifact is `linux-x64-miniSCP-Setup.tar.gz` — a self-extracting `installer` + README (2.x does the desktop-entry packaging and ships an uninstaller); no AppImage or `.deb`. Layout unverified until the first 2.x Linux build. ## First-Linux-boot smoke test 1. Install the AppIndicator package for your distro (table above), then `bun run start`. The window opens **and actually renders the UI** (a black window on NVIDIA means the DMABUF workaround didn't apply — check for `Failed to create GBM buffer` in the log), and the tray icon appears (or doesn't, on bare GNOME — that's the extension caveat, not a failure). 2. Right-click a file row: menu appears at the pointer, Send to Other Panel is first, Permissions is present, Escape / click-outside / arrow keys + Enter all work, and a right-click near the bottom-right flips the menu instead of overflowing. 3. Right-click empty panel space (New Folder / New File / Refresh / Show in File Manager) and a column header (checkmarks toggle columns live). 4. Copy Path puts the path on the clipboard; Show in File Manager opens the distro's file manager; Open (local file) launches the default app via `xdg-open`. 5. local↔remote transfers both directions; external editor round-trip (temp download → save → auto-upload) with the inotify watcher. 6. Move to Trash lands the item in the desktop's trash; connections.json is 0600. 7. `bun scripts/release.ts --target linux-x64` produces `release/linux-x64/miniSCP-Build-linux-x64-Setup.tar.gz`; unpack it elsewhere, run its `installer`, then launch the installed app (on NVIDIA with `WEBKIT_DISABLE_DMABUF_RENDERER=1`). ## Windows known issues (upstream electrobun, documented not fixed) - **HiDPI blur** — WebView2 renders at 1x on scaled displays (blackboardsh/electrobun#324, seen on 1.18.1). Most user-visible defect; 2.x lists per-monitor DPI setup among its Windows fixes — re-check on the first 2.x Windows build. - **Notifications** are legacy balloon style (`Shell_NotifyIcon`), not Windows toasts. - **Unsigned binaries** — Setup.exe and the portable exe both trigger SmartScreen / Mark-of-the-Web on first run. No Authenticode support (2.x: "Windows release signing is not currently part of Hutch's packaging pipeline"; a signtool post-hook is an option later). Installer unpacks to `%LOCALAPPDATA%`, desktop shortcut only; 2.x installer builds add an uninstaller (`%LOCALAPPDATA%\\\uninstall.exe`). - `hiddenInset` degrades to `hidden` on Windows — irrelevant here because non-mac windows use the native titlebar. - `findInPage` is a `window.find()` shim on WebView2 — irrelevant here, the editor find is custom. ## First-Windows-boot smoke test Code paths written blind (no Windows box was available); verify in order: 1. Build: `bun scripts/release.ts --target win-x64` on the Windows machine — Hutch, the core and the pinned Bun download into `~/.hutch` on first run; Setup.zip AND the portable zip appear under `release/win-x64/` (the portable step warns instead of failing if the assembled-app folder layout differs — fix the `appDir` path in release.ts if so). A v1 0.1.78 install must pick up the published 2.x build through its updater (the bridge release). 2. App boots, tray icon renders colored, window has a native titlebar. 3. Drives dropdown in each panel lists `C:\` (+ others); navigating to a drive root hides the `'..'` row; the path bar shows consistent `\` paths. 4. local↔remote transfers both directions; self-copy guard still blocks same-path copies. 5. External editor: default app opens via `cmd /c start`; the Choose… picker starts in Program Files and accepts an .exe; remote temp-edit → save → auto-upload works (directory watcher on NTFS). 6. Context menus: Send to Other Panel is the first row item; Show in Explorer, Move to Recycle Bin (item restorable from the bin), Copy Path; Permissions ABSENT on local rows, present on remote. 7. Ctrl+T/W/F/S and Ctrl+digit tab jumps; tooltips read "Ctrl+…". 8. connections.json ACL: `icacls connections.json` shows only the current user after saving a connection. 9. Editor/manager/Settings windows open, modal bounce works, Ctrl+, opens Settings and About shows from its About section. 10. Known-issue check: HiDPI blur on a scaled display (seen on 1.18.1 — 2.x's per-monitor DPI setup may have fixed it).