Skip to content

feat(task): add scheduled starts, drag reorder, and preemptive queue cap - #124

Merged
YueMiyuki merged 4 commits into
masterfrom
next-dev
Jul 5, 2026
Merged

feat(task): add scheduled starts, drag reorder, and preemptive queue cap#124
YueMiyuki merged 4 commits into
masterfrom
next-dev

Conversation

@YueMiyuki

@YueMiyuki YueMiyuki commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary by cubic

Adds per-task scheduled starts, drag-and-drop queue reordering, and a preemptive queue cap so downloads start on time and the queue stays easy to manage. Improves HTTP compatibility with HTTP/2 and per-task User-Agent overrides, fixes Cloudflare retries and DateTimePicker issues, and adds CI updater manifest generation.

  • New Features

    • Schedule downloads from Add Task or “Schedule…”, with a “Scheduled” status/tab. Tasks auto-start on time; missed schedules prompt on launch with “Start All”. “Start Now” lets you start a scheduled task immediately.
    • Drag to reorder tasks (with keyboard support), replacing “Move Up/Down”.
    • Preemptive queue cap so scheduled tasks can start predictably.
    • HTTP: enable HTTP/2 (ALPN) and honor per-task User-Agent.
    • Release: add updater manifest builder with tests and workflow tweaks for standardized assets.
  • Bug Fixes

    • Cloudflare retry now uses the imported cookie header and user agent correctly; DateTimePicker behavior fixed. Docs/error links now point to risuko.app.

Written for commit b7c9dec. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR introduces scheduled task starting (new "Scheduled" status, engine scheduler logic, backend commands, store/API wiring, ScheduleDialog/MissedScheduleDialog UI), replaces task move-up/down with drag-and-drop/keyboard reordering, adds a signed updater manifest build pipeline in the release workflow, enables HTTP/2 ALPN in the HTTP client, adjusts engine HTTP header/cookie/filename handling, and updates official website URLs plus minor fixes.

Changes

Scheduled Task Feature

