Monorepo architecture

One engine, three front-ends. The logic lives in packages/core, pure TypeScript, zero DOM or Node dependencies. On top of that core sit the web app (browser), the CLI (Node) and, in the future, a desktop app (Electron wrapping the web build).

Structure

embroidery-converter/
├── packages/
│   ├── core/              ← ALL the logic. TS strict, ZERO DOM/Node deps.
│   │   ├── src/
│   │   │   ├── ir.ts                  Intermediate Representation
│   │   │   ├── embcompress.ts        Greg-Hus expand()/compress()
│   │   │   ├── readers/
│   │   │   │   ├── vip.ts            (HUS body in VIP wrapper)
│   │   │   │   ├── zhs.ts
│   │   │   │   ├── pes.ts  dst.ts  jef.ts  vp3.ts  exp.ts  ...
│   │   │   ├── writers/
│   │   │   │   ├── zhs.ts            ← the gem: no open writer has ever touched ZHS
│   │   │   │   ├── dst.ts  pes.ts  jef.ts  vp3.ts  exp.ts  hus.ts  vip.ts  xxx.ts  ...
│   │   │   ├── registry.ts           extension → reader/writer
│   │   │   └── index.ts              convert(bytes, from, to) → bytes + warnings
│   │   └── test/                      Vitest, round-trip vs ../../fixtures
│   └── cli/               ← Node CLI, uses core
│       └── src/index.ts   embconv in.vip out.zhs | batch folder
├── apps/
│   ├── web/               ← Astro. Drag-drop,Web Worker?, JSZip download.
│   │   └── src/           100% client-side.
│   └── desktop/           ← (future) Electron. Loads apps/web/build and adds native batch on top.
└── fixtures/              VIP/ZHS pairs and other ground truth

Shared IR

The Pattern is modeled on pyembroidery's EmbPattern, but in strict TypeScript. See the IR page for the exact type. It is the contract between every reader and every writer.

Stack

Porting order (what got ported first)

  1. EmbCompress expand() + VIP reader + ZHS writer + IR. → VIP→ZHS in TS, with round-trip tests against fixtures/.
  2. Readers: ZHS, PES, DST, VP3. (XXX, JEF, EXP came later.)
  3. Writers: DST, PES (easy, well documented). Then JEF, VP3, HUS, VIP, XXX.
  4. Web app: drag-drop + single download + Studio (queue + panel).
  5. Batch CLI. (Desktop is future work.)
  6. Multi-color / trim ZHS writing — only after obtaining sample .zhs files with trims (see ZHS).

Why a monorepo

Because the logic gets written exactly once. Add a format to the core and, the same day, the web app, CLI and desktop know how to use it. Updating the core means updating everything. It is the laziest, most efficient pattern for a solo maintainer.

← How it works All formats →