Skip to content

feat(ui): add auto-close toggle when all torrent downloads complete - #22

Merged
kaiserc merged 23 commits into
mainfrom
feat/auto-close-on-complete
Sep 6, 2026
Merged

feat(ui): add auto-close toggle when all torrent downloads complete#22
kaiserc merged 23 commits into
mainfrom
feat/auto-close-on-complete

Conversation

@kaiserc

@kaiserc kaiserc commented Sep 2, 2026

Copy link
Copy Markdown
Owner

What and why

Adds an on-demand auto-close toggle (\Q\ / Shift+Q) that allows users to leave the app downloading unattended, automatically and cleanly shutting down the application once the last active/queued torrent has completed its download.

  • State & Action: Added \�utoClose: boolean\ and \ oggleAutoClose()\ to the UI store.
  • Queue Monitoring: When a download completes and \�utoClose\ is enabled, the app checks if any items remaining in the queue have status \downloading\ or \queued. If none remain, it displays a notice and schedules a clean exit in 2 seconds (allowing async file moves to settle).
  • Cancellation: If the user toggles auto-close off, any pending shutdown timer is immediately aborted.
  • UI Indicators: Shows \⏱ Auto-close\ in the top header and \Q Auto-close\ in the footer.
  • Cheatsheet & Keys: Added to \HELP_GROUPS\ under Navigate and Downloads.
  • Preview & Test Parity: Kept \makeStore\ in
    ender-previews-impl.tsx\ and \makeTestStore\ in \ estHarness.ts\ synchronized.

Checklist


  • pm run typecheck\ is clean

  • pm test\ passes (590 tests across 67 test files)
  • New logic has tests (vitest)
  • Keys updated in both \HELP_GROUPS\ and \ ooterHints\ in \src/ui/keymap.ts\
  • \Store\ field added to \makeStore\ in \scripts/render-previews-impl.tsx\ and verified via
    pm run previews\
  • OS-touching code works across all platforms
  • Conventional Commits title

