v1.1: fix transfer freeze, session tabs, per-panel toolbars, status bar
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>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Electroview } from 'electrobun/view';
|
||||
import type { AppRPC } from '@shared/rpc';
|
||||
import type { TransferSnapshot } from '@shared/types';
|
||||
@@ -13,8 +13,11 @@ export class RpcService {
|
||||
/** False when the page is opened in a plain browser instead of the app. */
|
||||
readonly available: boolean;
|
||||
|
||||
readonly transferUpdate = signal<TransferSnapshot | null>(null);
|
||||
readonly sessionClosed = signal<SessionClosedEvent | null>(null);
|
||||
// Push messages are delivered through plain callbacks, NOT signals: a signal
|
||||
// consumed from an effect that also writes signals re-triggers itself
|
||||
// (this froze the app), and signal coalescing would drop progress snapshots.
|
||||
private transferListeners = new Set<(s: TransferSnapshot) => void>();
|
||||
private closedListeners = new Set<(e: SessionClosedEvent) => void>();
|
||||
|
||||
private rpc: ReturnType<typeof Electroview.defineRPC<AppRPC>> | undefined;
|
||||
private ready: Promise<void> = Promise.resolve();
|
||||
@@ -27,8 +30,12 @@ export class RpcService {
|
||||
maxRequestTime: MAX_REQUEST_TIME_MS,
|
||||
handlers: {
|
||||
messages: {
|
||||
transferUpdate: (snapshot) => this.transferUpdate.set(snapshot),
|
||||
sessionClosed: (event) => this.sessionClosed.set(event),
|
||||
transferUpdate: (snapshot) => {
|
||||
for (const listener of this.transferListeners) listener(snapshot);
|
||||
},
|
||||
sessionClosed: (event) => {
|
||||
for (const listener of this.closedListeners) listener(event);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -43,7 +50,14 @@ export class RpcService {
|
||||
socket.addEventListener('error', () => resolve(), { once: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onTransferUpdate(listener: (snapshot: TransferSnapshot) => void): void {
|
||||
this.transferListeners.add(listener);
|
||||
}
|
||||
|
||||
onSessionClosed(listener: (event: SessionClosedEvent) => void): void {
|
||||
this.closedListeners.add(listener);
|
||||
}
|
||||
|
||||
/** Typed request proxy: `await rpc.request.listDir({...})`. Waits for the bridge. */
|
||||
|
||||
Reference in New Issue
Block a user