fix(slicer): encode file URL in protocol-handler scheme on Windows/Linux

"Open in Slicer" emitted `orcaslicer://open?file=<URL>` and
  `bambustudio://open?file=<URL>` by plain string concatenation, relying
  on a stale comment that claimed the browser preserves URLs in the query
  string. That ignores the slicer's own `url_decode()` on the received
  query (BS post_init → url_decode + split_str; OrcaSlicer Downloader
  regex + url_decode), so any already-percent-encoded character — most
  commonly `%20` from filenames with spaces — decoded to a literal space
  and the slicer's subsequent HTTP GET returned 0 bytes or 404.

  All three URL forms now use `encodeURIComponent()` (matching what the
  macOS `bambustudioopen://` branch was already doing, which is why the
  bug didn't surface on macOS). Corrected the file-level comment to
  document the actual invariant.

  Regression test in slicer.test.ts feeds the exact issue reproduction
  URL and asserts `%2520` appears in the generated href.
This commit is contained in:
maziggy
2026-04-23 10:03:44 +02:00
parent 62f2e616a0
commit 76b997fc8a
5 changed files with 25 additions and 18 deletions
+11 -10
View File
@@ -15,9 +15,13 @@
* - (orcaslicer|bambustudio|...)://open?file=<URL>
* - bambustudioopen://<URL>
*
* Key insight: Using ?file= query format, the browser's URL parser preserves
* http:// in the query string without any encoding. Only the macOS-specific
* bambustudioopen:// format needs encodeURIComponent (BS calls url_decode).
* Key insight: every form needs encodeURIComponent on the file URL, because
* the slicer calls url_decode() on the received query (post_init calls
* url_decode then split_str; MacOpenURL strips the prefix then url_decode;
* OrcaSlicer's Downloader regex-extracts then url_decode). Without encoding,
* any already-percent-encoded character in the download URL (most commonly
* %20 in filenames with spaces) decodes to a literal space and the slicer's
* subsequent HTTP fetch fails with a 0-byte body or 404. See issue #1059.
*/
export type SlicerType = 'bambu_studio' | 'orcaslicer';
@@ -51,22 +55,19 @@ export function detectPlatform(): Platform {
export function openInSlicer(downloadUrl: string, slicer: SlicerType = 'bambu_studio'): void {
let url: string;
const encoded = encodeURIComponent(downloadUrl);
if (slicer === 'orcaslicer') {
// OrcaSlicer: ?file= query format — http:// preserved in query string
url = `orcaslicer://open?file=${downloadUrl}`;
url = `orcaslicer://open?file=${encoded}`;
} else {
const platform = detectPlatform();
if (platform === 'macos') {
// macOS only: bambustudioopen scheme via MacOpenURL() callback.
// Must encode because bare http:// in authority gets mangled by browser.
// BS calls url_decode() after stripping "bambustudioopen://" prefix.
url = `bambustudioopen://${encodeURIComponent(downloadUrl)}`;
url = `bambustudioopen://${encoded}`;
} else {
// Windows/Linux: bambustudio://open?file= via post_init() CLI args.
// The ?file= query format preserves http:// without encoding.
// IMPORTANT: On Linux, BS only handles "bambustudio://open" prefix —
// it does NOT process "bambustudioopen://" (that's macOS-only).
url = `bambustudio://open?file=${downloadUrl}`;
url = `bambustudio://open?file=${encoded}`;
}
}