mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
Closes the longest-standing inventory gap — finding a specific spool
in a closet of 50 partials. Per-spool icon button on every inventory
card and table row, plus a "Print labels..." header action that opens
a multi-select picker pre-loaded with the currently filtered spools.
Four pre-built templates: AMS holder (30 x 15 mm) for the popular
Makerworld AMS Filament Label Holder, single box label (62 x 29 mm)
for Brother PT/QL or Dymo small labels, Avery L7160 (A4, 21 per
sheet), and Avery 5160 (US Letter, 30 per sheet). Each label carries
the colour swatch (with multi-colour gradient stripes for spools
with extra_colors set), brand, material, name, the *spool ID*
(bsaunder's articulated user-need: telling 8 spools of "PLA White"
apart, especially partials), and a QR code that deep-links to
/inventory?spool=<id> for phone-scan round-trips. Box-label adds
storage location; AMS-holder drops the QR — at 30 x 15 mm there is
no room for swatch + text + QR without truncating away the spool ID,
and AMS-bay identification is at arm's length where the swatch and
ID are enough.
Server-side rendering via ReportLab + qrcode (already a dep). Pure
Python, no headless browser, no system libs. Output is byte-identical
across browsers, Avery sheets align to <0.1 mm, and bulk export is
one click for one PDF. Two endpoints — POST /inventory/labels (local
DB) and POST /spoolman/labels (Spoolman-backed) — gated on
INVENTORY_READ, capped at 500 spools per request, returning
application/pdf via StreamingResponse. The renderer is decoupled
from the SQLAlchemy model via a LabelData dataclass so the same code
path serves both modes.
Modal picker scales to large libraries: search (substring match
across name / brand / #ID), material filter chips derived from the
visible spools, additive Select-all-visible / Deselect-visible /
Clear-all actions so selections survive filter changes. Restyled
twice in development — first cut used generic Tailwind which clashed
with the inventory's bambu-dark palette; second cut switched to
bambu-dark-secondary / bambu-green / bambu-gray to match.
Two render bugs found during visual inspection of generated PDFs and
fixed before commit:
1. AMS-30x15 template originally produced labels with only swatch
+ QR and no text at all — the side-by-side layout left <5 mm
for the text column, so the renderer bailed without drawing
anything. Layout split into tight (h<20mm) and roomy (h>=20mm)
regimes; tight regime drops the QR and gives the right column
to brand + material + a 13pt-bold spool ID.
2. Box-62x29 template aggressively truncated text — swatch + QR
each at ~14 mm on a 26mm-tall label squeezed the text column
to ~16 mm, turning "Polymaker Ivory" into "Polymak..." and
"Polymaker . PLA . Matte" into "Polymaker ...". Swatch capped
at 16 mm, QR capped at 18 mm and constrained to ~20% of width,
leaving the text column ~30 mm — full names render without
truncation.
Both bugs pinned by regression tests in test_label_renderer.py that
render with pageCompression=0 so the resulting PDF bytes contain the
text as ASCII and `assert b"Polymaker" in pdf` works.
41 lines
1.8 KiB
HTML
41 lines
1.8 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
|
<!-- L-4: Restrict Referer header to origin-only on cross-origin navigation so
|
|
sensitive tokens in query parameters are not leaked to third-party servers. -->
|
|
<meta name="referrer" content="strict-origin-when-cross-origin" />
|
|
<title>Bambuddy</title>
|
|
|
|
<!-- PWA Meta Tags -->
|
|
<meta name="description" content="Monitor and manage your Bambu Lab 3D printers" />
|
|
<meta name="theme-color" content="#00ae42" />
|
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
<meta name="apple-mobile-web-app-title" content="Bambuddy" />
|
|
|
|
<!-- Manifest -->
|
|
<link rel="manifest" href="./manifest.json" />
|
|
|
|
<!-- Favicons -->
|
|
<link rel="icon" type="image/png" sizes="32x32" href="./img/favicon-32x32.png" />
|
|
<link rel="icon" type="image/png" sizes="16x16" href="./img/favicon-16x16.png" />
|
|
<link rel="apple-touch-icon" sizes="180x180" href="./img/apple-touch-icon.png" />
|
|
|
|
<!-- Splash screens for iOS -->
|
|
<link rel="apple-touch-startup-image" href="./img/android-chrome-512x512.png" />
|
|
<script type="module" crossorigin src="./assets/index-DA6QDhrM.js"></script>
|
|
<link rel="stylesheet" crossorigin href="./assets/index-N3WJn-iA.css">
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<!-- Service Worker Registration (skip on SpoolBuddy kiosk).
|
|
Kept as an external file so the CSP `script-src 'self'` covers it
|
|
without needing 'unsafe-inline' or per-build hashes. -->
|
|
<script src="./sw-register.js"></script>
|
|
</body>
|
|
</html>
|