Commit Graph
1670 Commits
Author SHA1 Message Date
Donkie ba891d5830 client_v2: let a scanned tag keep the view you were in
Auto-navigate rewrote the URL to a bare `/?sel=spool:N`, which since the filter
menu landed means a tap throws away the grouping, sort and filter chips you had
set up -- and lands on the shipped default view rather than the remembered one.

openSearchResult is the navigation a picked search result already gets, and a
tag scan asks the same question: on the Library it merges the selection into
the current view, elsewhere it opens the Library on just that spool. The
inspector resolves a selection by id on its own, so the spool still opens when
the active filters exclude it from the list behind it.
2026-08-23 10:07:02 +02:00
Donkie f841da6561 Point SpoolTag.from_db at the class the table actually has
d43552b renamed the ORM class from SpoolTag to Tag and missed this annotation.
It is evaluated when the class body runs, so importing spoolman.api.v1.models
raised AttributeError -- the server could not start and every test in the tree
failed to collect.
2026-08-23 10:07:02 +02:00
Donkie 931e3c9b3b Merge remote-tracking branch 'origin/master' into worktree-nfc-design-docs
Master split filter application in two: the filters a filament can answer on
its own (_apply_filament_filters) now serve both the spool query and the new
include_empty query, which selects FROM the filaments so that a filament with
no spools still produces a group.

