Feat/variables phase2#88
Merged
Merged
Conversation
…e 2 step 2a) First slice of CSV support. No mapping UI yet (that's 2b); imported data lands in the store as csvDataset (transient, not persisted) and shows up as a small badge in the Variables tab. - pnpm add papaparse @types/papaparse - src/lib/csvImport.ts: parseCsvFile (reads file.text() first to sidestep PapaParse's jsdom-incompatible FileReader streaming) + parseCsvText helper. Pads ragged rows, auto-detects delimiter, surfaces filename / encoding / delimiter in source metadata. - src/lib/csvImport.test.ts: 7 tests covering happy path, ragged rows, quoted values, semicolon delimiter, empty / header-only files. - store: CsvDataset interface, csvDataset state, loadCsv + setActiveRow actions. NOT in persist partialize (would bloat localStorage and leak data); design.json mapping persistence is Phase 2b. - src/hooks/useCsvImportActions.ts: file-picker hook mirroring useDesignFileActions shape. - AppShell: TableCellsIcon, 'Import CSV data' menu entry, hidden file input, csvError piped into the existing notice banner. - VariablesPanel: 'CSV: filename.csv (N rows)' badge with clear button when a dataset is loaded. Locale keys deferred to end-of-branch sweep.
…ptions + UX iterations User opens the mapping modal from the CSV badge or via auto-open after import. The modal holds the full editing state in a draft, allowing atomic Apply / Cancel: - Per-variable column dropdowns, inline rename with empty/duplicate name validation - Inline 'Add variable' so missing slots can be filled without leaving the modal - 'will be created' marker on draft-only rows; X-discard button to drop them - CSV options block (delimiter, hasHeaderRow, skipRows, encoding), collapsed by default; live re-parse from cached raw bytes on any option change so the user sees the impact immediately - Encoding override: UTF-8 / Windows-1252 (ANSI) / ISO-8859-1 / UTF-16 LE via TextDecoder, re-decoding cached bytes per choice - Active-row stepper in the modal footer for preview navigation - Scrollable variable list with bound height CSV badge in the Variables panel: filename + row count + mapping completeness + cog (configure) + discard. Discard goes through a ConfirmDialog. Layout polished across multiple iterations to fit the narrow side panel without losing scanability. Persistence: mapping (bindings + headerSnapshot + parseOptions) round-trips with the design via design.json. Raw CSV bytes stay in a module-scope cache; the rows themselves don't persist.
…isibility, re-import confirm Three intertwined capabilities so the user can actually see what their CSV mapping does and trust it: 1. Active-row canvas substitution. Bound variables render the active CSV row's cell value on the canvas; ZPL output stays template form (^FN/^FV) so the print artifact is unchanged. Row stepper in the CSV badge for live navigation. Empty cells render as empty strings (no fallback to defaultValue) so a deliberate blank stays visible. 2. Preview/Schema render-mode toggle. Sits in the Variables-panel header, surfaces whenever any variable exists (independent of CSV state). Preview shows the resolved value; Schema renders «variableName» placeholders matching the InDesign Data Merge / Word Mail Merge idiom. canvasSettings.csvRenderMode persists with v3→v4 store migration. 3. Universal variable-source badge. TableCellsIcon for `csv`, ExclamationTriangleIcon (amber) for `orphan` mapping, MinusIcon (muted) for `default`. Same component renders in the Variables-panel rows and mapping-modal rows; tooltip names the bound header. Complemented on the canvas by a 12% opacity amber bbox tint behind fields rendering fallback content in preview mode (shapes with explicit width/height — text, box, ellipse, image, qrcode, datamatrix; barcode width is content-derived so tint silently skips, badge in the panel still covers it). Pure helpers in lib/variableBinding.ts: `getVariableSource`, `buildActiveCsvRow`, `resolveVariableValue` with mode and `isMappingCompatibleWith`. Layer-clean: lib does not depend on store; KonvaObject assembles inputs from store state. Re-import confirmation. Picking a new CSV while one is loaded now surfaces a dialog instead of silently overwriting. Compatibility against the saved mapping decides the shape: same headers (order- independent set comparison, or column-count match in headerless mode) → single Replace, mapping carries over; different → Cancel / Discard mapping / Keep & remap. Mapping persists the parseOptions it was last applied with (delimiter, hasHeaderRow, skipRows, encoding), used as defaults on re-import so a headerless or windows-1252 dataset is not re-parsed under defaults and falsely flagged as different. Tests: 19 new in variableBinding.test.ts (resolveVariableValue, buildActiveCsvRow, applyBindingToObject, getVariableSource) + 7 in Variable.test.ts (isMappingCompatibleWith). 1083 total.
… preview Three CSV-aware emit paths now line up with three distinct outputs: 1. Direct print (Send to Zebra) - sends template + ^XFR per-row merge blocks via the idiomatic ZPL data-merge protocol (^DFR:LBL.ZPL once, then N small ^XA^XFR:LBL.ZPL^FN…^FD…^XZ blocks). Volatile R: storage so the stored form drops on power-cycle, which matches a single-run batch. 2. File menu "Export batch ZPL (N labels)" - same batch output, downloaded as label-batch.zpl. Only surfaces when a CSV with at least one mapped variable is loaded. 3. Labelary preview (header Print button + canvas preview overlay) now substitutes the active CSV row (or defaults when no row) so the rendered image shows what would print for the selected row instead of empty ^FN slots. Emits flat ZPL (no ^FN) by pre- applying bindings to the object tree. The existing "Export ZPL" (template-form, round-trip-able) is unchanged. ZPL output panel stays template form too - that's the source-of-truth for editor round-trip. Pure helper `applyBindingToTree` recurses into groups so the substitution covers grouped fields, not just top-level leaves. `generateBatchZpl` takes narrow object shapes so it stays lib-pure (no store dependency). Five tests for generateBatchZpl cover template-stored-once, per-row recall blocks, unmapped/orphan skip, empty-cell payload, and zero-row template-only output. Four tests for applyBindingToTree cover leaves, group recursion, CSV row substitution, and schema-mode «name» replacement.
Bug fixes from multi-agent review: - generateBatchZpl: ^DFR injection now matches the first ^XA regardless of ~DY/~SD preamble (was start-anchored, silently failing on font-embed / instant-darkness designs and producing blank batches) - Per-row ^FD payload routed through fdField so CSV cells containing ^ or ~ don't terminate the field early - Export-batch menu entry disabled when canvas is empty (used to emit N empty ^XA...^XZ blocks) - Source-state badge now shows the bound column name inline; the icon-only variant was lost next to the bindings-count text - Mapping modal flags duplicate-column mappings with an inline amber warning + border - Row name validation clears stale errors on input change instead of lingering until the next blur - Saved-mapping warning surfaces when csvMapping persists but no csvDataset is loaded (reload, Discard CSV, Open Design) - Auto-suggest scoped to variables present at modal-open so inline-added drafts stay unbound until the user picks Architectural cleanup: - buildPreviewZpl extracted from enterPreviewMode and printLabel (deduped substitute-then-emit recipe) - shouldShowFallbackTint extracted from KonvaObject to lib so the rule is testable without Konva - New atomic applyMappingDraft store action collapses the modal's Apply from four separate set() calls into one (single zundo step) - useCsvImportActions reads store state after file.arrayBuffer() await instead of before, fixing the discard-during-IO race - Mapping modal gained a Sample column showing the active row's cell for each bound variable (industry-standard for data-merge tools) i18n (Phase 2e): 65 new keys (2 in t.app.*, 63 in t.variables.*) inserted into all 32 locale files via scripts/add_locale_key.local.py. All components (mapping modal, confirm dialog, source badge, variables panel, file menu entries) now consume tv.csv* keys. Quality tier high for Western European + several others; best- effort for CJK/RTL/Baltic/Balkan groups, marked for native review.
There was a problem hiding this comment.
Code Review
This pull request introduces CSV batch printing functionality, allowing users to import CSV files, map columns to variables, and export or print labels for each row. Key changes include the addition of the papaparse library, a new VariableMappingModal for configuring mappings, and logic to handle batch ZPL generation using printer-side templates (^DFR and ^XFR). I have identified a logic issue in the VariableMappingModal where the error state for adding variables is incorrectly handled due to React's asynchronous state updates, and a potential robustness issue with the regex used for ZPL template injection.
PR review: setAddError read 'failed' from a closure mutated inside the setDraftVariables updater. Works today because React generally runs updaters synchronously, but breaks in StrictMode (updater runs twice) and may break under concurrent rendering (updater deferred). Switched to an eligibility check against the current snapshot before the updater; the updater keeps its prev-based re-check so rapid clicks within one batch still avoid slot collisions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.