Commit Graph
21 Commits
Author SHA1 Message Date
Sergey Dontsov 13c37ffe51 fix(vp): scope saved AMS mapping to the printer it was resolved against
Round-2 review fixes for #2700.

Blocking: the toggle didn't actually gate the archive write. archive.py's
promotion fired for any print_data carrying ams_mapping, but bambu_mqtt's
request-topic interception captures ams_mapping unconditionally for every
print source (slicer-direct LAN prints included). Since main.py's
real-printer auto-archive path forwards the full MQTT payload as
print_data, every archive on any install — VP or not — grew
extra_data.slicer_ams_mapping. Fixed by replacing the print_data-sniffing
with an explicit `slicer_ams_mapping` param on archive_print() that only
the VP-queue path (already gated on save_ams_mapping) ever passes.

Blocking: a saved mapping could get reused on a printer it was never
resolved against — tray IDs only mean something relative to one printer's
AMS layout. extra_data.slicer_ams_mapping is now stored as
{mapping, printer_id} instead of a bare array:
- add_to_queue's fallback only fires when the reprint's target printer_id
  matches the mapping's origin printer.
- The frontend's archiveAmsMapping only surfaces (and the Mapping button
  only appears) when the print modal's selected printer matches too.
- A model-based VP (target_printer_id=None, no MQTT bridge to any real
  printer) never stamps a mapping in the first place — there's no live AMS
  layout for the slicer to have resolved tray IDs against.

Also from review:
- Multi-plate archives now get the Mapping button too (the per-plate
  FilamentMapping loop was missing archiveAmsMapping entirely).
- Added coverage for the previously-untested late-MQTT archive patch path
  (_restamp_recent_queue_item), including the model-based-VP skip case.
- usingArchiveMapping now also resets on printer change, not just
  plate/archive (it already worked via the printer-scoping above, but is
  now an explicit dependency too).
- The Mapping button's revert (OFF) now undoes only the slots it itself
  set, not every manual pick in scope — matches the comment above it.
- Added a comment on why negative-value slots (external spool) are
  skipped rather than cleared when applying a saved mapping.
2026-07-31 11:08:58 +03:00
Sergey Dontsov bab1cfb906 feat(vp): per-VP "Save AMS mapping" toggle + reprint auto-apply
Lets a reprint reuse the AMS slot the slicer itself picked, instead of
re-deriving one from the file's static type/color.

When a Print Queue VP has "Save AMS mapping" on, the slicer's own
live-resolved ams_mapping (from the project_file MQTT command) is
persisted onto the archive as extra_data.slicer_ams_mapping. A later
reprint can reuse it via a new "Mapping" button in the filament-mapping
panel — one click snaps every slot to the saved pick, click again
reverts to auto-match. Archive cards and queue rows get an "AMS mapping
saved" badge so it's visible beforehand. add_to_queue also falls back
to the saved mapping automatically when the caller sends no explicit
ams_mapping (e.g. a plain reprint with no per-slot edits).

The queue item's own ams_mapping (used for that dispatch) is still
captured unconditionally whenever the slicer provides it — that part is
a correctness fix, not gated behind the toggle. Only the archive
persistence for future reprints is opt-in.