The tag filter stays where it was, in _apply_spool_filters, and the comment
there now says why it cannot move: a tag is linked to a spool, so a filament
on its own can no more answer "which tag is on you" than it can answer "which
shelf are you on". Nothing routes a tag filter into the filament-driven query
-- it never calls this builder, and the group endpoint takes no `tag` -- so
there is no silently-dropped filter to guard against.
2026-08-23 10:06:30 +02:00
Donkie 9775c2c42e Merge pull request #1097 from Donkie/client-v2-issue-1080-rest
client_v2: sort direction, out-of-stock filaments and truncation tooltips
2026-08-23 09:04:47 +02:00
github-actions[bot] a9e50559bb Merge pull request #1109 from Donkie/weblatev2
Translations update from Hosted Weblate
2026-08-22 17:08:31 +00:00
Micha 91d5dc8f42 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:36 +02:00
cedrik-s 8528847957 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:36 +02:00
cedrik-s 5799fac1a6 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:36 +02:00
cedrik-s ce1c781ae7 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:36 +02:00
cedrik-s dc72f3d5eb Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:36 +02:00
cedrik-s aa5821f005 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:36 +02:00
cedrik-s 13f7667bf4 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:35 +02:00
cedrik-s ed732170d1 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:35 +02:00
Micha 8465481ab3 Translated using Weblate (German)
Currently translated at 70.9% (384 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-22 19:04:35 +02:00
github-actions[bot] 9935d74886 Merge pull request #1107 from Donkie/weblate
Translations update
2026-08-21 19:59:59 +00:00
rupefelo 35d10f899d Translated using Weblate (Portuguese)
Currently translated at 99.6% (292 of 293 strings)

Co-authored-by: rupefelo <rupefelo@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/spoolman/web-client/pt/
Translation: Spoolman/Web Client LEGACY
2026-08-21 19:56:01 +00:00
github-actions[bot] 785984e685 Merge pull request #1106 from Donkie/weblatev2
Translations update from Hosted Weblate
2026-08-21 19:55:49 +00:00
cedrik-s 8b71fb9345 Translated using Weblate (German)
Currently translated at 57.8% (313 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-21 21:51:23 +02:00
Micha 9bb8973af2 Translated using Weblate (German)
Currently translated at 57.8% (313 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-21 21:51:22 +02:00
Donkie 332b3e0fe4 Revert "Give phase 2 somewhere to record a tag's instance identity"
This reverts 9aea271.

The column bought one thing, that phase 2 would need no migration, and
cost something worse: it commits the schema to a shape nobody has run a
decoder against. Width, and above all the choice not to make it unique,
were read off a design document rather than off working code, and
goeland86's codec in #880 computes an effective_instance_uuid that may
not mean the same thing. Getting that wrong leaves us altering a
populated column on four databases, which is the exact operation the
"settle the shape before release" rule exists to avoid.

That rule earns its keep for target_type, target_value and filament_id,
where retrofitting a discriminator or a nullability change is painful.
Appending one nullable column later is not painful, so phase 2 can add
it when there is something to write into it.
2026-08-20 19:15:35 +02:00
Donkie 8d9eb7395d Merge pull request #1105 from jpapiez/upstream-chore-prettier-eol
chore(client): let prettier respect the working tree's line endings
2026-08-20 19:12:15 +02:00
Donkie 413254b79a Merge pull request #1104 from jpapiez/upstream-fix-backup-connections
fix(database): close the sqlite connections used to write a backup
2026-08-20 19:11:56 +02:00
Jeff PapiezandCopilot App 21c7b98844 Close the sqlite connections used to write a backup
A sqlite3.Connection used as a context manager is a transaction manager: it
commits or rolls back on exit, it does not close the connection. Both handles
stayed open past the `with` block in Database.backup().

backup_and_rotate() then unlinks and moves the .pending file it just wrote.
POSIX allows unlinking a file with open handles, so this was invisible on
Linux, but on Windows it raises WinError 32 and breaks both the scheduled
midnight backup and the manual backup endpoint. It also leaked a pair of
connections per backup on every platform.

Wrap both connections in contextlib.closing instead. Connection.backup()
commits on the destination internally, so nothing is lost by dropping the
transaction-managing context manager.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 21:50:54 -07:00
Jeff PapiezandCopilot App 97758f2c4c Let prettier respect the working tree's line endings
The root .gitattributes sets `* text=auto`, so a Windows checkout gets CRLF in
the working tree. Prettier defaults to `endOfLine: "lf"` and therefore reports
every text file as mis-formatted -- 163 files in client_v2 alone -- which makes
`npm run lint` and `lefthook run ci` unusable locally. The pre-commit hook is
worse: it runs `prettier --write .` with stage_fixed, so any commit made from
Windows rewrites and stages the whole client tree.

Set `endOfLine: "auto"` in both client configs. Git still normalizes to LF in
the index and in the repo thanks to `text=auto`, and CI runs on ubuntu-latest,
so LF in version control is unaffected; prettier just stops fighting the
checkout. This is Prettier's documented fix for this scenario.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 21:50:44 -07:00
Donkie 9aea271ee5 Give phase 2 somewhere to record a tag's instance identity
OpenPrintTag derives an instance_uuid from the brand uuid plus the tag's
hardware UID, and phase 2 needs it stored so that rescanning a tag updates
the spool it already created instead of making a second one.

Added now, into the migration that has shipped to nobody, because the
table is the expensive half to change once it is populated on four
databases. Nothing writes the column yet, which is the same position
filament_id is already in.

Indexed for that lookup and deliberately not unique. uid already enforces
that one physical tag identifies one thing, and copying a tag's payload
onto a second sticker can legitimately put one instance identity on two
rows pointing at the same spool, which a unique constraint would reject.
2026-08-19 22:47:37 +02:00
Donkie 6ab045cde8 Merge remote-tracking branch 'origin/master' into client-v2-issue-1080-rest
# Conflicts:
#	client_v2/src/lib/components/library/ListToolbar.svelte
2026-08-19 20:38:38 +02:00
Donkie 90492ff75d client_v2: make the no-spools filaments an opt-in view
Listing every filament you own no spools of turned out to be the wrong default.
Finding a spool to print with is the daily job and those rows cannot serve it,
and how many of them there are scales with the size of a user's catalogue -- a
no-op for one library and a tripling of another. Off by default, one click to
turn on, remembered afterwards: a missed feature beats a primary view that is
permanently worse until you find the switch.

The toggle sits beside Show Archived and shows a dismissible chip when on, and
is offered only while grouping by filament, since there is nowhere else to put
the empty groups.

Remembered through viewPrefs, which until now deliberately stored nothing that
hides rows -- a hidden filter restored days later reads as missing data. This
one only ever adds rows, so it cannot cause that confusion; the comment there
now says which side of the line it falls on and why. Entries written before the
flag existed keep working: a missing field means off rather than a corrupt entry
that would throw away the grouping the user picked.

The badge now reads "No spools" rather than "Out of stock". Spoolman knows what
you have recorded, not what is on your shelf.
2026-08-19 17:20:26 +02:00
github-actions[bot] 1f2670cf81 Merge pull request #1102 from Donkie/weblate
Translations update
2026-08-19 09:44:00 +00:00
github-actions[bot] 565abec742 Merge pull request #1101 from Donkie/weblatev2
Translations update from Hosted Weblate
2026-08-19 09:38:59 +00:00
Julia afb3094c1b Translated using Weblate (Polish)
Currently translated at 100.0% (293 of 293 strings)

Co-authored-by: Julia <juliazero2@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/spoolman/web-client/pl/
Translation: Spoolman/Web Client LEGACY
2026-08-19 11:35:03 +02:00
Julia 96065fbd9f Translated using Weblate (Polish)
Currently translated at 100.0% (541 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/pl/
2026-08-19 11:34:54 +02:00
github-actions[bot] 8d65e53e7c Merge pull request #1100 from Donkie/weblate
Translations update
2026-08-18 19:56:58 +00:00
Icezaza Ch 48221c270a Translated using Weblate (Thai)
Currently translated at 100.0% (293 of 293 strings)

Co-authored-by: Icezaza Ch <icezaza.jar@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/spoolman/web-client/th/
Translation: Spoolman/Web Client LEGACY
2026-08-18 19:51:27 +00:00
github-actions[bot] f9c2c3838a Merge pull request #1098 from Donkie/weblatev2
Translations update from Hosted Weblate
2026-08-18 17:56:20 +00:00
Icezaza Ch bd877c1ec7 Translated using Weblate (Thai)
Currently translated at 100.0% (541 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/th/
2026-08-18 19:51:40 +02:00
Liudas Ališauskas 328c508c39 Translated using Weblate (Lithuanian)
Currently translated at 34.7% (188 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/lt/
2026-08-18 19:51:30 +02:00
Micha 052d3e9eb9 Translated using Weblate (German)
Currently translated at 51.0% (276 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-18 19:51:25 +02:00
cedrik-s 9a98e8ba5d Translated using Weblate (German)
Currently translated at 51.0% (276 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-18 19:51:24 +02:00
Donkie 6dce8717a0 Fold the empty-group query back into find_groups
The first cut of include_empty grew a second 177-line function next to
find_groups that repeated its filters, ordering, pagination and hydration, so
every future change to grouping had to be made twice or silently diverge.

Only the FROM ever differed. Writing the aggregates so they hold for a group
with no spools in it -- COUNT over a column instead of COUNT(*), one explicit
zero for the one SUM -- removes the need for a second set, and with it the
COALESCE that CockroachDB rejected and the CAST that worked around it. What is
left is one query with a branch over how its rows are selected, and one shared
tail.

Spool-level filters are now refused alongside include_empty rather than quietly
producing a contradiction (every filament as empty, or no empty groups at all).
That also leaves the archived rule as the only spool-level condition the
filament-driven query has to carry, in its JOIN.

Group ordering now breaks ties on the grouped column. Every ordering can tie,
and empty groups tie on all four aggregates, so paging could otherwise repeat or
drop a group between pages.
2026-08-18 18:04:34 +02:00
Donkie 5b480f7db7 tests_frontend_v2: catch the tag spec up with the renamed strings
38b77a9 renamed the auto-navigate toggle to "Auto-navigate" and reworded
the pairing prompt to "Tap any tag...", but the spec still selected on the
old wording, so three tests could not find the control at all.

Checked every string the spec selects on against en/common.json rather
than the two CI happened to reach first, since fixing one stale selector
would have moved the failure to the next one.
2026-08-18 17:53:55 +02:00
Donkie 1b4beb9593 Merge pull request #1094 from Donkie/client-v2-filter-menu
client_v2: filter menu colours, materials, checkmarks and multi-select
2026-08-18 17:49:33 +02:00
Donkie 33ad8d7173 Show filaments you have no spools of
Grouping by filament aggregates over spools, so a filament you own no spools of
could never produce a group and simply vanished from the library. A filtered
view then looked fully stocked when it was the opposite, and the thing you
needed to re-order was the one thing not on screen (#1092).

GET /api/v1/spool/group takes include_empty, which inverts the query: spools are
aggregated per filament in a subquery that the filament list is LEFT JOINed
against, so filaments with no matching spools survive with their aggregates
folded to zero. Which side of that join a filter goes is the whole subtlety, and
the docstring spells it out - spool-scoped conditions have to stay in the
subquery or they delete the very rows the flag exists to keep.

The client asks for it while grouping by filament and every active filter is one
a filament can answer on its own. "Which of these are on Shelf A" has no answer
for a filament that is nowhere, and including them there would bury the shelf
under the whole catalogue. Such a group renders as an out-of-stock row: no
disclosure, no fetch, and a marker in place of the zeroes it would otherwise
report.

The group header also picks up the truncation tooltip added in the previous
commit. Its title sits under a link stretched across the whole row, so the
pointer never lands on the text itself and the tooltip has to hang off the row.
2026-08-18 17:33:29 +02:00
Donkie 713cb51f09 client_v2: reveal truncated text on hover
Text cut off with an ellipsis gave no way to read the value behind it, and that
value — a filament name, a location, a manufacturer — is usually the whole
reason the row is on screen (#1093).

A shared action measures the element and gives it a title only while the text
really is cut off, so rows that fit stay quiet instead of popping a redundant
tooltip under every pass of the pointer. It re-measures on resize and on text
changes alike: the list pane is resizable, and a live update can lengthen a name
without the column moving at all.

Applied to the spool rows, the unused-spool rows, the search results and the
dashboard chips.
2026-08-18 17:32:01 +02:00
Donkie fa39c6d08e client_v2: flip the sort direction from the chip's own arrow
The sort chip showed the direction but opened the menu wherever you clicked it,
so reversing an order meant reopening the dropdown and finding the field that
was already selected (#1091).

Split it into two controls sharing one frame: the field label still opens the
menu, the arrow only flips the direction. A divider and a hover of its own mark
the arrow as separate, and both halves stretch to fill the chip so the 44px
mobile tap target survives the split.
2026-08-18 17:31:38 +02:00
Donkie 61e2c0c27c client_v2: keep the frontend suite in step with the filter menu
Turning filter values into checkboxes that leave the menu open broke the
`addFilter` helper in two ways, and with it the grouping test that uses it
to pin the list down to its own spools.

An explicit `role="menuitemcheckbox"` replaces a button's implicit role, so
the helper's `getByRole("button")` no longer matched a value row at all;
and having picked one, the helper handed back a toolbar with the menu still
covering it, which the next call could not open through. It now asks for
the role the row actually has, and closes the menu itself, restoring the
postcondition every caller was already relying on.

The row's accessible name needed a real space rather than the margin that
had been standing in for one: without it a filament read as one
run-together word, "Galaxy BlackPLA".
2026-08-18 17:29:07 +02:00
Donkie 83ea5001fd Keep the NFC design notes out of the source tree
They are working notes and a wiki draft, not something the repo needs to
carry. The user-facing half is now published at
https://github.com/Donkie/Spoolman/wiki/Tag-scanners, with the diagram
source alongside it in the wiki repository.
2026-08-17 21:55:06 +02:00
github-actions[bot] 6fd64f9ed4 Merge pull request #1095 from Donkie/weblate
Translations update
2026-08-17 19:44:29 +00:00
Icezaza Ch 2edd96dbb0 Translated using Weblate (Thai)
Currently translated at 96.5% (283 of 293 strings)

Co-authored-by: Icezaza Ch <icezaza.jar@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/spoolman/web-client/th/
Translation: Spoolman/Web Client LEGACY
2026-08-17 21:40:22 +02:00
Liudas Ališauskas 61e1b62a38 Translated using Weblate (Lithuanian)
Currently translated at 99.3% (291 of 293 strings)

Co-authored-by: Liudas Ališauskas <liu2kas3@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/spoolman/web-client/lt/
Translation: Spoolman/Web Client LEGACY
2026-08-17 21:40:22 +02:00
Donkie 7e2e98b30a design: correct two claims the shipped relay made false
The phase 1 doc still said 'not yet implemented', and pointed at a
TagsSection path the frontend did not use.

More worth fixing: both documents offered an unguessable reader_id as a
weak secret for people who care. The reader registry that shipped
alongside undoes that entirely, since GET /api/v1/tag/reader hands every
recently-seen reader id to any caller by design, so the picker can list
them. Offering obscurity that the same API gives away is worse than
offering none.
2026-08-17 21:36:48 +02:00