@richardmcquiston01/unofficial-xcs-writer 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +86 -0
- package/CHANGELOG.md +37 -0
- package/README.md +26 -88
- package/dist/index.cjs +125 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -8
- package/dist/index.d.ts +80 -8
- package/dist/index.js +124 -9
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/API.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
## `assertXcsFormat(buffer: ArrayBuffer): void`
|
|
4
|
+
|
|
5
|
+
Validates that `buffer` is a parseable XCS JSON file containing a `canvas` array. Throws a descriptive error if the file is invalid or unrecognized.
|
|
6
|
+
|
|
7
|
+
## `readXcsFile(buffer: ArrayBuffer): string`
|
|
8
|
+
|
|
9
|
+
Decodes the buffer and returns the raw JSON string. Useful for inspection or pass-through scenarios.
|
|
10
|
+
|
|
11
|
+
## `extractXcsTokens(buffer: ArrayBuffer): string[]`
|
|
12
|
+
|
|
13
|
+
Scans all `TEXT` display objects across all canvases and returns the unique token names found (without braces). For example, `{{LastName}}` in the file → `"LastName"` in the returned array.
|
|
14
|
+
|
|
15
|
+
## `renderXcsFile(buffer, variables, values, fonts?): Uint8Array`
|
|
16
|
+
|
|
17
|
+
Applies substitution to every `TEXT` display object and returns the modified file encoded as a UTF-8 `Uint8Array`. Tokens with no matching entry in `values` fall back to `variable.defaultValue`, or an empty string if no default is set.
|
|
18
|
+
|
|
19
|
+
Whenever a display's text actually changes, its glyph outline data (`charJSONs` / `fontData.glyphData`) is regenerated from a real font so xTool Studio renders the new text correctly — xTool Studio only trusts a TEXT display's stored glyph shapes; it does not re-shape text on its own unless you manually reselect the font inside the app. Displays whose text doesn't change are left completely untouched.
|
|
20
|
+
|
|
21
|
+
Pass `fonts` to supply real font file bytes (TTF/OTF/WOFF) keyed by a display's `style.fontFamily`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const arialBuffer = await fetch('/fonts/arial.ttf').then(r => r.arrayBuffer());
|
|
25
|
+
renderXcsFile(buffer, variables, values, { Arial: arialBuffer });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Any `fontFamily` not present in `fonts` falls back to a bundled default (Arimo, Apache-2.0, metric-compatible with Arial) — so the fix works out of the box with no font files required, just with shapes drawn from Arimo rather than the exact requested typeface.
|
|
29
|
+
|
|
30
|
+
**Known limitation:** `renderXcsFile` only reproduces straight horizontal-baseline layout when substituting into an *existing* TEXT display. xTool Studio's `style.curveX`/`curveY` curved-text formula is undocumented and can't be reverse-decoded from a file alone, so a curved display's substituted text gets correctly-shaped glyphs laid out straight instead of on the original curve. This doesn't apply to the *building* API below — there, you supply the curve angle yourself (see `layoutCurvedGlyphText`), so there's nothing to decode.
|
|
31
|
+
|
|
32
|
+
## `XcsVariable`
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
interface XcsVariable {
|
|
36
|
+
token: string; // token name without braces, e.g. "LastName"
|
|
37
|
+
defaultValue?: string;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## `.xs` (v2 workspace) format
|
|
42
|
+
|
|
43
|
+
## `assertXsFormat(buffer: ArrayBuffer): void`
|
|
44
|
+
|
|
45
|
+
Validates that `buffer` is a ZIP archive with a `.format` entry equal to `"v2"` and a `project.json` entry. Throws a descriptive error otherwise.
|
|
46
|
+
|
|
47
|
+
## `extractXsTokens(buffer: ArrayBuffer): string[]`
|
|
48
|
+
|
|
49
|
+
Same as `extractXcsTokens`, but scans every `canvases/<canvasId>/displays-<chunkIndex>.json` chunk across the archive instead of a single `canvas` array.
|
|
50
|
+
|
|
51
|
+
## `renderXsFile(buffer, variables, values, fonts?): Uint8Array`
|
|
52
|
+
|
|
53
|
+
Same substitution and glyph-regeneration behavior as `renderXcsFile` (see above — including the same `fonts` parameter and curved-text limitation), applied to every displays chunk. Returns a re-zipped `.xs` archive; every entry other than the modified displays chunks is passed through byte-for-byte unchanged.
|
|
54
|
+
|
|
55
|
+
## `buildXsArchive(file: XCSFile): Uint8Array`
|
|
56
|
+
|
|
57
|
+
Packages an already-`generate()`d project (see Building, below) into a `.xs` v2 ZIP archive. You won't normally call this directly — use `XCSGenerator.toXsBytes()` instead, which calls it for you.
|
|
58
|
+
|
|
59
|
+
## `loadFont(buffer: ArrayBuffer)` / `loadDefaultFont()` / `layoutText(font, text, originX, originY)`
|
|
60
|
+
|
|
61
|
+
Lower-level glyph-extraction primitives everything else in this library is built on, exported for direct use. See `src/glyphs.ts` for the exact xTool JSON conventions these were reverse-engineered against.
|
|
62
|
+
|
|
63
|
+
## Building (`src/builder.ts`, `src/layout.ts`, `src/machines.ts`)
|
|
64
|
+
|
|
65
|
+
`createXCS(deviceId?)` / `new XCSGenerator(options?)` — starts a new project (one canvas, one default layer). `deviceId` (and `options.deviceId`) accepts a `XTOOL_MACHINES` key, a `MachineProfile` object, or a raw device id string — see "Building a project from scratch" in the [README](./README.md). Chainable methods:
|
|
66
|
+
|
|
67
|
+
| Method | Adds |
|
|
68
|
+
|--------|------|
|
|
69
|
+
| `.addText(text, x, y, options?)` | A `TEXT` display. Pass `options.layout` (see below) for real glyph outlines — without it, text renders as placeholder blocks in xTool Studio. |
|
|
70
|
+
| `.addPath(pathData, x, y, width, height, options?)` | A `PATH` display from SVG path data. |
|
|
71
|
+
| `.addBitmap(pngBase64, x, y, widthMm, heightMm, originWidthPx, originHeightPx, options?)` | An embedded PNG, physically sized in mm. |
|
|
72
|
+
| `.addLayer(color, name, order)` | A named, colored layer (one `#00befe` "Cyan" layer exists by default). |
|
|
73
|
+
|
|
74
|
+
`.addText()`/`.addPath()`/`.addBitmap()` all also accept a `processing: { processingType, values }` option — see "Building a project from scratch" in the [README](./README.md); it only affects `.toXsBytes()` output.
|
|
75
|
+
|
|
76
|
+
Then `.generate()` (the plain `XCSFile` object), `.toJSON()` (string), `.toBytes()` (`Uint8Array`, matching `renderXcsFile`'s output type, as `.xcs`), or `.toXsBytes()` (`Uint8Array`, matching `renderXsFile`'s output type, as `.xs`).
|
|
77
|
+
|
|
78
|
+
Text layout — build the `layout` option for `.addText()` with one of:
|
|
79
|
+
|
|
80
|
+
| Function | Layout |
|
|
81
|
+
|----------|--------|
|
|
82
|
+
| `layoutGlyphText(font, text, emSizeMm, xMm, yMm, letterSpacingMm?)` | Single-line, straight, anchored so the ink box's top-left lands at `(xMm, yMm)`. |
|
|
83
|
+
| `layoutMultilineGlyphText(font, text, emSizeMm, letterSpacingMm, lineHeightMult, align)` | Multi-line (`\n`-separated), straight, each line aligned and vertically stacked. |
|
|
84
|
+
| `layoutCurvedGlyphText(font, text, emSizeMm, curveDeg, boxWidthMm, boxHeightMm, align?, letterSpacingMm?)` | Single-line, curved along an arc spanning `curveDeg` degrees across a `boxWidthMm`-wide box (positive arches the apex upward, negative bows it downward — same convention as SVG `<textPath>`/canvas curved-text editors). Multi-line curved text isn't supported (matches typical curved-text editors, which also flatten multi-line curved text to one line). **Not yet visually verified against real xTool Studio rendering** (there's no way to check outside the application itself) — the geometry is self-consistent and unit-tested, but treat curved output as best-effort until you've spot-checked it. |
|
|
85
|
+
|
|
86
|
+
All three return `GlyphTextLayout | null` (`null` for empty/whitespace-only text). `translateGlyphLayout(layout, dxMm, dyMm)` cheaply shifts an already-built layout without relaying out the font.
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.8.1] - 2026-09-21
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **README restructure**: removed the "Releasing" subsection (internal maintainer process,
|
|
15
|
+
not user-facing); moved the full "API" section into a new `API.md` (added to `package.json`'s
|
|
16
|
+
`files` so it ships with the npm package), with the README's "API" section now just a link to
|
|
17
|
+
it; added a disclaimer blockquote under the title stating this is an unofficial,
|
|
18
|
+
community-built library not affiliated with xTool; removed the hyperlink on "xTool Creative
|
|
19
|
+
Space" in the intro paragraph (kept as plain text); moved "Buy Me a Coffee" up to sit directly
|
|
20
|
+
above the API section instead of at the very end.
|
|
21
|
+
- **Copyright section**: added a note that xTool, xTool Studio, and xTool Creative Space are
|
|
22
|
+
registered trademarks of Makeblock Co., Ltd., and that this project isn't affiliated with,
|
|
23
|
+
endorsed by, or sponsored by Makeblock or xTool.
|
|
24
|
+
|
|
25
|
+
## [0.8.0] - 2026-09-21
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **Machine catalog** (`src/machines.ts`): `XCSGeneratorOptions.deviceId`/`createXCS`'s argument
|
|
30
|
+
now accepts a `XTOOL_MACHINES` key -- currently 13 machines verified from real exports in this
|
|
31
|
+
repo (`P2S`, `F2 Ultra UV`, `S1`, `P3`, `M2`, `F2`, `F2 Ultra (Single)`, `F2 Ultra`, `M1 Ultra`,
|
|
32
|
+
`F1`, `F1 Lite`, `F1 Ultra`, `MetalFab`) -- and resolves that machine's real `extId`/`extName`/
|
|
33
|
+
`deviceCode` (`.xs` only)/default power, instead of reusing whatever string you pass for all of
|
|
34
|
+
them. A `MachineProfile` object can also be supplied directly for a machine not yet in the
|
|
35
|
+
catalog. Existing raw device id strings still behave exactly as before (unaffected, since they
|
|
36
|
+
simply don't match a catalog key).
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `createXCS()`/`new XCSGenerator()` with no `deviceId` (defaulting to `"P2S"`) now embeds the
|
|
41
|
+
real `deviceCode: "ZY013"` in `.toXsBytes()` output, instead of the previous placeholder
|
|
42
|
+
(`file.device.id`, i.e. `"P2S"`). `.xcs` output (`.toBytes()`) is unaffected -- it has no
|
|
43
|
+
`deviceCode` field.
|
|
44
|
+
|
|
8
45
|
## [0.7.0] - 2026-09-21
|
|
9
46
|
|
|
10
47
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# unofficial-xcs-writer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Disclaimer:** This is an unofficial, community-built library. It is not affiliated with, endorsed by, or sponsored by xTool in any way.
|
|
4
|
+
|
|
5
|
+
A framework-agnostic TypeScript library for reading, writing, building, and applying variable substitution to `.xcs` and `.xs` files produced by xTool Creative Space.
|
|
4
6
|
|
|
5
7
|
## What is an XCS / XS file?
|
|
6
8
|
|
|
@@ -113,92 +115,38 @@ and undocumented beyond real exports — see `xs_samples/*.xs`'s `profiles.json`
|
|
|
113
115
|
option behave as before: a generated `.xs` file's power/speed may need to be set manually in
|
|
114
116
|
xTool Studio before cutting/engraving.
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Decodes the buffer and returns the raw JSON string. Useful for inspection or pass-through scenarios.
|
|
125
|
-
|
|
126
|
-
### `extractXcsTokens(buffer: ArrayBuffer): string[]`
|
|
127
|
-
|
|
128
|
-
Scans all `TEXT` display objects across all canvases and returns the unique token names found (without braces). For example, `{{LastName}}` in the file → `"LastName"` in the returned array.
|
|
129
|
-
|
|
130
|
-
### `renderXcsFile(buffer, variables, values, fonts?): Uint8Array`
|
|
131
|
-
|
|
132
|
-
Applies substitution to every `TEXT` display object and returns the modified file encoded as a UTF-8 `Uint8Array`. Tokens with no matching entry in `values` fall back to `variable.defaultValue`, or an empty string if no default is set.
|
|
133
|
-
|
|
134
|
-
Whenever a display's text actually changes, its glyph outline data (`charJSONs` / `fontData.glyphData`) is regenerated from a real font so xTool Studio renders the new text correctly — xTool Studio only trusts a TEXT display's stored glyph shapes; it does not re-shape text on its own unless you manually reselect the font inside the app. Displays whose text doesn't change are left completely untouched.
|
|
135
|
-
|
|
136
|
-
Pass `fonts` to supply real font file bytes (TTF/OTF/WOFF) keyed by a display's `style.fontFamily`:
|
|
118
|
+
`createXCS('P2S')`'s argument (and `XCSGeneratorOptions.deviceId`) accepts a known machine name
|
|
119
|
+
from `XTOOL_MACHINES` — currently 13 machines verified from real exports in this repo (`"P2S"`,
|
|
120
|
+
`"F2 Ultra UV"`, `"S1"`, `"P3"`, `"M2"`, `"F2"`, `"F2 Ultra (Single)"`, `"F2 Ultra"`,
|
|
121
|
+
`"M1 Ultra"`, `"F1"`, `"F1 Lite"`, `"F1 Ultra"`, `"MetalFab"`) — and resolves the real
|
|
122
|
+
`extId`/`extName`/`deviceCode` (`.xs` only)/default power for that machine, instead of reusing
|
|
123
|
+
whatever string you pass for all of them. Export the same design for multiple machines by
|
|
124
|
+
building it once and calling `.toXsBytes()`/`.toBytes()` once per `createXCS(machineName)`:
|
|
137
125
|
|
|
138
126
|
```ts
|
|
139
|
-
|
|
140
|
-
renderXcsFile(buffer, variables, values, { Arial: arialBuffer });
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Any `fontFamily` not present in `fonts` falls back to a bundled default (Arimo, Apache-2.0, metric-compatible with Arial) — so the fix works out of the box with no font files required, just with shapes drawn from Arimo rather than the exact requested typeface.
|
|
144
|
-
|
|
145
|
-
**Known limitation:** `renderXcsFile` only reproduces straight horizontal-baseline layout when substituting into an *existing* TEXT display. xTool Studio's `style.curveX`/`curveY` curved-text formula is undocumented and can't be reverse-decoded from a file alone, so a curved display's substituted text gets correctly-shaped glyphs laid out straight instead of on the original curve. This doesn't apply to the *building* API below — there, you supply the curve angle yourself (see `layoutCurvedGlyphText`), so there's nothing to decode.
|
|
146
|
-
|
|
147
|
-
### `XcsVariable`
|
|
127
|
+
import { createXCS, XTOOL_MACHINES } from '@richardmcquiston01/unofficial-xcs-writer';
|
|
148
128
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
defaultValue?: string;
|
|
129
|
+
for (const machine of Object.keys(XTOOL_MACHINES)) {
|
|
130
|
+
const bytes = createXCS(machine).addPath('M0 0L10 0L10 10L0 10Z', 0, 0, 10, 10).toXsBytes();
|
|
131
|
+
// write `bytes` to e.g. `design-${machine}.xs`
|
|
153
132
|
}
|
|
154
133
|
```
|
|
155
134
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Validates that `buffer` is a ZIP archive with a `.format` entry equal to `"v2"` and a `project.json` entry. Throws a descriptive error otherwise.
|
|
161
|
-
|
|
162
|
-
### `extractXsTokens(buffer: ArrayBuffer): string[]`
|
|
163
|
-
|
|
164
|
-
Same as `extractXcsTokens`, but scans every `canvases/<canvasId>/displays-<chunkIndex>.json` chunk across the archive instead of a single `canvas` array.
|
|
135
|
+
A device id not in the catalog still works exactly as before (no `.xs` `deviceCode`, `extId`/
|
|
136
|
+
`extName`/`device.id` all set to that string, default power `55`); you can also pass a
|
|
137
|
+
`MachineProfile` object directly for a machine not yet in the catalog.
|
|
165
138
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Same substitution and glyph-regeneration behavior as `renderXcsFile` (see above — including the same `fonts` parameter and curved-text limitation), applied to every displays chunk. Returns a re-zipped `.xs` archive; every entry other than the modified displays chunks is passed through byte-for-byte unchanged.
|
|
169
|
-
|
|
170
|
-
### `buildXsArchive(file: XCSFile): Uint8Array`
|
|
171
|
-
|
|
172
|
-
Packages an already-`generate()`d project (see Building, below) into a `.xs` v2 ZIP archive. You won't normally call this directly — use `XCSGenerator.toXsBytes()` instead, which calls it for you.
|
|
173
|
-
|
|
174
|
-
### `loadFont(buffer: ArrayBuffer)` / `loadDefaultFont()` / `layoutText(font, text, originX, originY)`
|
|
175
|
-
|
|
176
|
-
Lower-level glyph-extraction primitives everything else in this library is built on, exported for direct use. See `src/glyphs.ts` for the exact xTool JSON conventions these were reverse-engineered against.
|
|
177
|
-
|
|
178
|
-
### Building (`src/builder.ts`, `src/layout.ts`)
|
|
179
|
-
|
|
180
|
-
`createXCS(deviceId?)` / `new XCSGenerator(options?)` — starts a new project (one canvas, one default layer). Chainable methods:
|
|
181
|
-
|
|
182
|
-
| Method | Adds |
|
|
183
|
-
|--------|------|
|
|
184
|
-
| `.addText(text, x, y, options?)` | A `TEXT` display. Pass `options.layout` (see below) for real glyph outlines — without it, text renders as placeholder blocks in xTool Studio. |
|
|
185
|
-
| `.addPath(pathData, x, y, width, height, options?)` | A `PATH` display from SVG path data. |
|
|
186
|
-
| `.addBitmap(pngBase64, x, y, widthMm, heightMm, originWidthPx, originHeightPx, options?)` | An embedded PNG, physically sized in mm. |
|
|
187
|
-
| `.addLayer(color, name, order)` | A named, colored layer (one `#00befe` "Cyan" layer exists by default). |
|
|
139
|
+
## Buy Me a Coffee
|
|
188
140
|
|
|
189
|
-
|
|
141
|
+
If this app, code, or repository has helped you or someone you know, please consider donating. I appreciate any help to offset the costs of development and/or AI Credits.
|
|
190
142
|
|
|
191
|
-
|
|
143
|
+
[**Donate via Stripe**](https://donate.stripe.com/00w5kD3Gj1Xo9v7gVOcs800), or scan:
|
|
192
144
|
|
|
193
|
-
|
|
145
|
+
[](https://donate.stripe.com/00w5kD3Gj1Xo9v7gVOcs800)
|
|
194
146
|
|
|
195
|
-
|
|
196
|
-
|----------|--------|
|
|
197
|
-
| `layoutGlyphText(font, text, emSizeMm, xMm, yMm, letterSpacingMm?)` | Single-line, straight, anchored so the ink box's top-left lands at `(xMm, yMm)`. |
|
|
198
|
-
| `layoutMultilineGlyphText(font, text, emSizeMm, letterSpacingMm, lineHeightMult, align)` | Multi-line (`\n`-separated), straight, each line aligned and vertically stacked. |
|
|
199
|
-
| `layoutCurvedGlyphText(font, text, emSizeMm, curveDeg, boxWidthMm, boxHeightMm, align?, letterSpacingMm?)` | Single-line, curved along an arc spanning `curveDeg` degrees across a `boxWidthMm`-wide box (positive arches the apex upward, negative bows it downward — same convention as SVG `<textPath>`/canvas curved-text editors). Multi-line curved text isn't supported (matches typical curved-text editors, which also flatten multi-line curved text to one line). **Not yet visually verified against real xTool Studio rendering** (there's no way to check outside the application itself) — the geometry is self-consistent and unit-tested, but treat curved output as best-effort until you've spot-checked it. |
|
|
147
|
+
## API
|
|
200
148
|
|
|
201
|
-
|
|
149
|
+
See [API.md](./API.md) for the full API reference — function signatures, options, types, and important behavior notes (glyph regeneration, curved-text limitations, machine catalog usage, etc.).
|
|
202
150
|
|
|
203
151
|
## Development
|
|
204
152
|
|
|
@@ -218,14 +166,6 @@ Output files after `pnpm build`:
|
|
|
218
166
|
| `dist/index.cjs` | CommonJS |
|
|
219
167
|
| `dist/index.d.ts` | TypeScript declarations |
|
|
220
168
|
|
|
221
|
-
### Releasing
|
|
222
|
-
|
|
223
|
-
Publishing to npm is automated. Bump the version in `package.json`, add a
|
|
224
|
-
`CHANGELOG.md` entry, then create a GitHub release tagged `vX.Y.Z` — the
|
|
225
|
-
[publish workflow](.github/workflows/publish.yml) checks the tag against
|
|
226
|
-
`package.json`, rebuilds, runs the tests, and publishes with provenance.
|
|
227
|
-
It needs an `NPM_TOKEN` repository secret with publish rights.
|
|
228
|
-
|
|
229
169
|
## Notes
|
|
230
170
|
|
|
231
171
|
- All inputs and outputs use plain browser-compatible types (`ArrayBuffer`, `Uint8Array`, `string`) — no Node.js APIs, no DOM, no framework required. (`atob`/`crypto.randomUUID` are used for the bundled font and glyph IDs; both are available in browsers and Node ≥ 19.)
|
|
@@ -238,10 +178,8 @@ It needs an `NPM_TOKEN` repository secret with publish rights.
|
|
|
238
178
|
|
|
239
179
|
[MIT](LICENSE). The bundled Arimo fallback font is separately licensed under Apache-2.0 (`third_party/arimo/LICENSE`).
|
|
240
180
|
|
|
241
|
-
##
|
|
181
|
+
## Copyright
|
|
242
182
|
|
|
243
|
-
|
|
183
|
+
(c)2026 Richard McQuiston
|
|
244
184
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
[](https://donate.stripe.com/00w5kD3Gj1Xo9v7gVOcs800)
|
|
185
|
+
xTool, xTool Studio, and xTool Creative Space are registered trademarks of Makeblock Co., Ltd. This project is not affiliated with, endorsed by, or sponsored by Makeblock Co., Ltd. or xTool.
|
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
XCSGenerator: () => XCSGenerator,
|
|
34
|
+
XTOOL_MACHINES: () => XTOOL_MACHINES,
|
|
34
35
|
assertXcsFormat: () => assertXcsFormat,
|
|
35
36
|
assertXsFormat: () => assertXsFormat,
|
|
36
37
|
buildXsArchive: () => buildXsArchive,
|
|
@@ -416,7 +417,7 @@ function coverResourceEntries(coverDataUrl) {
|
|
|
416
417
|
})
|
|
417
418
|
};
|
|
418
419
|
}
|
|
419
|
-
function buildXsArchive(file, processingBindings = []) {
|
|
420
|
+
function buildXsArchive(file, processingBindings = [], deviceCode = file.device.id) {
|
|
420
421
|
const canvas = file.canvas[0];
|
|
421
422
|
if (!canvas) {
|
|
422
423
|
throw new Error("Cannot build a .xs archive from a project with no canvas.");
|
|
@@ -478,7 +479,7 @@ function buildXsArchive(file, processingBindings = []) {
|
|
|
478
479
|
}),
|
|
479
480
|
[`devices/device-${file.device.id}.json`]: encodeJson({
|
|
480
481
|
id: file.device.id,
|
|
481
|
-
deviceCode
|
|
482
|
+
deviceCode,
|
|
482
483
|
extId: file.extId,
|
|
483
484
|
extName: file.extName,
|
|
484
485
|
power: [file.device.power],
|
|
@@ -766,6 +767,101 @@ function layoutCurvedGlyphText(font, text, emSizeMm, curveDeg, boxWidthMm, boxHe
|
|
|
766
767
|
};
|
|
767
768
|
}
|
|
768
769
|
|
|
770
|
+
// src/machines.ts
|
|
771
|
+
var XTOOL_MACHINES = {
|
|
772
|
+
P2S: {
|
|
773
|
+
name: "P2S",
|
|
774
|
+
extId: "P2S",
|
|
775
|
+
extName: "P2S",
|
|
776
|
+
deviceCode: "ZY013",
|
|
777
|
+
defaultPower: 55
|
|
778
|
+
},
|
|
779
|
+
"F2 Ultra UV": {
|
|
780
|
+
name: "F2 Ultra UV",
|
|
781
|
+
extId: "GS009-CLASS-4",
|
|
782
|
+
extName: "F2 Ultra UV",
|
|
783
|
+
deviceCode: "GS009-CLASS-4",
|
|
784
|
+
defaultPower: 5
|
|
785
|
+
},
|
|
786
|
+
S1: {
|
|
787
|
+
name: "S1",
|
|
788
|
+
extId: "S1",
|
|
789
|
+
extName: "S1",
|
|
790
|
+
deviceCode: "MD2",
|
|
791
|
+
defaultPower: 40
|
|
792
|
+
},
|
|
793
|
+
P3: {
|
|
794
|
+
name: "P3",
|
|
795
|
+
extId: "P3",
|
|
796
|
+
extName: "P3",
|
|
797
|
+
deviceCode: "ZY015",
|
|
798
|
+
defaultPower: 80
|
|
799
|
+
},
|
|
800
|
+
M2: {
|
|
801
|
+
name: "M2",
|
|
802
|
+
extId: "JS002",
|
|
803
|
+
extName: "M2",
|
|
804
|
+
deviceCode: "JS002",
|
|
805
|
+
defaultPower: 10
|
|
806
|
+
},
|
|
807
|
+
F2: {
|
|
808
|
+
name: "F2",
|
|
809
|
+
extId: "GS006",
|
|
810
|
+
extName: "F2",
|
|
811
|
+
deviceCode: "GS006",
|
|
812
|
+
defaultPower: 5
|
|
813
|
+
},
|
|
814
|
+
"F2 Ultra (Single)": {
|
|
815
|
+
name: "F2 Ultra (Single)",
|
|
816
|
+
extId: "GS007-CLASS-4",
|
|
817
|
+
extName: "F2 Ultra (Single)",
|
|
818
|
+
deviceCode: "GS007-CLASS-4",
|
|
819
|
+
defaultPower: 60
|
|
820
|
+
},
|
|
821
|
+
"F2 Ultra": {
|
|
822
|
+
name: "F2 Ultra",
|
|
823
|
+
extId: "GS004-CLASS-4",
|
|
824
|
+
extName: "F2 Ultra",
|
|
825
|
+
deviceCode: "GS004-CLASS-4",
|
|
826
|
+
defaultPower: 60
|
|
827
|
+
},
|
|
828
|
+
"M1 Ultra": {
|
|
829
|
+
name: "M1 Ultra",
|
|
830
|
+
extId: "M1Ultra",
|
|
831
|
+
extName: "M1 Ultra",
|
|
832
|
+
deviceCode: "ZH009",
|
|
833
|
+
defaultPower: 20
|
|
834
|
+
},
|
|
835
|
+
F1: {
|
|
836
|
+
name: "F1",
|
|
837
|
+
extId: "F1",
|
|
838
|
+
extName: "F1",
|
|
839
|
+
deviceCode: "MF1",
|
|
840
|
+
defaultPower: 10
|
|
841
|
+
},
|
|
842
|
+
"F1 Lite": {
|
|
843
|
+
name: "F1 Lite",
|
|
844
|
+
extId: "GS005",
|
|
845
|
+
extName: "F1 Lite",
|
|
846
|
+
deviceCode: "GS005",
|
|
847
|
+
defaultPower: 10
|
|
848
|
+
},
|
|
849
|
+
"F1 Ultra": {
|
|
850
|
+
name: "F1 Ultra",
|
|
851
|
+
extId: "F1Ultra",
|
|
852
|
+
extName: "F1 Ultra",
|
|
853
|
+
deviceCode: "GS002",
|
|
854
|
+
defaultPower: 20
|
|
855
|
+
},
|
|
856
|
+
MetalFab: {
|
|
857
|
+
name: "MetalFab",
|
|
858
|
+
extId: "HJ003",
|
|
859
|
+
extName: "MetalFab CNC Cutter",
|
|
860
|
+
deviceCode: "HJ003",
|
|
861
|
+
defaultPower: 1200
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
|
|
769
865
|
// src/builder.ts
|
|
770
866
|
var XCSGenerator = class {
|
|
771
867
|
canvasId;
|
|
@@ -773,12 +869,20 @@ var XCSGenerator = class {
|
|
|
773
869
|
displays = [];
|
|
774
870
|
layers = /* @__PURE__ */ new Map();
|
|
775
871
|
options;
|
|
872
|
+
/** From `XTOOL_MACHINES`/a supplied `MachineProfile`; `deviceId` itself when the machine isn't known. */
|
|
873
|
+
extName;
|
|
874
|
+
/** `.xs`-only; unset when the machine's real `deviceCode` isn't verified (see `src/machines.ts`). */
|
|
875
|
+
deviceCode;
|
|
776
876
|
/** Keyed by `JSON.stringify([processingType, values])` to merge displays sharing identical settings into one profile. */
|
|
777
877
|
processingBindings = /* @__PURE__ */ new Map();
|
|
778
878
|
constructor(options = {}) {
|
|
879
|
+
const machine = this.resolveMachine(options.deviceId);
|
|
880
|
+
const deviceId = machine?.extId ?? (typeof options.deviceId === "string" ? options.deviceId : "P2S");
|
|
881
|
+
this.extName = machine?.extName ?? deviceId;
|
|
882
|
+
this.deviceCode = machine?.deviceCode;
|
|
779
883
|
this.options = {
|
|
780
|
-
deviceId
|
|
781
|
-
devicePower: options.devicePower || 55,
|
|
884
|
+
deviceId,
|
|
885
|
+
devicePower: options.devicePower || machine?.defaultPower || 55,
|
|
782
886
|
canvasWidth: options.canvasWidth || 400,
|
|
783
887
|
canvasHeight: options.canvasHeight || 400
|
|
784
888
|
};
|
|
@@ -786,6 +890,11 @@ var XCSGenerator = class {
|
|
|
786
890
|
this.canvas = this.createCanvas();
|
|
787
891
|
this.addLayer("#00befe", "{Cyan}", 1);
|
|
788
892
|
}
|
|
893
|
+
/** Looks `deviceId` up in `XTOOL_MACHINES` by name, passes a supplied `MachineProfile` through, or returns `undefined` for a raw/unknown device id (including the "no option given" default of `"P2S"`). */
|
|
894
|
+
resolveMachine(deviceId) {
|
|
895
|
+
if (typeof deviceId === "object") return deviceId;
|
|
896
|
+
return XTOOL_MACHINES[deviceId ?? "P2S"];
|
|
897
|
+
}
|
|
789
898
|
/**
|
|
790
899
|
* Add a text object to the canvas. Pass `layout` (built via
|
|
791
900
|
* `layoutGlyphText`/`layoutMultilineGlyphText`/`layoutCurvedGlyphText`
|
|
@@ -914,7 +1023,7 @@ var XCSGenerator = class {
|
|
|
914
1023
|
canvasId: this.canvasId,
|
|
915
1024
|
canvas: [this.canvas],
|
|
916
1025
|
extId: this.options.deviceId,
|
|
917
|
-
extName: this.
|
|
1026
|
+
extName: this.extName,
|
|
918
1027
|
device: {
|
|
919
1028
|
id: this.options.deviceId,
|
|
920
1029
|
power: this.options.devicePower,
|
|
@@ -952,13 +1061,19 @@ var XCSGenerator = class {
|
|
|
952
1061
|
}
|
|
953
1062
|
/**
|
|
954
1063
|
* Export as a `.xs` (v2 workspace) ZIP archive instead of `.xcs` --
|
|
955
|
-
* see `buildXsArchive` in `src/xs.ts` for the container format
|
|
956
|
-
* its limitations (no processing profiles/device bindings yet).
|
|
1064
|
+
* see `buildXsArchive` in `src/xs.ts` for the container format.
|
|
957
1065
|
* Display objects themselves carry over unchanged; only the
|
|
958
|
-
* container differs from `toBytes()`.
|
|
1066
|
+
* container differs from `toBytes()`. `deviceCode` comes from
|
|
1067
|
+
* `XTOOL_MACHINES`/a supplied `MachineProfile` (see
|
|
1068
|
+
* `XCSGeneratorOptions.deviceId`) when known, else `buildXsArchive`
|
|
1069
|
+
* falls back to the device id itself, as before.
|
|
959
1070
|
*/
|
|
960
1071
|
toXsBytes() {
|
|
961
|
-
return buildXsArchive(
|
|
1072
|
+
return buildXsArchive(
|
|
1073
|
+
this.generate(),
|
|
1074
|
+
Array.from(this.processingBindings.values()),
|
|
1075
|
+
this.deviceCode
|
|
1076
|
+
);
|
|
962
1077
|
}
|
|
963
1078
|
/** Merges a display into an existing profile when another display already shares its exact `processing` settings, else starts a new one. */
|
|
964
1079
|
registerProcessing(displayId, processing) {
|
|
@@ -1208,6 +1323,7 @@ function createXCS(deviceId = "P2S") {
|
|
|
1208
1323
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1209
1324
|
0 && (module.exports = {
|
|
1210
1325
|
XCSGenerator,
|
|
1326
|
+
XTOOL_MACHINES,
|
|
1211
1327
|
assertXcsFormat,
|
|
1212
1328
|
assertXsFormat,
|
|
1213
1329
|
buildXsArchive,
|