Commit Graph
32 Commits
Author SHA1 Message Date
jgamboaandClaude 6bb4c3cd7d Merge main into feat/7css-skin + give the HTML context menu an Aero skin
main retired electrobun's native context menu (a documented no-op on Linux) in
favour of an HTML one on every platform. Merging that here needs the menu to
look right under the Win7 skin as well.

Conflicts, all resolved by keeping both sides:
- src/bun/index.ts — createAppRPC keeps the branch's mainWindow getter, drops
  main's removed contextMenu dep.
- ui/src/app/app.ts — imports both TitleBar and ContextMenu.
- ui/src/app/platform.ts — kept the branch's version (its IS_MAC_CHROME accounts
  for the frameless win7 chrome, and MOD_LABEL correctly keys on the real
  platform rather than the chrome), plus exporting IS_MAC and adding IS_LINUX
  for the menu's per-OS shell wording.

Aero menu (branch-only): 7.css has no menu component, so it is hand-authored
across both layers — the row <button>s shed their push-button slab in `skin`
(revert-layer only reaches `utilities` from there), and the square-cornered
slab, hairline border and gradient selection band are stated in `skinfix`.
Built strictly from existing --w7-* vars, never literal hex, so the
hand-written dark Win7 palette recolours it for free. Hover and the keyboard
cursor share one band (the component marks the cursor with `bg-accent`, which
is overridden alongside :hover) as a real Win7 menu does.

Also answers a question the merge raised: frameless windows DO work on Linux.
The GTK wrapper honours titleBarStyle "hidden" — it calls
gtk_window_set_decorated(false) and logs "GTK: Created window without
decorations (custom titlebar)". Verified on GNOME/Wayland and documented in
architecture.md.

Verified: backend `tsc --noEmit` clean, `bun run build:ui` clean (492.52 kB
initial, still under the 500 kB budget), app launches with the skin active,
frameless, no console errors and no GBM failures.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 14:59:02 +02:00
jgamboaandClaude 30a432cd37 Fix black window on Linux + NVIDIA (WebKitGTK DMABUF renderer)
On the NVIDIA proprietary driver the app booted, logged "miniSCP started",
reported no error — and painted a completely black window, with stderr showing
"Failed to create GBM buffer of size WxH: Invalid argument" and an X11
GLXBadWindow. WebKitGTK's DMABUF renderer cannot allocate buffers there;
WEBKIT_DISABLE_DMABUF_RENDERER=1 is the upstream workaround. Pre-existing on
Linux, unrelated to the context-menu change — it just had never been looked at
because nothing on Linux had been run before.

The app CANNOT apply this to itself. WebKit reads the variable when it forks
its web process, and Bun's `process.env` writes do not propagate to child
processes (verified: a spawned child sees it unset), so setting it in
src/bun/index.ts is inert even from a module imported before `electrobun/bun`.
It has to be in the environment before the app process starts, which means
every launch path must inject it:

- scripts/linux-gpu.ts — shared `launchEnv()` helper, gated on
  /proc/driver/nvidia so Intel/AMD keep hardware compositing, and never
  overriding an explicit value from the user's environment.
- scripts/dev.ts — uses it for `bun run dev`.
- scripts/start.ts (new) — `bun run start` previously called `bunx electrobun
  dev` straight from package.json, where no env could be set; it now goes
  through a script like dev does.
- scripts/release.ts — writes a `miniSCP.sh` wrapper beside bin/launcher in the
  Linux tarball, since a packaged app goes through none of the above.

Documented in gotchas.md (including that a "does it boot?" log check does NOT
catch this — the log looks healthy) and platform-notes.md, with the smoke test
updated to require that the UI actually renders.

Verified: backend `tsc --noEmit` clean, `bun run build:ui` clean, and
`bun run start` now boots with zero GBM/GLX errors.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 14:53:22 +02:00
jgamboaandClaude f7ebed5478 Make Linux a supported platform: HTML context menus + runtime deps + release target
`bun run start` died on Linux with ERR_DLOPEN_FAILED (missing
libayatana-appindicator3.so.1). Fixing the environment exposed the real gap:
electrobun's ContextMenu.showContextMenu is a documented no-op on Linux, and
since the UI suppresses the webview's own right-click menu, EVERY panel context
menu was silently dead there — Send to Other Panel, Open, Rename, Permissions,
Delete, Copy Path, Reveal and the column picker all unreachable. Electrobun
itself logs "Application menus are not supported on Linux. Implement menu UI in
your webview HTML instead." at boot.

- Context menus are now HTML on EVERY platform (ui/src/app/context-menu.ts +
  context-menu.service.ts), not just as a Linux fallback: one implementation
  cannot drift from a second one. The item builders are ported verbatim from
  the old bun menu, so ordering, per-platform labels and the Windows-local
  chmod hide are unchanged, and picks are emitted as the same `ctx-*` / `col:*`
  strings through RpcService.emitMenuAction — App.handleMenuAction is untouched
  and still the single dispatcher for both the macOS menubar and right-clicks.
  Adds what the native menu gave for free: arrow/Enter/Escape keys, viewport
  edge-flip, close on blur/resize/scroll. The Edit item now shows the real F4
  hint instead of the native menu's "e" mnemonic.
- New shellItemAction RPC replaces showRowMenu/showColumnsMenu — the only part
  the webview can't do itself (reveal, open-with-default, copy path). Deletes
  src/bun/context-menu.ts entirely.
- release.ts gains a linux-x64 target. Electrobun emits only the .tar.zst
  updater blob on Linux, so the artifact is a .tar.gz of the assembled app
  folder, mirroring the Windows portable zip.
- Docs: platform-notes gets a Linux runtime-dependency table (AppIndicator is
  linked HARD — a missing lib is a boot failure, not a degraded tray), the
  ldd diagnostic, GNOME tray caveat, known issues and a first-boot smoke test;
  gotchas gets the ERR_DLOPEN_FAILED trail and the benign GLX/GBM startup
  noise; architecture/build-and-release/README updated accordingly.

Verified: backend `tsc --noEmit` clean, `bun run build:ui` clean (399 kB
initial), app boots on Linux with no errors beyond the documented benign
warnings. NOT verified on macOS/Windows — the native menu is retired on those
platforms too, so the menus need eyeballing on a Mac before the next release.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 14:23:58 +02:00
jgamboaandClaude 07825c1bd1 Add a toggleable Windows 7 (7.css) skin behind a new skin setting
Evaluation branch: a full Aero skin, including frameless windows with our own
title bar, switchable against the existing modern look.

The app already had a four-axis runtime skinning mechanism and zero component
stylesheets, so the skin is one new setting plus one stylesheet section rather
than a parallel UI. The key move is cascade order: 7.css is imported into a
`skin` layer ABOVE Tailwind's `utilities`, so its bare element rules outrank
the ~1900 inline utility classes already in the templates. Template changes are
limited to additive, skin-inert hooks (ARIA roles, `has-scrollbar`,
`w7-toolbar`, `w7-btn`).

- styles.css: explicit layer order, `7.css/dist/7.scoped.css` via `layer(skin)`,
  a token bridge re-pointing the existing semantic tokens at Win7 values (no new
  tokens, so `@theme inline` is untouched), a hand-authored dark Win7 palette,
  and neutralizers that `revert-layer` 7.css's dialog-button metrics back to the
  utility layer. Neutralizers must live inside `skin`, not `skinfix` —
  `revert-layer` only reaches `utilities` from one layer up.
- Frameless chrome: `chromeOptions(skin)` is now the single place either skin's
  frame is decided. `titleBarStyle: "hidden"` on every platform, which is also
  the only way to drop the macOS traffic lights. New `title-bar` component
  mounted by all three window roots.
- Window controls over RPC (minimize/toggle-maximize/close/is-maximized), served
  per window kind so the X reuses each one's existing close path — editors still
  get their dirty prompt. electrobun has no maximize event, so the state is
  derived from `resize`; double-click-to-maximize is implemented here too.
- Font: Noto Sans vendored (OFL-1.1) under ui/src/fonts, since Segoe UI is
  absent off Windows and not redistributable.

Verified: backend `tsc --noEmit` clean, `bun run build:ui` clean (486 kB initial,
under the 500 kB budget), app launches with the skin active and no console
errors. The rendered result has not been eyeballed — screen capture and
accessibility are both denied to the shell on this machine.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 15:02:27 +02:00
jgamboaandClaude fc03fa6216 Make the darker tone true-black AMOLED
The darker tone sat at #090908 for --background while --chrome was already
pure black, which both inverted the elevation ramp (frame strips darker than
the content beside them) and missed the point of an AMOLED tone — only a
true #000000 pixel is actually off on OLED.

--background now collapses into --chrome at pure black, so the main window,
connection manager and editor all render black end to end: panes, bars,
sidebar, gutter and textarea. Elevation is kept where it earns its place —
dialogs, popovers and toasts stay #181817, hover and selection #272726 — and
--border/--input go from 12%/15% to 16%/18% white, since on pure black those
lines are the only thing left defining panes, bars and rows.

