Commit Graph
251 Commits
Author SHA1 Message Date
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 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 5624a0a605 client_v2: pick several filter values without reopening the menu
Narrowing a library to three filaments meant opening the filter menu,
stepping into Filament, picking one, and doing all of it again twice
more. The menu closed on every pick, and nothing in it said which values
were already on — the chips that say so sit in the toolbar, behind the
menu that covers them.

A value row now carries a checkbox and its state, the same way the "Show
archived" row already does, and picking one leaves the menu open. The two
go together: once the values are marked, the list reads as a multi-select,
and closing after a single pick is the surprising half. Escape and a click
outside still close it, and picking a filter property or a date range still
does too, since neither is a set.

Nothing has to be preserved by hand for this to work. Each toggle is a
query-only navigation that keeps the component mounted, and it already
asked to keep focus and not to scroll, so the search box stays focused and
the list stays where it was.

The Ctrl-modifier the issue suggested is deliberately not implemented: a
checkbox list that stays open needs no hint text to explain itself.

Closes #1090
Closes #1089
2026-08-17 21:00:17 +02:00
Donkie 6e289d76ca client_v2: show colors and material in the filament filter
The filament filter listed nothing but a label, which is the manufacturer
and the name. That is not enough to pick from: the colour is the thing
you actually recognise a filament by, and two filaments may legitimately
carry the same name in different materials, leaving the menu offering two
entries that read identically.

Each option now carries whatever it has beyond its label, so the menu
draws a swatch and the material where they exist and stays a plain list
of names where they don't (locations, lot numbers). The material sits
with the label rather than off at the row's right edge, since it is there
to qualify the name it follows. None of it costs a request: the option
list is built from whole filaments already fetched.

The search box narrows on the material too. It is on screen, so a query
that visibly matches a row has to keep it — and since terms match in any
order, "prusament petg" now finds one filament out of two same-named
ones.

Closes #1087
Closes #1088
2026-08-17 20:59:29 +02:00
Dr_Perry_Coke 2434b424db Translated using Weblate (Russian)
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/ru/
2026-08-16 18:48:22 +02:00
KnightArch fd9b06d33c Translated using Weblate (Portuguese (Brazil))
Currently translated at 31.2% (169 of 541 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/pt_BR/
2026-08-14 08:46:28 +02:00
Alessandro “Alessandrol” L bf265c12c3 Translated using Weblate (French)
Currently translated at 25.4% (137 of 539 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/fr/
2026-08-13 18:22:34 +02:00
Donkie 9423acbc9e client_v2: create a filament while changing what a spool holds
The change-filament dialog could only pick a filament that already existed,
in the catalog or in SpoolmanDB. But the only way to create one anywhere in
client_v2 is the add-spools flow, which always mints at least one spool — so
refilling a shelf slot with a roll nobody had catalogued meant adding a
throwaway spool and deleting it again, handing out exactly the new spool id
the feature exists to avoid (issue #1010).

The dialog now offers the same third path the add-spools flow does. Picking
it swaps the search for the new-filament form and applies as one action:
the filament (and its manufacturer) are created, then the spool is repointed
at it. The spool keeps its number, location, dates and usage history.

The form itself moves to NewFilamentCards, shared with AddSpoolModal so both
dialogs keep the per-record card layout from #1038, with its rules and its
mapping onto the API in lib/filament/draft.ts. The one difference is the
weight/spool weight/price trio: the add-spools flow writes those to the spool
it creates as well, and so keeps showing them in its spool block, while here
they belong to the filament alone.
2026-08-13 18:11:56 +02:00
Donkie aac4a26cde Merge master into client-v2-form-required-fields
Master reorganized the spool half of the add form into headline / fill /
where / money groups and added the roll-size presets. Kept that layout and
re-applied the required-field wiring on top of it: the data-field hooks, the
per-field touch handlers and the err() calls that decide when an error shows.

FIELD_ORDER follows the new reading order too, so Fill level now comes before
Spool Weight when the form picks which bad field to jump to.
2026-08-12 22:20:39 +02:00
Donkie 2d08061c45 client_v2: require the material when creating a filament
Leaving Material blank sent you to Density instead. Density is only prefilled
for a material Spoolman recognises, so a blank material surfaced as "Density
is required" — an error on the field you didn't fill in because of the field
you did miss, and inside the collapsed Advanced block at that.

Material is now required on this form, so the error lands on the field that
actually needs attention and Advanced stays shut. Typing a material we don't
have specs for still asks for a density afterwards, which is the right order
to be asked in.

Client-side only: the API still accepts a filament with no material, so
existing ones are unaffected.
2026-08-12 20:25:00 +02:00
Donkie aec9b276b2 client_v2: point at the first bad field instead of listing them all
The summary panel above the add button was more machinery than the problem
needed, and it spent five translation strings restating what the inline
field errors already say. Drop it.

Pressing Add still reveals every outstanding error and still opens the
collapsed Advanced block when the problem is hiding in there; it just takes
you to the first bad field and lets the error under it do the talking. The
issue count on the Advanced toggle and the "* required" legend go too.

The change now adds no new translation strings at all.
2026-08-12 20:12:36 +02:00
Donkie 0931b03a80 client_v2: say what is missing instead of disabling the add button
An incomplete add-spool form was answered with a dead Add button, leaving
"which field haven't I filled in?" to be found by eye. Worst case the answer
was invisible: density is required but only prefilled for known materials,
and it lives inside the collapsed Advanced block, so the form could look
complete and still refuse.

Add stays clickable. Pressing it on an incomplete form reveals every
outstanding error at once, lists them above the button as rows that jump to
their field, opens the section hiding one, and puts the caret in the first.
The list stays as a checklist and shrinks as fields are fixed. Per-field
errors now appear once you leave a field rather than immediately, so a
freshly opened form is quiet.

Make the asterisk mean one thing: every required field carries it and
nothing else does. Count was missing one. A "* required" legend says what it
means, and the controls carry aria-required/aria-invalid so it is not
conveyed by a glyph alone.

The extra-fields editor in settings follows the same convention: asterisks
on key, name and choices, and a save error now outlines and focuses the
field it came from.
2026-08-12 19:53:11 +02:00
Donkie 740b1ad808 Merge pull request #1070 from Donkie/client-v2-vendor-extra-fields
client_v2: edit the manufacturer's extra fields when the add flow creates one
2026-08-12 19:51:38 +02:00
Donkie 379354159a Merge pull request #1068 from Donkie/client-v2-searchable-menus
client_v2: make the long toolbar menus searchable
2026-08-11 22:48:02 +02:00
Donkie e9b06d91c7 Merge pull request #1067 from Donkie/worktree-issue-1059-transparency
client_v2: bring back translucent filament colors
2026-08-11 22:43:53 +02:00
Donkie fb723f0af8 client_v2: stop the alpha slider tiling colour under its border
A `background` shorthand resets background-origin to the padding box, so
the slider's gradient was sized to the padding box but painted out under
its 1px border — and backgrounds repeat, so the strip under the border
showed the far edge of the next tile: a hairline of full-opacity colour
down the transparent end. It surfaced wherever the border doesn't cover
that strip exactly, i.e. Firefox at fractional display scaling.

Anchor every layer to the border box, and keep the colour gradient from
repeating at all.
2026-08-11 22:35:15 +02:00
Micha 7a31afaa39 Translated using Weblate (German)
Currently translated at 50.8% (273 of 537 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-11 20:28:29 +00:00
Donkie b2c46e22c7 Merge pull request #1065 from Donkie/client-v2-weight-presets
client_v2: quick-pick roll sizes when adding spools
2026-08-11 22:28:08 +02:00
Donkie 84caec0f81 client_v2: drop the More details disclosure from the add-spool form
Lot number, usage dates and comment are back in the open. The lot number
moves next to Location, since it identifies the spool rather than pricing it.
2026-08-11 22:23:27 +02:00
Donkie 250aa7c308 client_v2: reorganize the add-spool form around what you know at a glance
The spool fields were one grid of interchangeable boxes, so the roll-size
shortcuts had nowhere to go but a second row under the weight field. Order
them by how easily they are answered instead: count and weight share the top
line, with the sizes beside the weight rather than under it; fill level and
location follow; spool weight and price drop to their own row as the numbers
that come off a product page; and lot number, usage dates and comment fold
into a 'More details' disclosure like the filament half above already does.
Net result is a shorter form than before the shortcuts existed.
2026-08-11 22:13:13 +02:00
Taylan Tatlı 7eaf37fa35 Translated using Weblate (Turkish)
Currently translated at 29.2% (157 of 537 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/tr/
2026-08-11 03:01:01 +00:00
KnightArch f462245622 Translated using Weblate (Portuguese (Brazil))
Currently translated at 26.8% (144 of 537 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/pt_BR/
2026-08-11 03:00:56 +00:00
Micha a46caaabf5 Translated using Weblate (German)
Currently translated at 50.6% (272 of 537 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-11 03:00:52 +00:00
Donkie 5deb6f8ca6 client_v2: quick-pick buttons for common roll sizes
The weight field in the add-spool modal now offers 250 g / 500 g / 750 g /
1 kg / 2 kg / 3 kg as one-click shortcuts, with the one matching the current
value marked. Closes #1051.
2026-08-10 20:44:39 +02:00
Donkie 948c95866d client_v2: bring back translucent filament colors
The legacy client could set a filament's alpha channel, and the API and
database have stored an 8-digit #RRGGBBAA code ever since the color_hex
alpha migration, but client_v2 had no way to edit it (#1059).

Each row of the colour editor gets an opacity slider and a percentage
field, both backed by the hex code's alpha byte, so a single- or
multi-colour filament can be marked translucent. Full opacity drops the
alpha channel again, keeping plain colours 6-digit for the API and for
ecosystem consumers, and the native RGB picker no longer wipes an alpha
that is already set.

Swatches layer a translucent colour over a checkerboard so it reads as
transparent rather than as a different, lighter colour.
2026-08-10 19:38:16 +02:00
Donkie 0cc77dccb3 client_v2: make the long toolbar menus searchable
The Library's sort and filter menus grow with the install. Every custom
extra field adds a sort field and a filter property, and a filter's
values are the whole library: every filament, every location, every lot
number. Past a point the only way through them was to scroll.

Each list now carries a search box once it is long enough to earn one,
so the group menu's five fixed entries stay as they were. Matching is a
plain substring test — these lists are on screen while you type, so a
fuzzy match that surfaced "Location" for "lot" would read as a bug —
widened only by folding diacritics and by letting whitespace-separated
terms match in any order, since a filament reads "<maker> <name>".

Escape clears the query, then closes the menu; Enter takes the first
entry still listed. The box takes focus as the menu opens, but only
where the pointer is fine: on a touch device that same focus would
raise the keyboard over the list it filters.

Closes #1045
2026-08-10 19:26:07 +02:00
Donkie 16feb404ba client_v2: edit the manufacturer's extra fields when the add flow creates one
The add-spool form can create a manufacturer, but offered no way to fill its
custom fields, unlike the filament and spool it creates alongside. Show them
in the manufacturer card, and send them with the POST that creates it.

The inputs only appear while the typed name is a new one: matching an
existing manufacturer links that record, and must never rewrite its values.

Fixes #1055
2026-08-10 19:14:34 +02:00
Micha 423c06abc8 Translated using Weblate (German)
Currently translated at 35.1% (182 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/de/
2026-08-09 21:56:39 +02:00
Donkie 7dc17f12da Merge pull request #1054 from Donkie/client-v2-date-filters
client_v2: filter the library by date
2026-08-09 21:56:27 +02:00
Donkie 587521eff6 client_v2: reword the manufacturer section note 2026-08-09 21:45:52 +02:00
Donkie 2c84acfd24 Filter dates with the grammar the extra fields already use
The two commits before this one invented `<field>_after`, `<field>_before`
and `<field>_unset` for the built-in timestamps. That was a third way to
ask a question this API already answers: extra-field filters have taken
`<start>|<end>` for datetimes and `<min>:<max>` for numbers since v0.26.0,
with an empty value meaning "unset", the same as it means for `location`
or `lot_nr`.

Two grammars for one question is bad on its own, but the split was worse
than cosmetic. Someone who learned `extra.opened_on=2025-01-01T00:00:00Z|`
and tried `last_used=2025-01-01T00:00:00Z|` got every spool back, because
an unknown query param is silently ignored.

So the eight parameters become three, named after their columns and
taking the shipped grammar:

    /spool?last_used=2026-08-01T00:00:00Z|      since
    /spool?last_used=|2026-01-01T00:00:00Z      until
    /spool?last_used=<start>|<end>              between
    /spool?last_used=                           never used

Commas OR several of those, and quotes are accepted and ignored, so every
filter on the endpoint is spelled the same way. `_unset` disappears: the
empty value already means unset everywhere else here. No new convention
enters the API, and the next filterable column costs one parameter rather
than three.

Two consequences worth naming. Malformed input is now a 400 with a message
instead of FastAPI's automatic 422, matching the other unparseable filters
on this endpoint. And "has a timestamp" (the old `_unset=false`) has no
spelling any more, because the shipped grammar has none; nothing offered
it in the UI.

The client is unchanged apart from how it builds the request. The chip
value in the app URL keeps its own grammar, since it also carries relative
offsets and "never", neither of which the API takes.
2026-08-09 15:18:46 +02:00
Donkie 43bf140675 client_v2: separate manufacturer, filament and spool in the add flow
Step 2 of the add-spool modal creates up to three records at once, but laid
them out as one flat run of fields, so there was no way to tell which record
a field would land on. The vendor combobox in particular sat among the
filament fields while quietly creating a second entity.

Lay the step out as one labelled block per entity: the manufacturer and the
new filament each get their own card, and the spool fields get a heading of
their own instead of just following on. Weight, spool weight and price are
written to both the new filament and the spool, so say so rather than let
the headings imply otherwise.

Closes #1038
2026-08-09 11:19:40 +02:00
Donkie b6d220a3ef Filter the library by spools that have never been used
A date range cannot express "no date at all": a NULL fails every
comparison, so no bound, however far back it reaches, selects the spools
that have never been used. That state needed a filter of its own.

Backend: /spool and /spool/group take first_used_unset / last_used_unset.
True matches only the spools missing that timestamp, false only those
that have one. Registered gets no such param — the server sets it at
creation and a spool never lacks one.

Client: the chip value `never` sits alongside the range grammar, and
Last Used / First Used offer it under their presets. It is labelled
"Never", deliberately not "unused": the list already calls a spool unused
when no weight has come off it, which is a different claim — a spool can
be unused and still carry a last_used date, or have used weight and no
date at all.
2026-08-09 11:02:26 +02:00
dimquea 01f13357b4 Translated using Weblate (Russian)
Currently translated at 75.8% (393 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ru/
2026-08-09 08:51:30 +00:00
변성운 e5daed84e6 Translated using Weblate (Korean)
Currently translated at 100.0% (518 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-09 08:51:27 +00:00
Sungjun Park bd244e8387 Translated using Weblate (Korean)
Currently translated at 100.0% (518 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-09 08:51:26 +00:00
변성운 d81e43aa56 Translated using Weblate (Korean)
Currently translated at 100.0% (518 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-09 08:51:25 +00:00
변성운 18e31bf4b1 Translated using Weblate (Korean)
Currently translated at 100.0% (518 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-09 08:51:24 +00:00
변성운 dd8e7868be Translated using Weblate (Korean)
Currently translated at 100.0% (518 of 518 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-09 08:51:23 +00:00
Donkie 179ea3b4d0 Filter the library by date range
The library could filter by every discrete property a spool has but by
none of its timestamps, so "what have I used this week" and "what has
been sitting untouched for a year" were questions the UI could not ask.

Backend: /spool and /spool/group take first_used / last_used /
registered `_after` and `_before` bounds, each optional, inclusive, and
resolved against the UTC-naive form the columns store (an offset-aware
value is converted; a naive one is read as UTC). A spool with no
timestamp matches neither end.

Client: the Filter menu gains the three timestamps, each opening presets
(last 24 hours ... older than 1 year) plus a custom range whose empty
end means open. A range lives in the URL as a chip value, `<from>..<to>`,
where an end is a calendar day or an offset back from now. Relative ends
stay relative all the way into the URL and are resolved at request time,
so a bookmarked "last 24 hours" keeps meaning the last 24 hours instead
of drifting into "since the day I made it".
2026-08-09 09:53:42 +02:00
Donkie 7f19ff0952 Merge pull request #1046 from Donkie/client-v2-numeric-inputs
client_v2: accept decimal commas in every numeric field
2026-08-08 20:10:03 +02:00
Donkie e09d63b443 client_v2: accept decimal commas in every numeric field
Most of the world writes 1,75 rather than 1.75, and `<input type="number">`
only accepts the separator the browser locale dictates: everything else it
reports as an empty value. A user typing a comma therefore watched a weight
or a diameter silently come out blank or, once the separator was dropped,
a hundred times too large. The same input type also accepts "e", "+" and "-"
anywhere in the text, since they are legal exponent syntax, and then reports
the whole value as empty too, so "1e" read back as nothing at all.

Numeric fields are now text inputs with `inputmode="decimal"`, guarded by a
shared `numericInput` action that filters at the keystroke: both separators
go in, letters cannot, and a pasted "12,5 g" is cleaned rather than refused.
Reading is centralised in `parseDecimal`, which takes either separator and
returns null instead of NaN for text that is not a number, so an unset field
stays distinguishable from a real 0. What the parent, and the API, receive is
always canonical.

The typed separator stays on screen: each field keeps a draft of the text and
gives it up only when the value moves somewhere that text no longer means, so
storing 1.5 does not rewrite a typed "1,5" between keystrokes. Arrow-key
stepping, which was native to the old input type, is now the component's own.

Covers the spool/filament/vendor inspectors, the add-spool modal (whose price
box was a bare text input accepting anything at all), extra fields of every
numeric type, the extra-field editor's order box and the low-stock threshold.
2026-08-08 19:42:39 +02:00
변성운 d7c2e52a67 Translated using Weblate (Korean)
Currently translated at 69.0% (355 of 514 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-08 08:12:32 +00:00
Sungjun Park 4ad904d270 Translated using Weblate (Korean)
Currently translated at 69.0% (355 of 514 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-08 08:12:32 +00:00
변성운 9b652b5d77 Translated using Weblate (Korean)
Currently translated at 69.0% (355 of 514 strings)

Translation: Spoolman/Spoolman Web UI v2
Translate-URL: https://hosted.weblate.org/projects/spoolman/spoolman-web-ui-v2/ko/
2026-08-08 08:12:31 +00:00
Donkie dd13f6ea34 Merge pull request #1042 from Donkie/client-v2-label-export-formats
client_v2: pluggable label export formats, add AML
2026-08-08 10:12:11 +02:00
Donkie 93f49449a0 client_v2: restore the remembered view by navigating, not by parsing
Folding the remembered grouping into parseLibraryState left the address
bar saying nothing while the list was grouped, and that gap broke the
group control: picking the default grouping serialises back to the very
same bare URL, and an unchanged URL means SvelteKit reuses its cached
load, so the choice never took effect. The load was also no longer a
pure function of the URL, which is what that per-URL cache assumes -
preloading the nav tab cached a redirect built from the old preference
and replayed it.

The page now navigates on arrival to the URL that spells the remembered
view out, replacing the history entry. The load goes back to reading the
URL alone, the view stays linkable, and every distinct view has a
distinct URL again. Only on arrival: a goto is a choice the user just
made and a popstate is Back, and overruling either would be wrong.
2026-08-08 10:01:10 +02:00
Donkie 538c78e7b3 client_v2: remember the library's grouping and sorting
Group and sort live in the URL, so they survive a reload of a library
link but not a trip to Settings and back: every other route links to a
bare `/`, which handed back the shipped view and made a deliberately
chosen grouping something the user re-picked several times a day.

A URL that mentions neither grouping nor sorting now means "however I
had it last", restored from localStorage; one that mentions either still
wins outright, so a shared link shows its recipient what its sender saw.
Only the layout is remembered - filters, search and archived-visibility
hide spools, and one silently restored days later reads as missing data.

Fixes #1036
2026-08-08 10:00:33 +02:00
Donkie bef99d5891 client_v2: stamp exported PNGs with their real resolution
Label designs are authored in millimeters and rasterized at the layout's
DPI, but canvas.toDataURL() writes no pHYs chunk, so every reader falls back
to 96 dpi. A 50x25mm label exported at 300 dpi opened as a ~156x78mm image,
and printing it "at 100%" produced a label three times too big.

Stamp pHYs with the layout's density on the way out, replacing any existing
chunk and inserting after IHDR so it precedes IDAT as the spec requires.
Anything that doesn't parse cleanly as a chunk stream is passed through
untouched — a wrong DPI beats a corrupted download.

Verified independently with Pillow: the stamped files are valid PNGs and
read back as exactly 300 and 203 dpi.

AML is unaffected; it carries the label size in mm in its own header.
2026-08-08 09:36:28 +02:00