Only zero is empty, and the last day is worth reading

This commit is contained in:
2026-08-21 11:57:17 -04:00
parent bbda5cbfe9
commit 74392ccad2
7 changed files with 317 additions and 57 deletions

View File

@@ -1,12 +1,13 @@
<template>
<div class="code-generator">
<div class="no-print">
<div class="page-header">
<h1>Barcode / QR Generator</h1>
<router-link to="/tools" class="btn btn-secondary">Back to Tech Tools</router-link>
</div>
<div class="page-header no-print">
<h1>Barcode / QR Generator</h1>
<router-link to="/tools" class="btn btn-secondary">Back to Tech Tools</router-link>
</div>
<div class="controls card">
<div class="workspace">
<div class="options no-print">
<div class="controls card">
<!-- What to encode -->
<div class="control-row">
<label>
@@ -285,33 +286,52 @@
{{ overlong.length }} row(s) could not be encoded as CODE128 or were empty:
{{ overlong.slice(0, 3).join(', ') }}{{ overlong.length > 3 ? ' ...' : '' }}
</p>
</div>
</div>
<!-- Preview doubles as the print surface: what is on screen is what prints. -->
<div class="sheet">
<div
v-for="item in visible"
:key="item.index"
class="label"
:class="labelClasses(item.page)"
>
<img v-if="showPicture(item.page) && pictureposition === 'behind'"
:src="pictureUrl" class="picture-behind" alt="" />
<div class="content">
<div v-if="showPicture(item.page) && pictureposition !== 'behind'" class="picturebox">
<img :src="pictureUrl" class="picture-img" alt="" />
</div>
<div class="codegroup">
<div v-if="layoutFor(item.page) !== 'textonly'" class="codebox">
<img v-if="images[item.index]" :src="images[item.index]" class="code-img" alt="" />
</div>
<div v-if="layoutFor(item.page) !== 'codeonly' && item.page.label" class="label-text">
{{ item.page.label }}
</div>
</div>
</div>
<span v-if="backenabled" class="side-flag">{{ item.page.side }}</span>
</div>
<!-- Preview doubles as the print surface: what is on screen is what
prints, so everything around it is chrome that print must strip. -->
<div class="preview">
<div class="preview-head no-print">
<h4 class="section-heading">Preview</h4>
<span class="preview-count">
{{ cardCount }} label{{ cardCount === 1 ? '' : 's' }}<span
v-if="backenabled">, {{ pages.length }} pages</span>
</span>
</div>
<div class="sheet">
<div
v-for="item in visible"
:key="item.index"
class="label"
:class="labelClasses(item.page)"
>
<img v-if="showPicture(item.page) && pictureposition === 'behind'"
:src="pictureUrl" class="picture-behind" alt="" />
<div class="content">
<div v-if="showPicture(item.page) && pictureposition !== 'behind'" class="picturebox">
<img :src="pictureUrl" class="picture-img" alt="" />
</div>
<div class="codegroup">
<div v-if="layoutFor(item.page) !== 'textonly'" class="codebox">
<img v-if="images[item.index]" :src="images[item.index]" class="code-img" alt="" />
<!-- An empty code box is indistinguishable from a label that
simply has no code on it, which is how a failed render
reads as "the barcode does not appear". Say so, on screen
only - the printed label must never carry this. -->
<span v-else class="code-missing">no code</span>
</div>
<div v-if="layoutFor(item.page) !== 'codeonly' && item.page.label" class="label-text">
{{ item.page.label }}
</div>
</div>
</div>
<span v-if="backenabled" class="side-flag">{{ item.page.side }}</span>
</div>
<p v-if="!pages.length" class="preview-empty no-print">
Nothing to print yet - enter content above.
</p>
</div>
</div>
</div>
</div>
@@ -732,6 +752,47 @@ watch([pages, codetype, backcodetype, errorcorrection], renderAll)
.code-generator { width: 100%; }
/* Settings on the left, the labels themselves on the right, where they stay in
view while the settings that change them are being worked. The preview
column scrolls on its own so a 500-label run cannot push the controls off
the screen. */
.workspace {
display: grid;
grid-template-columns: minmax(0, 3fr) minmax(300px, 2fr);
gap: 20px;
align-items: start;
}
.preview {
position: sticky;
top: 12px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px 16px 16px;
max-height: calc(100vh - var(--main-pad-top) - 24px);
overflow: auto;
}
.preview-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.preview-head .section-heading { margin-top: 0.25rem; }
.preview-count { font-size: 0.8125rem; color: var(--text-light); }
.preview-empty { color: var(--text-light); font-size: 0.8125rem; margin: 0; }
/* One column once the two would be too narrow to work in. */
@media (max-width: 1200px) {
.workspace { grid-template-columns: minmax(0, 1fr); }
.preview { position: static; max-height: none; }
}
.controls {
background: var(--bg-card);
color: var(--text);
@@ -905,18 +966,35 @@ watch([pages, codetype, backcodetype, errorcorrection], renderAll)
.label.type-barcode .codebox {
flex: 1 1 auto;
width: auto;
min-width: 0;
/* Never below 40% of the label: past that a CODE128 is too few narrow bars
to scan, so it is better for the caption to wrap or clip. */
min-width: 40%;
height: var(--tool-barh);
}
.label.layout-stack.type-barcode .codebox { min-width: 100%; }
/* Screen-only, and deliberately loud: an empty box means the render failed. */
.code-missing {
font-family: Arial, Helvetica, sans-serif;
font-size: 6px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--danger);
}
.code-img {
width: 100%;
height: 100%;
object-fit: contain;
}
/* The caption takes what it needs and no more. Both boxes used to grow, so a
caption of any length halved the barcode and then kept going - at 1x0.5in
with a couple of words it left a 36px stub that reads as "the code did not
render". A code that cannot be scanned is not a label. */
.label-text {
flex: 1 1 auto;
flex: 0 1 auto;
min-width: 0;
text-align: center;
font-family: "Arial Narrow", Arial, Helvetica, sans-serif;
@@ -943,7 +1021,20 @@ watch([pages, codetype, backcodetype, errorcorrection], renderAll)
@media print {
.side-flag { display: none !important; }
.code-missing { display: none !important; }
.sheet { padding: 0; display: block; gap: 0; }
/* The preview column is chrome. Its overflow matters most: a scrolling
ancestor clips a multi-page print to whatever was on screen. */
.workspace { display: block; }
.preview {
position: static;
max-height: none;
overflow: visible;
border: 0;
border-radius: 0;
padding: 0;
background: none;
}
/* The back's dotted variant outspecifies a bare .label reset, so it printed
a dashed rule down the edge of every second card. */
.label,