v1.2: overwrite confirm, transfer speed/ETA, settings + themes, fused macOS titlebar

- Overwrite confirmation: name-collision detection before enqueue; dialog shows
  source vs destination (size, mtime, 'newer' badge) with per-item Skip/Replace
  and batch Skip all / Replace all; type mismatches auto-skip with a toast;
  cancel aborts the whole operation
- Transfer speed + ETA: UI computes smoothed bytes/sec from progress deltas
  (callback bus makes deltas meaningful); shown in the expanded queue rows and
  the status-bar summary; unit follows the speed-unit setting
- Settings: bun-side SettingsStore (settings.json, 0600, atomic) + get/save RPC;
  SettingsService; gear popover on the tab row (theme segmented control, speed
  unit); persisted across restarts
- Light theme: data-theme on <html> drives token overrides; absent = follow OS
- macOS fused titlebar: hiddenInset + trafficLightOffset, tab row becomes the
  draggable titlebar (app-region drag/no-drag classes); other platforms keep a
  separate titlebar (gated by a platform value injected via window preload)
- README: known-issues note for the upstream Bun/Electrobun FFI crash

Verified: tsc + ui build clean; SettingsStore roundtrip + 0600 headless;
app boots and the UI fetches settings over RPC at startup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-11 17:14:50 +02:00
co-authored by Claude Fable 5
parent 262f4de04d
commit 6048d5c814
19 changed files with 571 additions and 14 deletions
+13 -1
View File
@@ -3,6 +3,7 @@ import { BRANDING } from "../shared/branding";
import { SessionRegistry } from "./fs/registry";
import { createAppRPC } from "./rpc-handlers";
import { ConnectionStore } from "./store/connections";
import { SettingsStore } from "./store/settings";
import { TransferEngine } from "./transfers/engine";
const PROD_URL = "views://app/index.html";
@@ -28,10 +29,13 @@ async function getMainViewUrl(): Promise<string> {
const registry = new SessionRegistry();
const engine = new TransferEngine(registry);
const store = new ConnectionStore();
const settings = new SettingsStore();
const events = { sessionClosed: (_info: { sessionId: string; reason: string }) => {} };
const rpc = createAppRPC({ registry, engine, store, events });
const rpc = createAppRPC({ registry, engine, store, settings, events });
const url = await getMainViewUrl();
const isMac = process.platform === "darwin";
new BrowserWindow({
title: BRANDING.appName,
url,
@@ -41,6 +45,14 @@ new BrowserWindow({
x: 120,
y: 120,
},
// macOS: fuse the tab bar with the titlebar — traffic lights overlay the
// UI's h-10 tab row (offset centers them vertically). Other platforms keep
// the native titlebar and the tab bar stays a separate row.
...(isMac
? { titleBarStyle: "hiddenInset" as const, trafficLightOffset: { x: 12, y: 13 } }
: {}),
// lets the UI pick the fused layout before first paint
preload: `window.__BUNSCP_PLATFORM=${JSON.stringify(process.platform)};`,
rpc,
});