Spool and color_catalog rows carry extra_colors (comma-separated hex
stops) and effect_type (14 visual variants: surface effects, sheen,
structural). The shared FilamentSwatch component renders gradient,
conic, effect overlay, and alpha-checkerboard consistently across the
inventory grid, table, group banner, card, ColorSection preview, and
catalog editor. Catalog hex_color accepts #RRGGBBAA so catalog entries
can carry transparency too.
The paste field accepts the exact format 3dfilamentprofiles.com puts on
its filament details pages, so users can copy a multi-colour combo
directly. The effect dropdown spans the full filament-variant
vocabulary -- surface effects (sparkle/wood/marble/glow/matte), sheen
variants (silk/galaxy/rainbow/metal/translucent), and structural
variants (gradient/dual-color/tri-color/multicolor). None of these
fields touch MQTT/firmware -- pure visual hint.
Spool group-key extended to include extra_colors + effect_type so
"Group similar" no longer collapses visually distinct spools.
Migrations: 4 idempotent ALTER TABLE ADD COLUMN (Postgres-safe), plus
ALTER COLUMN hex_color TYPE VARCHAR(9) on Postgres only (SQLite ignores
VARCHAR length).
Tests: 42 new backend (35 unit + 7 integration), 20 new frontend (14
FilamentSwatch + 3 ColorCatalogSettings + 3 InventoryPageGrouping
regression). 3522 backend + 1582 frontend tests pass; ruff clean.
Localised across all 8 UI locales.
Two new optional fields on Spool: free-text `category` (max 50) and
`low_stock_threshold_pct` (1-99). Powers the "differentiate critical
spools from prototype spools and alert at different thresholds" use
case from #729 without taking on the full multi-tag taxonomy + auto-
apply rules + per-tag alert system the ticket originally proposed.
Form gains:
- Category input with datalist autocomplete sourced from categories
already in use, so casing/spelling stays consistent.
- Per-spool low-stock threshold input. Empty = global default; the
global value renders as the placeholder.
Inventory page:
- New category filter chip (hidden until at least one spool carries
a category — keeps the chip row uncluttered).
- Stat-card "Low Stock" count and the "Low Stock" filter both honour
the per-spool override.
Plus: rename "Delete Tag" button to "Clear RFID Tag" (the original
ticket reporter mistook it for a taxonomy-tag delete; the button
actually clears the RFID UID/UUID off the spool record). Toast key
renamed from `tagDeleted` to `rfidCleared`.
i18n: full translations across all 8 locales.
Tests: 9 new backend schema tests (defaults, partial-update, range
rejection, max-length); 2 new frontend tests (per-spool threshold
pulls extra spools into low-stock count, filter chip hidden when no
categories exist).
feat(inventory): replace Spoolman iframe with internal inventory UI
When Spoolman is enabled, the Inventory page now uses the same internal
UI (spool list, create/edit modal, archive, delete, weight sync) backed
by a new proxy layer instead of opening an iframe.
SpoolBuddy's "Link to Spool" used the generic updateSpool API which only
set tag_uid, leaving tag_type and data_origin empty. Now uses linkTagToSpool
with tag_type='generic' and data_origin='nfc_link'.
Added a "Weight Check" inventory column (hidden by default) that compares
each spool's last scale measurement against calculated gross weight with
±50g tolerance. Shows green check for match, yellow warning + sync button
for mismatch. Backend stores last_scale_weight and last_weighed_at on each
spool when weight is synced via SpoolBuddy. Includes edge case handling
when scale weight < core weight. i18n keys added for all 6 locales.
Add weight_locked flag to spools that auto-sets when weight_used is
explicitly updated via the API. Both the MQTT AMS remain% auto-sync and
the manual force-sync endpoint skip locked spools. Usage tracker delta
tracking is unaffected. Users can re-enable AMS sync by setting
weight_locked to false.