Skip to content

Releases: crzykidd/labelforge

v0.1.3

08 Jun 06:05
3e09035

Choose a tag to compare

[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-prep Step 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 plain v0.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}/file endpoint and registered with the FontFace API on startup.

v0.1.2

07 Jun 19:34
d17be3e

Choose a tag to compare

[0.1.2] — 2026-06-07

Fixed

  • Published container image was unpullable (manifest unknown / 404) — the publish
    workflow built with docker/build-push-action defaults, 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 with min-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 :latest works again after the next publish.

v0.1.1

07 Jun 04:00
c04e195

Choose a tag to compare

[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 a chown hint) instead of a bare PermissionError.

Fixed

  • No more silent crash-on-start — required-env-var and configuration errors (e.g. a missing
    PRINTER_HOST or API_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=1 so logs are never lost to buffering on a fast restart, and creates/owns
    /data for 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 hardcoded 0.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 tools mypy >=2.1.0 /
    types-PyYAML >=6.0.12; frontend fabric 7.4.0, vite 8, typescript 6; the Docker base
    image to python: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. A frontend/src/vite-env.d.ts
    (vite/client reference) was added because TypeScript 6 now requires ambient types for the
    side-effect import './style.css'. Fabric 7's serialization was checked to still emit IText
    and preserve the labelforge_raw_content custom property, so existing saved templates and the
    server renderer are unaffected. No user-facing behaviour change.

v0.1.0

06 Jun 18:19
792021c

Choose a tag to compare

[0.1.0] — 2026-06-06

Security

  • Log-injection hardening (CWE-117) — user-influenced values that reach a log line
    (the history job_id path parameter and the requested label media on a media-mismatch
    warning) are now passed through a scrub() 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 CodeQL py/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 as display_name, shown in the template list and as the
    editor title. Renaming display_name after 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 a Red suffix (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 to 62red). 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), and architecture.md was 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-cut standard (v1.0.0)/release-prep and /release-cut slash commands added to .claude/commands/; publish workflow (build-and-push.yml) now fires on release: published (tag-push trigger removed); CLAUDE.md and standards.md updated. 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's labels.yml stale. On startup, labelforge
    performs a non-destructive 3-way merge: new entries from the bundled default are added,
    corrected field values (e.g. brother_part SKU 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.bak before any change. Opt out
    with CATALOG_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 mypymypy (≥1.11) and types-PyYAML are added to the dev extra; a [tool.mypy] section in pyproject.toml enables 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.NEARESTImage.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 for compose.yml / compose.dev.yml (wrong filenames) and used a bash -e one-liner that treated a missing file as a failure. The loop is now hardened to skip absent files and only fail on a bad docker compose config. It also seeds a throwaway .env from the tracked .env.example first, since the compose files reference env_file: .env (which is gitignored) and config would otherwise fail to resolve it. The CLAUDE.md convention note is corrected to match the actual filenames. Backend linting (ruff) is also clean: import order fixed in routes/print.py, datetime.UTC modernisation in templates/store.py, and long-line wraps across several backend/ files.

  • Dependabot now targets dev, never main, and runs monthly — all four ecosystems (github-actions, pip, npm, docker) in .github/dependabot.yml now set target-branch: dev, so dependency-bump PRs open against the working branch and only reach main through a managed release PR. Previously they defaulted to main, 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 ...

Read more