Always showing the lot was too bulky. It now sits behind "Show lot
numbers" in the Filter menu, next to the archived and no-spools
toggles, off by default. When on, every spool row in any grouping ends
its details line with the lot. Like showEmpty it lives in the URL (lots=1)
and is remembered per browser, since it only adds to what's shown.
The filament picker loaded up to 1000 filaments once and filtered them
in the browser, so anything past the first 1000 could not be printed.
It now works like the spool picker: 100 per page with the same pager
and sort, search through the backend, and All adding the current page.
Spools of one filament looked alike in the grouped list, with only the
usage status to tell them apart. Add the lot number after it when the
spool has one. A long lot is cut by the existing ellipsis, with the full
text on hover. Flat rows and other groupings are unchanged.
Backend via uv lock --upgrade, client and client_v2 via npm update.
SQLAlchemy stays on 2.0.x: 2.1 is in range, but sqlalchemy-cockroachdb
2.0.4 requires <2.1 and uv would otherwise downgrade the dialect to
2.0.3, which lacks that cap. Paraglide stays on 2.23.0: 2.27.0 (with
@inlang/sdk 4) hangs compiling the inlang project.
The import only looked for a vendor by external_id, so a manufacturer
the user had created by hand was missed and a duplicate was made. Fall
back to the existing case-insensitive name match before creating one.
Both external_id lookups now send a quoted term, since the filter is a
substring match otherwise and "Prusa" would match "Prusament".
The add-spool form and the new-filament flow turned a typed 0 into
"not set", so a spool or filament could not store a tare or price of 0
and fell back to the filament or vendor value instead. Blank still means
not set; 0 is now sent as 0. Comma decimals go through parseDecimal.
The spool inspector and the add spools dialog listed only what GET
/location returns, which is the locations that hold a spool right now.
An empty location kept as a dashboard card, like an idle toolhead,
could not be picked without typing it or parking another spool there
first. The old client merged its saved `locations` setting into these
dropdowns; this does the same with the dashboard's saved location
cards (which fall back to that old setting). The library's location
filter still lists only locations in use.
Fixes#1261
Card bodies were capped at min(52vh, 420px), so a group of more than
about eight spools scrolled inside its card while most of a desktop
screen stayed empty below it.
The cap now reaches down to the footer for a single row of cards, but
stops at 1000px. A card only fetches its next page when its list is
scrolled, so a body taller than one page of chips (about 1500px) shows
no scrollbar and never loads the rest; the cap proposed in the issue,
calc(100dvh - 220px), did exactly that on a 2160px tall screen. Narrow
touch layouts keep the old cap so a card can't swallow the page's
scroll. A Playwright spec pins the paging on a tall screen.
The filament inspector asked for its first 100 spools and the dashboard
for its first 1000 groups, and both showed the result as if it were
everything. A filament with more spools got a wrong count and a wrong
total remaining, and groups past the first 1000 never got a card.
Both now page through the whole list with a small allPages helper. The
inspector's sort gets an id tie-break so offset paging is stable, and
duplicates from a spool moving up mid-load are dropped.
The low-stock threshold was read back with `Number(stored) || default`,
so a threshold of 0 g came back as 150 on every reload. The search
box's colour-similarity slider had the same bug the other way round
(`n > 0`), turning a stored 0 (exact colour only) back into 20.
Read both with parseDecimal, which tells "nothing stored" apart from a
real 0, and still rejects NaN, negatives and other garbage.
A QR code or bookmark pointing at a spool that isn't on the first page of
the list left the inspector on "Loading..." for good. Arriving on the
Library rewrites the URL to restore the remembered view, which re-parses
the same selection into a new object. The inspector's fetch effect
followed that object, so it re-ran, its cleanup aborted the request, and
the re-run skipped the id it had already "attempted". Same for filament
and manufacturer links, and for ids that 404.
Follow the selection by value instead, and forget an attempt whose
request was cancelled before it answered.
Escape now closes the Filter, Group and Sort menus when they were opened
with a click, which left focus on their button where the menu never saw
the key.
The pager's page numbers and page-size picker wrap as one, so the
catalog's longer summary line no longer strands the picker on a line of
its own on phones.
"Show as a list" in the manufacturer inspector filters by the
manufacturer's id rather than its name, so a namesake stays out.
The filament and manufacturer lists now sort by every field of the
filament or manufacturer, their custom fields included, plus spool and
filament counts, and offer the same filters as the spool view. A filter
on the spools themselves leaves out the filaments with none, as it does
there.
/spool/group takes the matching sort keys: filament.<column>,
filament.vendor.<column>, filament.extra.<key>,
filament.vendor.extra.<key> and group.filament_count. Keys it can't
resolve are still ignored.
Rows show the id as #id on the left like spool rows, and the Library
tab menu loses its descriptive sublines.
The Library gets two power-user sub-views, /?view=filaments and
/?view=manufacturers, reached from a menu on the Library tab, the search
panel and the manufacturer inspector. Rows are lists rather than tables so
translated labels and long names never widen them. The spool view is
unchanged.
The group endpoint gains what the lists need, all additive: a
group.vendor_name sort, include_empty for group_by=vendor, filament_count
on vendor groups, an opt-in preview of a group's first spools or
filaments, and X-Total-Spools / X-Total-Remaining-Weight headers.
A spool label's custom QR target can now use fields like {filament.id},
so it can open the filament page while still printing spool fields such
as the lot number. Closes#1152.
The server checks the latest GitHub release in the background at startup
and every 12h, logs a warning when a newer version exists, and exposes
latest_version/update_available on /info. The client_v2 footer shows a
link to the new release. Disable with SPOOLMAN_UPDATE_CHECK=FALSE.
Review feedback on #1184. A native select replaces the cycle button, still
shown only when more than one camera is listed. Picking directly suits
desktops, where webcams have real names, and on Android the select opens the
system's full-screen picker. It also removes a trap in the cycle: qr-scanner
opens a default camera when the requested one fails, and cycling on from that
default could never reach the cameras after the failing one. Every camera is
now one choice away, and the select shows the camera actually streaming.
Cameras with no label are listed as "Camera 1", "Camera 2" and so on. The
list now comes from enumerateDevices() directly, because qr-scanner's
listCameras() fills gaps with an untranslated "Default Camera" / "Camera N"
that can't be told apart from a real device name.
A failed switch now shows the same classifyStartFailure() message as a failed
start, instead of leaving the stage black with only a console error. The
picker stays up so another camera can be tried. After a failed open the
scanner is stopped, and setCamera() only restarts a running scanner (or does
nothing for the same id), so a retry starts it explicitly.
The caption and its n/N position are gone, since the select names the
current camera itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>