Releases: crzykidd/labelforge
Release list
v0.1.9
[0.1.9] — 2026-09-19
Added
- Template fields can now be edited from the editor: a new FIELDS panel (below Elements) lets you set each field's type, required, default, and — for the first time — increment, closing a gap where batch/increment printing was fully wired at recall but had no way to turn it on. Two new field types round this out: List resolves its recall options from a named, reusable value list shared by every template that uses a field with that name (e.g.
{room}on any template offers the same "room" list, edited once from an E drawer next to the field); Enum keeps a one-off set of options scoped to just that template. Deleting a shared list doesn't affect existing templates or print history — a field pointing at a missing list just recalls as free text.
Fixed
- Saving a template's field schema (via the new FIELDS panel) no longer gets silently discarded.
PUT /api/templates/{name}was recomputing the schema from the previously stored one whenevercanvas_jsonwas also present in the request — which it always is on a normal Save — so any field-property edit sent in that same request was reverted immediately. - Wrap's target width is now orientation-aware. On a rotated template, text runs along the free length axis instead of the fixed print-head width, so it no longer wraps prematurely there — it only clamps to the print-head width when the element's own rotation actually puts it on that axis.
- Printing a template no longer stays silent about overflow when no preview was run first — the print confirmation now shows the same overflow warning the preview does.
Changed
- Wrap is now capped at a chosen number of lines instead of wrapping unlimited: the editor's Wrap checkbox is replaced with a select (Off / No limit / 2 / 3 / 4 / 5 lines). Content that needs more lines than the cap is truncated, and the same "content may be clipped" overflow warning used elsewhere flags it — never silent. Existing templates saved with wrap on and no cap keep wrapping unlimited, unchanged.
v0.1.8
[0.1.8] — 2026-09-19
Added
- Text elements have an opt-in Wrap checkbox in the template editor. When enabled, resolved text wraps at spaces to fit the element's own box width instead of running past it; a single word too wide to wrap is left on its own line, unbroken (still reported by the overflow warning). Off by default, so existing templates are unaffected.
Fixed
- A field value longer than the placeholder it was designed around (e.g.
{name}at 150pt withname = "Master Bedroom") no longer prints silently clipped. On continuous media the label now grows in whichever direction the content actually needs — previously a centre-anchored element could grow the label the wrong way and lose everything that overflowed the front. The "content may be clipped" warning also now checks the real value being printed, not just the stored placeholder, and catches a line too wide for the print head on continuous media, not just die-cut labels.
v0.1.7
[0.1.7] — 2026-09-19
Fixed
- Rotating an element 90° no longer prints it clipped with a large blank area above it. The server renderer now accounts for an element's rotation when sizing a continuous-media label (so the print length grows to fit the rotated content, matching the editor) and when checking a die-cut label for overflow. It also rotates left/top-origin elements (QR codes, barcodes) about the same point Fabric does in the editor, so their printed position matches what's shown on screen. Unrotated templates are unaffected.
v0.1.6
[0.1.6] — 2026-09-19
Added
- Template editor: elements panel, bounds clamping, grid + snap, keyboard shortcuts, and undo/redo. An elements list beside the canvas shows every object (type + content snippet) and flags any that lie off the visible label — click a row to select it, or click its red "off canvas" flag to pull just that element back inside. When anything is off the label, a "Bring all on-canvas" button appears above the list to recover everything at once. This rescues elements that were previously invisible, unselectable, and (on continuous media) silently inflating the printed length. Dragging and resizing clamp to the label bounds and snap to the label edges, horizontal/vertical centerlines, and an optional grid (toggle in the toolbar; remembered per browser). Arrow keys nudge the selected element by 1 label pixel (Shift: 10), Delete/Backspace removes it, and Escape deselects — all suspended while editing text inline. Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z undo/redo up to 50 steps.
- Element rotation snaps to 0/90/180/270° when dragged within about 8° of one of them, with a "Rotate 90°" button for a one-click quarter turn and a brief angle readout while rotating. Free rotation still works everywhere else.
- Templates support a rotated orientation, matching Quick Print. Set it in the editor toolbar (Standard / Rotated 90°): the design canvas transposes to the label's length axis so you type upright, and Preview/print shows the label rotated a quarter turn: the top of your design prints along the label's left edge, so turning the finished label clockwise reads it the same way up as the editor showed it. Continuous media's auto-length still grows correctly under rotation. Toggling orientation on an existing template never moves or resizes its elements; a status message warns that the layout may need adjusting. Requires a container image rebuild.
Fixed
- Selecting a text, QR, or barcode element in the template editor no longer reflows the toolbar and shifts the canvas down the page. Per-element controls now live in a fixed-height row that swaps its contents instead of resizing the toolbar around them.
- The "content may be clipped" warning shown when recalling a template now checks both edges of the label and understands rotated templates. Previously it only looked at the bottom edge, so an element running off the right of a die-cut label was never flagged, and a rotated template was measured against the wrong axis.
- CI no longer fails on unrelated pull requests. The
ruff format --checkgate reformatted Python code blocks embedded in Markdown (a behavior ruff added in 0.14), so documentation and archived handoff prompts failed the check and blocked every PR. Markdown is now excluded from ruff, and theruffdev dependency is pinned to a compatible range so the toolchain no longer changes under CI without a dependency bump. No runtime change.
Changed
- Rolled in three months of pending dependency updates. Runtime: FastAPI 0.141.1, pydantic-settings 2.15.0, qrcode 8.2. Build and test tooling: pytest 9.1.1, Vite 8.3.0, and the pinned GitHub Actions (checkout v7, setup-python v7, setup-node v7, codeql-action v4). No API, rendering, or printing behavior changes; the full test suite passes on the new versions.
v0.1.5
[0.1.5] — 2026-06-24
Added
- "Add Barcode" button in the template editor toolbar lets you place a barcode element on the canvas. A client-generated placeholder shows the element's position; the real barcode is rendered server-side on Preview/print using
python-barcode. Supported symbologies: Code 128 (default), Code 39, EAN-13, EAN-8, UPC-A. Use{fieldname}placeholders in the payload for variable barcodes. Note that fixed-length symbologies (EAN-13, EAN-8, UPC-A) require a correctly-sized digit string — an invalid payload reports an error on Preview/print rather than printing. Requires a container image rebuild.
Fixed
- Barcode custom props (
labelforge_barcode_payload,labelforge_barcode_symbology) were missing from theCUSTOM_PROPSserialization allow-list, causing barcode elements placed via raw canvas JSON to silently lose their props on save. Both props are now registered.
v0.1.4
[0.1.4] — 2026-06-23
Changed
- Corrected the HTTP API reference (
docs/features/api.md) to match the implementation: the auth model (almost every route requires the token, not just writes; the only open routes are/api/health,/api/printer/status,/api/version, and the OpenAPI/docs endpoints), the 401-vs-403 distinction (a wrong token returns 403), and the removal of a described-but-nonexistent cookie login / Cloudflare middleware. Documented the previously-missing routes (/api/health,/api/version,/api/admin/prune-history,/api/fonts/{name}/file,/api/templates/{name}/last-values), removed the nonexistent/api/printer/info, fixed response shapes (statusis"sent"not"printed";overflowis returned;printed_atis not), resolved the batch207to its actual 200/500 behavior, and noted the{"detail": {...}}error envelope and the?override=trueparam. Docs-only.
Added
- "Add QR" button in the template editor toolbar lets you place a QR code element on the canvas without hand-editing JSON. A client-generated placeholder image shows the element's position and payload; the real QR bitmap is rendered server-side on Preview/print. Use
{fieldname}placeholders in the payload to create variable QR codes. Payload and error-correction level (L/M/Q/H) are editable in the toolbar when a QR element is selected. Requires a container image rebuild. - App logo now appears in the nav bar (links home) and as the browser favicon.
Fixed
- QR code and barcode template elements now print correctly instead of as a solid black block. The server renderer resolves field placeholders in QR/barcode payloads, rasterizes with an integer-multiple NEAREST upscale (no antialiased grey edges), and pastes pure 0/255 pixels onto the print canvas so the 1-bit threshold produces a faithful result. A hard-threshold pass is applied after rasterization as a safety net.
- A wrong API token is now caught at entry: the token gate validates the candidate against the server before storing it, and shows an inline error if rejected. Any subsequent 401 or 403 from any API call (including previews, font loading, and history) clears the stored token and returns to the token gate with a "your token was rejected" message. A "Sign out / change API token" button is always visible in Settings when auth is enabled. Requires a container image rebuild.
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 ...