Layer / File(s) Summary
Engine task model
src-tauri/risuko-engine/src/engine/task.rs
Adds TaskStatus::Scheduled, start_at/schedule_missed fields, and RPC serialization for these fields.
Engine scheduler
src-tauri/risuko-engine/src/engine/manager.rs
Adds scheduling, check_scheduled_tasks promotion, strict-priority reconcile_active_set/preempt, tell_scheduled, and tests.
Backend commands
src-tauri/src/commands/engine_cmds.rs, src-tauri/src/lib.rs
Replaces sync_selected_task_order with reorder_tasks, set_task_schedule, start_task_now, tell_scheduled.
Shared types/constants/locales
src/shared/types/task.ts, src/shared/constants.ts, src/shared/locales/*/task.ts
Adds scheduling fields/constant and locale strings; removes SyncOrderResult.
Frontend API/store
src/renderer/api/Api.ts, src/renderer/store/task.ts
Adds scheduling/reorder API methods and store actions; adds scheduled category tracking.
DateTimePicker component
src/renderer/components/ui/date-time-picker/*
New calendar/time picker component with 12H/24H toggle.
Schedule UI integration
src/renderer/components/Task/ScheduleDialog.vue, MissedScheduleDialog.vue, Main.vue, Task/Index.vue, Sidebar/Index.vue, TaskItemActions.vue, TaskGeneral.vue, AddTask.vue, TaskStatus.vue, task.css
Wires scheduling dialogs, sidebar entry, action buttons, countdown display, and styling.
Drag reorder & removal of move up/down
TaskItem.vue, TaskList.vue, TaskActions.vue, commands.ts, main.ts, menu.rs, task.css
Adds drag/keyboard reorder; removes move-task-up/down UI, commands, and menu items.

Updater manifest and release signing

Layer / File(s) Summary
Release workflow
.github/workflows/release.yml
Preserves signed updater assets, emits fragments, adds updater-manifest job.
Manifest script & tests
scripts/build-updater-manifest.mjs, scripts/build-updater-manifest.test.mjs
Implements buildManifest and CLI, with validation tests.

HTTP client HTTP/2 support

Layer / File(s) Summary
ALPN configuration
src-tauri/risuko-http/Cargo.toml, client.rs, connector.rs
Enables http2 feature, sets ALPN order, tracks negotiated_h2.

Engine HTTP download improvements

Layer / File(s) Summary
Header/cookie/filename handling
src-tauri/risuko-engine/src/engine/http.rs
Adjusts User-Agent precedence, skips range probing with cf_clearance, improves filename resolution.

Misc fixes and URL updates

Layer / File(s) Summary
URL migration
README*.md, Copyright.vue, EngineClient.vue, menu.rs
Updates risuko.vercel.app to risuko.app.
Misc code fixes
clip_prompt.rs, CloudflareDialog.vue, shared/utils/index.ts, syncCategories.ts, batchQueue.ts
Fixes PID cast, cookie retry flow, locale date formatting, and export visibility.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

Suggested labels: next

Poem

A rabbit hops with clock in paw,
Scheduling burrows without a flaw,
Drag the carrots, line them right,
Sign the release and ship tonight,
h2 hums through tunnels new —
🐇 hop hop, the build is true!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main changes: scheduled starts, drag reordering, and queue capping.
Description check ✅ Passed The description is directly related to the changeset and accurately summarizes the new features and fixes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the next The "next" steps label Jul 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src-tauri/risuko-engine/src/engine/http.rs (1)

963-979: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

cf_clearance from load-cookies still reaches this probe
headers_have_cookie_name(&headers, "cf_clearance") only inspects the explicit Cookie header. Cookies loaded into the shared jar are injected later, so a jar-supplied cf_clearance still lets the range probe run. Check the jar here too, or ensure this guard only relies on header-based cookies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/risuko-engine/src/engine/http.rs` around lines 963 - 979, The
range-probe guard in the HTTP flow still only checks the explicit Cookie header
via headers_have_cookie_name, so a jar-injected cf_clearance can slip through
and trigger probe_range_support anyway. Update the probe gating logic around
wants_range_probe in http.rs to also consult the shared cookie jar (or otherwise
make the guard consistently header-only), and keep the cf_clearance skip
decision aligned with the actual cookie source before calling
probe_range_support.
.github/workflows/release.yml (1)

270-303: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep a plain Linux AppImage asset for tagged releases
.github/workflows/release.yml:270-303 publishes Risuko_<version>_linux_<arch>.AppImage.tar.gz for tagged Linux builds, but packages/risuko-app/bin.js still downloads Risuko_<version>_linux_<arch>.AppImage. That makes tagged-release Linux installs fail on first download. Keep the updater .tar.gz+.sig pair, and also upload the plain .AppImage as a separate asset.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 270 - 303, The tagged Linux
release path in release.yml only copies the updater artifact from the bundle, so
packages/risuko-app/bin.js cannot fetch the plain
Risuko_<version>_linux_<arch>.AppImage it expects. Update the release asset
handling in the tagged-release branch to keep publishing the updater .tar.gz and
.sig pair, and also add a separate upload/copy step for the plain .AppImage
using the existing appimage lookup logic so both artifact shapes are available.
src-tauri/risuko-engine/src/engine/task.rs (1)

17-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add Scheduled to status_as_str unit test.

TaskStatus::as_str() gains a mapping for TaskStatus::Scheduled to the "scheduled" string. The mapping itself is correct, but the existing status_as_str test (further down in the file) doesn't cover the new Scheduled variant, leaving this mapping without direct regression coverage.

♻️ Proposed test addition
     assert_eq!(TaskStatus::Waiting.as_str(), "waiting");
     assert_eq!(TaskStatus::Paused.as_str(), "paused");
+    assert_eq!(TaskStatus::Scheduled.as_str(), "scheduled");
     assert_eq!(TaskStatus::Complete.as_str(), "complete");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/risuko-engine/src/engine/task.rs` around lines 17 - 33, The
TaskStatus::as_str mapping for TaskStatus::Scheduled is correct, but the
status_as_str unit test does not cover it. Update the status_as_str test in
task.rs to include a case for TaskStatus::Scheduled returning "scheduled",
alongside the existing TaskStatus variants so the new mapping is directly
covered.
src/renderer/store/task.ts (1)

407-470: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fetch scheduled independently
get_global_stat only provides numActive, numWaiting, and numStoppedTotal, so scheduledCount can’t be derived here. As written, scheduled-only states collapse to 0, and the numWaiting > 0 gate also skips scheduled tasks whenever waiting is empty.

Fetch scheduled separately before deriving the sidebar counts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/store/task.ts` around lines 407 - 470, The sidebar count logic
in updateTaskCountsFromStat incorrectly derives scheduled tasks from the waiting
branch, so scheduled-only items can be missed or set to 0. Update the fetch flow
in updateTaskCountsFromStat to request the scheduled list independently via
api.fetchTaskList("scheduled") whenever counts are being refreshed, instead of
tying it to numWaiting. Then compute scheduledCount from that separate result
and keep the existing active, waiting, stopped, and completed handling intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 557-563: The release workflow step for uploading latest.json
should avoid the redundant third-party action and use the preinstalled gh CLI
instead. Update the upload step in the release job to perform the same GitHub
Release file upload directly with gh release upload, keeping the existing
tag_name/latest.json behavior intact. Use the existing release upload step as
the place to make this change and preserve the surrounding workflow logic.

In `@scripts/build-updater-manifest.mjs`:
- Around line 13-24: The platforms accumulator in build-updater-manifest.mjs
should not be a plain object because fragment keys like "__proto__" can collide
with Object.prototype and break the duplicate check or output. Change the
platforms initialization to a null-prototype object (or a Map) and keep the
existing frag.key validation and duplicate detection in the same loop so keys
are treated as ordinary entries rather than inherited properties.

In `@src-tauri/risuko-engine/src/engine/manager.rs`:
- Around line 2694-2731: `move_tasks` currently defaults to appending moved
items when `target_gid` is missing, which hides stale target state. Update
`move_tasks` in `manager.rs` to validate that `target_gid` exists in the
remaining task list before computing `insert_at`, and return an error instead of
falling back to `remaining.len()` if it is absent. Keep the existing
`move_set`/`moved` handling and ensure `reorder_tasks` callers in
`engine_cmds.rs` will surface the missing-target failure rather than silently
reordering to the end.

In `@src/renderer/components/Native/EngineClient.vue`:
- Around line 1048-1065: The checkMissedSchedules method currently reads only
the first scheduled-task page from fetchScheduledTaskList, so missed schedules
beyond the 5,000-item default are skipped. Update the checkMissedSchedules flow
to request only gid and scheduleMissed via keys: ["gid", "scheduleMissed"], then
loop through results using offset and num until no more tasks are returned,
aggregating missed entries before calling useAppStore().showMissedScheduled.

In `@src/renderer/components/Task/AddTask.vue`:
- Around line 207-215: The schedule field visibility in AddTask.vue is tied too
strictly to the dialog type, which breaks mixed queues where torrent, metalink,
and URI items coexist. Update the visibility logic around the DateTimePicker to
use a computed helper based on the actual queue contents and existing symbols
like queue, type, uriDraft, and ADD_TASK_TYPE.TORRENT instead of the hardcoded
'torrent' check. Also consider adding a user-facing hint when startAt is set but
some queued torrent items will ignore it so the dropped schedule is not silent.

In `@src/renderer/components/Task/ScheduleDialog.vue`:
- Around line 74-93: The schedule dialog still allows past times to be selected
and confirmed because `nextTwoAm()` is only used as a fallback and the current
`visible` watcher in `ScheduleDialog.vue` reuses `task.startAt` even when it has
already elapsed. Update the `visible` watcher to prefill `startAt` only when
`this.task?.startAt` is a valid future timestamp, otherwise fall back to
`nextTwoAm()`, and tighten the `confirm` disabled check so it only enables when
`startAt` is greater than `Date.now() / 1000`. Use the `nextTwoAm`, `visible`
watcher, and `confirm` logic in `ScheduleDialog.vue` as the main touchpoints.

In `@src/renderer/components/Task/TaskItem.vue`:
- Around line 10-24: The drag handle in TaskItem.vue has an ARIA/keyboard
mismatch: the element with role="button" in TaskItem and the related reorder
handle at the other referenced location should either support Enter/Space
activation or use a more appropriate draggable/slider-style role for the
Arrow-key-only interaction. Update onHandleKeydown and the handle markup so
keyboard users have a consistent activation model, and add a live-region
announcement after keyboard reorders to report the new position.

In `@src/renderer/components/Task/TaskList.vue`:
- Around line 11-23: The TaskList drag/drop marker currently uses a bare attr
attribute and the drag-move path repeatedly queries the DOM, so update the row
marker in TaskList.vue and any related lookup logic (such as closest("[attr]")
and getAttribute("attr")) to use a clearer data-task-key data attribute instead.
Also reduce work in onDragMove by caching the .task-list scroller (and any
needed measurements) when drag starts in onHandleDown, then reuse that cached
element during pointermove instead of re-querying and recomputing on every
event.
- Around line 265-285: The drag start logic in onHandleDown is leaking window
listeners when a new drag begins before the previous one ends. Before assigning
new bound handlers and calling window.addEventListener, clear any existing drag
state by invoking the existing cleanup path (for example clearDragState) or
explicitly removing the current _onDragMove/_onDragUp/_onDragCancel listeners
first. Keep the fix centered on onHandleDown and the shared drag-state cleanup
so repeated handle-down events cannot leave stale listeners attached.

In `@src/renderer/components/ui/date-time-picker/DateTimePicker.vue`:
- Around line 203-209: The month navigation buttons in DateTimePicker.vue are
icon-only and need accessible names. Update the prevMonth and nextMonth Button
elements in DateTimePicker to include clear aria-labels such as “Previous month”
and “Next month” (or equivalent visible text) so screen readers can announce
them, keeping the existing ChevronLeft/ChevronRight icons unchanged.

In `@src/shared/locales/zh-CN/task.ts`:
- Around line 5-22: The zh-CN task locale is missing new keys that exist in the
en-US task translations, so add the absent entries to the task locale object in
task.ts: start-now-fail, schedule-fail, missed-schedule-start-all-fail, and
reorder-handle. Make sure the wording matches the existing zh-CN style, and
include reorder-handle so TaskItem.vue can use a localized aria-label instead of
falling back to English.

In `@src/shared/locales/zh-TW/task.ts`:
- Around line 5-22: Add the missing zh-TW task locale entries that exist in
en-US and zh-CN: `start-now-fail`, `schedule-fail`,
`missed-schedule-start-all-fail`, and `reorder-handle`. Update the `task` locale
object in `src/shared/locales/zh-TW/task.ts` alongside the existing keys so the
drag-handle `aria-label` and schedule/start-now failure messages are translated
for zh-TW users.

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 270-303: The tagged Linux release path in release.yml only copies
the updater artifact from the bundle, so packages/risuko-app/bin.js cannot fetch
the plain Risuko_<version>_linux_<arch>.AppImage it expects. Update the release
asset handling in the tagged-release branch to keep publishing the updater
.tar.gz and .sig pair, and also add a separate upload/copy step for the plain
.AppImage using the existing appimage lookup logic so both artifact shapes are
available.

In `@src-tauri/risuko-engine/src/engine/http.rs`:
- Around line 963-979: The range-probe guard in the HTTP flow still only checks
the explicit Cookie header via headers_have_cookie_name, so a jar-injected
cf_clearance can slip through and trigger probe_range_support anyway. Update the
probe gating logic around wants_range_probe in http.rs to also consult the
shared cookie jar (or otherwise make the guard consistently header-only), and
keep the cf_clearance skip decision aligned with the actual cookie source before
calling probe_range_support.

In `@src-tauri/risuko-engine/src/engine/task.rs`:
- Around line 17-33: The TaskStatus::as_str mapping for TaskStatus::Scheduled is
correct, but the status_as_str unit test does not cover it. Update the
status_as_str test in task.rs to include a case for TaskStatus::Scheduled
returning "scheduled", alongside the existing TaskStatus variants so the new
mapping is directly covered.

In `@src/renderer/store/task.ts`:
- Around line 407-470: The sidebar count logic in updateTaskCountsFromStat
incorrectly derives scheduled tasks from the waiting branch, so scheduled-only
items can be missed or set to 0. Update the fetch flow in
updateTaskCountsFromStat to request the scheduled list independently via
api.fetchTaskList("scheduled") whenever counts are being refreshed, instead of
tying it to numWaiting. Then compute scheduledCount from that separate result
and keep the existing active, waiting, stopped, and completed handling intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 05801094-e072-4577-be31-d6faa2d14580

📥 Commits

Reviewing files that changed from the base of the PR and between c144682 and 58d986b.

📒 Files selected for processing (47)
  • .github/workflows/release.yml
  • README-CN.md
  • README.md
  • scripts/build-updater-manifest.mjs
  • scripts/build-updater-manifest.test.mjs
  • src-tauri/risuko-engine/src/engine/http.rs
  • src-tauri/risuko-engine/src/engine/manager.rs
  • src-tauri/risuko-engine/src/engine/task.rs
  • src-tauri/risuko-http/Cargo.toml
  • src-tauri/risuko-http/src/client.rs
  • src-tauri/risuko-http/src/connector.rs
  • src-tauri/src/commands/engine_cmds.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/managers/clip_prompt.rs
  • src-tauri/src/managers/menu.rs
  • src/renderer/api/Api.ts
  • src/renderer/components/About/Copyright.vue
  • src/renderer/components/Main.vue
  • src/renderer/components/Native/EngineClient.vue
  • src/renderer/components/Sidebar/Index.vue
  • src/renderer/components/Task/AddTask.vue
  • src/renderer/components/Task/CloudflareDialog.vue
  • src/renderer/components/Task/Index.vue
  • src/renderer/components/Task/MissedScheduleDialog.vue
  • src/renderer/components/Task/ScheduleDialog.vue
  • src/renderer/components/Task/TaskActions.vue
  • src/renderer/components/Task/TaskItem.vue
  • src/renderer/components/Task/TaskItemActions.vue
  • src/renderer/components/Task/TaskList.vue
  • src/renderer/components/Task/TaskStatus.vue
  • src/renderer/components/TaskDetail/TaskGeneral.vue
  • src/renderer/components/ui/date-time-picker/DateTimePicker.vue
  • src/renderer/components/ui/date-time-picker/index.ts
  • src/renderer/pages/index/commands.ts
  • src/renderer/pages/index/main.ts
  • src/renderer/store/app.ts
  • src/renderer/store/batchQueue.ts
  • src/renderer/store/task.ts
  • src/renderer/styles/components/task.css
  • src/renderer/utils/task.ts
  • src/shared/constants.ts
  • src/shared/locales/en-US/task.ts
  • src/shared/locales/zh-CN/task.ts
  • src/shared/locales/zh-TW/task.ts
  • src/shared/syncCategories.ts
  • src/shared/types/task.ts
  • src/shared/utils/index.ts
💤 Files with no reviewable changes (3)
  • src/renderer/pages/index/commands.ts
  • src/renderer/pages/index/main.ts
  • src/renderer/components/Task/TaskActions.vue

Comment thread .github/workflows/release.yml Outdated
Comment thread scripts/build-updater-manifest.mjs Outdated
Comment thread src-tauri/risuko-engine/src/engine/manager.rs
Comment thread src/renderer/components/Native/EngineClient.vue Outdated
Comment thread src/renderer/components/Task/AddTask.vue Outdated
Comment thread src/renderer/components/Task/TaskList.vue
Comment thread src/renderer/components/Task/TaskList.vue
Comment thread src/renderer/components/ui/date-time-picker/DateTimePicker.vue Outdated
Comment thread src/shared/locales/zh-CN/task.ts
Comment thread src/shared/locales/zh-TW/task.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 47 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/build-updater-manifest.mjs Outdated
Comment thread src/shared/locales/zh-CN/task.ts
Comment thread src/renderer/components/Task/ScheduleDialog.vue
Comment thread src-tauri/risuko-engine/src/engine/http.rs Outdated
Comment thread src-tauri/risuko-engine/src/engine/manager.rs
Comment thread src/renderer/components/Task/MissedScheduleDialog.vue Outdated
Comment thread scripts/build-updater-manifest.mjs Outdated
Comment thread src/renderer/components/Task/TaskItemActions.vue
Comment thread src/renderer/components/Task/MissedScheduleDialog.vue Outdated
Comment thread src/renderer/components/Task/Index.vue

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 20 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/renderer/store/task.ts Outdated
Comment thread src/renderer/components/Native/EngineClient.vue Outdated
Comment thread src/renderer/components/Task/ScheduleDialog.vue Outdated
Comment thread src/renderer/components/Task/TaskItem.vue
Comment thread src-tauri/risuko-engine/src/engine/http.rs Outdated
@YueMiyuki
YueMiyuki merged commit 763250c into master Jul 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

next The "next" steps

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant