diff --git a/CHANGELOG.md b/CHANGELOG.md index bdaae7263..f609711d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ All notable changes to Bambuddy will be documented in this file. - **Filament Track Switch (FTS) support — print modal filament dropdown is no longer empty when an X2D / H2D has the FTS accessory installed** ([#1162](https://github.com/maziggy/bambuddy/issues/1162), reported by @mkavalecz) — When the FTS accessory is installed the printer's MQTT changes one nibble of the per-AMS `info` bitmask: bits 8-11 flip from a fixed extruder ID (0x0 / 0x1) to `0xE` ("uninitialized"), because the AMS is no longer wired to a single nozzle — the FTS dynamically routes any slot to either extruder. Bambuddy's MQTT parser already skipped 0xE entries when building `ams_extruder_map` (matching BambuStudio's reading for boot-time transient state), so with the FTS installed the map ended up empty and the print modal's filament dropdown — which filters by `extruderId === nozzle_id` to prevent cross-nozzle assignment ("position of left hotend is abnormal" failures) — filtered out *every* loaded slot. Net effect: empty Filament Mapping dropdown on every dual-nozzle print with the FTS, even when the AMS was fully loaded with the right material. Detection comes from a new MQTT field — `print.device.fila_switch` — which is non-null only when the accessory is installed; it carries the routing topology as two arrays: `in[track] = currently fed slot (-1 = empty)` and `out[track] = extruder this track terminates at`. The fix surfaces this through a new `FilaSwitchState` dataclass on `PrinterState` (`installed`, `in_slots`, `out_extruders`, `stat`, `info`) and the equivalent `FilaSwitchResponse` Pydantic schema on the `GET /printers/{id}/status` route. Frontend (`useFilamentMapping.ts` + `FilamentMapping.tsx`) skips the per-extruder filter when `printerStatus.fila_switch?.installed === true` so any compatible AMS slot can satisfy any nozzle's filament requirement, since the FTS handles the routing. Slots currently fed into a track also get a routing badge in the dropdown — `[L]` or `[R]` — so the user can tell at a glance which slot the FTS is currently routing where (idle slots get no badge: they can be routed to either extruder on demand). The hard "no cross-nozzle assignment" filter on real dual-nozzle printers without the FTS stays untouched (still trips the same way it always has — `fila_switch == null` keeps the existing behaviour). 4 backend tests in `test_bambu_mqtt.py::TestFilamentTrackSwitchDetection` (default-not-installed, detect-from-MQTT-using-the-reporter's-bundle, no-fila_switch-field-stays-not-installed, missing-in-out-arrays-don't-crash) and 2 frontend tests in `useFilamentMapping.test.ts` (FTS-active drops the nozzle filter; explicit `fila_switch: null` keeps the filter applied). Upstream fila_switch payloads with anything other than the documented shape are tolerated — `installed` flips on the *presence* of the field, the routing arrays default to empty lists if missing, and the dropdown skips the badge for slots not currently in `in_slots`. ### Fixed +- **Frontend served behind a path-prefixed reverse proxy (e.g. `/bambuddy/` on Traefik / nginx / Cloudflare Tunnel) loaded a blank page** ([#1195](https://github.com/maziggy/bambuddy/issues/1195), reported by @Spegeli, follow-up to [#1167](https://github.com/maziggy/bambuddy/issues/1167)) — Vite's default `base: '/'` emits absolute asset URLs in the built `index.html` (`/assets/index-*.js`, `/assets/index-*.css`, `/manifest.json`, `/img/...`, `/sw-register.js`), which assumes the SPA is always served at the host root. Behind any path-prefixed reverse proxy — Traefik with a path prefix, nginx `location /bambuddy/`, Cloudflare Tunnel with path routing, Synology / Unraid reverse-proxy panels — the browser then requests those absolute paths from the host root, the proxy doesn't see them, and the upstream serves either a 404 or HTML for an unknown path with `Content-Type: text/plain`/`text/html`; the browser logs `Refused to apply style from '.../assets/index-*.css' because its MIME type is 'text/plain'` and renders a blank white page. Two-line fix: `frontend/vite.config.ts` sets `base: ''` so Vite's HTML transform rewrites every absolute asset reference to relative (`./assets/...`, `./manifest.json`, `./img/...`, `./sw-register.js`) — these resolve correctly against whatever subpath the document was served from. `frontend/public/sw-register.js` is a public-dir file Vite copies as-is, so its `navigator.serviceWorker.register('/sw.js')` call is changed to `register('sw.js')` (relative); the SW scope is automatically pinned to whatever subpath the document loaded from, which is exactly what every reverse-proxy-at-subpath user wants. Net effect: an `https://example.com/bambuddy/` deployment now loads correctly without any frontend rebuild on the user's side. **Out of scope for this change:** runtime API base detection — `API_BASE = '/api/v1'` in `frontend/src/api/client.ts` is still absolute, so API calls still go to the host root. This is intentional. The fix above closes the immediate "blank page" report; making the API base, React Router basename, PWA manifest scope, and service-worker scope all subpath-aware would mean rewriting how the SPA bootstraps and would touch PWA-install state, push-notification subscriptions, and deep-link reload semantics. The supported way to embed Bambuddy in Home Assistant remains the **Webpage panel + `TRUSTED_FRAME_ORIGINS`** path documented in the wiki — Bambuddy reachable on a stable URL (HTTP for HTTP-only HA, HTTPS via your own reverse proxy for HTTPS HA / Nabu Casa / custom-domain), iframe-embedded via the HA dashboard. HA Ingress / addon-based subpath embedding (which would require the runtime path detection above) is not supported by core. Documented explicitly in `docker.md` so users hit the right pattern first. + - **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) — Bambuddy ships strict anti-clickjacking headers (`X-Frame-Options: SAMEORIGIN` and CSP `frame-ancestors 'none'`) by default, which protects internet-exposed deployments from being embedded by hostile sites. But it also broke a documented integration path: Home Assistant's Webpage dashboard panel embeds Bambuddy via `