mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-07 06:31:22 +02:00
a19f74252e48a4e4451c90d49e5f9d026641f10b
2487
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a19f74252e |
Merge pull request #1263 from maziggy/0.2.4
**Bambuddy v0.2.4**
## ⚠ Upgrade Notes — Read Before Updating
**Most users are upgrading from 0.2.3.2.** The 0.2.4 stable release lands on `main`, so the in-app **Apply Update** button in Settings → System → Updates works for everyone — the 0.2.3.x updater is hardcoded to `origin/main`, and the 0.2.4-beta updater resolves to the latest release tag via the GitHub releases API (respecting `include_beta_updates`). Either way, you get 0.2.4. Below are the explicit Docker + native paths for users who'd rather drive the upgrade from the command line.
**Make a backup before upgrading** via Settings → Backup → Create Backup. Native install with `update.sh` snapshots the database automatically and rolls back on failure. Docker and fully-manual paths don't.
### Docker
Make sure your `docker-compose.yml` `image:` line points at `:0.2.4` (or `:latest` for the rolling stable tag).
```bash
docker compose pull
docker compose up -d
```
While you're there, refresh `docker-compose.yml` from the repo: the entrypoint changes in 0.2.4 mean `user: "${PUID:-1000}:${PGID:-1000}"` is now removed (the new gosu entrypoint owns privilege drop), `PUID` / `PGID` env vars are added with the same defaults, and the `./virtual_printer:/app/data/virtual_printer` bind mount is commented out by default. If you depended on that bind mount to share VP certs with a host install, uncomment it again — first start auto-chowns it.
### Native install — recommended path
```bash
sudo BRANCH=v0.2.4 /opt/bambuddy/install/update.sh
```
The `BRANCH=` env var tells `update.sh` to fetch the `v0.2.4` tag instead of tracking `origin/main`. Omit it (`sudo /opt/bambuddy/install/update.sh`) to follow `main` going forward. The script handles backup, service stop/start, `pip install`, and the frontend build with the correct working directory.
### Native install — manual path
```bash
sudo systemctl stop bambuddy
cd /opt/bambuddy
sudo -u bambuddy git fetch origin --tags
sudo -u bambuddy git checkout v0.2.4
sudo /opt/bambuddy/venv/bin/pip install -r requirements.txt
sudo systemctl start bambuddy
```
### Behaviour changes to know about
- **Docker entrypoint replaces the `chmod 777 /app/data` workaround.** If you've been carrying that, drop it — gosu chowns `/app/data` + `/app/logs` to `PUID:PGID` on first start (idempotent via sentinel file, so restarts skip the recursive traversal). Bind-mounted host directories are chowned through the mount the first time the entrypoint sees wrong ownership.
- **PostgreSQL restore from SQLite Local Backup now works end-to-end.** The `cannot drop table printers` abort that bit Postgres adopters in 0.2.3.x is gone — unblocks the SQLite → Postgres migration path.
- **Path-prefixed reverse proxies remain unsupported.** The `base: ''` Vite config that shipped briefly in 0.2.4b2 was reverted because it broke camera popups and deep-route initial loads. If you've been running Bambuddy at `/bambuddy/` on Traefik / nginx / Cloudflare Tunnel subpath, the documented workaround (NPM addon + Cloudflare Tunnel at a real domain + HA Webpage panel via `TRUSTED_FRAME_ORIGINS`) keeps working.
- **SpoolBuddy kiosks: in-app update + System buttons now work.** Settings → Update Daemon and the QuickMenu System buttons (Restart Daemon / Restart Browser / Reboot / Shutdown) no longer return "API keys cannot be used for administrative operations" — all five now route through `INVENTORY_UPDATE`. Stop reaching for SSH.
---
**Highlights**
0.2.4 is the cumulative result of three beta cycles (b1 → b3) and post-b3 work — three big-ticket features sit at the centre: **Server-side slicing** via OrcaSlicer / Bambu Studio sidecar (closes the gap where Bambuddy users had to keep a slicer install just to re-slice their archive), **Slicer Bundle (.bbscfg) import** so preset selection no longer has to round-trip Bambu Cloud, and a **unified Spoolman inventory UI** with AMS slot assignments, Storage Location, NFC write, and a filament-catalog picker that brings Spoolman feature parity with the local-mode inventory. Around them: **MakerWorld URL-paste import**, **MFA at-rest encryption auto-bootstrap** (default-on, no setup), **GitHub backup extended to Gitea + Forgejo**, **Tailscale integration for virtual printers**, **per-event ntfy priority**, **long-lived camera stream tokens for HA / Frigate / kiosks**, **Library Trash Bin + auto-purge**, and **spool label printing** including a new 40×30 mm template, hex colour code, and a bolder brand line.
Plus the largest contributor wave to date — see attribution per item below.
---
**New Features**
- **Virtual Printer non-proxy modes now mirror the live target printer to the slicer** ([#1193](https://github.com/maziggy/bambuddy/issues/1193) follow-up) — Cached-as-base mirror so AMS/k-profile/camera/status passes through the VP to BambuStudio when running queue or review mode (previously only proxy mode).
- **Server-side slicing via OrcaSlicer / Bambu Studio sidecar** ([PR #1144](https://github.com/maziggy/bambuddy/pull/1144) by @maziggy) — Bambuddy now slices STL/STEP/3MF server-side via an `orca-slicer-api` (or BambuStudio-API) sidecar container. Pick a printer + filament + process preset triplet in the SliceModal, dispatch, and the resulting 3MF is dropped into the library / queue. Embedded-settings fallback when the CLI can't resolve a preset.
- **Slicer Bundle (.bbscfg) import** — Upload a BambuStudio "Printer Preset Bundle" once per printer, then pick from it for every subsequent slice. Closes the long tail of preset-resolution corner cases (cloud presets behind login, "from User" sentinels, the `# `-prefix clone trick, dangling `inherits` on renamed parents). Works alongside the cloud/local/standard preset tiers — pick "Slicer bundle" in the SliceModal to skip PresetRef resolution entirely.
- **Multi-color slicing in the Slice modal with per-plate filament discovery** ([PR #1205](https://github.com/maziggy/bambuddy/pull/1205) by @maugsburger) — Slice modal auto-discovers per-plate filament requirements via a preview-slice call on unsliced project files, so the AMS-slot picker knows which slots the plate actually consumes before dispatch.
- **MakerWorld URL-paste import** ([PR #1099](https://github.com/maziggy/bambuddy/pull/1099) by @maziggy) — Paste a MakerWorld model URL into the import dialog and Bambuddy resolves the plate instances, lets you pick one, and drops it into the library. Authenticated via your existing Bambu Cloud session.
- **Unified Spoolman inventory UI + AMS slot assignments + Storage Location + NFC write + filament-catalog picker** ([PR #1063](https://github.com/maziggy/bambuddy/pull/1063), [PR #1114](https://github.com/maziggy/bambuddy/pull/1114), [PR #1241](https://github.com/maziggy/bambuddy/pull/1241) by @netscout2001 / @maziggy) — Spoolman-backed installs now get feature parity with local inventory: the inventory page renders the same way regardless of backend, "Assign to AMS" works both directions, the SpoolBuddy kiosk can write NFC tags directly to Spoolman spools, and the filament catalog picker browses Spoolman's filament library inline.
- **Tailscale integration for virtual printers** ([PR #1070](https://github.com/maziggy/bambuddy/pull/1070) by @legend813, follow-up by @maziggy) — Virtual printer card surfaces the host's Tailscale IP + MagicDNS hostname with a copy button, so you can paste a tailnet IP into the slicer for private WireGuard reach without port forwarding. Per-VP opt-out toggle.
- **MFA at-rest encryption — default-on via auto-bootstrap** ([PR #1231](https://github.com/maziggy/bambuddy/pull/1231) by @netscout2001) — OIDC `client_secret` and TOTP secret rows are now Fernet-encrypted by default. The key is auto-bootstrapped from `MFA_ENCRYPTION_KEY` env var → `DATA_DIR/.mfa_encryption_key` → auto-generated. New Settings → Authentication → Security tab surfaces the key source, encrypted/legacy row counts, and a `decryption_broken` recovery flag. Key file is included in Local Backup ZIPs so the restore is self-contained.
- **GitHub Backup extended to Gitea + Forgejo** ([PR #1160](https://github.com/maziggy/bambuddy/pull/1160), [PR #1255](https://github.com/maziggy/bambuddy/pull/1255) by @BurntOutHylian) — Settings → Backup → Git Provider now supports Gitea (1.18+) and Forgejo (all versions) alongside GitHub.com and GitHub Enterprise. Gitea uses the Contents API for atomic multi-file commits; Forgejo v15+ token-scope quirk on `/repos/` is handled via a `/user` pre-check. Tested against Gitea 1.24.7 / 1.25.4 / 1.26.1 and Forgejo v11 / v15 LTS.
- **Stock forecasting + Logistics view** ([PR #1184](https://github.com/maziggy/bambuddy/pull/1184) by @Keybored02) — New Inventory → Logistics tab forecasts when each material/colour combo will run out based on rolling burn rate and lets you flag spools for re-order.
- **Spool label printing — DK label printers + AMS layouts** ([#809](https://github.com/maziggy/bambuddy/issues/809)) — Print labels for any spool directly from inventory: roomy single-label (62×29, 40×30), tight AMS-strip (3-up), and Avery 5160 sheets. New 40×30 mm template, hex colour code on every label (`#RRGGBB`), and a bolder Helvetica-Bold brand line at arm's-length-readable size.
- **Embedded GCode viewer** ([PR #963](https://github.com/maziggy/bambuddy/pull/963) by @Soopahfly) — "3D Preview" button on every archive and library file opens a PrettyGCode viewer iframe — no external slicer needed for visual confirmation of which plate is which.
- **Copy spool — duplicate any spool in two clicks** ([PR #1246](https://github.com/maziggy/bambuddy/pull/1246) by @MiguelAngelLV) — Copy button next to Edit on every inventory row prefills SpoolFormModal with the source spool's settings (except `weight_used`, reset to 0). Saves serial-data-entry time for users with many near-identical spools.
- **Build-plate icon on archive cards + uniform printer/model line** ([#1253](https://github.com/maziggy/bambuddy/issues/1253), reported by @tonygauderman) — Archive cards now show an OrcaSlicer-style bed icon (Cool / Cool SuperTack / Engineering / High Temp / Textured PEI / Smooth PEI) so users can tell which build plate the print was sliced for at a glance. Backfill script available for older archives.
- **Library Trash Bin + Admin Bulk Purge + Auto-Purge** ([#1008](https://github.com/maziggy/bambuddy/issues/1008)) — Deleted library files now land in a trash bin instead of being permanently removed. Admin → Library → Trash for bulk purge, plus a configurable auto-purge schedule.
- **Archive Auto-Purge** ([#1008](https://github.com/maziggy/bambuddy/issues/1008) follow-up) — Same pattern for print archives — auto-delete archives older than N days, with bulk-purge admin UI.
- **Project URL + cover photo** ([#1155](https://github.com/maziggy/bambuddy/issues/1155)) — Projects can now carry a source URL and a cover photo (auto-derived from the source 3MF or user-uploaded), rendered on the project list and detail views.
- **"Not Printed" / "Printed" collections on the Archives page** ([#1153](https://github.com/maziggy/bambuddy/issues/1153)) — Filter archives by whether they've been printed at least once, useful for "what's still untried" hunting.
- **Virtual-printer archive name source toggle** ([#1152](https://github.com/maziggy/bambuddy/issues/1152)) — Choose whether VP-spawned archives are named from the source 3MF filename or the slicer-supplied print job name.
- **Enhanced filament colour handling: multi-colour gradients, transparency, visual effects** ([#1154](https://github.com/maziggy/bambuddy/issues/1154)) — Spool form supports multi-colour gradient stops, transparency, and effect overlays (Sparkle, Silk, Matte) — rendered correctly on the spool cards, AMS slot view, and inventory.
- **Per-spool category + low-stock threshold override** ([#729](https://github.com/maziggy/bambuddy/issues/729)) — Each spool can override the global low-stock threshold and carry a custom category, surfaced in the Logistics view.
- **Per-event ntfy priority** ([#990](https://github.com/maziggy/bambuddy/issues/990)) — Set ntfy push priority per notification event type (Print complete → default, Filament out → urgent, etc.).
- **Long-lived camera-stream tokens for HA / Frigate / kiosks** ([#1108](https://github.com/maziggy/bambuddy/issues/1108)) — Generate scoped, non-expiring tokens for embedding Bambuddy camera streams in Home Assistant Webpage cards, Frigate dashboards, or kiosk displays without baking in admin credentials.
- **Filament Track Switch (FTS) support** — Detect and surface FTS-equipped printers' track-switch events on the printer card.
- **AMS slot Load / Unload from the printer card** ([#891](https://github.com/maziggy/bambuddy/issues/891), reported by @NNeerr00, +1 from @cadtoolbox) — Click any AMS slot on the printer card to load or unload the filament — no more reaching for the BambuStudio app.
- **API keys can read Bambu Cloud presets on the owner's behalf** ([#1182](https://github.com/maziggy/bambuddy/issues/1182), reported by @turulix) — API keys can now route Bambu Cloud preset reads through the owner's stored cloud session, so Home Assistant / external automations can slice without separate cloud auth.
- **Home Assistant addon detection** — Bambuddy auto-detects when it's running as the HA OS addon and surfaces the right webhook URL / iframe origin on the Settings page.
- **OIDC: Azure Entra ID support — configurable email claim & verification + Remember Me persistent login** ([PR #1126](https://github.com/maziggy/bambuddy/pull/1126) by @netscout2001) — Custom email claim path (Azure's `preferred_username`/`emails[0]`), per-provider "Require email verified" toggle, and a "Remember me" checkbox on the login page for opt-in 30-day sessions.
- **OIDC auto-created users now get readable usernames and land in a configurable group** ([PR #1176](https://github.com/maziggy/bambuddy/pull/1176) by @netscout2001) — `preferred_username` / `name` claim is consumed first, falling back to email-prefix; default group for auto-provisioned OIDC users is now a Settings dropdown instead of hardcoded "Viewers".
- **Slicer presets now span Cloud, imported, and slicer-bundled tiers, end-to-end** — Unified preset resolution across the three sources so SliceModal, the dispatch path, and the preview-slice cache all agree on which preset a pick resolves to.
- **Plate-clear tracking and visibility on printer cards** ([PR #939](https://github.com/maziggy/bambuddy/pull/939) by @EdwardChamberlain) — Plate-clear gate persists across container restarts and Auto-Off power cycles, with a clearer pill on the printer card showing "Awaiting plate clear" vs "Ready to print".
- **Printer page header update** ([PR #1203](https://github.com/maziggy/bambuddy/pull/1203) by @EdwardChamberlain) — Cleaner header layout on the Printers page.
---
**Improved**
- **Docker data-volume ownership normalised at startup via gosu entrypoint** ([#1211](https://github.com/maziggy/bambuddy/issues/1211)) — Replaces the `chmod 777 /app/data` hack with a proper entrypoint that chowns `/app/data` + `/app/logs` to `PUID:PGID` and drops to that uid via gosu. Subsequent restarts skip the recursive traversal via a sentinel file — no startup penalty on multi-GB archive directories.
- **Spool edit form: persistent Extra Colours, distinguishable Dual Color vs Gradient, more visible Sparkle/checkerboard visuals** ([#1154](https://github.com/maziggy/bambuddy/issues/1154) follow-up, reported by @maugsburger) — UX pass on the colour-effects added in b1.
- **AMS slot "Assign to inventory spool" picker now lists every spool, including RFID-tagged Bambu Lab ones** ([#1133](https://github.com/maziggy/bambuddy/issues/1133)) — Previously hid RFID-bound spools, forcing users to clear the RFID tag first.
- **Inventory: "Delete Tag" button renamed to "Clear RFID Tag"** ([#729](https://github.com/maziggy/bambuddy/issues/729) follow-up) — Less alarming wording; clearer what the button actually does.
- **Nozzle icon on the dual-nozzle status card** ([#1115](https://github.com/maziggy/bambuddy/issues/1115)) — Visually distinguish left/right extruder activity at a glance.
- **Settings page: permission-gated instead of admin-only** — Every settings sub-page now respects per-group permissions (e.g. `SETTINGS_NOTIFICATIONS` for Notifications, `SETTINGS_PROFILES` for Profiles, etc.) so non-admin users with specific scopes can manage their own sections.
- **i18n: full key parity across all 8 locales** — CI gate now enforces parity across en/de/fr/it/ja/pt-BR/zh-CN/zh-TW.
- **Per-request trace ID column on every log line** — Plumbed through HTTP access log, application logs, and response headers so a support bundle can trace one request end-to-end.
- **Live slicer progress in the persistent slice toast** — Real progress percentage instead of a spinner, with URL-decoded filenames in the toast title.
- **Background-dispatch toast no longer reads as "frozen at 100%" for fast uploads** — Fast uploads (small files / fast networks) no longer leave the toast stuck at 100% for several seconds.
- **SpoolBuddy kiosk no longer shows main-app toasts** — Kiosk-side notification suppression so operator-side toasts don't leak onto the kiosk display.
- **SpoolBuddy kiosk: "Plate ready" pills under the printer status badges** — At-a-glance plate state alongside printer state.
- **MakerWorld URL-paste resolver shows which printer each plate was sliced for** — So the picker shows e.g. "Plate 1 (X1C)" / "Plate 2 (P1S)" instead of unlabelled.
---
**Fixed**
### Slicing / Dispatch
- 3MF profile-driven slicing silently produced wrong-printer output (every slice fell back to the source's embedded printer regardless of the picked profile).
- Sliced-archive card listed every project-wide AMS slot instead of just the filaments the print actually used.
- Sliced output of a "single-color" plate had filaments the user never picked.
- Slice modal had no warning when the picked printer profile didn't match the source 3MF's bound printer.
- Settings warning when OrcaSlicer is selected as the preferred slicer (vs Bambu Studio).
- MakerWorld P2S 3MFs failed to slice with "Param values in 3mf/config error: -1 not in range" ([#1201](https://github.com/maziggy/bambuddy/issues/1201), reported by @inorichi).
- Slicer "Send to printer" silently rejected the cached push_status with "storage needs to be inserted" on P1S/A1-class targets ([#1228](https://github.com/maziggy/bambuddy/issues/1228), reported by @rtadams89 and @smandon).
- Slicing a library file via API key fails with "no Bambu Cloud session is stored" even when the key has cloud access ([#1182](https://github.com/maziggy/bambuddy/issues/1182) follow-up, reported by @turulix).
- Slice button no longer enabled before the preview slice resolves.
- Reprint-from-archive failed with `0500_4003` SD R/W errors after a stuck dispatch ([#1136](https://github.com/maziggy/bambuddy/issues/1136)).
- P1P print dispatch failed with `0500_4003 "can't parse print file"` when the printer was slow to acknowledge ([#1150](https://github.com/maziggy/bambuddy/issues/1150), reported by @d3ni3).
- H2D Pro multi-plate dispatch double-/triple-fire ([#1157](https://github.com/maziggy/bambuddy/issues/1157)).
- Background-dispatch reported "Print started successfully" when the printer never actually transitioned ([#1134](https://github.com/maziggy/bambuddy/issues/1134), follow-up to [#1042](https://github.com/maziggy/bambuddy/issues/1042)).
- Queue auto-dispatched the next print onto a fouled bed after an aborted or cancelled print ([#1171](https://github.com/maziggy/bambuddy/issues/1171), reported by @tom5677).
- Queue item stuck at "printing" when print failed before reaching RUNNING ([#1111](https://github.com/maziggy/bambuddy/issues/1111)).
- Queue: batch (quantity>1) double-dispatched onto the same printer.
- Queue: active-item progress bar flashed 100% before dropping to 0%.
- Virtual Printer queue mode auto-dispatched onto the wrong colour when multiple compatible printers were available ([#1188](https://github.com/maziggy/bambuddy/issues/1188), reported by @EdwardChamberlain).
### AMS / Filament / Inventory
- X2D / H2D dual-nozzle without AMS: filament mapping reported "Required filament type not found in printer" even when the spools were physically loaded ([#1257](https://github.com/maziggy/bambuddy/issues/1257)).
- New AMS RFID rolls auto-named to the wrong colour when the hex is shared across material variants (e.g. PLA Matte rolls named "Jade White") ([#1227](https://github.com/maziggy/bambuddy/issues/1227)).
- Bambu RFID auto-match created duplicate inventory rows for Quick-Add and non-Bambu-branded spools ([#918](https://github.com/maziggy/bambuddy/issues/918)).
- Filament usage double-counted when AMS auto-falls-back to a same-material spool ([#957](https://github.com/maziggy/bambuddy/issues/957)).
- Spool form's "Slicer Preset" dropdown silently dropped Local Profiles when Bambu Cloud was connected, and collapsed per-printer/per-nozzle variants into a single entry ([#1248](https://github.com/maziggy/bambuddy/issues/1248), reported by @andretietz).
- Spool auto-assign hit `IntegrityError` on Postgres when AMS pushes arrived in quick succession.
- AMS slot configuration intermittently fails to reach the printer after several configs in a row ([#1164](https://github.com/maziggy/bambuddy/issues/1164), reported by @RosdasHH).
- Spool assignment to a reset AMS slot left the slot unconfigured both in Bambuddy and on the printer.
- AMS slot truncation hid the `@printer 0.4 nozzle` suffix; inline hover expansion added ([#1237](https://github.com/maziggy/bambuddy/issues/1237), reported by @basziee).
### Virtual Printer
- VP queue mode dispatched onto the wrong colour with multiple compatible printers ([#1188](https://github.com/maziggy/bambuddy/issues/1188)).
- VP cached-as-base mirror now overlays SD/storage indicators (`home_flag`, `sdcard`, `storage`) so P1S/A1-class targets don't fail BambuStudio's pre-flight ([#1228](https://github.com/maziggy/bambuddy/issues/1228)).
- VP Tailscale cert-renewal restart silently failed mid-way (follow-up to [#1070](https://github.com/maziggy/bambuddy/issues/1070)).
- Virtual printer card's Tailscale FQDN copy button failed on HTTP.
### Backup / Git Providers
- Gitea backups silently failed after the first run; Forgejo v15 token-scope quirk broke "Test Connection"; many failure paths surfaced cryptic one-word errors ([#1224](https://github.com/maziggy/bambuddy/issues/1224) reported by @rtadams89, [#1239](https://github.com/maziggy/bambuddy/issues/1239) + [PR #1255](https://github.com/maziggy/bambuddy/pull/1255) by @BurntOutHylian).
- Backups to Gitea / Forgejo failed with "Failed to create tree" on empty repos and `list indices must be integers or slices, not str` on populated repos ([#1224](https://github.com/maziggy/bambuddy/issues/1224), [#1225](https://github.com/maziggy/bambuddy/issues/1225)).
- Backup restore silently lost most data (Postgres restore from a SQLite backup).
- Postgres restore from a SQLite Local Backup aborted with `cannot drop table printers`.
### OIDC / Auth
- OIDC callback code/state max_length raised from 512 to 2048 ([#1024](https://github.com/maziggy/bambuddy/issues/1024), [PR #1024](https://github.com/maziggy/bambuddy/pull/1024) by @netscout2001).
- OIDC `auto_link_existing_accounts` now works with custom email claims (Azure Entra ID) ([#1088](https://github.com/maziggy/bambuddy/issues/1088), [PR #1142](https://github.com/maziggy/bambuddy/pull/1142) by @netscout2001).
- OIDC issuer trailing-slash mismatch ([#995](https://github.com/maziggy/bambuddy/issues/995), [#985](https://github.com/maziggy/bambuddy/issues/985)).
- OIDC settings form: "Require email verified" toggle no longer jumps layout when auto-link is enabled.
- Setup: re-enabling auth could 422 on a password the form no longer needs.
### Camera
- Camera preview popup opened to a blank page; deep-route refresh and direct URL load broken ([#1221](https://github.com/maziggy/bambuddy/issues/1221), reported by @enjoylifenow / @Haeckan / @elit3ge / @jc21).
- External-camera frames returned as black on go2rtc and other MJPEG sources ([#1177](https://github.com/maziggy/bambuddy/issues/1177), reported by @nkm8).
- Camera page ignored `?fps=N` URL parameter ([#1131](https://github.com/maziggy/bambuddy/issues/1131) diagnostic).
- Camera stream second viewer fails / kicks the first off ([#1089](https://github.com/maziggy/bambuddy/issues/1089)).
- Camera TLS proxy logged unhandled exceptions when ffmpeg dropped its half of the connection mid-stream under uvloop.
### File Management / Library
- 3D Preview returned `{"detail":"Not Found"}` in Docker installs ([#1218](https://github.com/maziggy/bambuddy/issues/1218)).
- Archive 3MFs (and library file bytes) silently deleted from disk on every print completion ([#1212](https://github.com/maziggy/bambuddy/issues/1212), reported by @abbasegbeyemi).
- Archive created with wrong plate metadata when consecutive plates of the same model are printed back-to-back ([#1204](https://github.com/maziggy/bambuddy/issues/1204), reported by @BurntOutHylian).
- Archive Reprint colliding with originals (carryover fix from 0.2.3 cycle).
- Moving a file to an external folder updated the DB row but never wrote the bytes to the mount ([#1112](https://github.com/maziggy/bambuddy/issues/1112) follow-up).
- Uploads to writable external folders silently landed in internal storage ([#1112](https://github.com/maziggy/bambuddy/issues/1112)).
- GCode Viewer had no in-app way to navigate back (only the browser's back button worked).
- Archives card's "Reprint" / "Schedule" / "Slice" button labels truncated to "Re..." / "Sc..." on narrow browser windows ([#1249](https://github.com/maziggy/bambuddy/issues/1249)).
- Printer file download 500'd on non-ASCII filenames; same crash latent in three sibling endpoints ([#1245](https://github.com/maziggy/bambuddy/issues/1245), reported by @1000Delta).
- Reprint-from-Archive left `created_by_id` as `NULL` ([#730](https://github.com/maziggy/bambuddy/issues/730) follow-up).
### Print Queue / Notifications
- Print-complete notification reported the slicer's pre-print estimate instead of the actual elapsed time ([#1198](https://github.com/maziggy/bambuddy/issues/1198), reported by @BurntOutHylian).
- User-cancelled prints surfaced as "1 problem" on the printer card AND were archived as "Layer shift" failures.
- Pending review card and the resulting archive name disagreed; `.gcode.3mf` filename suffix wasn't fully stripped ([#1152](https://github.com/maziggy/bambuddy/issues/1152) follow-up, reported by @smandon).
- Auto-Print G-code Injection: start snippet landed before printer startup, and `{placeholder}` substitution was silently broken ([#422](https://github.com/maziggy/bambuddy/issues/422) follow-up).
- Plate-clear button stayed visible after the API cleared `awaiting_plate_clear` outside the printer-card click path ([#1128](https://github.com/maziggy/bambuddy/issues/1128)).
### Printer Card / UI
- Printer card's "Show on Printer Card" smart-plug button toggled power without confirmation ([#1260](https://github.com/maziggy/bambuddy/issues/1260), reported by @thkl).
- Printer card always shows the first plate's thumbnail when printing a multi-plate 3MF ([#1166](https://github.com/maziggy/bambuddy/issues/1166), reported by @smandon).
- Printer Info modal: serial-number and IP-address copy buttons silently did nothing on plain-HTTP LAN deployments ([#1174](https://github.com/maziggy/bambuddy/issues/1174), reported by @BurntOutHylian).
- Label picker modal clipped the 4th template option and Cancel button on short viewports ([#1230](https://github.com/maziggy/bambuddy/issues/1230), reported by @elit3ge).
- Project cover photo thumbnail too small to recognise the print ([#1155](https://github.com/maziggy/bambuddy/issues/1155) follow-up, reported by @smandon).
- Project picker UX in archives ([#1151](https://github.com/maziggy/bambuddy/issues/1151)).
### iframes / Reverse Proxies
- iframe embedding from trusted origins (e.g. Home Assistant Webpage panel) no longer blocked ([#1191](https://github.com/maziggy/bambuddy/issues/1191), reported by @azurusnova).
- Frontend served behind a path-prefixed reverse proxy loaded a blank page in 0.2.4b2 — reverted (see Upgrade Notes) ([#1195](https://github.com/maziggy/bambuddy/issues/1195) → reverted in 0.2.4b3).
- Spoolman iframe silently blank on HTTPS Bambuddy with HTTP Spoolman ([#1096](https://github.com/maziggy/bambuddy/issues/1096)).
### MakerWorld
- MakerWorld sidebar entry visible to every user regardless of group permissions ([#1175](https://github.com/maziggy/bambuddy/issues/1175)).
- MakerWorld P2S 3MF parse error on slice ([#1201](https://github.com/maziggy/bambuddy/issues/1201)).
### SpoolBuddy (Kiosk)
- SpoolBuddy install.sh re-run failed with `Permission denied` on root-owned files in update mode.
- SpoolBuddy SSH update aborted with `TypeError: startswith first arg must be bytes or a tuple of bytes, not str` after the host-key store succeeded.
- SpoolBuddy SSH update crashed on Postgres with `value too long for type character varying(500)` when storing the device's RSA host key.
- SpoolBuddy SSH update fails with "permission denied for user spoolbuddy" after Bambuddy keypair rotation.
- SpoolBuddy kiosk Settings → Update button returned "API keys cannot be used for administrative operations".
- SpoolBuddy with Spoolman: NFC tag scan looked up local DB first; Assign-to-AMS no-op on freshly-linked spools; AMS slot picker hid the assigned spool; LinkSpoolModal showed "Unknown color"; tag-write didn't enforce uniqueness; kiosk display held stale state.
- SpoolBuddy AMS page: re-assigning a just-unassigned spool sometimes showed an empty picker ([#1133](https://github.com/maziggy/bambuddy/issues/1133) follow-up).
- SpoolBuddy kiosk screen-blank timeout setting was ignored after the first save.
- SpoolBuddy kiosk screen never blanked while a load cell was producing noisy readings.
### Docker / Install / Logging
- Docker permission errors on `/app/data/virtual_printer` and similar paths — fixed via gosu entrypoint (see Highlights).
- In-app upgrade was hardcoded to `origin/main` and silently no-op'd whenever the latest release wasn't on main.
- In-app upgrade clobbered SSH `origin` on developer checkouts.
- Native-install in-app upgrade silently skipped `pip install` and the new dependencies never landed.
- Settings table filled with duplicate rows on legacy SQLite installs.
- Install script failed for first-time users.
- "Open in Slicer" fails on Windows / Linux for any filename containing spaces or special characters ([#1059](https://github.com/maziggy/bambuddy/issues/1059)).
- `bambuddy.log` filling with `Exception terminating connection ... CancelledError` + `database is locked` cascades on long uploads ([#1112](https://github.com/maziggy/bambuddy/issues/1112) follow-up).
- Windows install: `bambuddy.log` filling with `WinError 10054`.
- `logs/bambuddy.log` was silently dropping records from named child loggers.
- Uvicorn HTTP access log was missing from `bambuddy.log`.
- Swagger UI link in Settings → API Keys rendered a blank page.
### Misc
- H2C dual-nozzle detection missed post-2026 serial batches ([#1105](https://github.com/maziggy/bambuddy/issues/1105)).
- Groups: edits to custom-group permissions appeared lost on reopen ([#1083](https://github.com/maziggy/bambuddy/issues/1083)).
- Settings: failed-save toast looped forever when the user lacked `settings:update`.
- Settings → API Keys: deleted key stayed on screen until manual reload.
- i18n placeholder mismatches in Japanese rendered literal `{{count}}` / `{{name}}` strings in the UI.
- `formatTimeOnly` tests failed under non-`:`-separator locales ([#1213](https://github.com/maziggy/bambuddy/issues/1213), reported by @maugsburger).
---
**Security**
- **python-multipart bumped to >=0.0.27** to clear CVE-2026-42561 (b3).
- **pip upgraded to >=26.1 inside the Docker image** to clear CVE-2026-6357 (medium; GitHub code-scanning alert #778). No `requirements.txt` change — the floor is enforced at the image-build layer where the vulnerable copy lived. (libexpat1 alert #795 also flagged by code-scanning is a DoS-only XML attribute-collision CVE with no patched Debian trixie package yet — left open as a tracking signal.)
---
**Contributors**
Big thanks to everyone who shipped code this cycle:
@netscout2001, @BurntOutHylian, @EdwardChamberlain, @Soopahfly, @legend813, @Keybored02, @maugsburger, @MiguelAngelLV
(See [CHANGELOG.md](https://github.com/maziggy/bambuddy/blob/main/CHANGELOG.md) for the full per-fix detail.)
|
||
|
|
686dce5af4 |
fix(gcode_viewer): close CodeQL XSS + useless-escape alerts on PR #1263
- slider-shim.js: HTML-attribute-escape opts.id before interpolation
(only caller passes a constant, but defends against future taint)
- prettygcode.js: drop useless \\? escape inside [...] character class
|
||
|
|
ac72aacbfa |
Tool: Bandit B108
Severity: Warning ×4 Issue: "Probable insecure usage of temp file/directory" — /tmp/<filename> literals used as synthetic DB field values in two integration tests Status: Fixed ──────────────────────────────────────── Tool: CodeQL Python / JS Severity: Pending Issue: Still running on the head SHA Status: — ──────────────────────────────────────── Tool: Trivy container scan Severity: Pending Issue: Still running Status: — ──────────────────────────────────────── Tool: Bandit (Python Security Analysis) Severity: Pass Issue: The separate Bandit run on the changes already passes Status: ✓ |
||
|
|
0457b92988 | Merge remote-tracking branch 'origin/main' into 0.2.4 | ||
|
|
6062b691d5 | Bumped version | ||
|
|
1c778e8a68 |
fix(security): bump pip to >=26.1 in Dockerfile (CVE-2026-6357)
The python:3.13-slim-trixie base image ships pip 26.0.1, which runs its self-update check after installing wheels — a malicious wheel that included a module name matching a deferred stdlib import (urllib, ssl, ...) could hijack the import inside the install step. GitHub code-scanning alert #778 flagged this as medium-severity. Dockerfile now upgrades pip to >=26.1 immediately before the requirements.txt install, so the requirements install runs under the patched pip and the resulting dist-info metadata in the final image is the fixed version. No requirements.txt change — the floor is enforced at the image-build layer where the vulnerable copy actually lived. |
||
|
|
a25177097d | Post work PR #1255 | ||
|
|
7afb303ffd |
feat(#1239): Update Gitea and Forgejo due to API changes from initial cut (#1255)
feat(#1239): first cut at Gitea backups silently failing after 1st run feat(#1239): Added Token Scope for Forgejo edge case. Also included: test coverage for fixes |
||
|
|
90c3efece9 | Housekeeping | ||
|
|
dfd9fcedf4 |
fix(printer-card): confirm before HA entity toggle on printer card (#1260)
Smart plugs with "Show on Printer Card" enabled appear as a chip in the HA-entities row below the main plug controls. One click cut power to the printer instantly — including mid-print — while the main Off button right next to it already routes through a ConfirmModal. The HA-row chip was added later and skipped the same gating. Branch on entity type: script.* entities keep firing instantly (fire-once triggers, not power switches — confirming each click would be annoying), but switch/light/anything-else entities now open a ConfirmModal first. Reuses the same variant="danger" + running-print warning copy as the existing power-off confirmation when status.state === 'RUNNING'. |
||
|
|
fed8f1f74f | Added install_docker for Windows 11 | ||
|
|
90b26e806b | Updated README | ||
|
|
28c68feaeb | Updated README | ||
|
|
ba3dc613d1 | Changed app defaults | ||
|
|
080176c6e5 |
fix(filament-mapping): X2D/H2D dual-nozzle without AMS lost
external-spool extruder routing (#1257) X2D with 0 AMS units and two external spools (Ext-L feeding left extruder, Ext-R feeding right) showed "Required filament type not found in printer" even when the matching filament was physically loaded. Cause: useFilamentMapping derived dual-nozzle status from ams_extruder_map being non-empty -- that map is populated from AMS info bits, so dual-nozzle printers without AMS got an empty map and hasDualNozzle=false. External spools then fell through to extruderId=undefined, and the nozzle-aware filter rejected every candidate because undefined !== 0/1. Prefer the hardware-reported printerStatus.nozzles array length as the dual-nozzle signal -- populated regardless of AMS configuration -- and keep the ams_extruder_map branch as fallback for older firmware that might not surface nozzles. Affects all dual-nozzle printers running without AMS: X2D, H2D, X2 Pro. Regression test pins both layers the bug straddled -- buildLoadedFilaments extruderId assignment per external spool, and computeAmsMapping picking the correct external for a per-nozzle requirement -- so a future change that re-breaks either fails CI. |
||
|
|
79d54a8d53 |
feat(archives): build-plate icon on cards + uniform printer/model line (#1253)
Show an OrcaSlicer-style bed icon in the archive card's printer-name row indicating which build plate the print was sliced for (Cool / Cool SuperTack / Engineering / High Temp / Textured PEI / Smooth PEI), with the full plate name in the hover tooltip. Closes the gap where users had to remember which plate matched a re-print or open the source 3MF in a slicer just to read the bed setting. Card row also unified: archives with a real Bambuddy-printer association used to render "H2D-1 GCODE ..." while slicer-only uploads rendered "Sliced for X1C GCODE ..." -- same line, two different shapes. Drop the "Sliced for " prefix so both render as a uniform "<name-or-model> [bed-icon] GCODE <hash>" row, scanning identically regardless of provenance. Backend: new bed_type column on print_archives (idempotent ALTER TABLE migration; SQLite + Postgres safe). Populated from curr_bed_type in Metadata/slice_info.config (per-plate, authoritative -- that's what got sent to the printer for the exported plate) with a fallback to project_settings.config for older 3MF shapes. Wired through both archive_to_response() (the hand-rolled dict converter that bypasses from_attributes -- easy to miss) and the /rescan endpoint, so old archives can be re-parsed via the existing per-archive Rescan button. Backfill script (scripts/backfill_archive_bed_type.py, --dry-run supported) re-opens every NULL archive's 3MF on disk to populate the column. Auto-loads .env from project root before importing backend modules (config.py reads DATABASE_URL from os.environ at import time, not from pydantic-settings at Settings() time) and prints the resolved DB URL with credentials redacted, so operators can confirm they're hitting the intended database -- Postgres or SQLite. Frontend: 6 OrcaSlicer-style PNGs ship in frontend/public/img/bed/ -- under /img/ because that path is already statically mounted; a toplevel /bed-icons/ tried first hit the SPA catch-all and returned index.html as text/html. New utils/bedType.ts maps slicer strings case-insensitively, covering both Bambu Studio and OrcaSlicer naming variants for the same physical plate. Unmapped or NULL bed_type simply omits the icon, so cards stay clean for pre-feature archives. |
||
|
|
18af751acd | Updated README | ||
|
|
47dd4dd016 | Updated README | ||
|
|
77bf53b7f1 |
fix(gcode-viewer): add in-app back button (was browser-back only)
Opening the GCode Viewer from a File Manager card or Archive card mounts
GCodeViewerPage as a full-height iframe inside the Layout shell. The page
rendered nothing but the iframe, so once the third-party viewer's UI took
over the content area there was no in-app affordance to return to the
originating list - only the browser's back button.
Add a thin bar above the iframe with an ArrowLeft button. The label adapts
to the entry point - "Back to Print Archives" when the URL carries
?archive=, "Back to File Manager" when it carries ?library_file=, generic
"Back" otherwise. Click prefers navigate(-1) so the user lands back in
their original list with scroll position and filters preserved; falls
back to /archives or /files when the page was opened in a fresh tab and
there's no SPA history to return to.
New gcodeViewer.{back, backToArchives, backToFiles} i18n namespace added
to all 8 locales with native translations.
|
||
|
|
83a83ed724 |
feat(labels): add 40x30 mm template, hex colour code, bolder brand (issue #809 follow-up)
Three enhancements requested by @oliboehm after the V1 label-printing ship in #809: - New box_40x30 single-label template (common DK/Brother roll size, good for filament-bag and storage-bin labels). Routes through the existing roomy layout since height >= 20 mm. - Colour hex code (#RRGGBB, alpha-stripped, uppercase) rendered on every label - useful when several near-identical material/colour spools sit next to each other and the swatch alone isn't enough to tell them apart. Skipped silently when rgba is None or malformed. - Brand line bumped to Helvetica-Bold (was regular) and a couple of points larger on both layouts so it reads cleanly at arm's length. Wired through the SpoolLabelTemplate union, the modal's TEMPLATE_OPTIONS, and the inventory.labels.templates.box40x30 i18n key in all 8 locales (native translations for de/fr/it/ja/pt-BR/ zh-CN/zh-TW). Modal regression test widened from 4 to 5 template buttons. Three new renderer tests pin the hex-code render, the hex-code skip on invalid rgba, and the bold-brand font reference. |
||
|
|
a193145522 |
fix(archives): hide truncated "Re..." / "Sc..." button labels on narrow card widths (issue #1249)
The archive card's action row crams 6 buttons into one line: 2 labelled (Reprint + Schedule, or Slice when un-sliced) plus 4 icon-only utilities. The labelled buttons used `flex-1` to share whatever the icon buttons left over, with the label gated on `hidden sm:inline truncate`. Tailwind viewport breakpoints can't see the card width. The grid grows columns alongside viewport (md:2 lg:3 xl:4), so cards stay ~320-380 px wide regardless of breakpoint, and the labelled buttons end up with ~30 px of space — enough to render "Re..." / "Sc..." and not much else. Bump the label breakpoint sm: -> xl: so labels appear only at viewport >= 1280px where the cards actually have room. Below that, the buttons render icon-only and the existing title= attribute serves as the hover tooltip. |
||
|
|
04096620a9 | Post work PR #1246 | ||
|
|
6a130c09d7 | [Feature] Copy filament (#1246) | ||
|
|
30fe88a334 |
fix(inventory): show all per-printer/per-nozzle variants in spool form's Slicer Preset dropdown (issue #1248)
Two defects in buildFilamentOptions, surfaced together:
1. The function was precedence-based — cloud presets short-circuited
the local-presets branch, silently hiding any imported Local Profile
while the user was logged into Bambu Cloud. The wiki documents the
dropdown as "merged and deduplicated" across cloud + local + built-in.
2. Cloud default presets and local presets were being collapsed by base
name (everything after "@" stripped), so all P1S/X1C/A1 variants of
"Bambu PLA Basic" rendered as a single row. The spool form is
printer-agnostic by design, so the right semantic is to show every
variant individually — the union across all printers — not collapse
them. AMS Slot is per-printer (it filters), the spool form is
union-of-all (it doesn't).
Rewrote the merge to push each cloud setting_id and each LocalPreset row
as its own FilamentOption with the full @printer suffix preserved in
displayName. Built-in dedup against cloud setting_id is kept (mirrors
ConfigureAmsSlotModal.tsx). Wired api.getBuiltinFilaments() into both
callers. slicer_filament persistence is unchanged so existing spools
keep slicing correctly.
|
||
|
|
f5ecc61cda |
fix(spoolbuddy): lower /update permission to INVENTORY_UPDATE so kiosk's own Settings -> Update button works
The kiosk's Settings -> Update Daemon button returned "API keys cannot
be used for administrative operations" because POST /spoolbuddy/devices/
{id}/update was gated on Permission.SETTINGS_UPDATE, and SETTINGS_UPDATE
is in the _APIKEY_DENIED_PERMISSIONS deny-list introduced by PR #1241.
Every kiosk-side request tripped the deny-list before the API key's
scope set (Read / Print Queue / Control / Legacy) was even consulted.
Same root cause as the four QuickMenu System buttons fixed in 0.2.4b3
(Restart Daemon / Restart Browser / Reboot / Shutdown). Missed /update
in that audit on the reasoning "replaces the daemon binary, different
threat surface" — but that's wrong: restart_daemon already replaces
the running daemon process, so daemon-replacement is not a step up in
blast radius. The SSH update is also strictly scoped to the one device
the operator physically controls (git fetch + pip install + systemctl
restart on that host) — same threat profile as the system commands
already running on INVENTORY_UPDATE.
Lower /spoolbuddy/devices/{id}/update from SETTINGS_UPDATE to
INVENTORY_UPDATE so it aligns with the rest of the kiosk-scoped routes
(calibration/tare, display, cancel-write, system/command,
system/command-result, update-status). The main Bambuddy in-app updater
at POST /api/v1/updates/apply keeps SETTINGS_UPDATE — that one runs on
the Bambuddy host and is correctly fenced behind the deny-list.
|
||
|
|
3f58fc74b4 |
fix(http): RFC 6266-encode Content-Disposition so non-ASCII filenames don't crash response (issue #1245)
Reported by @1000Delta. The printer file download (and three sibling endpoints) raised UnicodeEncodeError: 'latin-1' codec can't encode characters... on any filename outside U+0000..U+00FF (Chinese, Japanese, Arabic, accented Latin), because the route pushed `filename` straight into Content-Disposition: attachment; filename="...". Starlette/uvicorn encodes response headers as latin-1, so the assignment crashed at write-time. New backend/app/utils/http.py::build_content_disposition emits both an ASCII-stripped legacy filename="..." fallback and an RFC 5987 filename*=UTF-8''<percent-encoded> parameter. Every modern browser prefers the *= form, so the original Unicode filename round-trips through Save-As intact. Same shape was latent in three siblings and fixed in the same PR (no deferred follow-ups): archive QR endpoint (archive.print_name from 3MF metadata), project ZIP export (project.name — the existing isalnum() sanitiser passes non-ASCII through), and the PDF label streamer (latent today, callers ASCII-only but the helper hardens it). |
||
|
|
91fa9cd432 | Housekeeping | ||
|
|
829bc2dd6a | Bumped version | ||
|
|
a7b3e01b86 | chore(deps): bump python-multipart floor 0.0.26→0.0.27 (CVE-2026-42561) | ||
|
|
ef7fd4fa5c |
fix(spoolbuddy): respect Spoolman mode end-to-end + multiple cache/UX/permission fixes
Seven intertwined SpoolBuddy + Spoolman bugs from feature/spoolman-inventory-ui
testing, fixed as one batch since they all live on the same path:
1. /spoolbuddy/nfc/tag-scanned always tried local DB first and only
consulted Spoolman as a fallback on local-DB miss. A stale local
row silently won over the authoritative Spoolman record. Now gates
on _get_spoolman_client_or_none() so the route uses Spoolman
exclusively when enabled, local exclusively otherwise.
2. Dashboard "Assign to AMS" button was a no-op when the matched
spool wasn't yet in the cached spools query (newly created in
Spoolman, or unarchived after page load). The card rendered via
`displayedSpool ?? sbState.matchedSpool` fallback but the modal's
stricter guard silently failed to mount. New effectiveModalSpool
synthesises an InventorySpool-shaped object from the WebSocket-
delivered MatchedSpool (9-field subset, sufficient for the modal
since it only needs `id` to route the assign API).
3. AMS-page slot picker explicitly returned null for the
assign/unassign branch when a slot had a SpoolmanSlotAssignment
but no tag-linked spool — only Configure stayed visible. Now
resolves the assignment via spoolmanSlotAssignmentsAll +
spoolmanInventorySpoolsCache, renders a "Assigned spool" info
card, and exposes an Unassign button wired to a new
unassignSpoolmanSlotMutation (DELETE
/spoolman/inventory/slot-assignments/<id>).
4. LinkSpoolModal showed "Unknown color" for every Spoolman spool
because Spoolman doesn't standardise color_name — most installs
only populate color_hex and filament.name (which often carries
the colour, e.g. "PLA Basic Red"). _map_spoolman_spool now falls
back to the filament's subtype (filament name minus material
prefix) when color_name is empty, so spools are visually
distinguishable. The NFC write-tag warning specifically checks
the raw filament.color_name (not the mapped value) so the
"tag encodes empty color name" warning still fires on installs
that genuinely lack the field.
5. Writing a tag for spool B didn't clear the same tag from spool A,
so a single NFC UID could map to two spools at once and
find_spool_by_tag returned whichever came first in the cached
list. nfc_write_result now searches Spoolman for any other spool
currently bound to the target UID and clears its extra.tag
(best-effort: cleanup failure logs a warning but doesn't block
the write, since the chip is already written).
6. The kiosk display held stale spoolmanSlotAssignments cache
permanently because a long-running browser window has no
focus/remount triggers to fire a refetch. Adds
refetchInterval: 3_000 so the kiosk picks up changes from another
client (Bambuddy main UI, direct Spoolman edit) within seconds.
7. Kiosk QuickMenu System buttons (Restart Daemon / Restart Browser /
Reboot / Shutdown) all 403'd silently. /system/command was gated
on Permission.SETTINGS_UPDATE (T-Gap 2 from a prior security
audit) but every other kiosk-scoped device route uses
INVENTORY_UPDATE; the kiosk operator's session has the latter,
not the former. Lowered to INVENTORY_UPDATE so operators can
recover the kiosk from the kiosk. Risk is bounded — only the 4
named commands are accepted (no RCE), reboot/shutdown require
physical-access recovery anyway, the same operator already
controls printers + weighs spools. /update keeps SETTINGS_UPDATE
because it can replace the daemon binary.
|
||
|
|
b30a283184 |
Feature/spoolman inventory UI (#1241)
feat(spoolman-inventory): squashed feature work for rebase onto dev Squashed all commits from feature/spoolman-inventory-ui onto a single commit to enable a clean rebase onto dev. Original per-commit history preserved at backup tag backup/spoolman-inventory-ui-prerebase-20260507-105721. |
||
|
|
ceffcfaef6 |
fix(vp): overlay storage indicators on cached push so slicer pre-flight passes for P1S/A1 targets (issue #1228)
Slicer "Send to printer" worked on 0.2.3.2 with a queue-mode VP and
started failing on 0.2.4b3 with BambuStudio's generic "storage needs
to be inserted before send to printer" error. Multiple users
reported it across P1S, P2S, Docker bridge, macvlan, and host
networking. @rtadams89's debug-level support archive showed the
smoking gun: slicer establishes MQTT TLS, gets pushall +
get_version, then never opens an FTP connection — pre-flight
rejects before any data transfer.
The 0.2.3.2 synthetic stub baked in three SD/storage indicators
that BambuStudio's "Send" pre-flight reads: home_flag with bit 8
(HAS_SDCARD_NORMAL, 0x100), sdcard=True, and a storage:{free,total}
block. The 0.2.4b3 cached-as-base slicer-mirror (
|
||
|
|
554f5172f0 | Post work PR #1219 | ||
|
|
90743cfa39 |
feat(encryption): MFA at-rest encryption auto-bootstrap with status UI (#1219) (#1231)
chore(i18n): extend parity gate to all locales with strict/info tiers Previously the script only inspected en/zh-CN/zh-TW, leaving de/fr/it/ja/pt-BR drift invisible. Now locales are auto-discovered from src/i18n/locales/, and a STRICT list (de, zh-CN, zh-TW — currently in parity) gates CI while the rest report informationally until their drift is caught up. ja notably has 27 real placeholder bugs worth fixing before promotion to strict. |
||
|
|
bb03a2b373 |
fix(frontend): revert base: '' so deep SPA routes load their assets on initial navigation (issue #1221)
PR #1195 (
|
||
|
|
4c0a12b95e |
fix(label-picker): pack templates into a 2x2 grid so all 4 plus Cancel fit on tight viewports (issue #1230)
The earlier `min-h-0` fix on the spool list (
|
||
|
|
20fa8fbfdc |
fix(configure-ams-slot): expand long filament profile names inline on hover (issue #1237)
Long preset names like "SUNLU PETG GLOW IN THE DARK GEN2 @Bambu Lab
H2C 0.4 nozzle" were visually clipped in the Configure AMS Slot
modal's preset picker. With several near-identical entries differing
only in nozzle size, users had to open browser dev tools to tell
them apart.
A `title={preset.name}` alone was too slow visually — browsers wait
500-1000ms before rendering native tooltips. The row now un-truncates
inline on hover via group-hover:whitespace-normal + break-all, so the
full name appears the moment the cursor enters the row. `truncate`
stays as the default to keep the list compact when scanning.
The native `title={preset.name}` is also kept as a belt-and-braces
fallback for assistive tech and touch devices where :hover doesn't
fire. Both desktop and mobile layouts updated.
Test: new ConfigureAmsSlotModal.test.tsx regression that pins the
truncate / group-hover:whitespace-normal / group-hover:break-all
classes on the span, the title attribute, and the `group` class on
the parent button — so a future refactor that drops any of those
fails CI.
|
||
|
|
233808956b |
● fix(backup): Gitea wraps GitCommit in Commit schema — extract tree SHA from both shapes (issue #1224 follow-up)
Subsequent backups against Gitea 1.24+ failed with the opaque
"Backup failed: 'tree'" message after the initial-backup fix landed in
|
||
|
|
61314cf20b |
fix(label-picker): allow spool list to shrink so all 4 templates and Cancel stay visible (issue #1230)
The Print Labels modal used a flex column with overflow-hidden on the outer container, the spool list as the flex-1 shrinkable child, and the templates + footer as fixed siblings below it. The spool list had min-h-[160px], which combined with the implicit min-height: auto on flex items meant it could not yield space when the modal was tight — templates and the Cancel button overflowed the modal's max-h-[90vh] and got clipped. Reproducible on Windows 11 + Brave at 1080p with browser chrome / DPI scaling reducing the effective viewport. Switching to min-h-0 both removes the explicit floor and overrides min-height: auto so flex shrinking actually works; the spool list now yields height to keep all four templates and the Cancel button visible on constrained viewports. Larger viewports behave identically since flex-1 still grows to fill. Adds a regression test that asserts all four template names + the Cancel button render in the DOM and pins the structural fix by checking the spool list scroller has min-h-0 with no min-h-[…] literal. |
||
|
|
dac2a31192 |
Revert "feat(inventory): unified Spoolman inventory UI + AMS slot assignments…" (#1232)
This reverts commit
|
||
|
|
55d71498e9 |
feat(inventory): unified Spoolman inventory UI + AMS slot assignments + Storage Location + NFC write support + Spoolman Filament Catalog Picker (#1114)
feat(spoolman-inventory): squashed feature work for rebase onto dev Squashed all commits from feature/spoolman-inventory-ui onto a single commit to enable a clean rebase onto dev. Original per-commit history preserved at backup tag backup/spoolman-inventory-ui-prerebase-20260507-105721. |
||
|
|
ded161626a | Post work PR #1203 | ||
|
|
3c0c7a8ddc | [FEAT] Printer page header update (#1203) | ||
|
|
972e635233 |
fix(spool-tag-matcher): filter catalog lookup by material variant, not hex alone (issue #1227)
Three Bambu Lab catalog rows share #FFFFFF — Jade White (PLA Basic), Ivory White (PLA Matte), White (PLA Silk). The catalog lookup in create_spool_from_tray filtered by manufacturer + hex only with no ORDER BY, so SQLite returned rows in rowid order and the first-inserted entry (Jade White) won every RFID-driven spool creation regardless of the actual material the AMS reported. Inserting an Ivory White PLA Matte roll always produced a spool named "Jade White". Same class of bug bites any other shared-hex pair across PLA Basic / Matte / Silk; the whites were just the most visible. Fix: add a material filter using tray_sub_brands (the printer-reported material variant — "PLA Matte" / "PLA Basic" / "PLA Silk"), which matches the catalog's `material` column directly. Use the raw tray_sub_brands value (captured before the gradient/dual/tri-color subtype upgrade) because the catalog stores "PLA Basic" for gradient rolls too — the upgraded subtype lives on the spool, not the catalog. Also add ORDER BY id to the query so the fallback path (empty tray_sub_brands — third-party spools / OpenTag tags) is deterministic across SQLite + PostgreSQL instead of DB-implementation-defined. Tests: 4 new in test_spool_tag_matcher.py — Ivory White PLA Matte resolves to Ivory not Jade (the regression pin), PLA Silk White resolves to White, Jade White PLA Basic still works with all three #FFFFFF entries seeded, and the empty-sub_brands fallback stays deterministic via the new ORDER BY. Existing spools already mis-named in the database don't auto-correct on next AMS read — the matcher only fires on new RFID-driven creation. Affected users need a manual rename in Inventory after upgrading. |
||
|
|
7ee89b561b |
fix(backup): Gitea/Forgejo handle list-shaped ref response and empty-repo bootstrap (issue #1224 and #1225)
Two interacting bugs in the Gitea/Forgejo backend, both inherited from GitHubBackend because PR #1160 assumed Gitea's Git Data API was fully GitHub-compatible. It isn't, on two specific points: 1. List-shaped ref response. Gitea/Forgejo's GET /api/v1/repos/{owner}/{repo}/git/refs/heads/{branch} returns a GET /api/v1/repos/{owner}/{repo}/git/refs/heads/{branch} returns a list of matching refs even when only one matches; GitHub returns a single object. The inherited push paths did ref_response.json()["object"]["sha"] and crashed with "list indices must be integers or slices, not str" against any populated Gitea repo. 2. Empty-repo writes refused. GitHub accepts blob/tree/commit POSTs against a brand-new empty repo and creates the initial commit implicitly. Gitea refuses every blob POST with 404 until the repo has at least one commit, so _create_initial_commit silently failed: blobs returned 404, tree_items stayed empty, the tree POST then also 404'd ("Failed to create tree"). Fix lives entirely in GiteaBackend — github.py is untouched so the proven GitHub path takes zero risk. GiteaBackend now overrides push_files, _create_branch_and_push, and _create_initial_commit: - _ref_sha() helper accepts both list and dict shapes; called at the two SHA extraction sites in push_files and _create_branch_and_push. - _create_initial_commit posts to Gitea's Contents API (POST /api/v1/repos/{owner}/{repo}/contents with a files array plus branch + new_branch) which seeds the initial commit + branch in one transaction and is documented to work on empty repos. ForgejoBackend extends GiteaBackend with no overrides and inherits both fixes; tests pin that. |
||
|
|
c6e6c4cdd9 |
fix(usage-tracker): split filament weight when AMS auto-falls-back mid-print (issue 957)
When one spool ran out and the AMS transparently switched to a sibling
slot of the same material, the usage tracker credited the originally-
mapped spool with the full 3MF estimate AND added the fallback spool's
remain%-delta on top — so a 78g print could record as 138g across two
spools, leaving the empty spool's recorded weight beyond its label.
Two interacting bugs:
1. bambu_mqtt.py: the tray-change recorder gated on
`state in ("RUNNING", "PAUSE")`, but P2S firmware briefly transitions
out of RUNNING during the AMS swap (into LOADING etc.), so the
literal-string gate missed the switch entirely and tray_change_log
stayed empty. Re-key on the print-lifecycle flags
(_was_running and not _completion_triggered) so any tray change
between print start and completion is captured regardless of the
momentary gcode_state.
2. usage_tracker.py: the splitting branch was gated on
`not slot_to_tray`, so the splitting code only ran for prints where
the slicer mapping hadn't been captured — i.e. never on the actual
fallback case (slot_to_tray is populated by every print_cmd). Drop
the gate: when tray_change_log has > 1 entries, splitting takes
over and per-segment per-layer gcode usage replaces the stale
mapping. Path 2 (AMS remain%-delta) then naturally skips both trays
because they're already in handled_trays after splitting,
eliminating the double-credit.
|
||
|
|
a3e09891d1 |
fix(docker): copy gcode_viewer assets into the production image (issue #1218)
The embedded GCode viewer's static assets (gcode_viewer/) were never
copied into the production Docker image, so /gcode-viewer/ returned a
bare FastAPI 404 ({"detail":"Not Found"}) and 3D Preview broke for every
Docker user since the viewer landed in 0.2.4b1. The Vite production
build doesn't stage the directory either — the dev server serves it via
a configureServer middleware that's dev-only.
Dockerfile now copies gcode_viewer/ alongside the React build output.
Defence in depth: main.py logs an ERROR at startup when
_gcode_viewer_dir/index.html is missing so future packaging gaps surface
in docker logs and the support bundle instead of as silent runtime 404s.
The existing integration test accepted 404 unconditionally
(assert response.status_code in (200, 404)) so CI never caught the
missing files. Add test_gcode_viewer_index_served_when_assets_present
which skips when the directory is intentionally absent (unit-test envs)
but asserts 200 + non-empty HTML body when the assets do exist on disk —
so a broken COPY fails CI loudly rather than shipping a broken image.
|
||
|
|
f87749d683 | Updated CHANGELOG | ||
|
|
c68bd53255 | Updated README.md | ||
|
|
a50958e426 |
feat(slice-modal): Bundle tier for picking presets from imported .bbscfg
Closes the loop on the bundle work: users who imported a Printer
Preset Bundle via Settings → Slicer Bundles can now pick it in the
SliceModal and slice through the bundle dispatch path the backend
already supports.
UX:
- New "Slicer bundle" picker at the top of the modal, rendered only
when at least one bundle is imported (GET /slicer/bundles non-empty)
- Selecting a bundle replaces cloud/local/standard preset dropdowns
with bundle-scoped pickers (process + per-slot filament names from
the bundle). Printer is implicit (each .bbscfg has exactly one).
- Submit routes through SliceRequest.bundle so the backend skips
PresetRef resolution and asks the sidecar to materialise the JSON
triplet from the stored bundle by name.
- "None" leaves the modal on the original preset triplet path.
Frontend types: SliceBundleSpec + bundle?: SliceBundleSpec on SliceRequest.
|