feat(labels): cut marks in the sheet margin for trimming with a paper cutter

Label sheets printed on plain A4 are cut with a lever paper cutter. Each
template can now print small hairline cut marks in the sheet margin: one
at both ends of every cut line (left/right edge of each column, top/bottom
edge of each row), 1 mm away from the label and never closer than 2 mm to
the paper edge, so they print on normal printers and never touch a label.
Labels without a gap share one mark. Marks are drawn on every sheet for
the full grid, since the cutter cuts across the whole sheet.

On by default (new column cropMarks, default true); switch it off in the
template designer for pre-die-cut label sheets.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-28 06:11:03 +02:00
1 parent 445fb3b94b
commit 888edf7e1d
9 files changed
+280

No files matched your search

@@ -46,6 +46,7 @@ export default function EditLabelTemplatePage() {
marginLeftMm: template.marginLeftMm,
gapXMm: template.gapXMm,
gapYMm: template.gapYMm,
cropMarks: template.cropMarks,
isDefault: template.isDefault,
}}
onSubmit={handleSubmit}
@@ -82,6 +82,7 @@ export interface LabelDesignerValue {
marginLeftMm: number;
gapXMm: number;
gapYMm: number;
cropMarks: boolean;
isDefault: boolean;
}
@@ -146,6 +147,7 @@ export function LabelDesigner({ initial, onSubmit, submitLabel }: LabelDesignerP
const [marginLeftMm, setMarginLeftMm] = useState(initial?.marginLeftMm ?? 5);
const [gapXMm, setGapXMm] = useState(initial?.gapXMm ?? 2);
const [gapYMm, setGapYMm] = useState(initial?.gapYMm ?? 2);
const [cropMarks, setCropMarks] = useState(initial?.cropMarks ?? true);
const [isDefault, setIsDefault] = useState(initial?.isDefault ?? false);
const [elements, setElements] = useState<LabelElement[]>(initial?.elements ?? []);
const [selectedId, setSelectedId] = useState<string | null>(null);
@@ -245,6 +247,7 @@ export function LabelDesigner({ initial, onSubmit, submitLabel }: LabelDesignerP
marginLeftMm,
gapXMm,
gapYMm,
cropMarks,
isDefault,
};
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
@@ -287,6 +290,7 @@ export function LabelDesigner({ initial, onSubmit, submitLabel }: LabelDesignerP
if (typeof value.marginLeftMm === "number") setMarginLeftMm(value.marginLeftMm);
if (typeof value.gapXMm === "number") setGapXMm(value.gapXMm);
if (typeof value.gapYMm === "number") setGapYMm(value.gapYMm);
if (typeof value.cropMarks === "boolean") setCropMarks(value.cropMarks);
}
setSelectedId(null);
toast.success("Vorlage importiert.");
@@ -315,6 +319,7 @@ export function LabelDesigner({ initial, onSubmit, submitLabel }: LabelDesignerP
marginLeftMm,
gapXMm,
gapYMm,
cropMarks,
isDefault,
});
router.push("/settings/automation?tab=label-templates");
@@ -470,6 +475,22 @@ export function LabelDesigner({ initial, onSubmit, submitLabel }: LabelDesignerP
/>
</div>
</div>
<label className="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl border border-border p-3 has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-ring">
<input
type="checkbox"
className="mt-1 size-4 accent-[var(--primary)]"
checked={cropMarks}
onChange={(event) => setCropMarks(event.target.checked)}
/>
<span className="flex flex-col">
<span className="font-medium">Schnittmarken am Blattrand</span>
<span className="text-sm text-muted-foreground">
Kleine Striche im Rand an jeder Schnittkante – zum Anlegen am Papierschneider.
Ausschalten bei vorgestanzten Etikettenbögen. Rand mindestens 5 mm, damit die
Striche gedruckt werden.
</span>
</span>
</label>
</CardContent>
</Card>