Skip to content

Feat/zpl cw custom fonts#75

Merged
u8array merged 6 commits into
mainfrom
feat/zpl-cw-custom-fonts
May 19, 2026
Merged

Feat/zpl cw custom fonts#75
u8array merged 6 commits into
mainfrom
feat/zpl-cw-custom-fonts

Conversation

@u8array
Copy link
Copy Markdown
Owner

@u8array u8array commented May 19, 2026

No description provided.

u8array added 5 commits May 19, 2026 22:45
Adds a ^CW alias->path table to the label config. Each entry registers
a single-character alias [A-Z0-9] that ^A{alias} fields can reference
instead of the verbose ^A@...E:font.TTF form.

Generator emits one ^CW per non-empty mapping after the geometry/
default-font block. Parser persists mappings to labelConfig and resolves
^A{alias} field references to the corresponding font path via a runtime
alias map.

UI lives in its own Custom Fonts collapsible section (default closed).
Each row has a single-char alias input, a path input, and a delete
button arranged in a CSS grid (fixed + flexible + auto) so the path
column does not collapse. The alias input filters to [A-Z0-9] while
typing; duplicates are flagged with a red border and aria-invalid
plus a tooltip. Rows with both fields empty auto-remove on blur.

Path suggestions come from a datalist that lists the standard Zebra
drive prefixes (E:, R:, A:, B:) plus E:{name} for every font uploaded
via the Fonts tab. A short, plain-language InfoIcon tooltip explains
the section purpose without using ZPL syntax.

Default-text-style Font input now suggests the union of the Zebra
built-in font letters plus any user-defined custom-font aliases, so a
mapped letter is reachable from ^CF as well.
Each uploaded font now has a single-character alias input alongside its
name. Typing a letter writes a ^CW mapping for the active label
(path = E:{name}); clearing the input removes the mapping. This
short-circuits the previous flow of upload, switch tabs, open Custom
Fonts section, add a row, pick the path from the suggestions.

Layout switched from flex to grid to avoid the inputCls.w-full vs
flex-1 collision that previously shrank sibling content to zero width.
Locale keys fonts.aliasHint / fonts.aliasAssigned added in all 32
languages via a one-shot inserter script (the shared add_locale_key
helper only handles second-level blocks; fonts is top-level).
Pulls the ALIAS_CHAR_RE strip pattern and the upsert-mapping logic out
of CustomFontsSection and FontManager into a shared src/lib/customFonts
module. Both UI surfaces now call normalizeAlias and
upsertCustomFontMapping instead of inlining the same regex/filter
sequence. The helpers are pure and covered by a small unit suite.

Adds DEFAULT_FONT_DRIVE = 'E:' as a named constant so the assumption
that uploaded fonts target flash storage is visible.

Widens the Fonts-tab alias column from 2rem to 3rem and changes the
placeholder from '?' (questioning) to 'A-Z' (range hint).
Removes the Custom Fonts section from the Properties panel and moves
its responsibilities into the Fonts tab. Each uploaded font now has an
inline single-character alias slot; mappings to fonts NOT uploaded
locally live in a collapsible "Printer-resident fonts" sub-section. A
single place owns all ^CW configuration.

Polish along the way:
- Confirm dialog (existing ui/ConfirmDialog abstraction) gates font
  deletion to prevent accidental loss.
- Inline alias placeholder changed from "?" to "A-Z" range hint;
  decorative "F" badge dropped (visual noise without function).
- "+ Add font" and "+ Add printer font" share dashed-border styling.
- AddFontForm auto-fills printer filename with file.name.toUpperCase()
  when the field is left blank.
- Font name spans get title=name so truncated entries reveal on hover.
- ManualMappingsSection auto-removes rows where both alias and path
  stay empty across a blur (deferred via requestAnimationFrame so
  tabbing between sibling inputs does not cut the row).
- nextFreeAlias() picks the next unused letter starting at I (skips
  built-in Zebra font letters 0/A-H) when the user clicks Add.

Generator now rewrites ^A@<rot>,h,w,E:NAME.TTF into ^A{alias}<rot>,h,w
whenever the user has registered an alias for that path. The verbose
form stays for paths without a matching ^CW entry. Saves bytes in the
output and makes the user's alias choices visible in the generated ZPL.

Default-font datalist (Properties → Default Text Style → Font) now
includes any user-registered ^CW alias alongside the Zebra built-ins,
with the referenced filename shown as a label so a bare letter like
"X" is not mistaken for a built-in font.

ZPL-domain constants centralised in src/lib/customFonts:
ZPL_BUILTIN_FONT_IDS, ZPL_DRIVE_PREFIXES, DEFAULT_FONT_DRIVE,
uploadedFontPath(). UI files import instead of redeclaring.

Three new top-level fonts.* locale keys + one renamed locale set
(manualMappings*); inserted via small one-off scripts because the
shared add_locale_key helper only handles second-level blocks.
The auto-rewrite regex accepts [NIRB] but only `N` was exercised.
Convert the test to it.each so a future change that, say, breaks
the rotation capture group fails on the matrix instead of slipping
through with the still-passing happy path.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive system for managing custom Zebra (ZPL) fonts, including a new FontManager UI for mapping font paths to single-character aliases, ZPL generation optimizations that emit ^CW commands, and parser enhancements to resolve these aliases. Feedback focuses on improving the robustness of the implementation, specifically: fixing a bug in the row deletion logic triggered by focus changes, replacing unstable React keys in the font list, expanding the ZPL generator's regex to support various printer drives beyond the default, and ensuring the parser upserts font mappings to prevent duplicate state entries.

Comment thread src/components/Fonts/FontManager.tsx Outdated
Comment thread src/components/Fonts/FontManager.tsx Outdated
Comment thread src/components/Fonts/FontManager.tsx Outdated
Comment thread src/lib/zplGenerator.ts Outdated
Comment thread src/lib/zplParser.ts Outdated
- ManualMappingsSection: handleBlur now checks
  row.contains(document.activeElement) so tabbing between sibling
  inputs no longer removes the row mid-traversal. Row key falls back
  to the auto-assigned alias rather than the unstable list index, so
  React preserves identity across deletes.
- Generator: ^A@ rewrite regex accepts any [A-Z]: drive prefix
  instead of hardcoding E:. Current text emit still uses E: only,
  but the rewrite stays correct if other drives appear via import or
  a future text emit change.
- Parser: ^CW now upserts by alias instead of appending. Two ^CW
  lines for the same alias collapse to the final mapping, matching
  the runtime fontAliases.set last-wins semantics. Multi-alias
  mappings sharing the same path are still kept separate.

Addresses Gemini review comments on #75.
@u8array u8array merged commit 6a5c8c6 into main May 19, 2026
2 checks passed
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