Post work PR #1413

This commit is contained in:
maziggy
2026-05-18 12:03:21 +02:00
parent feb44a9dc1
commit ae0f485a84
3 changed files with 384 additions and 382 deletions
+2
View File
@@ -14,6 +14,8 @@ All notable changes to Bambuddy will be documented in this file.
- **Archives → Print Log: filename column expands to fit available width and wraps long names instead of clipping at 200 px (#1406, requested by @daFreeMan)** — Reporter on a 27" monitor saw long filenames like `Simple_Print_Monitor_-_ST7789_1.54_display_case_` truncated even though the table had plenty of unused horizontal space. The print-name `<span>` had a hard `truncate max-w-[200px]` cap that ignored viewport width entirely. Replaced with `break-words` + a `title` attribute, dropping the explicit max-width so the column auto-sizes to content. On wide screens the full name shows on a single line; on narrow ones it wraps inside the cell instead of forcing horizontal scroll. The `title` hover preserves the original tooltip affordance for the rare case where a really long name still gets truncated by viewport constraints. Frontend build clean; 23 ArchivesPage tests still pass.
### Fixed
- **Library "Open in Slicer": broken when the display name lacked `.3mf` or contained `/ \ ? #` (#1413, contributed by @benhalverson in #1416, reported by @ddingg)** — Reporter on Windows 11 / Chrome saw Bambu Studio and OrcaSlicer reject the slicer URL from the 3D-preview modal's "Open in Slicer" button with a parse error, even though downloading the same URL with curl worked. The MakerWorld "Save and open" path and the "Recent imports" entry both worked fine — different code path. Root cause: `GET /library/files/{file_id}/dl/{token}/{filename}` uses the URL-tail filename purely as a hint for the slicer to detect the file format from the path; the backend itself reads `file.filename` from the DB (`library.py:3806`) and ignores the URL segment. When `ModelViewerModal.handleOpenInSlicer` passed the modal `title` (display name like `"Mecha Mewtwo No AMS Multi Color Parted Statue"` — no extension) verbatim through `encodeURIComponent`, the resulting URL ended without `.3mf` and the slicer's client-side extension sniff refused to parse the response. The same path also let `/ \ ? #` through, which can survive `encodeURIComponent` (`/` is unencoded by spec) and break the slicer's URL parser separately. Fix adds a small `buildSlicerUrlFilename(filename)` helper to `frontend/src/api/client.ts` that strips `/ \ ? #` (replacing them with `_`) and appends `.3mf` when missing; `getLibrarySlicerDownloadUrl` now routes the filename through it before `encodeURIComponent`. The `.3mf` check is case-insensitive (`safe.toLowerCase().endsWith('.3mf')`) so `model.3MF` is handled correctly — a subtle improvement over the equivalent inline logic that's still present on the archive-side helpers `getArchiveForSlicer` and `getArchiveSlicerDownloadUrl` (call-site consolidation is a separate follow-up). Safe because `ModelViewerModal.tsx:264` already gates the button to `fileType === '3mf'` library files, so unconditional `.3mf` append never produces nonsense like `model.gcode.3mf`. Two new tests in `frontend/src/__tests__/api/client.test.ts` cover both branches (display name without extension → `.3mf` appended; display name with `/ ? #` → replaced with `_`). 21 client tests green; frontend build clean.
- **Add Spool modal: hex colour field can be typed into character-by-character again (#1407, reported by @anthonyma94)** — Pre-fix, after typing the first hex character the input's value snapped to e.g. "A00000" (the #1055 fix aggressively padded to 8 chars on every keystroke), the cursor jumped to the end, and the next keystroke landed at position 7 — which the original 7-char-truncation branch then dropped. Net effect: only the first character was ever typed; the rest stayed as zeros unless the user pasted a full hex code. Fix splits "what the user is typing" from "what gets sent to the backend": the input now has its own draft state holding 0–6 chars freely, and `updateField('rgba', ...)` only fires once the draft reaches a complete 6-char RGB (commits as `<6chars>FF`). On blur, a partial 1–5 char draft is right-padded with `0` and committed so the form state always carries a valid 8-char rgba — preserves the #1055 invariant that the backend never sees a malformed value, without re-introducing the truncate-on-keystroke trap. A `useEffect` keeps the draft in sync when an external action (the colour picker, a swatch click, edit-mode load) changes the canonical hex. Paste of 7-/8-char strings truncates to the leading RGB triplet: Bambu filaments are opaque and the UI never exposed an alpha affordance, so dropping the (undocumented) "paste with alpha" case is fine. The existing `ColorSectionHexInput.test.tsx` was rewritten to match the new contract — 8 tests covering both new behaviours (draft reflects each keystroke, no commit while partial, commits on length 6, blur-padding for partials, no commit when cleared then blurred) and the kept #1055 invariants (committed rgba is always 8 hex chars, 7-/8-char paste truncates, non-hex chars stripped). 51 spool-form frontend tests green; frontend build clean.
- **Virtual Printer queue: timelapse / bed-leveling / flow-cali / vibration-cali / layer-inspect now inherit the slicer's choice instead of always falling back to global defaults (#1403, reported by @pwostran)** — Reporter sliced in OrcaSlicer with timelapse enabled, sent to a VP queue, started the job from the queue and got no timelapse video. The dispatch chain itself was correct (queue item → scheduler → MQTT command honours `timelapse`); the gap was at queue-add time: the VP's `_add_to_print_queue` read `default_timelapse` from settings (introduced in #1235 to stop column defaults from winning), but ignored the slicer's project_file MQTT command entirely. The slicer's choice — which Bambu Handy / Bambu Studio / Orca all surface in their "Print options" dialog and ship in the MQTT payload as `timelapse: true|1` — was being thrown away. So a user with `default_timelapse=false` (the new-install value) would have to either flip the global setting or manually edit every queue item, even though their slicer's UI was clearly saying "record timelapse for this job". Fix: `on_print_command` in the VP manager now stashes the slicer's project_file dict keyed by filename, and `_add_to_print_queue` waits up to 2 s for that capture before reading the settings fallback. Each option flows through per-field — slicer value wins if present, else the existing settings default (so users who explicitly set `default_timelapse=true` in their VP workflow card still get that on slicers that don't send a print command). MQTT field naming is preserved exactly: `bed_leveling` (single L) on the wire stays mapped to `bed_levelling` (double L) on the Bambuddy column. Integer 0/1 from H-family slicers and bool true/false from P1/X1 slicers both coerce correctly via `bool()`. Wait is skipped when there's no MQTT server attached to the VP instance (covers unit tests calling `_add_to_print_queue` directly so they don't pay the 2 s tax) and capture is consumed on use so the dict stays bounded across many prints. Two new regressions in `test_virtual_printer.py::TestPrintQueueMode`: `test_add_to_print_queue_inherits_slicer_print_options` (slicer=True overrides settings=False across all 5 fields; capture is consumed) and `test_add_to_print_queue_coerces_slicer_integer_zero_one` (H-family integer payload is coerced). The existing `#1235` test (`test_add_to_print_queue_uses_workflow_defaults_from_settings`) still passes because the no-MQTT-attached gate skips the wait, so the settings fallback path is preserved when no slicer capture exists. **Plus two side-bugs surfaced while investigating Martin's "modal not respected" hypothesis** — the support-package evidence cleared the reprint modal (46 `print_scheduler` and 33 `background_dispatch` events with `timelapse: true` shipped to real P1S printers, end-to-end working), but the same dig turned up two latent issues worth fixing in the same pass: (a) **`POST /webhook/printer/{id}/start` was broken on four axes** — `await printer_manager.start_print(...)` against a `def` (not `async def`) function, `queue_item.archive_id` (int) passed as the `filename` arg, `printer_manager.get_status(...).get(...)` against a `PrinterState` dataclass (not a dict), and every print option discarded (timelapse, bed_levelling, AMS mapping). The route would 500 before ever reaching the printer. Rewritten to mirror `POST /print-queue/{item_id}/start`: just clear `manual_start=False` on the next pending queue item and let the scheduler dispatch it with the queue's stored options intact. Three new regressions in `test_webhook_start_print.py` (clears `manual_start`, preserves stored print options, 404 when no pending items / unknown printer). (b) **`vibration_cali` default drift in `background_dispatch.py`** — `ReprintRequest.vibration_cali` and `FilePrintRequest.vibration_cali` both default to `True` (matches Bambu Studio behaviour for X1/P1 series), but the two `_process_job` call sites read `job.options.get("vibration_cali", False)`. Cosmetic today because the frontend always sends the field, but a latent landmine for any future caller that bypasses the schema (e.g. an internal dispatcher seeding options programmatically). Both call sites flipped to `True`; new contract test `test_dispatch_option_defaults_align_with_request_schema_defaults` introspects the source to lock the alignment for all six print-option fields so this drift can't recur. 116 VP unit tests green; 4999 backend tests green; ruff clean.
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -26,7 +26,7 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-C8m37mQ7.js"></script>
<script type="module" crossorigin src="/assets/index-BD1bsaGc.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-KYwGxnG9.css">
</head>
<body>