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.
This commit is contained in:
maziggy
2026-06-12 13:12:20 +02:00
parent f2a3917e90
commit 61df0b68d2
5 changed files with 31 additions and 16 deletions
@@ -245,14 +245,20 @@ export function FilamentMapping({
<option value="" className="bg-bambu-dark text-bambu-gray">
-- Select slot --
</option>
{loadedFilaments
.filter(
(f) =>
item.nozzle_id == null ||
ftsInstalled ||
f.extruderId === item.nozzle_id,
)
.map((f) => {
{/*
#1722: every loaded slot is offered for every filament row,
regardless of which extruder the slot is wired to. Before this
change a slot was only listed when its extruder matched the
filament's slicer-assigned nozzle (item.nozzle_id), which
locked users out of cross-extruder picks even when they'd
intentionally loaded the required filament into the "other"
AMS. The L/R badge on the filament row still tells the user
what the slicer planned; the dropdown now trusts the user to
pick based on their physical setup. Printer firmware accepts
or rejects the ams_mapping at start-print — failure is loud,
not silent.
*/}
{loadedFilaments.map((f) => {
const remainingWeight = trayRemainingWeightMap.get(f.globalTrayId);
const remainingLabel = remainingWeight != null
? t('printModal.slotRemainingShort', {