Conversation
Add initial Android build and development support
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds Android support and packaging: Node24 Actions pin, Android build/sign CI job and helper scripts, JNI-backed Tauri Android commands, renderer Android UX and stores, engine/network (µTP/DHT/magnet) updates, CSS/locales/config changes, and workspace version bumps. ChangesAndroid Enablement and Packaging
Estimated code review effort Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
|
There was a problem hiding this comment.
Pull request overview
Adds first-class Android support to Risuko, including a Tauri Android target, JNI helpers for native intents (file/folder reveal, directory picker, notifications, system bars), a mobile UI layer (bottom nav, swipe-back guard, mobile toolbar), a configurable log directory override, and an auto locale that follows the system language. Bumps the app version to 0.3.4.
Changes:
- New Android Tauri target: Gradle project under
src-tauri/gen/android/, KotlinMainActivity/RisukoForegroundService, Rustcommands/android_intent.rsJNI bridge, Android-onlyCargo.tomldeps, conditional compilation of desktop-only managers (tray/menu/autostart/trash/nosleep), and signing/build scripts. - Renderer mobile experience:
platform.tsdetectsandroid,Main.vuegains a bottom nav + back-gesture guard,App.vuehides the title bar and syncs system bars,Task/*andDragSelectadapt to touch,SelectDirectory.vueuses SAF + translates URIs to filesystem paths, and a newlogDirOverridepreference with localized strings (en-US, zh-CN, zh-TW). - Misc:
selectedGidListnow tracks row keys (withselectedGids/selectedTaskRowsaccessors), task-list per-page selector moved to the toolbar, version 0.3.3 → 0.3.4 across all package manifests, default locale changed toauto, default file-allocation set tononeon Android, default download dir uses public Downloads on Android.
Reviewed changes
Copilot reviewed 122 out of 193 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src-tauri/src/commands/android_intent.rs | New JNI bridge for intents, directory picker, notifications, system bars |
| src-tauri/src/commands/file_cmds.rs | Splits reveal_in_folder/open_path/trash_item per platform; Android uses MainActivity.revealFolder and FileProvider |
| src-tauri/src/commands/{app_cmds,event_cmds,config_cmds,health_cmds}.rs | Android-stub branches for tray/progress/autolaunch and new Android commands |
| src-tauri/src/managers/{menu,tray,mod}.rs | Gate desktop tray/menu behind cfg(not(target_os = "android")) |
| src-tauri/risuko-engine/src/{config/defaults.rs,engine/http.rs}, config/mod.rs | Android default download dir, file-allocation=none, locale=auto |
| src-tauri/risuko-cookies/{src/lib.rs,Cargo.toml} | rookie is desktop-only; Android returns empty results |
| src-tauri/Cargo.toml + Cargo.lock | Add jni/ndk-context on Android; move trash/autostart/nosleep/single-instance to non-Android deps |
| src-tauri/capabilities/{default,desktop}.json | Split desktop-only capabilities; restrict shell:allow-open URL scopes |
| src-tauri/tauri.{conf,android.conf}.json | Bump to 0.3.4 and set Android identifier app.risuko.mobile |
| src-tauri/gen/android/** | New Android Gradle project, Kotlin sources, resources, manifest |
| src-tauri/icons/{icon.svg,android/values/ic_launcher_background.xml} | New 512x512 background + adaptive icon background color |
| src/renderer/components/Main.vue | Android bottom nav + back-gesture handling |
| src/renderer/pages/index/App.vue | Hide title bar on Android, sync system bars, add platform class |
| src/renderer/components/Task/{Index,TaskList,TaskItem,TaskItemActions,TaskActions,AddTask}.vue | Row-keyed selection, Android-friendly tap selection, per-row delete, mobile toolbar |
| src/renderer/components/TaskDetail/{Index,TaskGeneral}.vue | Sheet close button, Android-aware reveal path |
| src/renderer/components/Native/{SelectDirectory,ShowInFolder}.vue | SAF picker + URI translation; new button styling |
| src/renderer/components/Preference/Advanced.vue | Log-dir-override field + localized tip |
| src/renderer/components/Subnav/SubnavSwitcher.vue, Health/Index.vue, Rss/Index.vue, DragSelect/Index.vue, ui/confirm-dialog/ConfirmDialog.vue | Mobile layout tweaks, touch action gating |
| src/renderer/store/{task,preference,app}.ts | Row-keyed selection helpers, auto locale resolution, Android download notification sync |
| src/renderer/utils/native.ts | getTaskRevealDir, chunk meta cleanup, sidecar helper |
| src/renderer/api/Api.ts | Android notification IPCs |
| src/renderer/shims/platform.ts, router/index.ts, pages/index/main.ts | Android UA detection, /preference redirect, locale resolution at init |
| src/shared/locales/{en-US,zh-CN,zh-TW}/{app,preferences,task}.ts, index.ts | New strings + auto language + getSystemLocale |
| src/shared/{configKeys.ts,types/config.ts} | log-dir-override key |
| src/renderer/styles/{app,components/preferences,components/task,components/task-detail,components/input}.css | Mobile/responsive styles |
| static/logo.svg, src/renderer/assets/logo.svg | Expanded viewBox for padding |
| scripts/android-env.mjs, scripts/sign-android-apks.mjs | Toolchain wrapper + APK signing helper |
| package.json, packages/**/package.json, pnpm-workspace.yaml | 0.3.4 bump + Android scripts |
| README.md, README-CN.md | Android build instructions |
| .github/workflows/{release,publish-npm,codeql-analysis,bump-homebrew-tap}.yml | Android release job + Node 24 flag |
| .gitignore | Track src-tauri/gen/android/ |
There was a problem hiding this comment.
8 issues found across 193 files
Note: This PR contains a large number of files. cubic only reviews up to 100 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
Re-trigger cubic
There was a problem hiding this comment.
Actionable comments posted: 19
🤖 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 57-58: The release workflow currently runs "pnpm install
--no-frozen-lockfile" which permits lockfile drift; update the step in
.github/workflows/release.yml so the Install dependencies job uses a frozen
lockfile by replacing the flag with "--frozen-lockfile" (or simply "pnpm
install" if your CI uses strict locking) to ensure deterministic, auditable
installs for releases.
- Line 21: Pin every workflow action `uses:` entry to its immutable commit SHA
instead of tag names (e.g., replace `actions/checkout@v4`,
`pnpm/action-setup@v4`, `actions/setup-node@v4`, `actions/setup-java@v4`,
`android-actions/setup-android@v3`, `dtolnay/rust-toolchain@stable`,
`softprops/action-gh-release@v2`, `actions/upload-artifact@v4`,
`tauri-apps/tauri-action@v0` with their corresponding full commit SHAs), add
`with: persist-credentials: false` to both `actions/checkout` steps, and change
both `pnpm install --no-frozen-lockfile` invocations to use a frozen lockfile
(e.g., `--frozen-lockfile`) to ensure reproducible, hardened CI runs.
- Around line 20-21: The checkout steps using actions/checkout@v4 (the steps
named "Check out Git repository" in the android-release and release jobs)
currently use the default persist-credentials behavior; update both checkout
steps to set persist-credentials: false so the GITHUB_TOKEN is not written into
.git/config (i.e., add the persist-credentials: false input to the
actions/checkout@v4 step in each job).
In `@scripts/android-env.mjs`:
- Line 95: The CodeQL finding is a false positive for the console.log call;
update the console.log(`JAVA_HOME=${env.JAVA_HOME}`) line to include a short
inline comment clarifying this is a non-sensitive filesystem path and not a
secret so the warning can be ignored (e.g. add "// CodeQL: false positive -
JAVA_HOME is a path, not a secret"). Keep the logging behavior unchanged but add
the explanatory comment adjacent to the console.log statement.
In `@scripts/sign-android-apks.mjs`:
- Line 7: Replace the URL-based pathname conversion for projectRoot with a
proper file URL to filesystem path conversion: use
fileURLToPath(import.meta.url) and pass that result to resolve instead of new
URL("..", import.meta.url).pathname; update the reference around the projectRoot
initialization (the const projectRoot = ... line) to import fileURLToPath from
'url' and call resolve(fileURLToPath(new URL("..", import.meta.url))) or first
compute fileURLToPath(new URL("..", import.meta.url)) then resolve it so the
path is platform-safe.
- Around line 19-22: The fail() helper currently calls process.exit(1) which
aborts the process immediately and prevents finally blocks (such as the keystore
cleanup in the signing flow) from running; change fail() to throw a descriptive
Error instead (e.g., throw new Error(message)) so normal exception propagation
triggers finally cleanup, and update all call sites of fail() if they expect a
non-returning function; additionally, when computing projectRoot from a file://
URL replace using URL(...).pathname with Node's fileURLToPath to produce correct
Windows paths (import fileURLToPath from 'url' and use fileURLToPath(fileUrl)
where URL(...) was used).
In `@src-tauri/risuko-engine/src/engine/http.rs`:
- Around line 877-891: Add a brief inline comment above the Android-specific
branch in the falloc_mode computation explaining why we force Mode::None on
Android (e.g., Android's fallocate may be unsupported or unreliable on many
filesystems/devices), so future maintainers understand the special-case; insert
the comment near the block that compares mode == super::falloc::Mode::Falloc and
returns super::falloc::Mode::None inside the #[cfg(target_os = "android")]
section.
In `@src-tauri/src/lib.rs`:
- Around line 90-98: resolve_log_dir currently treats
std::fs::create_dir_all(&candidate) success as proof the override is writable,
but that can be false if the directory exists and is not writable; after
create_dir_all(&candidate) attempt to verify write access by trying to open (and
then close/remove) a file in the candidate directory (for example use
std::fs::OpenOptions on candidate.join("risuko.log") or create a temporary file)
and if that open fails, log the error and return default_log_dir.to_path_buf();
update resolve_log_dir to perform this write check and fall back to
default_log_dir on failure so init_logging can reliably create risuko.log.
In `@src/renderer/components/Native/ShowInFolder.vue`:
- Around line 2-4: The button in ShowInFolder.vue is icon-only and lacks an
accessible name; update the <button class="show-in-folder"
`@click.stop`="onFolderClick"> to provide an accessible label (e.g., add
aria-label or aria-labelledby) using the existing localized string for "show in
folder" (use the existing i18n key) so screen readers announce the action for
the FolderOpen icon (preserve size and onFolderClick handler).
In `@src/renderer/components/Preference/Advanced.vue`:
- Around line 1105-1115: The mo-show-in-folder button is wired to
:path="logPath" while the textarea displays visibleLogPath, causing a mismatch
when an override is selected; update the component to pass the same displayed
path to mo-show-in-folder (use visibleLogPath instead of logPath, or derive the
path the button receives from visibleLogPath) so the "show in folder" action
always reveals the directory shown in the textarea (ensure handleLogDirSelected
still updates visibleLogPath as needed).
In `@src/renderer/components/Task/Index.vue`:
- Around line 35-41: The aggregate progress row in Task/Index.vue is being shown
on desktop as well and duplicates the desktop summary from TaskActions.vue;
limit it to mobile by gating the element with a mobile/platform check (e.g.,
change the v-if to "showTotalProgress && isMobile"). Add or reuse a
computed/property named isMobile (or import the existing isMobile() helper from
your platform/utils or store) in the component script so the template can
evaluate it; alternatively, remove or gate the redundant desktop rendering in
TaskActions.vue instead if you prefer centralizing the toolbar display logic.
In `@src/renderer/components/Task/TaskItem.vue`:
- Line 5: The template adds the selected class directly on .task-item via the
:class binding in TaskItem.vue (e.g., ":class='{ ... selected: selected }'"),
but the stylesheet expects a parent selector ".selected .task-item", so the
styles never apply; update the CSS selector in
src/renderer/styles/components/task.css to target ".task-item.selected" (or
change the CSS rules from ".selected .task-item" to ".task-item.selected") and
make the same change for the other binding instance in this component (the
second :class block around the action/secondary element) so the selected
highlight activates when the selected prop is true.
In `@src/renderer/components/Task/TaskList.vue`:
- Around line 11-13: The conditional fallback `item._displayKey || item.gid` is
redundant because DisplayTask always sets `_displayKey`; update TaskList.vue to
use `item._displayKey` consistently (remove the `|| item.gid` fallback) in the
row `:attr`/`:key` and any selection logic (e.g., where `isItemSelected(item)`
or `handleItemClick(item, $event)` relies on the key) so it matches the
`key-field="_displayKey"` used by the virtual scroller and the `v-for` key
semantics.
In `@src/renderer/store/task.ts`:
- Around line 474-481: The current filter only checks underlying gid against
orderedData, allowing stale file-row keys (like "gid#f0") to persist; instead
build a Set of actual visible row keys from orderedData (e.g.
orderedData.map(row => row.key) or the property that represents the rendered row
key) and then filter this.selectedGidList to keep only entries that exactly
match one of those visible keys; update the block that uses orderedData and
this.selectedGidList to use the visibleKeys set for pruning.
In `@src/renderer/styles/android.css`:
- Around line 499-503: The CSS for selectors html.platform-android .subnav-badge
and html.mobile-phone .subnav-badge (and the other affected blocks noted) must
be linted: remove stray blank lines between declarations, change the keyword
casing for currentColor to the linter-expected form (e.g., currentcolor), and
reformat any multiline calc() expressions so each line ends with the operator
(e.g., "calc(100% -" on the same line as the operator) or keep the operator
attached to its operand per the scss/operator-no-newline-after rule; update the
blocks around the indicated selectors (and similar blocks at the other ranges)
accordingly to satisfy value-keyword-case, declaration-empty-line-before, and
scss/operator-no-newline-after.
In `@src/renderer/styles/components/preferences.css`:
- Around line 297-314: The .dev-log-path-input rule removes the focus outline
making keyboard focus invisible; restore an accessible focus indicator by
removing the hard outline: 0 change and instead add a :focus-visible rule for
.dev-log-path-input that applies a visible, high-contrast ring (e.g., thin
outline or box-shadow) matching the UI theme variables (use
--foreground/--accent or a dedicated --focus color) and preserve keyboard-only
visibility; ensure the focus style has sufficient contrast and does not rely on
color alone so keyboard users can track focus.
In `@src/renderer/utils/native.ts`:
- Around line 17-23: The dirname function only checks for '/' so Windows paths
with backslashes aren't handled; update dirname (and callers like
getTaskRevealDir) to compute the last separator by considering both '/' and '\\'
(e.g., get lastIndexOf for '/' and for '\\' and use the larger index) or use a
platform-aware API (path.dirname) if available; ensure the index comparison and
slice logic use that combined index so Windows paths return the parent directory
instead of the full file path.
In `@src/shared/locales/zh-CN/app.ts`:
- Around line 4-11: The new locale keys primary-navigation, nav-tasks, nav-rss,
nav-health, nav-settings, android-back-again-to-quit, and
android-storage-access-required were added to zh-CN/app.ts but are missing from
the other locale app.ts files; open each other locale's app.ts (e.g., en-US,
zh-TW and any remaining locales) and add these keys with appropriate
translations (or copy the en-US text as placeholders) to keep all locale objects
consistent, preserving the same key names and string types as used in
functions/components that read them.
In `@src/shared/locales/zh-TW/preferences.ts`:
- Around line 205-207: The new locale keys "log-dir-override-placeholder" and
"log-dir-override-tips" were added in zh-TW but are missing from most other
locale preference files; add these two keys to every
src/shared/locales/*/preferences.ts (or implement a runtime fallback) so the
labels won't be undefined when switching languages—either copy the en-US values
into each locale file (and translate where appropriate) or update the locale
loader to fall back to en-US for missing keys; ensure the keys are exported in
the same object shape as other preference strings so existing consumers can
access "log-dir-override-placeholder" and "log-dir-override-tips" safely.
🪄 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: 41cb60b5-93bf-4624-9043-ad15d178da1e
⛔ Files ignored due to path filters (100)
src-tauri/Cargo.lockis excluded by!**/*.locksrc-tauri/gen/android/.editorconfigis excluded by!**/gen/**src-tauri/gen/android/.gitignoreis excluded by!**/gen/**src-tauri/gen/android/app/.gitignoreis excluded by!**/gen/**src-tauri/gen/android/app/build.gradle.ktsis excluded by!**/gen/**src-tauri/gen/android/app/proguard-rules.prois excluded by!**/gen/**src-tauri/gen/android/app/proguard-tauri.prois excluded by!**/gen/**src-tauri/gen/android/app/src/main/AndroidManifest.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/java/app/risuko/mobile/MainActivity.ktis excluded by!**/gen/**src-tauri/gen/android/app/src/main/java/app/risuko/mobile/RisukoForegroundService.ktis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/drawable/empty_splash_icon.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/layout/activity_main.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngis excluded by!**/*.png,!**/gen/**src-tauri/gen/android/app/src/main/res/values-night/themes.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/values-v31/themes.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/values/colors.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/values/ic_launcher_background.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/values/strings.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/values/themes.xmlis excluded by!**/gen/**src-tauri/gen/android/app/src/main/res/xml/file_paths.xmlis excluded by!**/gen/**src-tauri/gen/android/build.gradle.ktsis excluded by!**/gen/**src-tauri/gen/android/buildSrc/build.gradle.ktsis excluded by!**/gen/**src-tauri/gen/android/buildSrc/src/main/java/app/risuko/mobile/kotlin/BuildTask.ktis excluded by!**/gen/**src-tauri/gen/android/buildSrc/src/main/java/app/risuko/mobile/kotlin/RustPlugin.ktis excluded by!**/gen/**src-tauri/gen/android/gradle.propertiesis excluded by!**/gen/**src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jar,!**/gen/**src-tauri/gen/android/gradle/wrapper/gradle-wrapper.propertiesis excluded by!**/gen/**src-tauri/gen/android/gradlewis excluded by!**/gen/**src-tauri/gen/android/gradlew.batis excluded by!**/gen/**src-tauri/gen/android/settings.gradleis excluded by!**/gen/**src-tauri/icons/128x128.pngis excluded by!**/*.pngsrc-tauri/icons/128x128@2x.pngis excluded by!**/*.pngsrc-tauri/icons/32x32.pngis excluded by!**/*.pngsrc-tauri/icons/64x64.pngis excluded by!**/*.pngsrc-tauri/icons/Square107x107Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square142x142Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square150x150Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square284x284Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square30x30Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square310x310Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square44x44Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square71x71Logo.pngis excluded by!**/*.pngsrc-tauri/icons/Square89x89Logo.pngis excluded by!**/*.pngsrc-tauri/icons/StoreLogo.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-hdpi/ic_launcher.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-hdpi/ic_launcher_round.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-mdpi/ic_launcher.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-mdpi/ic_launcher_round.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xhdpi/ic_launcher.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xxhdpi/ic_launcher.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.pngis excluded by!**/*.pngsrc-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.pngis excluded by!**/*.pngsrc-tauri/icons/icon.icois excluded by!**/*.icosrc-tauri/icons/icon.pngis excluded by!**/*.pngsrc-tauri/icons/icon.svgis excluded by!**/*.svgsrc-tauri/icons/ios/AppIcon-20x20@1x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-20x20@2x-1.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-20x20@2x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-20x20@3x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-29x29@1x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-29x29@2x-1.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-29x29@2x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-29x29@3x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-40x40@1x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-40x40@2x-1.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-40x40@2x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-40x40@3x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-512@2x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-60x60@2x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-60x60@3x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-76x76@1x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-76x76@2x.pngis excluded by!**/*.pngsrc-tauri/icons/ios/AppIcon-83.5x83.5@2x.pngis excluded by!**/*.pngsrc/renderer/assets/logo.svgis excluded by!**/*.svgstatic/logo.svgis excluded by!**/*.svg
📒 Files selected for processing (93)
.github/workflows/bump-homebrew-tap.yml.github/workflows/codeql-analysis.yml.github/workflows/publish-npm.yml.github/workflows/release.yml.gitignoreREADME-CN.mdREADME.mdpackage.jsonpackages/risuko-app/package.jsonpackages/risuko-cli/npm/darwin-arm64/package.jsonpackages/risuko-cli/npm/darwin-x64/package.jsonpackages/risuko-cli/npm/linux-arm64-gnu/package.jsonpackages/risuko-cli/npm/linux-x64-gnu/package.jsonpackages/risuko-cli/npm/win32-arm64-msvc/package.jsonpackages/risuko-cli/npm/win32-x64-msvc/package.jsonpackages/risuko-cli/package.jsonpackages/risuko-js/npm/darwin-arm64/package.jsonpackages/risuko-js/npm/darwin-x64/package.jsonpackages/risuko-js/npm/linux-arm64-gnu/package.jsonpackages/risuko-js/npm/linux-x64-gnu/package.jsonpackages/risuko-js/npm/win32-arm64-msvc/package.jsonpackages/risuko-js/npm/win32-x64-msvc/package.jsonpackages/risuko-js/package.jsonpnpm-workspace.yamlscripts/android-env.mjsscripts/sign-android-apks.mjssrc-tauri/Cargo.tomlsrc-tauri/capabilities/default.jsonsrc-tauri/capabilities/desktop.jsonsrc-tauri/icons/android/values/ic_launcher_background.xmlsrc-tauri/icons/icon.icnssrc-tauri/risuko-cookies/Cargo.tomlsrc-tauri/risuko-cookies/src/lib.rssrc-tauri/risuko-engine/src/config/defaults.rssrc-tauri/risuko-engine/src/config/mod.rssrc-tauri/risuko-engine/src/engine/http.rssrc-tauri/src/commands/android_intent.rssrc-tauri/src/commands/app_cmds.rssrc-tauri/src/commands/config_cmds.rssrc-tauri/src/commands/event_cmds.rssrc-tauri/src/commands/file_cmds.rssrc-tauri/src/commands/health_cmds.rssrc-tauri/src/commands/mod.rssrc-tauri/src/lib.rssrc-tauri/src/managers/menu.rssrc-tauri/src/managers/mod.rssrc-tauri/src/managers/tray.rssrc-tauri/tauri.android.conf.jsonsrc-tauri/tauri.conf.jsonsrc/renderer/api/Api.tssrc/renderer/components/DragSelect/Index.vuesrc/renderer/components/Health/Index.vuesrc/renderer/components/Main.vuesrc/renderer/components/Native/SelectDirectory.vuesrc/renderer/components/Native/ShowInFolder.vuesrc/renderer/components/Preference/Advanced.vuesrc/renderer/components/Rss/Index.vuesrc/renderer/components/Subnav/SubnavSwitcher.vuesrc/renderer/components/Task/AddTask.vuesrc/renderer/components/Task/Index.vuesrc/renderer/components/Task/TaskActions.vuesrc/renderer/components/Task/TaskItem.vuesrc/renderer/components/Task/TaskItemActions.vuesrc/renderer/components/Task/TaskList.vuesrc/renderer/components/TaskDetail/Index.vuesrc/renderer/components/TaskDetail/TaskGeneral.vuesrc/renderer/components/ui/confirm-dialog/ConfirmDialog.vuesrc/renderer/pages/index/App.vuesrc/renderer/pages/index/main.tssrc/renderer/router/index.tssrc/renderer/shims/platform.tssrc/renderer/store/app.tssrc/renderer/store/preference.tssrc/renderer/store/task.tssrc/renderer/styles/android.csssrc/renderer/styles/app.csssrc/renderer/styles/components/input.csssrc/renderer/styles/components/preferences.csssrc/renderer/styles/components/task-detail.csssrc/renderer/styles/components/task.csssrc/renderer/utils/native.tssrc/shared/configKeys.tssrc/shared/locales/en-US/app.tssrc/shared/locales/en-US/preferences.tssrc/shared/locales/en-US/task.tssrc/shared/locales/index.tssrc/shared/locales/zh-CN/app.tssrc/shared/locales/zh-CN/preferences.tssrc/shared/locales/zh-CN/task.tssrc/shared/locales/zh-TW/app.tssrc/shared/locales/zh-TW/preferences.tssrc/shared/locales/zh-TW/task.tssrc/shared/types/config.ts
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/renderer/utils/native.ts (1)
17-23:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHandle root-level POSIX paths in
dirname().Line 20 still treats
index === 0as “no parent”, sodirname("/file.part")returns the file path instead of/. That leaves_isFileEntryreveal targets wrong for files directly under the filesystem root.Suggested fix
function dirname(path = ""): string { const value = `${path || ""}`.replace(/[/\\]+$/g, ""); const index = Math.max(value.lastIndexOf("/"), value.lastIndexOf("\\")); - if (index <= 0) { + if (index < 0) { return value; } + if (index === 0) { + return value[0]; + } return value.slice(0, index); }🤖 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/utils/native.ts` around lines 17 - 23, The dirname() helper incorrectly treats index === 0 as “no parent” so paths like "/file.part" return the full path; update dirname(path) to return the root separator for root-level POSIX paths: when index === 0 and the original input starts with "/", return "/" instead of value; otherwise keep existing behavior (including existing handling for backslashes/Windows). Ensure this change is made inside the dirname function referenced above so files directly under "/" yield "/" as their directory.
♻️ Duplicate comments (1)
src/renderer/store/task.ts (1)
475-480:⚠️ Potential issue | 🟠 Major | ⚡ Quick winBuild
visibleKeysfrom display rows, notorderedData.
orderedDatais stillDownloadTask[], so_displayKeyis missing here. That makesvisibleKeyseffectively{undefined}and prunes every selected row on refresh.💡 Suggested fix
- const visibleKeys = new Set( - orderedData.map((task) => task._displayKey), - ); + const visibleKeys = new Set( + this.displayTaskList.map((task) => task._displayKey || task.gid), + ); this.selectedGidList = this.selectedGidList.filter((key) => visibleKeys.has(key), );🤖 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 475 - 480, visibleKeys is being built from orderedData (DownloadTask[]) which lacks _displayKey, so visibleKeys contains undefined and clears selectedGidList; change the source to the rendered/display rows that include _displayKey (e.g. this.displayData or the variable holding display rows) — e.g. construct visibleKeys with new Set(this.displayData.map(row => row._displayKey)) and then filter this.selectedGidList against that set so only currently visible display keys are kept.
🤖 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 `@scripts/android-env.mjs`:
- Line 95: The current console.log call in scripts/android-env.mjs masks
JAVA_HOME while the comment claims a CodeQL false positive; decide and apply one
of two fixes: either restore the original logging by changing the console.log to
print the actual environment value (use env.JAVA_HOME) and keep the explanatory
comment about CodeQL false positive, or if you intentionally want to suppress
the path, keep the masked output but remove or edit the "CodeQL: false positive"
phrase so the comment no longer asserts it’s a false positive; update the line
that references JAVA_HOME (the console.log call) and the adjacent comment
accordingly.
In `@src-tauri/risuko-bt/src/torrent.rs`:
- Around line 366-370: serve_v2_layers is being used as a proxy for "we can
answer BEP-52/hash requests" but it's computed before building the hybrid
piece-layer tables (from_layer_bytes) so a failed build leaves hash_tables =
None while we still announce truncated v2 hashes; change the logic so that
announcing or serving v2 layers requires successful table construction: after
calling from_layer_bytes(...) and assigning hash_tables, set or update
serve_v2_layers to supports_v2_wire(&init.meta) && hash_tables.is_some(), and
update announce_hashes and any other checks that rely on serve_v2_layers (e.g.,
where you decide to announce v2/truncated info-hashes) to also explicitly verify
hash_tables.is_some() (or use the new combined flag) before advertising or
responding to BEP-52 requests.
In `@src-tauri/risuko-bt/src/utp/stream.rs`:
- Around line 736-739: The loop copies bytes one-by-one from st.recv_ready into
buf which causes many function calls; instead compute n, get contiguous slices
from the VecDeque via st.recv_ready.as_slices() (or call
st.recv_ready.make_contiguous() then as_slices()), copy up to n bytes from the
first slice then (if needed) from the second slice into buf with one or two
buf.put_slice calls, and finally remove the bytes with
st.recv_ready.drain(0..n); update the code around st.recv_ready, buf.put_slice,
as_slices/make_contiguous and drain to perform batched copies.
In `@src-tauri/risuko-engine/src/engine/http.rs`:
- Around line 956-968: The code is currently applying
adopt_content_type_extension (via probe_for_name/ adopt_content_type_extension)
to any extensionless filename, which incorrectly rewrites explicit user output
names; update the logic to only adopt a content-type extension when the filename
was URL-derived by gating these branches with the existing
filename_was_url_derived boolean (or add an explicit allow_content_type_adoption
flag passed into run_single_download and checked here), i.e., before calling
adopt_content_type_extension and before setting
adopted_filename/filename/part_path ensure filename_was_url_derived (or the new
flag) is true so user-provided out values are never modified; apply the same
guard to the identical block around lines 2045–2049.
In `@src-tauri/risuko-engine/src/engine/manager.rs`:
- Around line 551-559: The code checks still_active before calling
resolve_and_add_magnet().await but does not re-check after the await, so if the
task was paused/removed while resolve was in flight you may incorrectly set its
status to Error and emit DownloadError; update the logic around
resolve_and_add_magnet (and the similar block at 577-625) to re-acquire
tasks.read().await and recompute still_active (matching task.gid,
TaskKind::Torrent, TaskStatus::Active, and uri == &magnet_uri) after the await
completes and only transition the task to TaskStatus::Error and send
DownloadError when the re-checked still_active is true. Ensure you reference and
use the same guard/conditions (tasks, TaskKind::Torrent, TaskStatus::Active,
magnet_uri, gid) when deciding whether to mutate state or emit errors.
- Around line 2283-2310: ensure_active_magnet_resolvers() currently holds
tasks.read() while awaiting torrent_ids.read() and pending_magnets.read(), which
can deadlock with remove() that locks torrent_ids → pending_magnets → tasks; fix
by normalizing lock order: acquire torrent_ids.read() and pending_magnets.read()
before tasks.read(), or else grab tasks (clone the minimal data needed) and drop
the tasks guard before reading torrent_ids and pending_magnets. Update the block
that builds jobs (the let jobs = { ... } snippet) so that reads occur in the
same order as remove() (torrent_ids, pending_magnets, then tasks) or so tasks is
not held while awaiting the other two; ensure the code still clones task.gid,
uris and options (using options.merge_task_options) while not holding
conflicting locks.
In `@src-tauri/risuko-engine/src/engine/torrent.rs`:
- Around line 301-360: Extract the duplicated save-metadata block from both
resolve_and_add_magnet and add_magnet into a single helper function (e.g.,
save_torrent_metadata_if_enabled(bytes: &[u8], options: &Map<String, Value>,
output_dir: &Path) -> impl Future<Output=()>) and call it from both places; move
the logic that reads "bt-save-metadata" from options, resolves dir (using
options.get("dir") or output_dir), calls bt::parse_torrent, builds the safe
filename with sanitize_file_stem, creates parent dirs and writes the .torrent
file (logging info/warn) into that helper, and replace the inline blocks in
resolve_and_add_magnet and add_magnet with a call to this new function before
invoking add_torrent_bytes/other downstream calls.
In `@src/renderer/components/Task/TaskActions.vue`:
- Around line 16-22: The new select-all control in TaskActions.vue is an
interactive, click-only <i> element (class "task-action") which is not
keyboard-focusable or accessible; replace the <i> with a semantic <button
type="button"> (still using class "task-action") wired to onSelectAllClick, bind
the disabled state to selectableTaskCount === 0, and add an accessible name
(e.g., aria-label="Select all tasks" or visible text) so keyboard and
assistive-tech users can focus and activate the control; keep the ListChecks
icon as the visual child.
In `@src/renderer/styles/responsive.css`:
- Around line 104-108: The calc() expression assigned to the bottom property
breaks the stylelint scss/operator-no-newline-after rule because the operators
are on their own lines; inline the operators with their operands so the
expression is on one line (e.g., change bottom: calc(env(safe-area-inset-bottom)
+ var(--android-bottom-bar, 72px) - 14px);), keeping the same terms
env(safe-area-inset-bottom), var(--android-bottom-bar, 72px) and - 14px and
preserving the calc() semantics.
---
Outside diff comments:
In `@src/renderer/utils/native.ts`:
- Around line 17-23: The dirname() helper incorrectly treats index === 0 as “no
parent” so paths like "/file.part" return the full path; update dirname(path) to
return the root separator for root-level POSIX paths: when index === 0 and the
original input starts with "/", return "/" instead of value; otherwise keep
existing behavior (including existing handling for backslashes/Windows). Ensure
this change is made inside the dirname function referenced above so files
directly under "/" yield "/" as their directory.
---
Duplicate comments:
In `@src/renderer/store/task.ts`:
- Around line 475-480: visibleKeys is being built from orderedData
(DownloadTask[]) which lacks _displayKey, so visibleKeys contains undefined and
clears selectedGidList; change the source to the rendered/display rows that
include _displayKey (e.g. this.displayData or the variable holding display rows)
— e.g. construct visibleKeys with new Set(this.displayData.map(row =>
row._displayKey)) and then filter this.selectedGidList against that set so only
currently visible display keys are kept.
🪄 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: 437f09d2-0043-4865-8d0f-ef1aa4ffd383
⛔ Files ignored due to path filters (3)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc-tauri/gen/android/app/src/main/java/app/risuko/mobile/MainActivity.ktis excluded by!**/gen/**src/renderer/pages/index/favicon.icois excluded by!**/*.ico
📒 Files selected for processing (69)
.github/workflows/release.ymlbiome.jsonpackage.jsonpackages/risuko-app/package.jsonpackages/risuko-cli/npm/darwin-arm64/package.jsonpackages/risuko-cli/npm/darwin-x64/package.jsonpackages/risuko-cli/npm/linux-arm64-gnu/package.jsonpackages/risuko-cli/npm/linux-x64-gnu/package.jsonpackages/risuko-cli/npm/win32-arm64-msvc/package.jsonpackages/risuko-cli/npm/win32-x64-msvc/package.jsonpackages/risuko-cli/package.jsonpackages/risuko-js/npm/darwin-arm64/package.jsonpackages/risuko-js/npm/darwin-x64/package.jsonpackages/risuko-js/npm/linux-arm64-gnu/package.jsonpackages/risuko-js/npm/linux-x64-gnu/package.jsonpackages/risuko-js/npm/win32-arm64-msvc/package.jsonpackages/risuko-js/npm/win32-x64-msvc/package.jsonpackages/risuko-js/package.jsonpnpm-workspace.yamlscripts/android-env.mjsscripts/sign-android-apks.mjssrc-tauri/risuko-bt/Cargo.tomlsrc-tauri/risuko-bt/examples/download_magnet.rssrc-tauri/risuko-bt/examples/probe_handshake.rssrc-tauri/risuko-bt/src/core/hash.rssrc-tauri/risuko-bt/src/core/magnet.rssrc-tauri/risuko-bt/src/core/merkle.rssrc-tauri/risuko-bt/src/core/mod.rssrc-tauri/risuko-bt/src/dht.rssrc-tauri/risuko-bt/src/lib.rssrc-tauri/risuko-bt/src/magnet.rssrc-tauri/risuko-bt/src/peer.rssrc-tauri/risuko-bt/src/peer/connection.rssrc-tauri/risuko-bt/src/session.rssrc-tauri/risuko-bt/src/torrent.rssrc-tauri/risuko-bt/src/utp.rssrc-tauri/risuko-bt/src/utp/packet.rssrc-tauri/risuko-bt/src/utp/socket.rssrc-tauri/risuko-bt/src/utp/stream.rssrc-tauri/risuko-bt/src/wire/extended.rssrc-tauri/risuko-bt/src/wire/handshake.rssrc-tauri/risuko-engine/src/config/defaults.rssrc-tauri/risuko-engine/src/engine/http.rssrc-tauri/risuko-engine/src/engine/manager.rssrc-tauri/risuko-engine/src/engine/torrent.rssrc-tauri/src/commands/android_intent.rssrc-tauri/src/commands/file_cmds.rssrc-tauri/src/lib.rssrc/renderer/components/Native/ShowInFolder.vuesrc/renderer/components/Preference/Advanced.vuesrc/renderer/components/Preference/Basic.vuesrc/renderer/components/Preference/CloudSinks.vuesrc/renderer/components/Task/AddTask.vuesrc/renderer/components/Task/Index.vuesrc/renderer/components/Task/SelectTorrent.vuesrc/renderer/components/Task/TaskActions.vuesrc/renderer/components/Task/TaskItem.vuesrc/renderer/components/Task/TaskList.vuesrc/renderer/components/ui/confirm-dialog/ConfirmDialog.vuesrc/renderer/pages/index/index.htmlsrc/renderer/store/task.tssrc/renderer/styles/android.csssrc/renderer/styles/components/preferences.csssrc/renderer/styles/components/task.csssrc/renderer/styles/responsive.csssrc/renderer/utils/native.tssrc/shared/locales/en-US/task.tssrc/shared/utils/taskSelection.tstsconfig.json
💤 Files with no reviewable changes (2)
- src-tauri/risuko-bt/src/core/hash.rs
- src-tauri/risuko-bt/src/core/magnet.rs
There was a problem hiding this comment.
6 issues found across 72 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src-tauri/risuko-engine/src/config/defaults.rs">
<violation number="1" location="src-tauri/risuko-engine/src/config/defaults.rs:100">
P2: Write probe uses fixed file name and truncate. Can clobber existing file. Use unique probe name and create_new.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src-tauri/risuko-bt/src/torrent.rs (1)
362-421:⚠️ Potential issue | 🟠 Major | ⚡ Quick winClamp BEP-52 v2 reserved-bit advertisement to Merkle table availability (outbound, and mirror for inbound too)
SpawnPeer.advertise_v2is written into the BT handshake viaHandshake::new_with_v2(..., advertise_v2), setting the BEP-52 v2 reserved bit. Outbound dials currently passinit.advertise_v2even whenserve_v2_layersisfalse(Merkle table build failed), so peers can observe v2 capability but then receiveHashRejectforHASH_REQUESTbecausebuild_hash_responserejects whentablesisNone.🛠️ Suggested change
- let advertise_v2 = init.advertise_v2; + let requested_advertise_v2 = init.advertise_v2; ... let serve_v2_layers = supports_v2 && hash_tables.is_some(); + let advertise_v2 = requested_advertise_v2 && serve_v2_layers;Also check the inbound path:
SessionbuildsKnownInfoHash { advertise_v2: handle.advertise_v2 }, andhandle.advertise_v2is still set frominit.advertise_v2regardless ofserve_v2_layersintorrent.rs, so inbound handshakes can diverge as well.🤖 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-bt/src/torrent.rs` around lines 362 - 421, The PR shows advertise_v2 can be set true even when Merkle tables failed to build; clamp advertisement to actual serving ability by forcing advertise_v2 = init.advertise_v2 && serve_v2_layers before it's stored/propagated. Concretely, replace usages that copy init.advertise_v2 into SpawnPeer.advertise_v2, Handshake::new_with_v2(..., advertise_v2) and KnownInfoHash / handle.advertise_v2 with the clamped value (referencing advertise_v2, serve_v2_layers, init.advertise_v2, SpawnPeer.advertise_v2, Handshake::new_with_v2, KnownInfoHash and handle.advertise_v2) so outbound and inbound handshakes only set the BEP-52 v2 reserved bit when hash_tables is Some.
🤖 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 `@src-tauri/risuko-bt/src/wire/extended.rs`:
- Around line 120-139: Add round-trip tests for the extended handshake's yourip
field by creating two new test cases that serialize and deserialize the
extension with yourip set to an IPv4 (4-byte) and an IPv6 (16-byte) address
respectively; use the existing encode/parse helpers used by the current
handshake tests to build a dict with key b"yourip" and raw bytes for the
addresses, call the same code paths that produce and consume the struct (the
code that constructs Self { ..., yourip, ... } and the corresponding
serializer), and assert the deserialized yourip matches the original IpAddr::V4
and IpAddr::V6 values to ensure round-trip coverage.
In `@src/renderer/utils/native.ts`:
- Around line 17-26: The dirname function currently returns "C:" for Windows
drive-root parents (e.g. "C:\file.tmp") which drops the trailing separator;
update dirname (the function named dirname) so when you compute the slice for
parent directories you preserve the separator for drive roots: after computing
index = Math.max(...), if index > 0 and the character before index is ':'
(detecting a Windows drive like "C:"), return value.slice(0, index + 1) instead
of value.slice(0, index); keep the existing special-case handling for index ===
0 and the trailing-separators trimming.
---
Outside diff comments:
In `@src-tauri/risuko-bt/src/torrent.rs`:
- Around line 362-421: The PR shows advertise_v2 can be set true even when
Merkle tables failed to build; clamp advertisement to actual serving ability by
forcing advertise_v2 = init.advertise_v2 && serve_v2_layers before it's
stored/propagated. Concretely, replace usages that copy init.advertise_v2 into
SpawnPeer.advertise_v2, Handshake::new_with_v2(..., advertise_v2) and
KnownInfoHash / handle.advertise_v2 with the clamped value (referencing
advertise_v2, serve_v2_layers, init.advertise_v2, SpawnPeer.advertise_v2,
Handshake::new_with_v2, KnownInfoHash and handle.advertise_v2) so outbound and
inbound handshakes only set the BEP-52 v2 reserved bit when hash_tables is Some.
🪄 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: f2e185a8-1443-451b-8bc6-71cd04feb613
📒 Files selected for processing (15)
packages/risuko-js/package.jsonscripts/android-env.mjssrc-tauri/risuko-bt/examples/probe_handshake.rssrc-tauri/risuko-bt/src/torrent.rssrc-tauri/risuko-bt/src/utp/stream.rssrc-tauri/risuko-bt/src/wire/extended.rssrc-tauri/risuko-engine/src/config/defaults.rssrc-tauri/risuko-engine/src/engine/http.rssrc-tauri/risuko-engine/src/engine/manager.rssrc-tauri/risuko-engine/src/engine/torrent.rssrc-tauri/src/lib.rssrc/renderer/components/Task/TaskActions.vuesrc/renderer/store/task.tssrc/renderer/styles/responsive.csssrc/renderer/utils/native.ts
There was a problem hiding this comment.
2 issues found across 15 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…f sensitive information' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@scripts/android-env.mjs`:
- Around line 92-95: The logging is inconsistent: ANDROID_HOME and
ANDROID_NDK_HOME are printed but JAVA_HOME is only reported as "set"; update the
check that references env.JAVA_HOME so it prints the actual path like the others
(e.g., replace the console message in the block that checks env.JAVA_HOME to log
JAVA_HOME=${env.JAVA_HOME}) to restore consistent debugging output for all three
environment paths; if you need to silence a CodeQL secret-warning, add an inline
suppression comment adjacent to that log statement after making it print the
value.
🪄 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: f96d18a0-ed0c-45a4-aa17-7153d9c6c6fb
📒 Files selected for processing (1)
scripts/android-env.mjs
|
#79 is probably related too |
Summary by cubic
Adds full Android support with SAF directory picker, external storage access, foreground download notifications, and CI to build/sign/upload split-per-ABI APKs. Improves torrent speeds with µTP transport and better DHT, fixes Android picker/layout issues, and updates docs/scripts for Android dev.
New Features
src-tauri/gen/android(Gradle + Kotlin, Tauri activity).DocumentsContract.pnpm android:*,scripts/android-env.mjs,scripts/sign-android-apks.mjs; release builds split-per-ABI, signed, and uploaded; workflows force Node 24 for JS actions.Refactors
src-tauri/capabilities/desktop.json; tightenshell:allow-openallow-list.jni/ndk-context; move desktop-only plugins off Android targets.@risuko/*packages.Written for commit e1fb4ca. Summary will update on new commits.
Review in cubic
Summary by CodeRabbit
New Features
Bug Fixes & Improvements
Chores