From 4f59e256529024eeafd4d7dcae495603f8e40a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B6=BE=E7=80=AC=E6=A1=83=E6=A1=83?= Date: Thu, 10 Sep 2026 09:28:50 +0800 Subject: [PATCH 1/3] ci: declare rustfmt/clippy components in rust-toolchain.toml The 1.98.1 pin installs without rustfmt and clippy, so the fmt and clippy jobs fail at the shim before running. Listing the components in the toolchain file makes rustup auto-install them on every host, including the CI runners that invoke cargo with no setup action. --- rust-toolchain.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c08f3dd..6618266 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,7 @@ [toolchain] channel = "1.98.1" +# CI invokes rustfmt/clippy through the rustup shim with no setup action, so +# the pinned toolchain must declare its components here — the runner images +# only preinstall them for their default toolchain. +components = ["rustfmt", "clippy"] targets = ["x86_64-pc-windows-msvc", "wasm32-unknown-unknown"] From a230c37281d31de57b3615720ee510b420957ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B6=BE=E7=80=AC=E6=A1=83=E6=A1=83?= Date: Thu, 10 Sep 2026 09:29:23 +0800 Subject: [PATCH 2/3] web: stack file-row status below the name on narrow screens On phone-width viewports the right-aligned status column shared a flex line with a long file name and wrapped one word per line. Below 560px the row now wraps: name and action icons keep the first line and the status takes a full-width line underneath. --- web/style.css | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/web/style.css b/web/style.css index 8a2da81..47cfab0 100644 --- a/web/style.css +++ b/web/style.css @@ -333,6 +333,26 @@ footer { } footer a { color: var(--dim); } +/* --- narrow screens --- */ + +/* On a narrow viewport the status column (flex: 1, right-aligned) is + squeezed by a long file name into a one-word-per-line vertical strip. + Stack the row instead: name + icons on the first line, the status on its + own full-width line below, left-aligned. */ +@media (max-width: 560px) { + main { padding: 1.5rem 0.9rem 2.5rem; } + + #dropzone { padding: 1.75rem 1rem; } + + .file .row { flex-wrap: wrap; } + .file .name { flex: 1; min-width: 0; } + .file .status { + order: 6; /* after the download/remove icons */ + flex-basis: 100%; + text-align: left; + } +} + /* --- animations --- */ @keyframes fadeIn { From 512a6270663a34f0261c9ea385ac4e6b72e154fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B6=BE=E7=80=AC=E6=A1=83=E6=A1=83?= Date: Thu, 10 Sep 2026 09:32:58 +0800 Subject: [PATCH 3/3] web: recognize Android targets and point users at the CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dropping an .apk/.apks/.xapk package or a .so library reported "not recognized — will be skipped", which reads as "not protected" when the real gap is that the Android pipeline (filesystem orchestration in senbei-io) has no wasm build. These files now get an explicit android pseudo-kind at staging time: the row explains that the CLI handles them, and they neither enable the Unpack button nor error out mid-run. --- web/app.js | 34 +++++++++++++++++++++++++++++++--- web/index.html | 4 +++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/web/app.js b/web/app.js index e2367b1..d7d2341 100644 --- a/web/app.js +++ b/web/app.js @@ -50,6 +50,22 @@ const KIND_LABEL = { 'native-dll': 'protected native DLL', 'managed-dll': 'protected managed DLL', metadata: 'il2cpp metadata', + 'android-package': + 'Android app package — the web build cannot unpack these yet; use the senbei CLI', + 'android-so': + 'Android AArch64 library — the web build cannot unpack these yet; use the senbei CLI', +}; + +// Android targets are recognized by extension so the row can explain the +// situation instead of reporting a protected file as unrecognized: the +// Android pipeline is filesystem orchestration (senbei-io) with no wasm +// build, so these files need the CLI. The pseudo-kinds are labels only — +// they never reach the worker. +const ANDROID_KIND = { + apk: 'android-package', + apks: 'android-package', + xapk: 'android-package', + so: 'android-so', }; const COMPANION_SVG = @@ -91,8 +107,13 @@ async function stageFiles(list) { // the PE header fields); read a small slice, not the whole file. const head = new Uint8Array(await file.slice(0, 65536).arrayBuffer()); // Companions are ciphertext fragments; detect() only makes sense on the - // base module, so skip it for `._` files. - const kind = file.name.endsWith('._') ? undefined : detect(head); + // base module, so skip it for `._` files. Android targets short-circuit + // detect() as well: an ELF/zip never classifies as a protected PE, and + // the row must carry the android pseudo-kind for its status line. + const ext = file.name.slice(file.name.lastIndexOf('.') + 1).toLowerCase(); + const kind = file.name.endsWith('._') + ? undefined + : (ANDROID_KIND[ext] ?? detect(head)); const old = files.get(file.name); files.set(file.name, { file, @@ -284,7 +305,10 @@ function render() { const unpackable = [...files].some( ([name, e]) => - !name.endsWith('._') && e.kind !== undefined && e.state === 'staged', + !name.endsWith('._') && + e.kind !== undefined && + !e.kind.startsWith('android-') && + e.state === 'staged', ); unpackBtn.disabled = !unpackable; actions.hidden = files.size === 0; @@ -364,6 +388,10 @@ unpackBtn.addEventListener('click', async () => { continue; } + // Android rows are informational only (no wasm pipeline); their staged + // status line already says to use the CLI. + if (entry.kind.startsWith('android-')) continue; + entry.state = 'working'; render(); try { diff --git a/web/index.html b/web/index.html index 59e9c2c..7a7cacc 100644 --- a/web/index.html +++ b/web/index.html @@ -56,7 +56,9 @@

Senbei web

Protected .exe / .dll modules, optional ._ companions, or an il2cpp - global-metadata.dat. + global-metadata.dat. Android packages + (.apk / .apks / .xapk) and + .so libraries are recognized but need the CLI.