Skip to content

Reorganize examples into three tiers; promote ListLayout into the library - #55

Merged
PeteRichardson merged 15 commits into
mainfrom
worktree-examples-reorg
Aug 30, 2026
Merged

Reorganize examples into three tiers; promote ListLayout into the library#55
PeteRichardson merged 15 commits into
mainfrom
worktree-examples-reorg

Conversation

@PeteRichardson

Copy link
Copy Markdown
Owner

Reorganizes the package's ad-hoc examples into three tiers, and promotes list-layout
logic that was duplicated across two examples into the library.

Implements the design spec
via this plan (both included in this PR).

What changed

Tier 1 — tutorial snippets. New top-level Snippets/ directory, auto-discovered
by SwiftPM with no Package.swift entry. Five ~15-line tutorials, each teaching one
thing, compiled on every build so a tutorial referencing a renamed API breaks CI
instead of quietly misleading a reader.

Tier 2 — labelprint. Replaces example-listlabel. Reads lines on stdin and
prints them as one long label; keeps only stdin parsing now that layout lives in the
library. Subcommands print (default), preview, zpl, list. Drops the
example- prefix, since these are meant to be symlinked into ~/bin.

Tier 3. compare-renderers renders the same ZPL through both Labelary and
zpl2png to expose renderer disagreements — this is the ZPL-debugging tool.
batch-badges renders one Stencil template over a CSV to make N labels. LabelGUI is
now documented as the Tier 3 GUI example.

Library: ListLayout. generate_label was duplicated across two examples and
had never been tested. It is now one public ListLayout type in
Sources/LabelKit/Labels/, with 10 unit tests. This is what unblocks
t#42t can consume it rather
than copying it a third time.

Library: StencilTemplateStore.init(fileURL:). The store previously resolved
only to Application Support / group-container paths, so examples depended on
invisible machine state. example-label ran locally only because LabelGUI had left
a templates.json in a group container; on a fresh clone it threw. Examples now
read seed templates checked into the repo.

CI. The repo had none. This matters beyond hygiene: snippets' entire anti-rot
guarantee is that swift build compiles them, which is only true if something runs it.

Docs. The DocC catalog referenced StencilProcessor and LengthInjector
neither has ever existed. Corrected to ResolveTemplates / InjectLength.

Notable fixes found during review

  • CRLF input was completely broken in the list parser. Swift treats \r\n as a
    single Character, so split(separator: "\n") never split CRLF text — a 5-line
    list parsed to one item with raw CR bytes landing inside ^FD. Any
    Windows-authored or pasted list hit this. Fixed with tests.
  • ListLayout.printWidthDots ignored its environment, hardcoding 1200. It
    coincidentally matched 4-inch stock, which is why nothing caught it; on label2x1
    the ZPL declared 1200 dots while the geometry said 600, so previews and print
    would disagree. Now derived from the environment, with an explicit override.
  • compare-renderers aborted before the offline renderer if the network one failed,
    forfeiting its entire offline value. Each renderer is now isolated, and each
    reports its PNG dimensions — which is how you see the disagreement.

Deliberately not done

Verification

swift build clean; 110 tests passing (up from 89). Every example was exercised
end-to-end. Nothing was sent to the physical printer at any point.

Three-tier examples taxonomy (tutorial snippets / useful tools / advanced)
with acceptance criteria, promoting list layout into the library, extracting
the EventKit reminders example to its own repo, and adding CI.
…plates

Examples and snippets can now read templates checked into the repo instead of
depending on Application Support state that does not exist on a fresh clone.
Promotes layout logic that was duplicated across two examples into the library,
where it is unit-tested for the first time and reusable by outside consumers.
Layout now comes from LabelKit's ListLayout; the tool keeps only stdin parsing.
Drops the example- prefix since Tier 2 tools are meant to be installed.
Compiled on every build, so a tutorial referencing a renamed API breaks CI.
example-label's template and preview behavior moved to snippets; its
dual-renderer behavior becomes a debugging tool for renderer disagreements.
…ions

- Give each renderer its own error boundary so one failing (e.g. Labelary
  unreachable offline) does not stop the other from running; report the
  failure and continue instead of propagating.
- Report each rendered PNG's pixel dimensions and byte size alongside the
  inline preview, parsed from the IHDR chunk without an image framework
  dependency — this is the actual diagnostic signal when the two renderers
  disagree, per the project spec ("shows both inline and reports
  dimensions").
Reminders.swift has no home in a companion `reminders` repo yet, so
example-reminderlist and Examples/ReminderList/ are NOT removed in this
commit (deferred until that repo exists and builds) - this only covers
the doc-cleanup portion of the examples reorganization.

- LabelKit.md: StencilProcessor/LengthInjector never existed; replaced
  with the real processor names ResolveTemplates/InjectLength, added
  ListLayout to Other Types, and fixed a stale `example-label` shell
  snippet.
- README.md: rewrote the Examples section into the three real tiers
  (Snippets/ tutorials, labelprint, Advanced/), fixed the printer-host
  override examples and the StencilTemplateStore consumer reference -
  all previously pointed at example-label/example-listlabel, which no
  longer exist.
- AGENTS.md: updated the Commands block to match.

KeyValueContext investigation (Apps/LabelGUI/LabelGUI/KeyValueContext.swift
vs Sources/LabelKit/Processors/KeyValueContext.swift): not a fork despite
the shared filename. The LabelGUI file imports LabelKit and defines only
KeyValueTableView, a SwiftUI view consuming LabelKit's KeyValueContext/
KeyValueRow types directly - it re-declares neither. Classification of
its full 164 lines: (a) GUI-specific, stays in LabelGUI - all of it.
(b) generally useful, promote to LabelKit - none. (c) redundant, delete -
none. No library API change made or warranted.

swift build && swift test: Build complete!, 105 tests / 25 suites passed.
swift package describe confirms products labelprint, compare-renderers,
batch-badges, example-reminderlist - no example-label, no example-listlabel.
…dge cases)

- labelprint: split on any newline (not just \n) so CRLF/lone-CR input
  parses correctly instead of collapsing into one corrupted item; trim
  with .whitespacesAndNewlines to keep stray CR out of ^FD content
- ListLayout: printWidthDots now derives from the environment's
  RenderGeometry when unset, instead of a hardcoded 1200 that silently
  disagreed with non-4x stocks like label2x1
- compare-renderers: pngDimensions verifies the PNG signature before
  reading IHDR offsets, instead of returning confident nonsense for
  non-PNG input
- batch-badges: CSV rows with the wrong field count now raise a
  ValidationError naming the row and expected/actual counts, instead of
  silently zipping to an empty value
- README: document LabelGUI explicitly as the Tier 3 GUI example
- spec: correct the structure block to name PrintAList.swift, matching
  the shipped tree, instead of the never-built PrintToNetwork.swift

Adds CRLF/lone-CR and printWidthDots coverage to the existing test
suites (105 -> 110 tests).
@PeteRichardson PeteRichardson self-assigned this Aug 30, 2026

@PeteRichardson PeteRichardson left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@PeteRichardson
PeteRichardson merged commit 6e32361 into main Aug 30, 2026
1 check passed
@PeteRichardson
PeteRichardson deleted the worktree-examples-reorg branch August 30, 2026 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant