c4364c10512d7fa712a2cce5005fecc966f1a473
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c4364c1051 |
Fix the dead win7 context menu, flatten the selection, add a tab menu
Right-click did nothing under the win7 skin, and then swallowed the next
click. 7.css declares `position: relative` on ul[role=menu] and the `skin`
layer outranks `utilities`, so Tailwind's `.fixed` lost: [style.left/top.px]
became flow offsets from the menu's static position — the last child of the
h-screen frame — and every menu rendered below the viewport, unreachable
behind overflow:hidden. Its backdrop is a plain <div>, unmatched by 7.css, so
that stayed genuinely fixed and ate the following click. Regressed in
|
||
|
|
89af52e1c0 |
Clip the close button's hover glow at the frame edge; add a hover probe
Hovering the win7 X painted a red mark on the window border's far edge. Diagnosed programmatically, per request, with a new scripts/hover-probe.ts: Firefox headless has no pointer, so the probe copies the built stylesheet with every `:hover` rewritten to a `.simhover` class, renders the app's exact frame markup (w7-frame + direct-child title-bar + aria-label'd controls) with the X force-hovered, and pixel-scans four regions for red-dominant pixels, printing a verdict table and exiting non-zero on artifacts. Verdict from the first run: the right border and the region beside it were CLEAN — the reported mark is the glow escaping over the TOP edge. 7.css's hover ::before carries box-shadow 0 0 7px 3px #e68e75, a child's box-shadow is not clipped by the parent's border or radius, and the caption buttons sit flush against the window's top edge (border-top: 0) — so 100 red pixels landed above the frame: a tint across the top border and a halo floating on the desktop past it. Fix: clip-path inset(0 0 -1rem 0) on `.w7-frame > .title-bar` in skinfix. The glow now dies at the frame border — where the real Win7 DWM stopped it — and the bottom stays open so the authentic bleed toward the caption survives. clip-path clips hit-testing too, but only outside the region; the drag area is the title-bar's own box, entirely inside. Scoped to w7-frame so harness and demo windows keep stock 7.css behaviour. Verified: probe verdict now clean (escape regions 0, the intended in-frame glow still measures 30 px — the effect survived, only the escape is gone); the resting-state harness diffs exactly zero, so nothing but hover pixels changed; tsc + build clean. The probe stays in scripts/ as the regression test; gotchas.md records both the clipping rule and the :hover -> class technique. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
9c54a441a9 |
Scale stage 3: shortcuts service + Ctrl/Cmd +/-/0 for the scale
Groundwork for the vim-style bindings planned next: the app previously had NO
keyboard abstraction — `event.metaKey || event.ctrlKey` copy-pasted inline in
six places, no typing-target guard anywhere, every window root matching keys
by hand.
New @core/shortcuts.service.ts owns:
- isMod() (also accepts MouseEvent — Mod+click toggle-select uses the same
rule) and isTypingTarget();
- ONE declarative keymap: resolve(event, scope) -> ShortcutId | null, scoped
main / editor / manager. Layout rules are encoded once: digits, +/- and
Alt-letter chords match on event.code (AZERTY digit rows, layouts where '+'
needs Shift, macOS Option-Z producing Ω); plain letter chords match on
event.key so they follow the user's layout.
Both app.ts, both editor-window.ts and both connection-manager.ts now resolve
through the service and switch on ids — behaviour-preserving for every
existing shortcut, including the deliberate quirks (Ctrl+Tab never meta
because Cmd-Tab is the macOS app switcher; Cmd+9 = last tab; editor Mod+W is
the Windows/Linux arm, macOS routes via the app menu). Escape and Tab stay
component-local on purpose: their meaning is per-window state (find bar open?
close prompt up? caret in the textarea?), not a global binding. The file
panels keep their contextual key state machine but delegate modifier tests.
On top of the table: Ctrl/Cmd +/-/0 steps/resets the interface scale in every
window kind, all three previously unclaimed. isTypingTarget is applied
per-shortcut, not globally in resolve(): the scale keys must not fire while
typing ('-' in a rename prompt must type '-'), but the editor WANTS Mod+S
mid-typing and the tab shortcuts keep their existing reach.
Docs: architecture.md's density prose now describes the uiScale lever;
gotchas.md gains the scale section — the disassembly evidence that native
page zoom is an empty stub on Linux (so nobody tries it again), the
everything-must-be-rem rule, the skinfix-outranks-variants trap the harness
caught (48px close button silently 29px), and the px-mirrors-in-TS class of
bug with the remPx() remedy.
Verified: tsc + build clean; live boot with the user's real legacy settings
file migrates textSize:large -> uiScale 1.2 and renders scaled. Not verified
here (needs interaction): the stepper clicks and the shortcut keys themselves.
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
c8ccc6b918 |
Skin is a launch-time choice; stop applying it to a live window
Switching Modern -> Win7 at runtime broke the UI. SettingsService.applySkin toggled the `.win7` class the moment the setting changed — harmless when there was one component tree, and a broken window as soon as there were two. The tree is bootstrapped once from the preload-baked __APP_SKIN, so the live flip painted 7.css onto shadlike markup that has no .window.glass, no .title-bar, no menu[role=tablist] and no <table> ListView. The CSS landed on a tree that cannot receive it. A reload would not have helped, and neither would anything short of a restart: - The preload is a persistent WebKitUserScript injected at DOCUMENT_START, so its string is frozen at webview construction — loadURL() re-runs it with the OLD __APP_SKIN. updatePreloadScriptToWebView exists in the FFI but is reachable only from electrobun's internal <electrobun-webview> handler. - titleBarStyle/transparent have no setters; a live window cannot be re-framed. win7 -> modern is the dangerous direction — the modern tree draws no title bar, so it would land in a frameless transparent window with no way to move, minimize or close it. - Recreating the main window is also out: exitOnLastWindowClosed is true, the main rpc is captured by six push closures, the tray captures `win`, and listeners cannot be removed. So: applySkin is deleted outright, from setSkin AND from applyAll — the latter matters independently, since applyAll runs on the settingsChanged push and was therefore breaking every open editor and manager window too. Clicking a skin now only persists it. Also fixed a second leak found while tracing this: manager-window and editor-windows build from `currentSettings()`, the LIVE snapshot, so a window opened after a pending change would boot the new tree and frame while main still ran the old one. `initSessionSkin` now freezes the skin at startup before any window exists, and buildPreload/chromeOptions read that pinned value — chromeOptions loses its parameter so no call site can get it wrong. The options row shows the saved value as pending (skinPending() compares settings.skin() against APP_SKIN) with the hint retexted in all 7 locales; it said "Reopen the window to change its frame", which described the deleted design. Verified: tsc + build:ui clean; both skins boot correctly from a persisted setting (win7 frameless, modern with the native frame). The live flip is now impossible by construction, which is the check that matters here — the UI bundle contains no classList write for win7 at all, the only writer is buildPreload, and sessionSkin has exactly one assignment site. Not verified: the click itself and the pending hint appearing, which need an interaction pass — no window automation is available here. Both are present in the built bundle. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
5bbfc2f475 |
Document the core/skins split
architecture.md gains a "UI layout: one core, two skin trees" section — the folder map, the barrel contract that lets main.ts stay ignorant of both trees, why duplicate selectors across trees are safe, and where a new rule belongs (core / a skin / shared tokens). Its "Window kinds" section now describes two-axis root selection, and the "Skins" section is no longer stale prose about a deleted design: it covers the stylesheet split and the two-phase nature of a skin change (CSS live, tree and frame at next window open). gotchas.md gains a section for the split, all of it earned during the work: `.win7` cannot be renamed because 7.scoped.css is precompiled against that literal; reach core through @core/* rather than relative paths, which is what let 67 imports survive two relocations; @font-face url() rebases when a stylesheet moves and fails silently; the @layer statement must stay first; duplicate selectors across trees are safe; and ngtsc type-checks components nothing imports, so a green build does say something about the inactive skin. Also corrected the component paths that moved under skins/, and the updater.service.ts link that moved under core/. Checked every ../ link in docs/ resolves. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
06a14fb6a1 |
Make w7-flat actually flatten; document this round
The status bar's transfers summary is a text toggle, but 7.css painted it as a full Aero push button — a stray bordered slab sitting in the status line. Marked it w7-flat, the per-button escape hatch that existed for exactly this and had never been used. It did not work, for a reason worth keeping: 7.css restates border-color at `.win7 button:not(:disabled):not(:hover)` = (0,3,1), which outranks `.win7 .w7-flat` = (0,2,0). The .w7-toolbar arm of the same rule only ever won that fight by tying on specificity and landing later in the same layer — an accident w7-flat did not share. Split the rule: `background: revert-layer` stays in `skin` (it must land on `utilities`), and the paint-suppression literals move to `skinfix`, where layer order settles it instead of a specificity arms race. Docs updated in the same change per the working agreement — architecture.md gains the tabs contract, the w7-flat/w7-toolbar split and the frame transparency invariant; gotchas.md gains a win7-skin section (unlayered beats every layer, the (0,3,1) border-color restatement, overflow-x clipping the y-axis, 7.css's hardcoded light literals, exact selectors) plus the two Angular traps this round hit: a duplicate `class` attribute is silently dropped, and a backtick inside a component template's HTML comment terminates the TS template literal. Also recorded the verification trap that cost three wrong readings today: `bun run start` leaves the old instance running and `import -window` grabs the OLDEST match, so screenshots silently come from a stale build — this is what made the w7-flat fix above look like a no-op when it was already correct. Kill and count instances before capturing. And `pkill -f 'electrobun dev'` matches the shell running it and kills your own command. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
ef34104cf0 |
Fix white/inverted flash when windows open
Three layers: windows are created hidden and revealed on the webview's first dom-ready (800ms fallback); the bun preload bakes the persisted theme data-attributes so the first paint is already themed; and index.html carries an inline background style covering the pre-stylesheet paint (values mirrored from styles.css tokens). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc5cb1f707 |
Developer documentation: architecture, build/release, platforms, gotchas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |