Make existing controls real + add robustness pass

Wire up controls that were rendered but inert, expose unused server
actions, and harden the live update path.

Controls now functional:
- File priority dropdowns (Content tab) now persist via new
  setFilePriority server action
- Context menu: Set location, Limit download/upload rate, Set share
  limit (wire setSavePath/setDownLimit/setUpLimit/setRatioLimit)
- Trackers sidebar filter actually filters (snapshot now carries
  per-torrent trackerHosts)

Robustness:
- Grid updates cells in place when row order/columns are unchanged
  (preserves text selection/scroll, no per-second listener re-bind)
- Boot retries until the server responds; SSE drops show a
  "Reconnecting" banner; failed actions/add/delete surface a toast
- Prune selection/detail for torrents that disappear
- Drop Backspace-as-delete (kept Delete)

Cleanup:
- Remove pseudo DHT/PeX/LSD names from the real-tracker host pool in
  mock data (they leaked into announce URLs and the sidebar)
- Remove dead detailTab import/export

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ookami125 2026-06-16 21:24:20 -04:00
parent bc1be49a37
commit 0b35571565
5 changed files with 172 additions and 21 deletions

View file

@ -9,8 +9,6 @@ let currentHash = null;
let refreshTimer = null;
let detailHeight = 300; // px, persisted across re-renders so resizing sticks
export function detailTab() { return activeTab; }
export function renderDetailShell(hash, host) {
currentHash = hash;
// Drive the container's grid track (persists across row clicks / re-renders),
@ -58,7 +56,7 @@ async function loadTab() {
if (activeTab === 'general') body.innerHTML = renderGeneral(await api.torrent(currentHash));
else if (activeTab === 'trackers') body.innerHTML = renderTrackers(await api.trackers(currentHash));
else if (activeTab === 'peers') body.innerHTML = renderPeers(await api.peers(currentHash));
else if (activeTab === 'content') body.innerHTML = renderFiles(await api.files(currentHash));
else if (activeTab === 'content') { body.innerHTML = renderFiles(await api.files(currentHash)); bindFilePriorities(body); }
else if (activeTab === 'pieces') body.innerHTML = renderPieces(await api.pieces(currentHash));
} catch { /* ignore transient */ }
};
@ -176,6 +174,13 @@ function renderFiles(files) {
</tbody></table>`;
}
function bindFilePriorities(body) {
const hash = currentHash;
body.querySelectorAll('.prio-sel').forEach((sel) =>
sel.addEventListener('change', () =>
api.action('setFilePriority', [hash], { index: +sel.dataset.file, priority: +sel.value })));
}
/* ---------- Pieces ---------- */
function renderPieces(data) {
const { pieces, pieceSize, pieceCount } = data;