mirror of
https://github.com/phantomptr/ps5upload.git
synced 2026-10-22 03:42:55 -05:00
* fix(connection): a console whose key changed opens the pairing dialog, not a 20 s retry The helper check (step 2) carried the engine's sentence "a different device answered at this address", not the ava1_wrong_console token, so it never opened the pairing dialog: it polled for 20 s and told the user to send the helper again, which cannot help. The sentence now counts as a pairing problem; step 2 stops polling and opens the dialog, which offers "Forget the old one and pair this one". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(zip): a resumed whole file is finished when the next file arrives before its commit After a resume, a file with every byte durable but no Done is in flight with written == size; its commit writes the descriptor. When the next file's data reached the sink first, append refused it ("file 6 ended at 1048576 of 1048576 bytes") and the Stored-zip download failed instead of resuming (seen on main CI). The sink now finishes that file itself, as commit would; a late commit is a no-op. Regression test added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(ci): transfer tests lift their throttle once interrupted; optimize crypto deps in dev ava1_transfer_integration ran past the 25-minute CI step: resumes after a planned interruption kept crawling at the test's throttle, and the crypto/hash/compression crates are 10-50x slower unoptimized. ChaosProxy gets a live set_bytes_per_sec; the cancel-midway and mid-stream-drop tests lift the cap once the interruption has landed. Only those dependency crates build at opt-level 3 in dev (our own code stays debuggable). post_commit_failure_is_not_a_resend is capped so the destination is created while data still flows. Locally: 251 s -> 98 s for the transfer suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(test): the slow-disk label is not checked on CI runners, where the debug sender is slower than the disk Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * feat(files): add folders and drag-and-drop uploads; queue and activity follow the selected console - Files: Add folder (uploads it whole into the open folder) and drag-and-drop of files and folders from the file manager, with a drop highlight. A dropped .pkg is copied there, not offered for install (/files now owns its drops). - Upload's queue shows only the selected console's items (it showed every console's); Activity likewise, plus local-only work. - New strings translated in all 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * revert(ci): drop the dev opt-level overrides for crypto crates Lifting the throttle after the planned interruption gives most of the speedup alone (transfer suite 251 s -> 112 s locally; 98 s with the overrides). With the overrides the resume-after-cancel tests failed under ASan and coverage on CI only (progress never crossed the cancel threshold before the upload ended); main passes them. Not worth the risk for 14 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(test): the dest-ends relay test caps its source so the destination ends it mid-transfer On a fast runner the uncapped 128 MiB relay finished before the destination's first durable report (which is what ends its job). The source is now read at 16 MiB/s (~8 s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * feat(files): right-click menu, keyboard shortcuts, column sorting, typed path FileZilla-style browsing on the Files screen: - right-click a row: Open, Download, Cut, Copy, Paste into (folders), Rename, Copy path, Delete - double-click opens; Ctrl/Cmd+C/X/V/A, Delete, F2 rename, F5 refresh, Backspace up, Enter, Esc - sort by Name, Size or Modified (folders first) - the path bar is editable: type a path and press Enter Paste can target any folder now (the row menu's Paste into). Strings in all 21 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(ava1): a lane that joins right after Welcome is no longer refused as an unknown session Both servers sent Welcome before registering the session, and a client opens its lanes the moment it is welcomed: under load a Join could beat the registration and be refused ("unknown session"), dropping the connection. The console's C server now registers the session before Welcome goes out; the Rust server's lane waits briefly (2 s, within the join deadline) for the registration. Seen in CI coverage (close_race, 48 concurrent sessions). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * 6.1.0: version and changelog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(ava1): a dropped upload job wakes its readers instead of leaking a parked thread The sender's readers park on the read-ahead budget inside spawn_blocking; only closing it wakes them, and that ran only on a normal exit. A job future dropped mid-job (a relay whose destination ended, a torn-down session task) left a reader parked for good: a leaked blocking thread that also kept its runtime from shutting down. That was the relay test that hung CI on small runners (reproduced 3/4 on 2 CPUs in Docker; 8/8 pass with the fix). A drop guard now stops and wakes the readers however the job ends. Regression test added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * docs(changelog): 6.1.0 lists the stopped-copy fix Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff * fix(test): the launches test waits for a launch to start instead of a fixed 80 ms Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XR8fQMhp7Rdgq2e4U9oDff --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
// Desktop drag-and-drop of a path onto a screen, through the webview's drop events (Tauri
|
|
// delivers host paths). Not on Android or in the browser build, where pickers cover it.
|
|
|
|
import { getCurrentWebview } from "@tauri-apps/api/webview";
|
|
import { useEffect, useRef, useState } from "react";
|
|
|
|
import { isAndroid } from "./platform";
|
|
import { isTauriEnv } from "./tauriEnv";
|
|
import { engineIsOnThisDevice } from "../state/engine";
|
|
|
|
/** A .pkg is a package to install; AppShell's app-wide drop listener routes those. */
|
|
const isPackage = (path: string) => /\.pkg$/i.test(path);
|
|
|
|
/**
|
|
* Calls `onDrop` with the first dropped path while `enabled`; returns whether a drag is over
|
|
* the window, for highlighting a drop zone. The subscription is cleaned up even when it
|
|
* resolves after unmount, as Upload's is.
|
|
*/
|
|
export function useWebviewDrop(onDrop: (path: string) => void, enabled: boolean): boolean {
|
|
return useWebviewDropCore(
|
|
(paths) => {
|
|
const first = paths[0];
|
|
if (first && !isPackage(first)) onDrop(first);
|
|
},
|
|
enabled,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Every dropped path, packages included, while `enabled` (the Files screen copies whatever is
|
|
* dropped into the open folder). Returns whether a drag is over the window.
|
|
*/
|
|
export function useWebviewDropAll(onDrop: (paths: string[]) => void, enabled: boolean): boolean {
|
|
return useWebviewDropCore((paths) => {
|
|
if (paths.length > 0) onDrop(paths);
|
|
}, enabled);
|
|
}
|
|
|
|
function useWebviewDropCore(onDrop: (paths: string[]) => void, enabled: boolean): boolean {
|
|
const [active, setActive] = useState(false);
|
|
const latest = useRef({ onDrop, enabled });
|
|
// The subscription outlives renders; it reads the newest handler through this ref.
|
|
useEffect(() => {
|
|
latest.current = { onDrop, enabled };
|
|
});
|
|
|
|
useEffect(() => {
|
|
// A dropped path is on this device; a remote engine can't open it.
|
|
if (!isTauriEnv() || isAndroid() || !engineIsOnThisDevice()) return;
|
|
let unlisten: (() => void) | null = null;
|
|
let cancelled = false;
|
|
const p = getCurrentWebview().onDragDropEvent((e) => {
|
|
if (cancelled) return;
|
|
const t = e.payload.type;
|
|
if (t === "enter" || t === "over") setActive(latest.current.enabled);
|
|
else if (t === "leave") setActive(false);
|
|
else if (t === "drop") {
|
|
setActive(false);
|
|
if (latest.current.enabled) latest.current.onDrop(e.payload.paths ?? []);
|
|
}
|
|
});
|
|
p.then((fn) => {
|
|
if (cancelled) fn();
|
|
else unlisten = fn;
|
|
}).catch(() => {
|
|
// The webview went away before the subscription completed: nothing to undo.
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
unlisten?.();
|
|
};
|
|
}, []);
|
|
|
|
return active;
|
|
}
|