From 90c3c6a01f46c9b29c5d92702d7383f32e8951d7 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 19 Aug 2026 16:36:29 +0200 Subject: feat(ui): 11-point UI overhaul — tabs, transfers, settings, uploads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hub/UI: - Icon-only group tabs (chat, files, settings) with per-group default tab - Transfer widget: filename becomes a clickable link to open completed downloads - Pulse animation on transfer icon (pale→dark green) while active - Download button feedback in FilePreview (spinner, auto-reset) - Group mute toggle persists across navigation - Login page autofocus, chat refocus after send - Theme toggle closes menu, status badge and duplicate connecting removed - Create-folder restricted to operators, download-path note removed - User preferences API (CRUD) with Alembic migration - Profile: email display/edit via PATCH /v1/users/me - Settings: "Defaults" section for default tab selector - All 10 locale files updated Node: - upload_dir in node.toml: separate filesystem path for uploads - Root.direct flag: uploads land at root path, no subdirectory - CLI --upload-dir flag on `group add` - Admin UI accepts upload_dir Client (Electron): - shell.openPath bridge for opening completed downloads - platform.js passes open callback from native save Co-Authored-By: Claude Opus 4.6 --- packages/meshbay-client/src/main.js | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'packages/meshbay-client/src/main.js') diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js index 6228189..0ed06d9 100644 --- a/packages/meshbay-client/src/main.js +++ b/packages/meshbay-client/src/main.js @@ -505,6 +505,7 @@ function registerBridge() { // The renderer still never names a path. It asks; the user chooses once; the // main process holds the handle and the renderer refers to it by an opaque id. const sinks = new Map(); + const completedPaths = new Map(); let sinkId = 0; /** `name`, or the first "name (n).ext" that is not taken — never an overwrite. */ @@ -611,10 +612,18 @@ function registerBridge() { const sink = sinks.get(String(id)); if (!sink) return false; sinks.delete(String(id)); + completedPaths.set(String(id), sink.path); await new Promise((resolve) => sink.stream.end(resolve)); return true; }); + ipcMain.handle('save:open', async (_e, id) => { + const p = completedPaths.get(String(id)); + if (!p) return false; + await shell.openPath(p); + return true; + }); + ipcMain.handle('save:abort', async (_e, id) => { const sink = sinks.get(String(id)); if (!sink) return false; -- cgit v1.2.3