@liiift-studio/vf-clamp-cli 0.1.1 → 0.2.2

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/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  A command-line interface for [`@liiift-studio/vf-clamp`](https://www.npmjs.com/package/@liiift-studio/vf-clamp) — restrict variable font axis ranges from the terminal without writing any JavaScript.
4
4
 
5
+ ![vf-clamp CLI: inspecting a variable font's axes and named instances, then clamping Regular–Bold to a Text WOFF2](https://raw.githubusercontent.com/Liiift-Studio/vf-clamp-cli/main/assets/demo.gif?v=1)
6
+
7
+ ## Try it live
8
+
9
+ Want to see what axis-clamping does before installing anything? **[vfclamp.com](https://vfclamp.com)** is an interactive web demo of the same axis-constraint engine that powers this CLI — upload a variable font, pick named instances or set axis ranges in the browser, and download the restricted result. The CLI runs the identical engine locally and headless for scripting and batch workflows.
10
+
5
11
  ## Install
6
12
 
7
13
  Requires **Node.js 18 or newer**.
@@ -130,15 +130,81 @@ async function runClamp(fontPath, opts) {
130
130
  }));
131
131
  const written = await writeOutputs(writeInputs, outputDir, { force: opts.force !== false });
132
132
  if (opts.json) {
133
- process.stdout.write(JSON.stringify({ written }) + '\n');
133
+ // v0.2.x: include per-output structural diagnostics when --json is set
134
+ // so machine consumers (build pipelines, batch scripts) can read the
135
+ // same instance / axis / pinned / size summary the Glyphs and
136
+ // RoboFont plugins show in their size-estimate strip.
137
+ const diagnostics = writeInputs.map((result, i) => {
138
+ const req = outputRequests[i];
139
+ return {
140
+ path: written[i],
141
+ ...summariseRequest(req, result.buffer),
142
+ };
143
+ });
144
+ process.stdout.write(JSON.stringify({ written, diagnostics }) + '\n');
134
145
  }
135
146
  else {
136
- for (const filePath of written) {
147
+ for (let i = 0; i < written.length; i++) {
148
+ const filePath = written[i];
137
149
  // Bare paths on stdout so consumers can `... | xargs` them.
138
150
  process.stdout.write(`${filePath}\n`);
151
+ if (opts.verbose) {
152
+ // Diagnostic line mirrors the Glyphs+RoboFont dialog's size
153
+ // estimate: `~38 KB · 5 instances · 3 ax · 1 pinned`. Goes to
154
+ // stderr so it doesn't pollute the xargs-friendly stdout.
155
+ const req = outputRequests[i];
156
+ const result = writeInputs[i];
157
+ if (req && result) {
158
+ process.stderr.write(` ${formatSummary(summariseRequest(req, result.buffer))}\n`);
159
+ }
160
+ }
139
161
  }
140
162
  }
141
163
  }
164
+ /**
165
+ * Compute the structural-summary fields shown in --verbose / --json output.
166
+ * Mirrors the Glyphs and RoboFont dialogs' `_count_structural` helper:
167
+ * number of licensed instances, total axes the user touched, number of
168
+ * those axes pinned to a single value, and the resulting file size.
169
+ *
170
+ * Master count is intentionally omitted — the engine doesn't expose
171
+ * post-instancer master geometry through its public API, and computing
172
+ * it from the OUTPUT buffer would require re-parsing TTFont in JS.
173
+ */
174
+ function summariseRequest(req, buffer) {
175
+ const instances = req?.instances?.length ?? 0;
176
+ const axesObj = req?.axes ?? {};
177
+ const axes = Object.keys(axesObj).length;
178
+ let pinned = 0;
179
+ for (const value of Object.values(axesObj)) {
180
+ // Numbers in the axes map mean "pin to this value"; objects with
181
+ // equal min/max are also effectively pinned.
182
+ if (typeof value === 'number') {
183
+ pinned++;
184
+ }
185
+ else if (value && typeof value === 'object'
186
+ && 'min' in value && 'max' in value
187
+ && value.min
188
+ === value.max) {
189
+ pinned++;
190
+ }
191
+ }
192
+ return { sizeBytes: buffer.byteLength, instances, axes, pinned };
193
+ }
194
+ /** Human-readable form of summariseRequest's output. */
195
+ function formatSummary(s) {
196
+ const parts = [];
197
+ const sizeKB = Math.round(s.sizeBytes / 1024);
198
+ parts.push(`~${sizeKB.toLocaleString()} KB`);
199
+ if (s.instances > 0) {
200
+ parts.push(`${s.instances} instance${s.instances !== 1 ? 's' : ''}`);
201
+ }
202
+ if (s.axes > 0) {
203
+ const pinnedSuffix = s.pinned > 0 ? ` · ${s.pinned} pinned` : '';
204
+ parts.push(`${s.axes} ax${pinnedSuffix}`);
205
+ }
206
+ return parts.join(' · ');
207
+ }
142
208
  /** Commander value collector — appends each repeated flag value into an array. */
143
209
  function collect(value, previous) {
144
210
  return [...previous, value];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/vf-clamp-cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.2",
4
4
  "description": "CLI for @liiift-studio/vf-clamp — restrict variable font axis ranges from the command line",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "prepublishOnly": "npm test && npm run build"
24
24
  },
25
25
  "dependencies": {
26
- "@liiift-studio/vf-clamp": "^2.0.1",
26
+ "@liiift-studio/vf-clamp": "^2.1.4",
27
27
  "commander": "^12.0.0"
28
28
  },
29
29
  "devDependencies": {