Commit Graph
27 Commits
Author SHA1 Message Date
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 91e4219994 fix(inventory): assign-spool picker note visible on mobile (#793 follow-up)
The original #793 fix added the spool note as an HTML title= tooltip
on each picker button in AssignSpoolModal.tsx. title= only surfaces
on hover, which doesn't exist on touch devices — phone users tapping
a card just selected it, the note never appeared. Users who store
their tracking ID in the note field were blind on mobile (raised by
@EmcetPL on the closed issue).

Render the note as a small muted truncated line directly under the
weight on both the internal-inventory branch (line 436-ish) and the
Spoolman branch (line 510-ish): text-[10px] text-bambu-gray/70 mt-1
truncate, kept inside the truthy `&&` guard so empty notes don't add
a blank row. The existing title={spool.note} is preserved on the new
<p> so desktop hover and mobile long-press still surface the full
untruncated text for notes that overflow the truncate.

Mirrored across both inventory branches per the parity rule
(internal and Spoolman pickers stay shape-equal). No backend change,
no new state, no popover, no new touch target.
2026-06-27 09:56:15 +02:00
maziggy 8957cfaeef fix(inventory): assign-spool toast no longer claims AMS configured when slot was empty (#1680)
Backend correctly defers MQTT configuration when the AMS slot is empty
  at assign time (state ∈ {9, 10}) — firmware drops the push silently;
  on_ams_change replays the config once a spool is detected. The
  response carries pending_config=true to communicate that. The
  printer-card AssignSpoolModal was ignoring the flag and always
  showing "Spool assigned and AMS slot configured" — the SpoolBuddy
  modal has handled this since it shipped. Now mirrors the same
  branch and shows "Assigned. Slot will configure when you insert
  the spool." when pending_config is true.
2026-06-08 08:34:33 +02:00
maziggy 9347921378 fix(inventory): drop profile-only mismatch popup and clarify reconfigure intent (#1552)
Reporter assigned a spool to a slot whose stored slicer profile differed
  from the new spool's, got the Cancel / Assign Anyway popup, and was under
  the impression that confirming the popup just linked the spool in
  Bambuddy's DB without pushing the new profile to the AMS — i.e. that he
  then had to manually open Configure AMS Slot to fix it. The auto-push has
  actually been in place since the assign route existed:
  backend/app/api/routes/inventory.py::assign_spool calls
  apply_spool_to_slot_via_mqtt after upserting the SpoolAssignment row,
  which publishes ams_filament_setting + extrusion_cali_sel over MQTT, and
  backend/app/api/routes/spoolman_inventory.py::assign_spoolman_slot does
  the same for the Spoolman backend. The only short-circuit is when
  firmware reports the slot explicitly empty (tray_state in {9, 10}), in
  which case main.py::on_ams_change deferred-replays the configure once a
  spool appears. So the popup was friction without revealing what it did.

  Two changes:

  - frontend/src/components/AssignSpoolModal.tsx and
    spoolbuddy/AssignToAmsModal.tsx: profile-only mismatch no longer
    fires the popup. The condition becomes
    `if (materialMatchResult !== 'exact')` instead of
    `materialMatchResult !== 'exact' || !profileMatches`. The 'profile'
    member is dropped from the mismatchType union and its standalone
    branch in each popup render body is removed as dead code. Material
    mismatch still warns — Bambu firmware can refuse the print when the
    type is wrong.

  - Every firing warning (material, partial, material+profile,
    partial+profile) now appends one line via a new
    inventory.assignReconfigureNote i18n key:
    "The AMS slot will be reconfigured to use the spool's profile."
    Real translations across all 9 locales per
    feedback_translate_dont_fallback; parity script clean at 4999
    leaves per locale.
2026-05-29 10:38:11 +02:00
maziggy 632334953c fix(inventory): support transparent / clear filament end-to-end (#1545)
Reporter wanted to select a transparent filament colour in the spool
  editor; CMW-ISS confirmed on v0.2.5b1 that AMS-detected transparent
  spools were silently labelled "Black" in the filament-mapping dropdown
  because the colour name resolver dropped the alpha byte and the underlying
  RGB 000000 HSL-bucketed to "Black". Spoolman already supported 8-digit
  hex; the built-in inventory didn't.

  Eight collapsing sites fixed together so transparent reaches the user
  intact:

  - frontend/src/utils/colors.ts: hexToColorName / getColorName /
    resolveSpoolColorName / isLightColor short-circuit to "Clear" for
    alpha=00 before HSL bucketing or catalog lookup
  - frontend/src/utils/amsHelpers.ts::normalizeColor preserves the alpha
    byte when alpha < FF (normalizeColorForCompare unchanged so type/colour
    matching is unaffected)
  - frontend/src/components/spool-form/constants.ts: new
    { name: 'Clear', hex: '00000000' } preset in QUICK_COLORS
  - frontend/src/components/spool-form/ColorSection.tsx: hex draft accepts
    0-8 chars, commits at 6 (+FF) or 8 verbatim; blur pads 7-char to 8;
    selectColor passes 6-char as +FF / 8-char verbatim; isSelected matches
    on full rgba; swatch buttons paint a checkerboard for alpha=00
  - backend/app/api/routes/printers.py::get_available_filaments preserves
    the full rgba on both AMS and vt_tray branches (6-char dedup key
    unchanged)
  - backend/app/services/spoolman.py::parse_ams_tray drops the silent
    00000000 -> F5E6D3FF cream rewrite — the swatch renderer paints a
    checkerboard underlay for alpha < FF already (added in #1154), so the
    rewrite was hidden technical debt that made every AMS-detected
    transparent spool land in inventory as cream
  - backend/app/services/spool_tag_matcher.py::create_spool_from_tray
    short-circuits the colour-catalog lookup for alpha=00 and stores
    color_name="Clear" directly — otherwise an RFID-tagged transparent
    Bambu spool would resolve against the #000000 catalog row (or "Black"
    via the HSL fallback) before the frontend's resolver ever saw it
  - Two shared helpers in utils/colors.ts — getSwatchStyle(rgba) (style
    object: checkerboard for alpha=00) and spoolColorString(rgba)
    (8-char hex string for SVG fill) — applied to every simple-swatch
    site that would otherwise have rendered Clear spools as solid black:
    LabelTemplatePickerModal, SpoolBuddyInventoryPage (SpoolCircle + dot),
    SpoolBuddyAmsPage (both branches), SpoolBuddyWriteTagPage (4 sites),
    ForecastPanel, AssignToAmsModal, AssignSpoolModal (both branches),
    InventorySpoolInfoCard, TagDetectedModal, SpoolInfoCard, LinkSpoolModal,
    and the FilamentSwatch tooltip title fallback

  Intentionally NOT changed: native <input type="color"> keeps 6-char hex
  (can't pick alpha; onChange still emits +FF, correct); Spoolman's
  _find_or_create_filament strips alpha (Spoolman catalog is 6-char only);
  print_scheduler colour matching strips alpha (auto-mapping treats Clear
  as Black for slot compatibility); label_renderer prints "#RRGGBB" on the
  physical label (printers can't print transparency, swatch fill via
  _color_from_hex still honours alpha).
2026-05-29 10:15:53 +02:00
maziggy 9bcaafbc1a fix(assign-spool): refresh printer status after assignment so card updates without manual Force-refresh (#1414 follow-up)
Reporter (@snozzlebert on A1 mini external slot) saw the
  Filament page Location column update correctly after Assign
  Spool, but the Printer card kept showing "Empty slot" until
  they manually pressed Force-refresh. MQTT command was going
  through fine; gap was client-side.

  AssignSpoolModal's two onSuccess callbacks invalidated the
  inventory / slot-assignment queries but never invalidated
  ['printerStatus', printerId] and never issued a pushall. For
  Bambu RFID-tagged spools the printer echoes the new tray_type
  on its own; for non-RFID spools and A1 mini external slots
  the firmware doesn't volunteer that state change.

  Added nudgePrinterRepublish() helper called from both onSuccess
  paths: api.refreshPrinterStatus(printerId) to issue the pushall
  (same call the Force-refresh button uses) plus invalidate
  printerStatus so the refetch lands. Refresh failures are
  swallowed — the assignment itself succeeded; a stale-cache
  nudge that didn't go through shouldn't surface as "assign
  failed". Same pattern as ConfigureAmsSlotModal since #1235,
  with the extra pushall because assign-spool affects firmware-
  side state.
2026-05-19 11:41:53 +02:00
maziggy 7d7267e9cb fix(ui): z-index / stacking-context follow-ups to #1336
Two latent issues surfaced after the original AssignSpoolModal z-50 →
  z-[100] bump landed:

  1. Material-mismatch ConfirmModal hidden behind AssignSpoolModal.
     ConfirmModal's overlay was hardcoded to z-50 in its wrapper, so once
     the parent moved to z-[100] the nested confirmation dialog sat
     behind it. Added an optional overlayZIndex prop to ConfirmModal
     (defaults to z-50 — none of the 82 other call sites change), and
     the mismatch site in AssignSpoolModal passes z-[110] so the warning
     stacks above its parent.

  2. FilamentHoverCard / EmptySlotHoverCard covered by sibling printer
     cards on the dashboard. The popovers used position:absolute with
     z-[60] inside the trigger, but every printer card creates its own
     stacking context (drop-shadow filter on the slot tiles is enough),
     and z-index doesn't cross stacking-context boundaries — the next
     sibling card always wins by DOM order. Visible as the "Jade White
     · Bambu PETG HF" tooltip getting half-eaten by the neighbour card's
     AMS column.

     Fixed by portaling both hover cards to document.body with
     position:fixed and screen-space coordinates from
     triggerRef.getBoundingClientRect(). Coords recompute on visibility
     change, scroll (capture), and resize so the popover follows the
     trigger when the viewport moves; a requestAnimationFrame re-measure
     after the first paint avoids a one-frame flicker before the card
     has its rendered dimensions. Hover handlers are wired on both the
     trigger AND the portaled card so moving the cursor from slot to
     popover doesn't auto-dismiss after 100 ms. Top/bottom placement
     and arrow-pointer logic preserved.
2026-05-15 09:41:26 +02:00
maziggy 5150a5a0fa fix(inventory): raise AssignSpoolModal z-index so it sits above the mobile sidebar drawer (#1336)
Both used z-50, so DOM stacking-order let the sidebar bleed over the
  modal on narrow viewports. Bumped to z-[100] to match the convention
  used by GitHubBackupSettings, FilamentHoverCard, and the Layout
  confirmation modal.
2026-05-14 17:07:48 +02:00
MartinNYHC 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.
2026-05-08 11:52:42 +02:00
MartinNYHC dac2a31192 Revert "feat(inventory): unified Spoolman inventory UI + AMS slot assignments…" (#1232)
This reverts commit 55d71498e9.
2026-05-07 11:30:31 +02:00
Sn0rrii 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.
2026-05-07 11:15:24 +02:00
maziggy 11fe68578e fix(#1133): list every spool in the AMS-slot inventory picker
The picker that opens from <FilamentHoverCard> / SpoolBuddy's slot-action
  sheet had two stacked filters that together blocked a real workflow:

  1. AssignSpoolModal only listed spools whose tag_uid AND tray_uuid were
     both null, hiding any Bambu Lab spool that had been auto-created from
     RFID or scanned via SpoolBuddy NFC.
  2. FilamentHoverCard rendered its inventory section only when the slot's
     vendor was not 'Bambu Lab', so even with #1 fixed the assign button
     wasn't visible on a BL slot.

  Both filters blocked the same use case: a user has a Bambu Lab spool in
  inventory but doesn't want to scan via SpoolBuddy NFC every time and
  just wants to pick it from the list.

  Both gates removed. Modal lists every spool that isn't already taken by
  another (printer/ams_id/tray_id) tuple. Hover-card inventory section
  renders for every vendor including Bambu Lab. The AMS-vs-external
  special-case in the modal collapsed too — external slots used to be the
  only path that allowed picking a tagged spool, that distinction is gone.

  Empty slots lost their assign affordance entirely. A physically empty
  slot has no spool to attach an inventory record to, and offering the
  action there only led to users assigning the wrong spool to a slot the
  printer hadn't actually loaded yet. Bambuddy: EmptySlotHoverCard's
  inventory prop removed; PrintersPage drops the matching inventory
  props on three sites. SpoolBuddy: slot-action picker gates the
  assign/unassign block on slotActionPicker.tray !== null.

  Modal also gets defensive hardening from the rollout investigation:
  its own dedicated cache key (['inventory-spools', 'assign-modal']) so
  it can't be poisoned by other components calling getSpools() with
  different includeArchived args; getSpools(true) + client-side
  !archived_at filter so the picker sees the full inventory regardless
  of cache priming order; "Show all spools" toggle now bypasses BOTH
  filters (was only bypassing material/profile, label was a lie); a
  small "X fetched · Y archived · Z assigned" counter in the empty state
  so future "missing spool" reports are debuggable from screenshots.

  i18n.inventory.noManualSpools renamed to inventory.noAvailableSpools
  with new copy ("No spools available. Add a spool to your inventory or
  unassign one from another slot first.") since the empty-state premise
  changed. Localised across all 8 languages.

  15 frontend tests: assign/unassign render for vendor: 'Bambu Lab',
  non-BL vendors unchanged, EmptySlotHoverCard renders no assign
  affordance, configure button still works on empty slots, picker lists
  BL spools alongside manual ones, picker drops spools assigned
  elsewhere unless toggle is on, picker drops archived spools always,
  toggle escape-hatch shows everything, empty-state copy update.
  SpoolBuddy unassign now invalidates both ['spool-assignments'] and
  ['spool-assignments', printerId] so the modal's cache stays fresh
  (dual cache-key consolidation deferred to its own PR).
2026-04-26 11:45:18 +02:00
maziggy bc895dff6a ● fix(ams): restore Configure/Assign actions on reset slots + relax Assign Spool filtering (#1047)
Three related fixes reported together:

  (1) After resetting an AMS slot, the printer card showed "Empty Slot"
  with no Configure or Assign Spool actions while SpoolBuddy's AMS page
  still let the user re-configure the same slot. Commit c9efa4b8 (#784)
  added a `tray?.state === 10` gate to the EmptySlotHoverCard actions,
  intended to hide them on physically-empty slots (state=9). In practice
  firmware often reports state=9 (or omits state entirely) after a
  user-initiated reset even when a spool is still present, so the gate
  hit the wrong case. The gate was redundant anyway — EmptySlotHoverCard
  only renders when tray_type is empty — so it's removed at both the
  standard-AMS and AMS-HT render paths.

  (2) After configuring a slot with a Generic profile, the Assign Spool
  modal hid manually-added inventory spools even when material matched,
  unless the user flipped "Show all spools". The filter required exact
  slicer_filament_name equality, which manually-added spools don't
  populate. Filter now prefers exact slicer-profile match when both
  sides have one, and falls back to partial material match in either
  direction (so a "PLA" spool shows up for a "PLA Basic" slot).

  (3) On assign, the mismatch dialog fired on every Generic spool
  because Bambu Studio / OrcaSlicer profile names carry an @printer
  nozzle (variant) qualifier while the tray stores the bare base name.
  Both the filter and checkProfileMatch now strip everything from @
  onward before comparing.

  Adds 3 regression tests covering each path.
2026-04-20 12:45:49 +02:00
Keybored d4913ef0df [Feature] Improve AssignSpoolModal filtering logic (#889)
[Feature] Improve AssignSpoolModal filtering logic (#889)
2026-04-08 08:28:25 +02:00
maziggy 88c0503009 Show spool notes as hover tooltip in Assign Spool modal (#793) 2026-03-25 08:55:06 +01:00
maziggy a4006408a2 Fix white filament color swatches invisible in light theme (#726)
Changed filament color circle borders from border-white/20 to
  border-black/20 across all views so white spools are distinguishable
  against light backgrounds.
2026-03-17 13:08:04 +01:00
maziggy 11610a5b48 Compact assign spool modal grid layout (#725)
- Change spool list from vertical to 3-column grid (2-col on small screens)
  - Widen modal from max-w-md to max-w-2xl
  - Increase scroll area from max-h-64 to max-h-96
  - Show compact cards with name, color dot, and remaining/total weight
2026-03-17 12:40:38 +01:00
Keybored 3ca91eb6d1 [Feature] Add material mismatch and insufficient filament checks (#720)
[Feature] Add material mismatch and insufficient filament checks (#720)
2026-03-16 15:30:36 +01:00
Matteo Parenti 07015a7f8f fix(i18n): update remaining filament labels in multiple languages 2026-02-18 14:16:03 +01:00
Matteo Parenti 7efbb1fc58 Show remaining spool weigth instead of used 2026-02-17 17:25:04 +01:00
maziggy ce1b3b2741 Revert "Merge pull request #361 from Keybored02/0.2.0b"
This reverts commit 0743bc2df1, reversing
changes made to 292c5e855c.
2026-02-15 09:55:05 +01:00
MartinNYHC 9fb0c59dba Merge branch '0.2.0b' into 0.2.0b 2026-02-15 09:13:19 +01:00
maziggy a37dfaf7fb feat: dual external spool support, AMS slot model filtering & pre-population
Backend:
- Add dual external spool support for H2D (vt_tray as list: Ext-L/Ext-R)
- Add cloud filament ID map endpoint (/cloud/filament-id-map)
- Fix RFID spool data erased by periodic AMS updates (skip tag matcher
  for RFID-tagged trays)
- Fix AMS slot config overwrites RFID spool state
- Fix K-profile selection corrupts existing profiles on X1C/P1S
- Resolve K-profiles filament name via cloud filament ID map
- Update print scheduler and usage tracker for dual external spools

Frontend:
- Add printer model filtering to ConfigureAmsSlotModal (cloud/local/builtin
  presets filtered by @BBL model suffix and compatible_printers)
- Add pre-population for configured slots (preset, color, K-profile)
- Add K-Profiles view with accurate filament name resolution
- Internationalize all ConfigureAmsSlotModal strings (en/de/fr/it/ja — 21 keys)
- Add 5 new ConfigureAmsSlotModal tests (model filtering, pre-selection,
  color pre-population, i18n)
- Update PrintersPage for dual external spool rendering

Docs:
- Update CHANGELOG, README, website features, and wiki AMS docs
2026-02-15 08:04:01 +01:00
Matteo Parenti 33c4bd8f84 Fix spool assignments query, mismatch dialog premature closing, confirm modal black text, add missing de.ts, fr.ts, and ja.ts locales 2026-02-14 09:00:17 +01:00
Matteo Parenti 51d3a92263 Add material mismatch check 2026-02-13 17:01:16 +01:00
maziggy e6e62f2a68 Add spool inventory: AMS slot assignment, usage tracking, and remaining weight editing
Built-in spool inventory with AMS slot assignment for non-BL spools,
automatic filament usage tracking via 3MF estimates, and remaining
weight editing in the spool form.

Inventory features:
- AMS slot assignment: assign/unassign manual spools to AMS tray slots
- Filter out Bambu Lab spools (RFID-managed) from assignment modal
- Filter out already-assigned spools (one spool per slot)
- Auto-unlink manual assignments when a BL spool is inserted
- Hide assign/unassign UI on BL-occupied slots
- Case-insensitive fingerprint matching for auto-unlink logic

Usage tracking:
- 3MF-based filament consumption tracking for non-BL spools
- Extract per-filament used_g from archived 3MF slice_info
- Map 3MF slot_id to AMS (ams_id, tray_id) positions
- Scale estimates by print progress for failed/aborted prints
- Skip BL spools (tracked via AMS remain% delta, Path 1)
- Prevent double-counting with handled_trays set

Other:
- Add remaining weight field to spool edit form
- Fix hardcoded "Empty" string in AssignSpoolModal (now i18n)
- Add backend unit tests for usage_tracker (11 tests)
- Add frontend tests for AssignSpoolModal filters (5 tests)
- Update CHANGELOG, README, wiki docs, and website
2026-02-12 15:42:27 +01:00
maziggy ec82092bc7 Sync 2026-02-12 07:07:50 +01:00