The concurrency setting now caps total concurrent file STREAMS, not just jobs:
a resizable counting semaphore is shared engine-wide, and each job copies its
files through a small worker pool drawing from it. One folder job can use the
whole budget; several jobs share it fairly; Single stays strictly serial.
Cancel/session-death tear down every in-flight stream of the job.
Verified headless: 6-file folder at budget 3 -> exactly 3 parallel streams;
budget 1 -> serial; two folder jobs never exceed the combined budget; cancel
mid-parallel leaves no straggler streams. Also demoed a real remote error
surfacing in a failed job (upload into /proc -> server message on the row).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Options popover: 'Finished transfers' Auto-clear | Keep (default auto-clear,
the previous behavior); persisted like the rest
- Transfer list header: 'N active · M finished' + Clear finished button
(dismisses completed/failed/cancelled only, never running/queued)
- Failed rows now name the file that failed alongside the error message
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Folder select showed '(none)' regardless of the actual folder (Angular sets
[value] on the select before its options render), so 'move out of folder'
silently saved the old folderId. Per-option [selected] bindings fix both the
display and the save.
- Folder rows gain 'Open all in tabs': each connection opens in its own new
tab with the left panel connecting (right stays local); manager closes.
- Folder-row action buttons no longer shift the row on hover (opacity toggle
instead of display toggle); also canonicalized accent-primary classes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Standard menus (App: hide/quit; Edit: clipboard roles enabling Cmd+C/V/X/Z;
View: Refresh Cmd+R + fullscreen; Window: minimize/zoom/cycle) plus File with
New/Close Tab (Cmd+T/W), New Folder/File, and a Connect submenu built from
saved connections (folders as nested submenus, Connection Manager entry).
Custom actions forward to the webview as menuAction messages; the menu rebuilds
whenever connections change. macOS-only per electrobun support.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Synthetic cursor-navigable first row (hidden at /) — Enter/double-click goes to
the parent like the toolbar button. Excluded from selection, rename, delete,
chmod, and transfers; shown with an up-arrow icon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tabs live in their own min-w-0 overflow-x-auto container (scrollbar hidden,
trackpad scrolls); the new-tab button and the options gear sit outside it and
stay pinned. The active tab scrolls into view when it changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
--row-h and --path-ctl-h CSS vars per size level; Compact brings the path
input/buttons to 24px, aligned with the panel action buttons.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tabs are draggable within the strip: HTML5 DnD with the WebKit setData quirk
handled, an inset primary insertion line at the drop position (no layout
shift), and the dragged tab dimmed. Reordering only touches the workspaces
array order; ids, sessions, and the active tab are unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Transfers: Single | Multi in the options popover, with an 'At once' picker
(2/3/4/6/8) when Multi. Engine concurrency becomes an instance field applied
live: at boot from settings.json and on every saveSettings (raising the limit
starts queued jobs immediately). Verified headless: 3 slow jobs overlap at
concurrency 3, stay strictly serial at 1, clamp works.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Segmented row in the options popover; applied via data-text on <html> driving
CSS variables (--list-font / --list-meta-font / --path-font), persisted in
settings.json like theme and speed unit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 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>
Freeze root cause: TransfersService consumed transferUpdate through an effect
that both read and wrote the jobMap signal — the first progress message spun
the effect into an infinite re-run loop, pegging the webview thread while the
copy completed fine bun-side. Push messages now flow through plain callback
registries on RpcService (also stops signal coalescing from dropping progress
snapshots). Verified live: a 60MB boot-time self-test copy streamed progress
into the running UI and the post-completion panel refreshes arrived.
- Tabs: workspaces (left+right PanelState pair each) in PanelsService; tab bar
replaces the header; sessions persist in background tabs; Cmd+T/Cmd+W/Ctrl+Tab;
closing a tab disconnects its sessions; last tab can't close
- Per-panel toolbar (top, icon-only): direction-explicit copy (-> / <-),
rename, new folder/file, chmod, delete; panel footers removed
- VSCode-style status bar: transfer summary (spinner/percent/queued/failures,
click to expand the job list) + active panel info; function bar removed
- transfer details list is collapsible, auto-expands on failure
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- assets/icon.png (generated by scripts/make-icon.py) + icon.iconset
(scripts/gen-iconset.sh); electrobun compiles it to AppIcon.icns
- branding.icon points at the iconset
- README: features, stack, dev/build, rebranding, plaintext-password warning,
shortcuts, v1 scope
- verified: full production build (ng build + electrobun build) produces a
launchable BunSCP.app that boots standalone from views://app
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- prompt/confirm/chmod dialogs (signals-driven via DialogsService)
- FileActionsService: acts on selection or cursor row of the active panel
- function bar + F2/F7/F8/Delete keys; delete confirms and recurses
- verified provider ops + walkTree + removeRecursive on a scratch tree
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- shared/types + shared/rpc: full wire contract (Result envelope, no exceptions cross the bridge)
- FileSystemProvider interface, LocalProvider, SessionRegistry with built-in 'local'
- walk.ts: provider-generic recursive walk + removeRecursive
- rpc-handlers wired into BrowserWindow; 10min request timeout for slow SFTP ops
- Angular: RpcService (Electroview bridge + plain-browser guard), PanelsService with
signals-based PanelState (sort/cursor/selection), FilePanel component with
keyboard nav (arrows/Space/Enter/Backspace/Tab), path bar, toasts
- verified: both panels listDir home over the bridge in the running app
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Electrobun 1.18 shell with branding in src/shared/branding.ts
- Angular 22 zoneless UI in ui/ (workspace), Tailwind v4 shadcn-style tokens
- ng-icons/mynaui icon registry
- Node-version shim so Angular CLI runs under Bun without Node installed
- ssh2 SFTP verified working under Bun against test box
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>