No plumbing needed: buildPreload already bakes data-dark-tone onto every
window pre-paint, and tone changes broadcast live to open windows.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 14:00:01 +02:00
jgamboaandClaude e4ba846719 Retone dark theme to warm gray #1a1a19 with layered surfaces
The dark palette was shadcn's cool zinc (#202023, hue ~286). Anchor it on
#1a1a19 instead — cds gray-830, a warm near-neutral (hue 107) — and retone
the whole dark neutral family to match, since a warm background under cool
panels clashes. Lightness ramp is preserved; chromatic tokens (destructive,
success, icon hues, accent formulas) and the light palette are untouched.

Also introduce --chrome so the app isn't one flat tone: frame strips (tab
bar, status bar, transfer bar, editor/manager header+footer, connection
sidebar) now sit one step below the content panes, with popovers and hover
above them — the standard dark-UI elevation order.

Dark ramp: #151514 chrome, #1a1a19 content, #20201f card, #2e2e2d hover;
the "darker" tone drops chrome to true black. Light gains only a zinc-50
chrome step.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 13:20:47 +02:00
jgamboaandClaude 697f7f22c8 Rename Copy to Send and put it first in the row context menu
The main feature (copy selection to the other panel, F5) is now labeled
"Send" in the toolbar button, tooltips, and context menu across all 7
locales. Internal identifiers (copyToOtherPanel, ctx-copy, i18n keys)
and secondary strings (toasts, status bar, errors) are unchanged.
The context-menu entry moves ahead of Open/Edit as the first row item.
README, marketing site, and the Windows QA checklist updated to match.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 13:12:05 +02:00
jgamboaandClaude Fable 5 f87077b2b7 Document expected memory footprint
~200 MB macOS / ~300 MB Windows is normal for a Bun + system-WebView app;
record the process breakdown, why Windows (WebView2/Chromium) is higher,
that it's not a bug, and the only real growth levers, so it isn't
re-investigated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 13:17:34 +02:00
jgamboa b5b1320776 sections reordered 2026-07-15 13:09:29 +02:00
jgamboaandClaude Fable 5 b2cf000293 Fix ⌘Q not quitting on macOS
The Quit item was role:"quit" with no accelerator, so ⌘Q was never
bound. Make it a custom action + accelerator "q" dispatched to
Utils.quit(), which emits before-quit so the existing guard (active
transfers / unsaved editors, native confirm dialog) runs instead of the
OS terminating directly. Handled before the manager-open check so ⌘Q
works with the connection manager open too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 17:51:29 +02:00
jgamboa f86b10335e little text 2026-07-14 17:08:32 +02:00
jgamboa 6ecb1b8339 disable analytics 2026-07-14 16:50:22 +02:00
jgamboaandClaude a40433b99e Add built-in auto-updater with tab-bar update pill
Wires electrobun's Updater to the /dl download host. The packaged app checks
https://miniscp.com/dl on launch + every 6h; when a newer build exists, an
accent-colored "update tool" appears next to Connections in the tab bar. It
announces the update, becomes its own inline progress bar while downloading,
then flips to "Restart to apply" (two-step: download, then user-triggered
relaunch).

- electrobun.config.ts: release.baseUrl = https://miniscp.com/dl (baked into
  version.json at build; miniscp.com fronted by an external proxy for TLS).
- rpc.ts: checkForUpdate/downloadUpdate/applyUpdate requests + updateStatus push.
- rpc-handlers.ts + index.ts: call Updater; poll on launch+6h; stream
  onStatusChange progress. checkForUpdate self-guards the dev channel.
- rpc.service.ts + new updater.service.ts: signal-based state
  (available -> downloading -> ready, ready derived from downloadUpdate ok).
- tab-bar.ts: single bg-primary pill; progress fills the pill itself.
- icons.ts: register mynaDownload. i18n: tabBar + menu keys across all locales.
- menu.ts + app.ts: "Check for Updates…" app-menu item -> UpdaterService.check().
- docs: build-and-release + architecture updated.

Unsigned builds still update (applyUpdate strips the macOS quarantine xattr).
Verified: tsc --noEmit (exit 0) + build:ui AOT (exit 0). Live pill + full
download/apply flow require a two-build test on the host.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 16:43:40 +02:00
jgamboaandClaude 43f1168d6a Add landing page + Caddy download host (site/)
Self-contained, no-JS landing page (tinygrad-minimal) that doubles as the
artifact download host, served by Caddy on an internal :80 box.

- site/index.html: static page, "Same tool, only a little different", feature
  highlights (remote->remote, symmetric panels, ssh_config awareness, edit-in-
  place, self-copy guard, ...), fixed stable download links, FAQ (unsigned notes).
- site/Caddyfile: one flat /dl/ folder with browse + range requests and per-type
  Cache-Control (patches immutable, update.json no-cache, installers short).
- site/deploy.sh: drop a build's artifacts/stable-* into /dl/ (renames the win
  portable zip to a stable name).
