P1: typed RPC bridge + FileSystemProvider + dual-panel local browsing
- 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>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
const SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
export function formatSize(bytes: number): string {
|
||||
let value = bytes;
|
||||
let unit = 0;
|
||||
while (value >= 1024 && unit < SIZE_UNITS.length - 1) {
|
||||
value /= 1024;
|
||||
unit++;
|
||||
}
|
||||
return `${unit === 0 ? value : value.toFixed(1)} ${SIZE_UNITS[unit]}`;
|
||||
}
|
||||
|
||||
const DATE_FORMAT = new Intl.DateTimeFormat(undefined, {
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
|
||||
export function formatDate(epochMs: number): string {
|
||||
if (!epochMs) return '';
|
||||
return DATE_FORMAT.format(new Date(epochMs));
|
||||
}
|
||||
|
||||
/** 0o755 -> "rwxr-xr-x" */
|
||||
export function formatMode(mode: number): string {
|
||||
const flags = 'rwxrwxrwx';
|
||||
let out = '';
|
||||
for (let i = 0; i < 9; i++) {
|
||||
out += mode & (0o400 >> i) ? flags[i] : '-';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user