Split out from the original combined PR per review: this half is
genuinely opt-in and low-risk (#2684). The dispatch-time validation
gate that keeps a stored mapping honest (#1308) changes behaviour for
every existing user and will land as its own PR.

Review fixes applied:
- _extract_slicer_ams_mapping_json: dropped the unreachable `v is None`
  arm and rejected bool explicitly (isinstance(v, int) accepts bool).
- Translated the Russian docstring text to English.
- save_ams_mapping's model comment moved to a trailing comment on the
  column line, matching the file's convention.
- usingArchiveMapping now resets when the plate or archive changes, so
  the Mapping button can't read ON against a mapping it never applied.
- Translated "Click to change slot assignment" and "Re-read".
- add_to_queue's fallback is now called out explicitly in code comments
  and covered by three new integration tests (fallback fires, explicit
  mapping wins, unrelated extra_data doesn't false-trigger).

Closes #2684
2026-07-31 11:08:58 +03:00
maziggy aa443c6e83 fix(print): keep filament gram usage visible when the name is long (#2669)
In the Print dialog's Filament Mapping, each required filament shows its
name and the grams the job needs, e.g. "Bambu PLA Basic (281.2g)". Name and
grams shared one fixed-width column with truncate on the whole string, so a
long name pushed the "(...g)" off the end and clipped it -- partially on a
wide screen, entirely in mobile portrait. The gram usage is the number that
matters (does the spool have enough left?), so it shouldn't be the part that
gets dropped.

Pin the gram usage (shrink-0, whitespace-nowrap) and let only the name
truncate, with the full name on hover. Applied to both the Specific-Printer
(FilamentMapping) and Any-model (PrinterSelector) panels. Layout only.
2026-07-27 09:22:14 +02:00
maziggy b5da9be794 fix(print-modal): map each plate on its own, and show the panel that does it (#2551)
Selecting several plates hid the filament mapping panel but did not stop the
modal sending a mapping. With no single plate selected it fell back to the
whole file's filament list -- the union of every plate -- and matched against
that. Tray assignment is stateful, so where plate 1 prints red on slot 1 and
plate 2 prints red on slot 2, slot 1 claimed the only red spool and slot 2 fell
through to a type-only match on black. That one mapping went out with every
plate, and the scheduler uses a stored mapping verbatim, so plate 2 printed in
the wrong colour -- decided by a panel the user never saw.

Fetch each selected plate's requirements and map them separately: one panel per
plate, named after it, with its own tray overrides, and each queue item carries
its own plate's mapping. A fan-out across several printers would be a panel per
plate per printer, so those items carry no mapping and the scheduler maps each
plate against the printer it picks. Model mode is unchanged -- no printer means
no trays to map onto.

The tray matcher existed twice and this needed a third caller, so extract it
once and have both existing paths delegate; its 62 tests pass unchanged.

The bug only reproduces with a realistic query cache -- the shared test harness
sets gcTime: 0, which evicts the union and makes the modal look innocent -- so
the new modal tests bring their own client.
2026-07-13 09:44:45 +02:00
maziggy 1d344a8536 fix(ui): resolve light-theme low-contrast semantic text app-wide (#1909)
The app was built dark-first, so hundreds of hardcoded Tailwind semantic
text/icon utilities at light shades (text-amber-400, text-blue-300, ...) had
no dark: variant. With darkMode:'class' they applied in light theme too,
producing washed-out text on pale tints and white cards — including the three
reported spots (AMS Drying banner, Archives no-3MF warning, debug-logging
banner). Give each a theme-aware pair: a darker readable shade in light theme
with the original pinned to dark:, so dark theme is unchanged. ~100 files.

The bambu-* CSS-variable palette (self-correcting) and the dark-only SpoolBuddy
kiosk are left untouched. Plain text-white is already theme-aware via the
existing index.css .text-white override, so it needed no changes.
2026-07-07 09:54:10 +02:00
maziggy b1cb26f6ee feat(ams-backup): add status badge + toggle, fix prefer-lowest (#1766)
Two tightly-coupled deliverables in one drop -- a new AMS Filament Backup
  status/control surface, and the #1766 fix that depends on it.

  Added -- AMS Filament Backup status + control
  - Parse bit 18 of top-level print.cfg into PrinterState.ams_filament_backup
    on every push_status. Verified against OrcaSlicer source
    (DeviceManager.cpp:4961) and a live H2D ON/OFF capture. Tri-state
    (None = A1 family / pre-cfg push) preserves today's behaviour.
  - Hold-timer guard (3 s) prevents stale frames from flickering the badge
    back to the printer's old cfg after a user-initiated toggle.
  - POST /printers/{id}/ams-backup toggle, set_ams_filament_backup() client
    method calling _set_print_option("auto_switch_filament", enabled).
  - GET /printers/{id}/inventory-remain endpoint exposes the same map the
    dispatcher uses (internal and Spoolman modes both work uniformly).
  - Small icon badge in the printer card's "Filaments" section header
    (placement reads as printer-wide because the cfg bit is printer-wide,
    not per-AMS). Click to toggle, success toast.
  - 5 i18n keys x 11 locales for the badge UI.

  Fixed -- #1766: prefer_lowest didn't pick lowest, ignored backup state
  - Backend gate in _compute_ams_mapping_for_printer: coerce prefer_lowest
    to False when status.ams_filament_backup is False; log the skip.
  - New effectivePreferLowest(setting, backup) helper applied at every
    frontend sort entry point: single-printer PrintModal, multi-printer
    hook per-printer, PrinterSelector InlineMappingEditor, FilamentMapping
    standalone editor (the last had NO preferLowest awareness at all
    before this change).
  - New preferLowestSortKey(f, inventoryByTrayId) mirrors backend's two-tier
    key exactly, including the banding tie-break (regular AMS < AMS-HT <
    external) so the client-side pre-compute matches the dispatch-time pick.
    An earlier draft used a flat `amsId * 4 + trayId` priority which gave
    external slots (ams_id = -1) a NEGATIVE priority -- caught in code
    review before commit.
  - Settings -> Filament -> "Prefer lowest remaining filament" gets an
    explanatory note about the printer-side AMS Backup dependency, with
    i18n key in all 11 locales.
2026-06-20 12:07:20 +02:00
maziggy 61df0b68d2 fix(print-modal): allow cross-extruder AMS slot picks on dual-nozzle (#1722)
Before this change, the Re-print and Schedule modals' per-filament
  slot dropdown hid every slot whose extruder didn't match the filament's
  slicer-assigned nozzle. On H2D with AMS A+C on the left and B on the
  right, an L-assigned filament could only pick A or C and an R-assigned
  one could only pick B — locking the user out of cross-extruder picks
  even when they'd intentionally loaded the required filament into the
  other AMS.

  The slicer wasn't the source of the asymmetry: BambuStudio Desktop,
  OrcaSlicer Desktop, and the Bambuddy sidecar all produced identical
  filament_map values for the same source 3MF. Bambuddy's UI filter was.

  Drop the f.extruderId === item.nozzle_id clause in FilamentMapping's
  loadedFilaments filter. Single-nozzle and FTS short-circuits stay; the
  L/R row badge stays as a hint to the slicer's intent. Printer firmware
  decides at start-print whether the cross-extruder ams_mapping is valid.
2026-06-12 13:12:20 +02:00
maziggy 6b477088a2 fix(queue): extend Charcoal-style label fix to Specific-Printer panel (#1718 round 3)
Round 2 fixed the model-mode FilamentOverride: tray_info_idx →
  sub-brand, plus a material-disambiguated colour name from a new
  /inventory/colors/by-material endpoint. The printer-mode panel that
  renders the same 3MF (FilamentMapping) was reading the same raw
  fields — item.type for the required label, getColorName(item.color)
  for the swatch tooltip — and was not touched, so picking "Specific
  Printer" still showed "Required: PLA - Black" for a slice the
  "Any H2D" branch already labelled "Bambu PLA Matte - Charcoal".

  Extract the three-query resolution machinery from FilamentOverride
  into a shared hook useFilamentLabels (returns positional
  {resolvedName, colorLabel} per slot). Both panels call it; both
  read the same labels. The hook also owns extractMaterialHint so the
  "strip leading brand token" rule has one source of truth.

  FilamentMapping required-side now reads {resolvedName} instead of
  {item.type}; swatch tooltip reads `Required: {resolvedName} -
  {colorLabel}` instead of `Required: {item.type} -
  getColorName(item.color)`.
2026-06-11 14:49:52 +02:00
maziggy 3b1395e3c0 fix(queue): surface force-color-match checkbox in specific-printer mode (#1717)
The Print Queue's schedule dialog hid the per-slot "Force color match"
  checkbox when a specific printer was picked, even though the scheduler
  in print_scheduler.py:535 honours force_color_match regardless of how
  the queue item was created. Model-mode ("Any A1") rendered
  FilamentOverride which carries the checkbox; printer-mode rendered
  FilamentMapping which had no force-match UI at all. Pure UI gap.

  Extend FilamentMapping to accept optional forceColorMatch +
  onForceColorMatchChange props, mirroring FilamentOverride's signature,
  and render the same <Palette>-iconed checkbox under each filament row
  when a handler is wired. PrintModal/index.tsx passes the existing
  forceColorMatch state through — same object both modes write into so
  toggling between modes preserves the user's selection. No new i18n
  keys (printModal.forceColorMatch already ships in all 11 locales).
2026-06-11 13:51:39 +02:00
maziggy b45ca2a662 feat(printer): support Filament Track Switch (FTS) accessory in print modal (#1162)
The FTS routes any AMS slot to either extruder, so AMS info reports
  bits 8-11 = 0xE (uninitialized) and ams_extruder_map ends up empty.
  The print modal's per-nozzle dropdown filter then hides every loaded
  slot, leaving the user with an empty filament dropdown.

  Detection: parse print.device.fila_switch from MQTT push_status into a
  new FilaSwitchState dataclass on PrinterState; surface it through the
  GET /printers/{id}/status response as a nullable FilaSwitchResponse.

  Frontend: useFilamentMapping and FilamentMapping skip the per-extruder
  filter when fila_switch.installed is true. Slots currently fed into a
  track display an [L]/[R] routing badge in the dropdown so the user
  can see where the FTS is currently routing them.

  Tests: 4 backend unit (TestFilamentTrackSwitchDetection), 2 backend
  integration (status route), 2 hook regression, 2 component regression.
2026-04-29 16:18:45 +02:00
maziggy 940810cba3 Show filament subtypes in all mapping dropdowns (#624)
All filament mapping dropdowns (single-printer, multi-printer, and
  "Print to Any" model-based) showed only the base material type (e.g.
  "PLA") without distinguishing subtypes like "PLA Basic" vs "PLA Matte",
  making entries with different subtypes but same color look identical.

  Backend: Add tray_sub_brands to available-filaments response and include
  it in the dedup key so different subtypes of the same color appear as
  separate entries.

  Frontend: Show tray_sub_brands in FilamentMapping, FilamentOverride, and
  PrinterSelector dropdowns, falling back to base type when unset. Add
  traySubBrands field to LoadedFilament interface.
2026-03-06 08:35:06 +01:00
MartinNYHC 15437534ed Fix formatting of option elements in FilamentMapping 2026-02-27 13:31:51 +01:00
tridev ae9818c96c feat(queue): show spool grams left in filament slot mapping 2026-02-27 13:29:39 +01:00
maziggy 1404242e0c Fix cost tracking issues 2026-02-20 16:31:16 +01:00
Matteo Parenti 9f19e8a2ff Add currency symbol, hide projected cost for no costs configured, add cost rounding to decimal point, fixed isExternal logic 2026-02-20 11:36:26 +01:00
Matteo Parenti 04ffca204f Add cost tracking for spools and usage history 2026-02-19 14:13:18 +01:00
maziggy 29e9593f6c AMS mapping fixes (7 bugs):
- backend/app/services/print_scheduler.py — hard nozzle filter + external spool extruder
- backend/app/api/routes/printers.py — ams_extruder_map race condition
- backend/app/utils/threemf_tools.py — group_id priority + dual-nozzle detection
- frontend/src/hooks/useFilamentMapping.ts — external spool extruder
- frontend/src/pages/PrintersPage.tsx — AMS-HT globalTrayId
- frontend/src/components/PrintModal/FilamentMapping.tsx — dropdown nozzle filter

Usage tracking fix:
- backend/app/services/bambu_mqtt.py — capture last valid progress/layer
- backend/app/services/usage_tracker.py — use captured values on cancel

UI fix:
- frontend/src/components/spool-form/ColorSection.tsx — color tooltip z-index

Tests:
- backend/tests/unit/test_scheduler_ams_mapping.py — 63 tests
- frontend/src/__tests__/hooks/useFilamentMapping.test.ts — 43 tests

Static assets:
- static/ — rebuilt frontend bundles
2026-02-18 09:17:50 +01:00
maziggy f9b47282a1 Nozzle-aware AMS mapping for dual-nozzle printers, BL spool detection fix, AMS startup fix, SQLite WAL (#318)
Dual-nozzle H2D/H2D Pro: filament matching now respects nozzle assignments
from the 3MF file. Each AMS unit feeds a specific nozzle (L/R), and the
scheduler/frontend constrain matching to only trays on the correct nozzle.
Falls back to unfiltered matching when no trays exist on the target nozzle.
L/R badges shown in the filament mapping UI. Translated in en/de/ja/it.

Fix AMS slot config overwritten on startup: on_ams_change unconditionally
unlinked BL spool assignments on every MQTT pushall, then re-assigned them
sending ams_filament_setting without setting_id — clearing the printer's
filament preset. Now compares spool RFID identifiers before unlinking.

Fix BL spool detection false positives: removed tray_info_idx from detection
logic in both backend is_bambu_lab_spool() and frontend isBambuLabSpool().
Third-party spools using Bambu generic presets had GF-prefixed tray_info_idx
values, causing misidentification. Now uses only tray_uuid and tag_uid.

SQLite WAL mode with 5s busy timeout reduces "database is locked" errors.
2026-02-13 11:48:32 +01:00
maziggy e1224316a7 Add per-printer AMS mapping for multi-printer selection
- New useMultiPrinterFilamentMapping hook for parallel printer status fetching
- Per-printer custom slot configuration with "Custom mapping" checkbox
- Auto-configure using RFID data to match filaments by type/color
- Match status indicator (exact/partial/missing) under each printer
- Re-read button to refresh loaded filaments
- New setting: "Expand custom mapping by default" in Settings → Filament
- FilamentMapping component now accepts defaultExpanded prop

Frontend:
- PrinterSelector: Inline mapping editor for each selected printer
- PrintModal: Fetches settings, auto-expands mapping when setting enabled
- SettingsPage: Toggle for per_printer_mapping_expanded

Backend:
- Added per_printer_mapping_expanded to AppSettings schema
- Added boolean parsing in settings routes
- Added integration tests for new setting

Closes #104
2026-01-20 16:59:18 +01:00
maziggy 02f6de4adc New Features:
- Print from File Manager with plate selection, AMS mapping, and print options. Closes #94
- Add to queue without creating archive (reduces clutter)
- Queue displays library file name, thumbnail, and print time

Fixes:
Queue items from File Manager now reference library files directly instead of
creating archives upfront. Archives are created automatically when prints start.
2026-01-20 15:37:20 +01:00
maziggy 0ac344a764 - Moved print modal to global function
- Fixed bug in file manager where content of sub folders was not shown
2026-01-20 13:44:15 +01:00