ralyodio and others added 23 commits August 3, 2026 12:57
* Add .github/workflows/coinpay.yml via sh1pt coinpay-invoice@1.0.0 (#3)

Co-authored-by: sh1pt-actions-fleet[bot] <287014002+sh1pt-actions-fleet[bot]@users.noreply.github.com>

* Add .github/workflows/vu1nz-scan.yml via sh1pt vu1nz-scan@1.0.1 (#2)

Co-authored-by: sh1pt-actions-fleet[bot] <287014002+sh1pt-actions-fleet[bot]@users.noreply.github.com>

* chore: add ThreatCrush security scan on pull requests

vu1nz-scan.yml reviews a PR's diff with an LLM and needs an API key to do it.
This is the other half: offline static analysis over the whole tree, no
secrets, no network beyond the install. It looks for what a diff review is
worst at spotting — a credential committed three releases ago, an injection
pattern in a file nobody touched this PR — and writes SARIF, so findings land
in the Security tab with history rather than only in a comment.

Fails on critical/high to match vu1nz-scan.yml, which already exits 1 on
high/critical. That floor is quieter than it sounds: ThreatCrush caps
pattern-confidence findings at medium, so a bare "this construct exists on
this line" match cannot break a build. Only contextual and evidence findings
reach it. main is at 0 critical / 0 high today, so this is green on arrival
and needs no suppressions.

Node 22, not the pack's 20: it matches engines.node and the CI matrix floor.
Not 24 — better-sqlite3 has no prebuild there yet and the install falls
through to a node-gyp source build and fails.

The workflow is arranged so a scan that did not run can never report as a scan
that found nothing. The SARIF file, not the exit code, is the evidence a scan
happened; --format support is checked up front rather than inferred from an
exit code, because a CLI without it exits 1 on the unknown option, which is
the same code the CLI uses for "findings at or above --fail-on".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: sh1pt-actions-fleet[bot] <287014002+sh1pt-actions-fleet[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: added support for highlighted rows in search results

* feat: added bold to Source Text

* feat: added highlights to Added date

* feat: added highlighted item support for downloaded items

* feat: added highlighted item support for seeding list

* fix: formatting

* fix: removed highlight colors and only relying on dimColor

* fix: formatting

* fix: dim happens when the item is not selected

* fix: removed highlight colors

---------

Co-authored-by: dev <static.aron@gmail.com>
…aairon#146)

magnetFromTorrentFile threw the announce list away and rebuilt the magnet
from the public defaults alone, so a torrent that isn't on the public DHT --
a private tracker, a small private swarm -- sat at zero peers forever. On a
private tracker the passkey that makes an announce work at all lives in that
URL, so dropping it is fatal rather than just slower. This is the path both
the watch folder and `torlnk <file>.torrent` take.

The file's own trackers now go in ahead of the defaults, deduplicated, and
buildMagnet takes them as an optional third argument so every existing caller
is untouched. Since we're already stat-ing to read the file: a .torrent is
metadata and stays in the low megabytes, so anything larger is refused rather
than pulled into memory whole -- a watch folder takes whatever is dropped in
it, including a mis-named disk image.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Dropping a file on a terminal pastes its path into whatever is reading
input, which is already how people expect to hand a client a .torrent. The
search field took a magnet or an infohash but treated a path as a search
query, so the obvious gesture silently searched for "C:\Users\..." instead.

Each emulator escapes that path its own way and none of them agree: Windows
Terminal and PowerShell wrap it in double quotes and leave a trailing space,
macOS Terminal and iTerm2 escape spaces and parens with backslashes, GNOME
Terminal pastes a percent-escaped file:// URI. resolveTorrentPath unwraps
all three back to a plain path, with the backslash rule split by platform --
an escape on macOS and Linux, a path separator on Windows, so a Windows path
is never mangled. Its tests drive both platforms from either host rather
than only the half that matches the runner.

Paste (v) takes a path too, since copying a file in a file manager puts one
on the clipboard, and `torlnk <file>.torrent` runs its argument through the
same normalizer so a shell-quoted or file:// argument works there as well.
A path that turns out not to be a readable torrent says so rather than
falling back to a search, so the failure is never silent. No new key, no
change to an existing one -- Enter on the search field already meant "do
something with this", and this widens what it accepts.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Follow-up to baairon#184.

runSeed returned as soon as the torrent was added, so the process stayed
alive only because webtorrent's handles did and queue.suspend() never ran on
shutdown. watch and serve both hold themselves open and suspend on both
signals; seed now does the same, and the CLI exits once that flush returns.

POST /add accepts a base64 .torrent, which is one 20-byte hash per piece and
grows by a third again in base64. The 64KB body cap was sized for magnets, so
it fit every torrent small enough to test with and answered 413 on a large
multi-file release.

startEngine prefers the stored .torrent, which does not carry the announce
URLs that mergeMagnetTrackers folds onto a row assembled from several
sources. Metadata is saved as soon as it arrives, so resuming a partial
download dropped them; the magnet's trackers are now passed as announce
regardless of which source wins.

The new path tests asserted POSIX literals, which resolve drive-qualified on
Windows and failed the suite there. Also plainer seed wording in the README
and --help, and readParsed re-indented after its extraction.
The concurrency group is keyed on github.ref, which is one value for all of
main, so cancel-in-progress cancelled the run still in flight whenever a
second merge landed before the first had finished. A batch of stacked merges
left every commit but the last with no CI result of its own.

Cancellation is now scoped to pull requests, where superseding the run in
flight is the intent: only a branch's latest commit needs a verdict. Every
commit pushed to main keeps its own run.

Bump to 1.8.0.
Adds an auto-close toggle (Q / Shift+Q) that allows the application to cleanly shut down once the last active/queued torrent finishes downloading.

- Adds autoClose state and toggleAutoClose method to Store
- Listens to queue 'completed' event and schedules graceful shutdown (with a 2s settle delay for background file moves) when no active/queued downloads remain
- Displays '⏱ Auto-close' badge in the header and status feedback in the footer
- Adds Q shortcut to HELP_GROUPS under Navigate and Downloads
- Keeps previews and test harnesses in sync
Adds an auto-close toggle (Q / Shift+Q) that allows the application to cleanly shut down once the last active/queued torrent finishes downloading.

- Adds autoClose state and toggleAutoClose method to Store
- Listens to queue 'completed' event and schedules graceful shutdown (with a 2s settle delay for background file moves) when no active/queued downloads remain
- Displays '⏱ Auto-close' badge in the header and status feedback in the footer
- Adds Q shortcut to HELP_GROUPS under Navigate and Downloads
- Keeps previews and test harnesses in sync
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

0 finding(s)

No findings.

@kaiserc
kaiserc merged commit 9f2ab93 into main Sep 6, 2026
5 checks passed
@kaiserc
kaiserc deleted the feat/auto-close-on-complete branch September 6, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants