Releases: crzykidd/labelforge
v0.1.3
[0.1.3] — 2026-06-07
Changed
- README "What's New" now uses a tiered format — feature releases keep a full overview entry, patch releases get a one-line summary linking to the changelog;
/release-prepStep 4 updated to match.
Added
- feat: label pickers default to the last label you used (remembered across sessions via localStorage); applies to Quick Print, New Template, and Save As.
- feat: app version shown in a fixed footer on every page, linking to its GitHub release notes; shows an "Update available" indicator and a one-time release-notes popup when a newer release is detected (toggle in Settings → Updates, on by default; backend-proxied with a 6-hour cache so the browser never contacts GitHub directly).
- feat: dev/unreleased builds now show a
-dev+<sha>suffix in the version footer (e.g.v0.1.2-dev+8e32bb1) and never show the "Update available" nag; release builds remain plainv0.1.2.
Fixed
- fix: render templates at the correct position when elements use centered origins (
originX: 'center'/originY: 'center'); previously such elements were shifted right and down by half their box size, fanning wider elements further than narrow ones. - fix: editor canvas now shows the selected font instead of a serif fallback; server fonts are loaded into the browser via the new
GET /api/fonts/{name}/fileendpoint and registered with the FontFace API on startup.
v0.1.2
[0.1.2] — 2026-06-07
Fixed
- Published container image was unpullable (
manifest unknown/ 404) — the publish
workflow built withdocker/build-push-actiondefaults, which attach provenance/SBOM
attestations and turn each pushed tag into an OCI image index whose per-platform and
attestation manifests are untagged. The weekly "Cleanup Container Images" job deleted
untagged versions withmin-versions-to-keep: 0, removing those child manifests and leaving
every tag (:latest,:v0.1.1, …) pointing at a missing manifest. The build now pushes a
plain single-platform manifest (provenance: false,sbom: false), and the cleanup keeps a
buffer (min-versions-to-keep: 5) with a warning that untagged-deletion is unsafe for
indexed/attested images. Pulling:latestworks again after the next publish.
v0.1.1
[0.1.1] — 2026-06-06
Added
- Detailed, fail-fast startup logging — the container now logs its version and Python
version, the effective (non-secret) configuration, the data directory, whether the database
was created or opened, any schema migrations applied, and a "startup complete" line. Logging
is configured before the config is loaded and sent unbuffered to stdout, so a misconfiguration
is reported clearly instead of crashing silently. - Permission preflight on
DATA_DIR— startup now write-probes the data directory and, if
it isn't writable by the container's runtime user (uid 1000), aborts with an actionable
CRITICAL message (showing the uid/gid and achownhint) instead of a barePermissionError.
Fixed
- No more silent crash-on-start — required-env-var and configuration errors (e.g. a missing
PRINTER_HOSTorAPI_TOKEN) previously raised at import time before logging was set up,
so a misconfigured deployment failed with no usable output. Configuration now loads behind
logging and reports exactly which variable is missing. The Docker image also sets
PYTHONUNBUFFERED=1so logs are never lost to buffering on a fast restart, and creates/owns
/datafor the runtime user so named-volume deployments work out of the box. The in-app/API
version display also now reflects the real package version instead of a hardcoded0.0.1.
Changed
- Dependency updates — rolled in the pending Dependabot bumps: backend
fastapi >=0.136.3,
pydantic >=2.13.4,python-barcode >=0.16.1, and dev toolsmypy >=2.1.0/
types-PyYAML >=6.0.12; frontendfabric 7.4.0,vite 8,typescript 6; the Docker base
image topython:3.14-slim; and CI actions (docker/metadata-action@v6,
docker/build-push-action@v7,github/codeql-action@v4). Verified locally: backend lint +
mypy 2.x + tests pass, and the frontend type-checks and builds. Afrontend/src/vite-env.d.ts
(vite/clientreference) was added because TypeScript 6 now requires ambient types for the
side-effectimport './style.css'. Fabric 7's serialization was checked to still emitIText
and preserve thelabelforge_raw_contentcustom property, so existing saved templates and the
server renderer are unaffected. No user-facing behaviour change.
v0.1.0
[0.1.0] — 2026-06-06
Security
- Log-injection hardening (CWE-117) — user-influenced values that reach a log line
(the historyjob_idpath parameter and the requested label media on a media-mismatch
warning) are now passed through ascrub()helper that strips CR/LF before interpolation,
so a crafted value can't forge additional log entries. No behaviour change for legitimate
input. - Code-scanning cleanup — documented three intentionally-empty exception handlers flagged
by CodeQLpy/empty-except(shutdown-task cancellation, best-effort printer-socket close,
malformed stored-payload fallback); the socket-close handler now logs at debug instead of
silently swallowing. No behaviour change. - No exception detail in the printer-status error response (CWE-209) —
GET /api/printer/status
returned the raw exception text in its 503 body when status was unavailable, which CodeQL
flagged as information exposure. It now logs the exception server-side and returns a generic
"Printer status is currently unavailable." message.
Added
-
Friendly template names — when creating a template, type a human-readable name (e.g.
Spool Label); the URL slug (spool-label) is auto-derived and shown as a live read-only
hint. The friendly name is stored asdisplay_name, shown in the template list and as the
editor title. Renamingdisplay_nameafter creation is not yet available in the UI. Requires
a container image rebuild. -
DK part number in the template list — the Media column now shows the Brother DK part
number with dimensions (e.g.DK-1209 (62×29mm)) instead of the raw media id. Two-color
media gets aRedsuffix (e.g.DK-2251 (62mm) Red). If the media id is not in the catalog,
the raw id is shown as before. Requires a container image rebuild. -
Print a template on a different label media at recall time (one-off) — the recall page
now shows a media selector instead of a read-only badge, defaulting to the template's own
media (e.g. a two-color template defaults to62red). Same-width media appear first
(most likely to fit the design without adjustment); a "Loaded in printer" toggle narrows
the list to the roll currently mounted. The stored template media is never mutated. The
chosen media is logged to history and reproduced faithfully on reprint. The Print button is
gated until a fresh preview has been taken after any media change. If the chosen media
doesn't match the roll actually loaded, the printer-status check still blocks with a 409,
but the recall page now offers a "print anyway" confirmation to override it. Requires a
container image rebuild. -
Mono + red notice on recall — when a template contains red elements and a mono
(single-color) media is selected, an inline notice explains that red will print in black.
The renderer already maps red → black automatically; no action is needed. -
Overflow warning on recall — when a die-cut media is chosen and the content extends
past its printable height, an inline warning appears near the preview. Printing still
proceeds — the user decides from the preview whether to adjust or proceed.
Changed
-
Docs reconciled with shipped features — the README's "What it does" was rewritten to
cover everything now implemented (two-color printing, printer-status/loaded-media detection,
the label catalog, settings/retention, print-time media override, batch printing, and
DISABLE_AUTH), and gained a "Running it" section with a configuration table. The PRD's
in-scope list now includes two shipped features it omitted (one-off media override at recall;
two-color red text in templates), andarchitecture.mdwas corrected to reference the real
compose filenames (docker-compose.yml/docker-compose.dev.yml). Docs-only. -
Template list actions are now compact icon buttons — the per-row Print / Edit / Delete
buttons were full-size text buttons that, together with a verbose timestamp, overflowed the
card. They're now small icon buttons (with tooltips and accessible labels), the Updated
column shows a shorter date (no seconds) on a single line, and the table fits within the card
without widening the layout. Requires a container image rebuild. -
Adopted
release-prep-and-cutstandard (v1.0.0) —/release-prepand/release-cutslash commands added to.claude/commands/; publish workflow (build-and-push.yml) now fires onrelease: published(tag-push trigger removed);CLAUDE.mdandstandards.mdupdated. Developer/process-facing only — no runtime change.
Fixed
-
"Run cleanup now" works again — the Settings → History & Retention "Run cleanup now"
button returned Method Not Allowed: the frontend posted to/api/admin/prune-history,
but that route was never implemented, so the request fell through to the SPA catch-all (a
GET) and 405'd. The endpoint now exists (auth-gated, like the other admin routes) and
prune_history()returns the number of jobs removed, so the button reports e.g. "Cleanup
done — 3 job(s) removed." Requires a container image rebuild. -
Upgrade now delivers new and corrected default catalog entries (#16) — upgrading the
container image no longer leaves the operator'slabels.ymlstale. On startup, labelforge
performs a non-destructive 3-way merge: new entries from the bundled default are added,
corrected field values (e.g.brother_partSKU fixes) are applied to fields the operator
never customized, and any operator customizations or custom media entries are preserved and
never deleted. A backup is written to$DATA_DIR/labels.yml.bakbefore any change. Opt out
withCATALOG_AUTO_MERGE=false. Requires a container image rebuild.
Added
POST /api/admin/reload-catalog— re-runs catalog reconciliation and reloads the catalog
from disk without restarting the container. Returns a JSON summary of entries added/updated
and whether the operator file was rewritten. Requires API token.
Changed
-
CI now type-checks the backend with mypy —
mypy(≥1.11) andtypes-PyYAMLare added to the dev extra; a[tool.mypy]section inpyproject.tomlenables the pydantic plugin and per-module stub overrides for unstubbed third-party libs (brother_ql,qrcode,barcode). Enabling type-checking surfaced a latent Pillow resampling deprecation (Image.BICUBIC/Image.NEAREST→Image.Resampling.*) and tightened several return types. Developer-facing only — no runtime behavior change. -
CI: compose validation now targets
docker-compose.yml/docker-compose.dev.yml— the compose job previously looked forcompose.yml/compose.dev.yml(wrong filenames) and used abash -eone-liner that treated a missing file as a failure. The loop is now hardened to skip absent files and only fail on a baddocker compose config. It also seeds a throwaway.envfrom the tracked.env.examplefirst, since the compose files referenceenv_file: .env(which is gitignored) andconfigwould otherwise fail to resolve it. TheCLAUDE.mdconvention note is corrected to match the actual filenames. Backend linting (ruff) is also clean: import order fixed inroutes/print.py,datetime.UTCmodernisation intemplates/store.py, and long-line wraps across severalbackend/files. -
Dependabot now targets
dev, nevermain, and runs monthly — all four ecosystems (github-actions, pip, npm, docker) in.github/dependabot.ymlnow settarget-branch: dev, so dependency-bump PRs open against the working branch and only reachmainthrough a managed release PR. Previously they defaulted tomain, cluttering the release queue with PRs that could never be allowed to auto-merge. The version-update cadence is also relaxed from weekly to monthly to suit a low-maintenance released project (security updates, when enabled, are advisory-driven and unaffected by this schedule). Process-only — no runtime change.
Added
-
Load previous values on template recall — the recall form now has a Load previous values button (only for templates with variable fields). Clicking it fills the form with the field values from the last time this template was printed, so you can make quick adjustments without re-typing. The button is disabled when the template has no print history. The most recent print job for each template is now also protected from retention pruning, so these values survive cleanup. Requires a container image rebuild.
-
Text-color control always visible in template editor — the Black / Red color dropdown in the toolbar is now visible for all templates, not just two-color media. On mono media the Red option is present but disabled, with a tooltip explaining it requires a two-color label (e.g. 62red); on two-color media Red is selectable as before. Hovering over the Add Text button now also shows a tooltip noting
{fieldname}placeholder syntax. Requires a container image rebuild.
Fixed
-
Continuous templates now extend to fit large text — previewing or printing a continuous-roll template (e.g. 62mm endless) where the last text element uses a large font no longer cuts off the bottom of that text. Previously the render trusted the editor's browser-measured font height, which is shorter than what Pillow actually draws at the same point size; the canvas was too short and the last line was clipped. The renderer now measures rasterized text height with Pillow before sizing the canvas. Die-cut template rendering is unchanged.
-
Template preview no longer fails when the template has variable fields — clicking Preview in the editor on a template containing
{fieldname}placeholders previously returned "Missing required field" because the preview route used the same strict field-validation as the print route. The preview route now fills missing fields with their stored default (if any) or the field name itself as a sample value, so{type}renders ...