@openpresentation/cli 0.1.1 → 0.3.0
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 +6 -11
- package/dist/index.js +78 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,23 +4,18 @@ A local CLI for agents and people working with `.opf.json` presentations. Create
|
|
|
4
4
|
|
|
5
5
|
The CLI bundles its OPF schema, catalogs, and validator. It needs no separate core package, API key, or network connection at runtime. `opf --version` reports the CLI and bundled core versions. It does not render slides; successful validation is not visual verification.
|
|
6
6
|
|
|
7
|
-
## Install
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The CLI is published on npm. Install the current release:
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
|
|
13
|
-
pnpm test:cli:packed
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
That creates `artifacts/cli/openpresentation-cli-0.1.0.tgz`. Install it globally using its absolute path:
|
|
17
|
-
|
|
18
|
-
```sh
|
|
19
|
-
npm install -g /absolute/path/to/opf/artifacts/cli/openpresentation-cli-0.1.0.tgz
|
|
12
|
+
npm install -g @openpresentation/cli
|
|
20
13
|
opf --version
|
|
21
14
|
```
|
|
22
15
|
|
|
23
|
-
Or install
|
|
16
|
+
Or install it as a development dependency and use `npx --no-install opf`. CLI 0.2.0 bundles OPF 0.5.0 and supports rich table cells and headers during validation and pagination.
|
|
17
|
+
|
|
18
|
+
To verify the standalone package from source, run `pnpm install` and `pnpm test:cli:packed`. This creates `artifacts/cli/openpresentation-cli-0.2.0.tgz`, which can be installed using its absolute path. For source development, run `pnpm --filter @openpresentation/cli build` and `node packages/cli/dist/index.js --help`.
|
|
24
19
|
|
|
25
20
|
## Create and validate
|
|
26
21
|
|
package/dist/index.js
CHANGED
|
@@ -8163,7 +8163,7 @@ function createDataContent(input, options) {
|
|
|
8163
8163
|
return { chart: { type: options.chartType ?? "column", data: { columns: names, rows: rows2 } } };
|
|
8164
8164
|
}
|
|
8165
8165
|
|
|
8166
|
-
// ../javascript/dist/chunk-
|
|
8166
|
+
// ../javascript/dist/chunk-C7GKRLI6.js
|
|
8167
8167
|
var MAX_COMPOSITION_DEPTH = 32;
|
|
8168
8168
|
function resolveFontFamilies(input) {
|
|
8169
8169
|
const scheme = record(input);
|
|
@@ -8325,6 +8325,16 @@ function richTextLayouter(input, box, requestedSize, options) {
|
|
|
8325
8325
|
y += height;
|
|
8326
8326
|
return line;
|
|
8327
8327
|
});
|
|
8328
|
+
if (options.uniformLineHeight) {
|
|
8329
|
+
const height = Math.max(...richLines.map((line) => line.height));
|
|
8330
|
+
y = 0;
|
|
8331
|
+
for (const line of richLines) {
|
|
8332
|
+
line.baseline += y - line.y;
|
|
8333
|
+
line.y = y;
|
|
8334
|
+
line.height = height;
|
|
8335
|
+
y += height;
|
|
8336
|
+
}
|
|
8337
|
+
}
|
|
8328
8338
|
return { lines: ranges.map((range) => whole.slice(range.start, range.end)), fontSize, lineHeight: Math.max(fontSize * 1.22, ...richLines.map((line) => line.height)), richLines, height: y, overflow: y > box.height + 0.01 || richLines.some((line) => line.width > box.width + 0.01) };
|
|
8329
8339
|
};
|
|
8330
8340
|
return layout2;
|
|
@@ -8399,13 +8409,48 @@ ${flatten(entry.description)}` : ""}`;
|
|
|
8399
8409
|
if (field === "quote") return flatten(record(value).text ?? value);
|
|
8400
8410
|
return void 0;
|
|
8401
8411
|
}
|
|
8402
|
-
function
|
|
8403
|
-
const
|
|
8404
|
-
|
|
8405
|
-
const
|
|
8406
|
-
const
|
|
8407
|
-
const
|
|
8408
|
-
|
|
8412
|
+
function layoutTable(value, box, options = {}) {
|
|
8413
|
+
const scale = options.scale ?? 1, requested = 15 * scale, minimum = (options.minFontSize ?? 16) * scale;
|
|
8414
|
+
if (![box.x, box.y, box.width, box.height, scale, minimum].every(Number.isFinite) || box.width <= 0 || box.height <= 0 || scale <= 0 || minimum <= 0) throw new RangeError("Table dimensions, scale and font sizes must be finite and positive.");
|
|
8415
|
+
const table = record(value), headers = Array.isArray(table.columns) && table.columns.length > 0;
|
|
8416
|
+
const values = [...headers ? [table.columns] : [], ...Array.isArray(table.rows) ? table.rows : []];
|
|
8417
|
+
const columnCount = Math.max(1, ...values.map((row) => row.length)), cellWidth = box.width / columnCount;
|
|
8418
|
+
const width = Math.max(scale, cellWidth - 20 * scale), padding = 12 * scale;
|
|
8419
|
+
const fitCell = (cell2, height, min) => {
|
|
8420
|
+
const textBox = { width, height: Math.max(scale, height) };
|
|
8421
|
+
return Array.isArray(cell2.value) ? fitRichText(cell2.value, textBox, requested, min, { style: cell2.style, textMeasurement: options.textMeasurement, uniformLineHeight: true }) : fitText(flatten(cell2.value), textBox, requested, min, textWidthMeasurer(resolveTextStyle(cell2.style, options.textMeasurement), options.textMeasurement));
|
|
8422
|
+
};
|
|
8423
|
+
const textHeight = (fit) => "height" in fit ? fit.height : fit.lines.length * fit.lineHeight;
|
|
8424
|
+
const measured = values.map((row, index2) => {
|
|
8425
|
+
const header = headers && index2 === 0;
|
|
8426
|
+
const cells = Array.from({ length: columnCount }, (_, column) => {
|
|
8427
|
+
const path2 = `${options.path ?? "table"}.${header ? "columns" : `rows.${index2 - Number(headers)}`}.${column}`;
|
|
8428
|
+
return { value: row[column], path: path2, header, style: { fontFamily: options.fontFamily ?? "sans-serif", fontWeight: header ? 700 : 400, italic: false, path: path2 } };
|
|
8429
|
+
});
|
|
8430
|
+
const natural2 = Math.max(...cells.map((cell2) => textHeight(fitCell(cell2, scale, requested)) + padding));
|
|
8431
|
+
const preferred2 = Math.max(54 * scale, natural2);
|
|
8432
|
+
const needed2 = Math.min(preferred2, Math.max(...cells.map((cell2) => textHeight(fitCell(cell2, scale, minimum)) + padding)));
|
|
8433
|
+
return { cells, preferred: preferred2, natural: natural2, needed: needed2 };
|
|
8434
|
+
});
|
|
8435
|
+
const preferred = measured.reduce((sum, row) => sum + row.preferred, 0), natural = measured.reduce((sum, row) => sum + row.natural, 0), needed = measured.reduce((sum, row) => sum + row.needed, 0);
|
|
8436
|
+
let y = box.y, overflow = cellWidth <= 20 * scale;
|
|
8437
|
+
const rows2 = measured.map((row) => {
|
|
8438
|
+
const height = preferred <= box.height ? row.preferred : natural <= box.height ? row.natural + (row.preferred - row.natural) * (box.height - natural) / Math.max(Number.EPSILON, preferred - natural) : needed <= box.height ? row.needed + (row.natural - row.needed) * (box.height - needed) / Math.max(Number.EPSILON, natural - needed) : row.needed * box.height / needed;
|
|
8439
|
+
const rowBox = { x: box.x, y, width: box.width, height };
|
|
8440
|
+
const cells = row.cells.map((cell2, column) => {
|
|
8441
|
+
const cellBox = { x: box.x + column * cellWidth, y, width: cellWidth, height };
|
|
8442
|
+
const textBox = { x: cellBox.x + 10 * scale, y: y + 8 * scale, width, height: Math.max(scale, height - padding) };
|
|
8443
|
+
const fit = fitCell(cell2, textBox.height, minimum);
|
|
8444
|
+
overflow ||= height <= padding || fit.overflow;
|
|
8445
|
+
return { value: cell2.value, path: cell2.path, header: cell2.header, rich: Array.isArray(cell2.value), box: cellBox, textBox, textStyle: resolveTextStyle(cell2.style, options.textMeasurement), fit };
|
|
8446
|
+
});
|
|
8447
|
+
y += height;
|
|
8448
|
+
return { box: rowBox, cells };
|
|
8449
|
+
});
|
|
8450
|
+
return { rows: rows2, columnCount, height: y - box.y, overflow };
|
|
8451
|
+
}
|
|
8452
|
+
function tableOverflows(value, box, scale, settings, options, path2) {
|
|
8453
|
+
return box.width <= 0 || box.height <= 0 || layoutTable(value, box, { scale, minFontSize: settings.minFontSize, fontFamily: options.fonts?.body, textMeasurement: options.textMeasurement, path: path2 }).overflow;
|
|
8409
8454
|
}
|
|
8410
8455
|
function flatten(value) {
|
|
8411
8456
|
if (value == null) return "";
|
|
@@ -8534,7 +8579,7 @@ function composeSlide(input, options = {}) {
|
|
|
8534
8579
|
const fit = fitContent(node.field, node.value, text, box, 25 * scale, (settings.minFontSize ?? 16) * scale, node.path);
|
|
8535
8580
|
score += (25 * scale - fit.fontSize) / scale + (fit.overflow ? 1e3 : 0);
|
|
8536
8581
|
}
|
|
8537
|
-
if (node.field === "table" && tableOverflows(node.value, box, scale, settings, options)) score += 1e3;
|
|
8582
|
+
if (node.field === "table" && tableOverflows(node.value, box, scale, settings, options, node.path)) score += 1e3;
|
|
8538
8583
|
if (box.width < 100 * scale || box.height < 60 * scale) score += 100;
|
|
8539
8584
|
return score;
|
|
8540
8585
|
};
|
|
@@ -8589,7 +8634,7 @@ function composeSlide(input, options = {}) {
|
|
|
8589
8634
|
arrange(pending, contentBox, rootSettings, composition.mode ? 0 : placeholders.length);
|
|
8590
8635
|
for (const item of items) {
|
|
8591
8636
|
for (const key of ["x", "y", "width", "height"]) item.box[key] = round(item.box[key]);
|
|
8592
|
-
if (item.field === "table" && tableOverflows(item.value, item.box, scale, item.composition, options)) diagnostics.push({ code: "text-overflow", path: item.path, message: "Table cells do not fit; use fewer rows, fewer columns, or split the table across slides." });
|
|
8637
|
+
if (item.field === "table" && tableOverflows(item.value, item.box, scale, item.composition, options, item.path)) diagnostics.push({ code: "text-overflow", path: item.path, message: "Table cells do not fit; use fewer rows, fewer columns, or split the table across slides." });
|
|
8593
8638
|
if (item.text?.overflow) diagnostics.push({ code: "text-overflow", path: item.path, message: "Text exceeds its cell at the minimum font size; shorten it, increase its space, or split the slide." });
|
|
8594
8639
|
}
|
|
8595
8640
|
for (const group of groups) for (const box of [group.box, group.contentBox]) for (const key of ["x", "y", "width", "height"]) box[key] = round(box[key]);
|
|
@@ -8616,7 +8661,7 @@ function resolveCanvasDimensions(input) {
|
|
|
8616
8661
|
return { width, height };
|
|
8617
8662
|
}
|
|
8618
8663
|
|
|
8619
|
-
// ../javascript/dist/chunk-
|
|
8664
|
+
// ../javascript/dist/chunk-KSN6RQG2.js
|
|
8620
8665
|
var presentation = {
|
|
8621
8666
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
8622
8667
|
"$id": "https://openpresentation.org/schema/opf/v1",
|
|
@@ -12297,9 +12342,19 @@ Resolution order: inline catalogs.fontSchemes.records[] -> catalogs.fontSchemes.
|
|
|
12297
12342
|
"columns": {
|
|
12298
12343
|
"type": "array",
|
|
12299
12344
|
"items": {
|
|
12300
|
-
"
|
|
12345
|
+
"oneOf": [
|
|
12346
|
+
{
|
|
12347
|
+
"type": "string"
|
|
12348
|
+
},
|
|
12349
|
+
{
|
|
12350
|
+
"type": "array",
|
|
12351
|
+
"items": {
|
|
12352
|
+
"$ref": "#/$defs/TextRun"
|
|
12353
|
+
}
|
|
12354
|
+
}
|
|
12355
|
+
]
|
|
12301
12356
|
},
|
|
12302
|
-
"description": "Optional column labels rendered above table rows.",
|
|
12357
|
+
"description": "Optional column labels rendered above table rows. Each label is a string or an array of TextRun values for inline formatting.",
|
|
12303
12358
|
"examples": [
|
|
12304
12359
|
[
|
|
12305
12360
|
"Region",
|
|
@@ -12482,9 +12537,15 @@ Resolution order: inline catalogs.fontSchemes.records[] -> catalogs.fontSchemes.
|
|
|
12482
12537
|
},
|
|
12483
12538
|
{
|
|
12484
12539
|
"type": "null"
|
|
12540
|
+
},
|
|
12541
|
+
{
|
|
12542
|
+
"type": "array",
|
|
12543
|
+
"items": {
|
|
12544
|
+
"$ref": "#/$defs/TextRun"
|
|
12545
|
+
}
|
|
12485
12546
|
}
|
|
12486
12547
|
],
|
|
12487
|
-
"description": "A cell in table content."
|
|
12548
|
+
"description": "A cell in table content. Scalar values keep their text representation; TextRun arrays preserve inline formatting and use mixed-style text measurement."
|
|
12488
12549
|
},
|
|
12489
12550
|
"Catalogs": {
|
|
12490
12551
|
"type": "object",
|
|
@@ -33678,7 +33739,7 @@ var catalogEntries = [
|
|
|
33678
33739
|
], records: fontSchemes, index: catalogIndexes.fontSchemes }
|
|
33679
33740
|
];
|
|
33680
33741
|
|
|
33681
|
-
// ../javascript/dist/chunk-
|
|
33742
|
+
// ../javascript/dist/chunk-23MXJL2K.js
|
|
33682
33743
|
var import__ = __toESM(require__(), 1);
|
|
33683
33744
|
var import_ajv_formats = __toESM(require_dist(), 1);
|
|
33684
33745
|
var catalogIds = {
|
|
@@ -34672,7 +34733,7 @@ function assertValidPresentation(value) {
|
|
|
34672
34733
|
assertValid(value, "presentation");
|
|
34673
34734
|
}
|
|
34674
34735
|
|
|
34675
|
-
// ../javascript/dist/chunk-
|
|
34736
|
+
// ../javascript/dist/chunk-C6U7LLBT.js
|
|
34676
34737
|
var OPFPaginationError = class extends Error {
|
|
34677
34738
|
constructor(message, diagnostics = []) {
|
|
34678
34739
|
super(message);
|
|
@@ -35112,7 +35173,7 @@ async function main(argv) {
|
|
|
35112
35173
|
return;
|
|
35113
35174
|
}
|
|
35114
35175
|
if (argv.length === 1 && argv[0] === "--version") {
|
|
35115
|
-
print({ cli: "0.
|
|
35176
|
+
print({ cli: "0.3.0", opf: "0.6.0" });
|
|
35116
35177
|
return;
|
|
35117
35178
|
}
|
|
35118
35179
|
const [command, ...args] = argv;
|