- Flat folder matches electrobun's artifacts/ layout, so it's also the future
  updater baseUrl; per-build folders/symlink dropped in favor of it.
- docs/build-and-release.md: document the download host + deploy flow.

Verified: caddy validate + live smoke test (links, cache headers, 206 range,
404 fallback, deploy.sh copy/rename) against a temp root.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 15:59:05 +02:00
jgamboaandClaude Fable 5 a0f56822cd Internationalization: 7 languages with live switching
Custom signal-based runtime (no i18n dependency): shared src/shared/i18n
dictionaries, ui I18nService.t() reactive by construction so language
switches live, and a backend i18n.ts for native menus + thrown errors.
Every user-facing string keyed across templates, toasts, dialogs, file
kinds, columns, macOS menu/context menus, and errors. Language picker in
Options (auto-detects OS locale, falls back to English); switching
relabels the webview and native chrome instantly. English/Spanish/French/
German fully drafted; Chinese/Japanese/Basque drafted, flagged for native
review. Docs updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:52:54 +02:00
jgamboaandClaude d8cfc54049 Add CLAUDE.md working agreements
Codify three standing behaviors so they persist across sessions: ask
clarifying questions in plan mode until unambiguous; verify then
auto-commit + push after an approved plan; keep docs/ up to date in the
same change.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 14:18:15 +02:00
jgamboaandClaude Fable 5 ee5a9e13e6 Auto-refresh the active tab's panels (silent, non-disturbing)
Local panels are fs.watched (LocalDirWatcher + setWatchedDirs -> pathRefresh);
remote panels poll every 3s. New refresh({silent}) skips the loading/busy
flags and touches no DOM, snapshotting sessionId+cwd to discard results if the
user navigated mid-flight. Only the active tab is live; switching tabs
refreshes the new pair. A future on/off setting is left as commented
breadcrumbs. Docs updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 14:03:35 +02:00
jgamboa 260e2d64b2 new icon and assets 2026-07-14 14:00:36 +02:00
jgamboa 39b1858957 connection manager paragraphs tweak 2026-07-14 12:39:26 +02:00
jgamboa 5863ae1079 Revert back to more informative text in Import button 2026-07-14 10:22:56 +02:00
jgamboa 2d95857de1 Button ui tweaks 2026-07-14 09:43:06 +02:00
jgamboaandClaude Fable 5 e2f3737656 Redo connection manager: Stacks, More options, drag & drop, WinSCP import
Folders become Stacks (connections.json v2, no v1 migration): collapsible
stack-first list, open-stack-in-tabs (first reuses a pristine tab), drag
connections between stacks. More... reveals a per-connection Local
directory applied to the other panel when local and untouched. Import
reads a WinSCP.ini export incl. deobfuscated passwords.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:28:30 +02:00
jgamboa 9a4420fdc2 Toast fixes 2026-07-13 18:06:48 +02:00
jgamboaandClaude Fable 5 dda596c50c Panel busy states: Connecting placeholder with Cancel, slow-load overlay
Connecting blanks the list to a fading spinner with the target name and
a Cancel button (token discards late results and orphan sessions).
Listings >300ms dim the list under a spinner. All panel input is
blocked while busy; the source picker stays usable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:36:44 +02:00
jgamboaandClaude Fable 5 4a793ca50e Remove disconnect button; picking Local or closing the tab covers it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:55:06 +02:00
jgamboa 9f79bb0b17 Change dark coloring 2026-07-13 15:53:37 +02:00
jgamboaandClaude Fable 5 bdae49ba24 Dark tone setting: grey Dark (default) vs near-black Darker
Tone row appears under Theme when the effective theme is dark.
Grey palette uses macOS-like #26262a background.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:43:01 +02:00
jgamboa eb86ca514d New tab icon sizing change 2026-07-13 14:28:53 +02:00
jgamboaandClaude Fable 5 628ccebd9a Rename Text size to Interface; scale panel header with it
Action buttons, server picker, and its dropdown now follow the size
setting via new --action-h/--action-font/--action-icon variables.
Default tier matches the old fixed sizes; tab bar stays fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 14:26:25 +02:00
jgamboa 09792c93cf Fix light-dark scroll bars 2026-07-13 14:19:46 +02:00
jgamboaandClaude Fable 5 f4819a58fe List ~/.ssh/config hosts in the panel source dropdown
Read-only group below saved connections; hosts resolve HostName/User/
Port/IdentityFile (Include, wildcards, %-tokens) and connect on click.
New ssh-agent auth kind covers hosts without a usable identity file;
ProxyJump/ProxyCommand hosts are shown disabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 14:10:52 +02:00
jgamboa fdf65766b5 Change to mynaDots icon, size change 2026-07-13 13:49:32 +02:00