Skip to content

feat(image): printer-storage UI for ~DY+^XG upload/recall#85

Merged
u8array merged 2 commits into
mainfrom
feat/dy-xg-ui
May 21, 2026
Merged

feat(image): printer-storage UI for ~DY+^XG upload/recall#85
u8array merged 2 commits into
mainfrom
feat/dy-xg-ui

Conversation

@u8array
Copy link
Copy Markdown
Owner

@u8array u8array commented May 21, 2026

No description provided.

Makes the ~DY+^XG feature from PR #84 usable for new images in the
designer — until now it only kicked in when importing existing labels
that already used the upload+recall pattern.

Properties-panel section at the end of the image properties, with a
consistent 'Printer storage' header (info icon, border-t separator)
visible in both states:

- Off-state: 'Activate' button. Toggling on sets storedAs to
  { device: 'R', name: 'IMG_xxxx' } with an auto-generated 4-char UUID.
- On-state: device dropdown (R/E/B/A as plain letters), name input
  (max 8 chars, auto-uppercase, [A-Z0-9_] filter, empty-guard against
  broken ZPL), path preview, 'Ship bytes' checkbox + hint (decouples
  upload from recall: off = ^XG only, on = ~DY+^XG), 'Embed inline'
  button as the toggle-off.

Parser extension: ^XG without a preceding ~DY is now a valid import
(recall-only). Creates an image object with storedAs.embedInZpl=false
and no cached bytes; surfaced as a partial finding so the import
report flags the degraded preview.

Cache deletion: trash button next to the image-source dropdown opens
a ConfirmDialog (destructive, autoFocus on Cancel) explaining that
the bytes vanish for ALL labels referencing this image. Previously
only the image *object* could be removed (Del key), not the cached
bytes — so old uploads accumulated in localStorage with no UI to
clean them up.

Canvas resize via handles: the image registry had no commitTransform,
so Konva applied sx/sy as a visual scale but nothing translated that
back into widthDots — the only way to resize was the properties
panel. Now: cached PNG = aspect-locked via the dominant-axis drag
(Math.abs heuristic so all 8 handles work for both grow and shrink);
no cache (placeholder) = free-form via heightDots so the user can
shape the box as a layout placeholder.

Empty source selection: handleImageSelect bailed early on an empty
imageId, leaving the 'Select image…' placeholder option functionally
dead. Now it clears imageId + _gfaCache, which is the right answer
for recall-only setups where the user wants a storedAs path without
local preview bytes.

Canvas placeholder: 🖼 emoji replaced by a Konva.Path rendering the
Heroicons 'photo' outline. The emoji rendered inconsistently across
OSes (color on macOS, monochrome on Linux, missing on some Windows
configurations); the vector path is deterministic.

Shared infrastructure that fell out of the work:
- src/lib/storagePath.ts extended with STORAGE_DEVICES, StorageDevice,
  MAX_STORAGE_NAME_LEN, STORAGE_NAME_FILTER_RE, defaultStorageName —
  all storage-path concepts living in one canonical place so the
  parser, emitter, and image registry stay in lockstep.
- src/components/Properties/styles.ts: new buttonCls for secondary-
  action buttons (Upload, toggle, Embed inline), eliminating the
  four-times-repeated Tailwind string across image.tsx and text.tsx.

ZPL codes (^XG, ~DY) are kept to tooltips only, never in user-visible
labels. New locale keys (storage, storeOnPrinter, storeOnPrinterHint,
storeInline, embedInZpl, embedInZplHint, removeFromCache,
removeFromCacheConfirm) cover all 32 locales.
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 "recall-only" mode for images, enabling the use of graphics already stored on a printer via the ^XG command without re-uploading them in every ZPL job. Key updates include modifications to the ZPL parser and generator to support these references, the introduction of a cross-platform PlaceholderIcon, and a new UI for managing printer storage settings like device selection and filename. The PR also adds functionality to remove images from the local cache and improves resizing behavior for placeholders. Reviewers identified a potential runtime error with crypto.randomUUID() in non-secure contexts and noted bugs where the embedInZpl state was not preserved during UI updates.

Comment thread src/lib/storagePath.ts
Comment thread src/registry/image.tsx
Comment thread src/registry/image.tsx
Device-selector and name-input both rebuilt storedAs as
{ device, name } without the existing embedInZpl flag. Silent
regression: a user setting recall-only (embedInZpl=false) and then
changing the device would silently flip back to the default (true),
re-emitting ~DY uploads the user explicitly opted out of.

Spread the existing storedAs so embedInZpl survives partial updates.
@u8array u8array merged commit 1cfe3e0 into main May 21, 2026
2 checks passed
@u8array u8array deleted the feat/dy-xg-ui branch May 24, 2026 11:04
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